2021-02-23 19:06:39 -05:00
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
2023-02-02 17:15:17 -08:00
2021-02-23 19:06:39 -05:00
package accountscmd
import (
2022-08-05 16:08:48 -04:00
"errors"
2021-02-23 19:06:39 -05:00
"fmt"
"os"
"strings"
"time"
"github.com/hashicorp/boundary/api"
"github.com/hashicorp/boundary/api/accounts"
"github.com/hashicorp/boundary/internal/cmd/base"
2022-08-05 13:21:46 -04:00
"github.com/hashicorp/go-secure-stdlib/parseutil"
2021-07-14 17:38:52 -07:00
"github.com/hashicorp/go-secure-stdlib/password"
2021-07-14 17:37:40 -07:00
"github.com/hashicorp/go-secure-stdlib/strutil"
2021-02-23 19:06:39 -05:00
)
func init () {
extraActionsFlagsMapFunc = extraActionsFlagsMapFuncImpl
extraSynopsisFunc = extraSynopsisFuncImpl
extraFlagsFunc = extraFlagsFuncImpl
extraFlagsHandlingFunc = extraFlagsHandlingFuncImpl
executeExtraActions = executeExtraActionsImpl
}
type extraCmdVars struct {
flagPassword string
flagCurrentPassword string
flagNewPassword string
}
func extraActionsFlagsMapFuncImpl () map [ string ][] string {
return map [ string ][] string {
"change-password" : { "id" , "current-password" , "new-password" , "version" },
"set-password" : { "id" , "password" , "version" },
}
}
func extraSynopsisFuncImpl ( c * Command ) string {
switch c . Func {
case "change-password" :
2021-04-12 08:49:22 -07:00
return "Change the password on an account"
2021-02-23 19:06:39 -05:00
case "set-password" :
2021-04-12 08:49:22 -07:00
return "Directly set the password on an account"
2021-02-23 19:06:39 -05:00
default :
return ""
}
}
func ( c * Command ) extraHelpFunc ( helpMap map [ string ] func () string ) string {
var helpStr string
switch c . Func {
case "" :
return base . WrapForHelpText ([] string {
"Usage: boundary accounts [sub command] [options] [args]" ,
"" ,
" This command allows operations on Boundary account resources. Example:" ,
"" ,
" Read a account:" ,
"" ,
2021-06-08 11:31:58 -05:00
` $ boundary accounts read -id acctpw_1234567890` ,
2021-02-23 19:06:39 -05:00
"" ,
" Please see the accounts subcommand help for detailed usage information." ,
})
case "change-password" :
helpStr = base . WrapForHelpText ([] string {
"Usage: boundary accounts change-password [options] [args]" ,
"" ,
" This command allows changing the password (with verification of the current password) on account-type resources, if the types match and the operation is allowed by the given account type. Example:" ,
"" ,
" Change the password on a password-type account:" ,
"" ,
2021-06-08 11:31:58 -05:00
` $ boundary accounts change-password -id acctpw_1234567890 -current-password <empty, to be read by stdin> -new-password <empty, to be read by stdin>` ,
2021-02-23 19:06:39 -05:00
"" ,
"" ,
})
case "set-password" :
helpStr = base . WrapForHelpText ([] string {
"Usage: boundary accounts set-password [options] [args]" ,
"" ,
" This command allows setting the password on account-type resources, if the types match and the operation is allowed by the given account type. Example:" ,
"" ,
" Set the password on a password-type account:" ,
"" ,
2021-06-08 11:31:58 -05:00
` $ boundary accounts set-password -id acctpw_1234567890 -password <empty, to be read by stdin>` ,
2021-02-23 19:06:39 -05:00
"" ,
"" ,
})
}
return helpStr + c . Flags (). Help ()
}
func extraFlagsFuncImpl ( c * Command , _ * base . FlagSets , f * base . FlagSet ) {
for _ , name := range flagsMap [ c . Func ] {
switch name {
case "password" :
f . StringVar ( & base . StringVar {
Name : "password" ,
Target : & c . flagPassword ,
Usage : "The password for the account. If not specified, the command will prompt for the password to be entered in a non-echoing way." ,
})
case "current-password" :
f . StringVar ( & base . StringVar {
Name : "current-password" ,
Target : & c . flagCurrentPassword ,
Usage : "The current password for the account. If not specified, the command will prompt for the password to be entered in a non-echoing way." ,
})
case "new-password" :
f . StringVar ( & base . StringVar {
Name : "new-password" ,
Target : & c . flagNewPassword ,
Usage : "The new password for the account. If not specified, the command will prompt for the password to be entered in a non-echoing way." ,
})
}
}
}
2021-04-08 20:54:32 -04:00
func extraFlagsHandlingFuncImpl ( c * Command , _ * base . FlagSets , opts * [] accounts . Option ) bool {
2022-08-05 13:21:46 -04:00
if strutil . StrListContains ( flagsMap [ c . Func ], "password" ) {
switch c . flagPassword {
case "" :
2022-08-05 19:27:14 -04:00
fmt . Print ( "Please enter the password (it will be hidden): " )
2022-08-05 13:21:46 -04:00
value , err := password . Read ( os . Stdin )
fmt . Print ( "\n" )
if err != nil {
c . UI . Error ( fmt . Sprintf ( "An error occurred attempting to read the password. The raw error message is shown below but usually this is because you attempted to pipe a value into the command or you are executing outside of a terminal (TTY). The raw error was:\n\n%s" , err . Error ()))
return false
}
fmt . Print ( "Please enter it one more time for confirmation: " )
confirmation , err := password . Read ( os . Stdin )
fmt . Print ( "\n" )
if err != nil {
c . UI . Error ( fmt . Sprintf ( "An error occurred attempting to read the password. The raw error message is shown below but usually this is because you attempted to pipe a value into the command or you are executing outside of a terminal (TTY). The raw error was:\n\n%s" , err . Error ()))
return false
}
if strings . TrimSpace ( value ) != strings . TrimSpace ( confirmation ) {
c . UI . Error ( "Entered password and confirmation value did not match." )
return false
}
c . flagPassword = strings . TrimSpace ( value )
default :
2022-08-05 16:08:48 -04:00
password , err := parseutil . MustParsePath ( c . flagPassword )
switch {
case err == nil :
case errors . Is ( err , parseutil . ErrNotParsed ):
c . UI . Error ( "Password flag must be used with env:// or file:// syntax or left empty for an interactive prompt" )
return false
default :
2022-08-05 13:21:46 -04:00
c . UI . Error ( fmt . Sprintf ( "Error parsing password flag: %v" , err ))
return false
}
c . flagPassword = password
2021-02-23 19:06:39 -05:00
}
}
2022-08-05 13:21:46 -04:00
if strutil . StrListContains ( flagsMap [ c . Func ], "current-password" ) {
switch c . flagCurrentPassword {
case "" :
2022-08-05 19:27:14 -04:00
fmt . Print ( "Please enter the current password (it will be hidden): " )
2022-08-05 13:21:46 -04:00
value , err := password . Read ( os . Stdin )
fmt . Print ( "\n" )
if err != nil {
c . UI . Error ( fmt . Sprintf ( "An error occurred attempting to read the password. The raw error message is shown below but usually this is because you attempted to pipe a value into the command or you are executing outside of a terminal (TTY). The raw error was:\n\n%s" , err . Error ()))
return false
}
c . flagCurrentPassword = strings . TrimSpace ( value )
default :
2022-08-05 16:08:48 -04:00
password , err := parseutil . MustParsePath ( c . flagCurrentPassword )
switch {
case err == nil :
case errors . Is ( err , parseutil . ErrNotParsed ):
c . UI . Error ( "Current password flag must be used with env:// or file:// syntax or left empty for an interactive prompt" )
return false
default :
2022-08-05 13:21:46 -04:00
c . UI . Error ( fmt . Sprintf ( "Error parsing current password flag: %v" , err ))
return false
}
c . flagCurrentPassword = password
2021-02-23 19:06:39 -05:00
}
}
2022-08-05 13:21:46 -04:00
if strutil . StrListContains ( flagsMap [ c . Func ], "new-password" ) {
switch c . flagNewPassword {
case "" :
2022-08-05 19:27:14 -04:00
fmt . Print ( "Please enter the new password (it will be hidden): " )
2022-08-05 13:21:46 -04:00
value , err := password . Read ( os . Stdin )
fmt . Print ( "\n" )
if err != nil {
c . UI . Error ( fmt . Sprintf ( "An error occurred attempting to read the password. The raw error message is shown below but usually this is because you attempted to pipe a value into the command or you are executing outside of a terminal (TTY). The raw error was:\n\n%s" , err . Error ()))
return false
}
2022-08-05 19:27:14 -04:00
fmt . Print ( "Please enter it one more time for confirmation: " )
2022-08-05 13:21:46 -04:00
confirmation , err := password . Read ( os . Stdin )
fmt . Print ( "\n" )
if err != nil {
c . UI . Error ( fmt . Sprintf ( "An error occurred attempting to read the password. The raw error message is shown below but usually this is because you attempted to pipe a value into the command or you are executing outside of a terminal (TTY). The raw error was:\n\n%s" , err . Error ()))
return false
}
if strings . TrimSpace ( value ) != strings . TrimSpace ( confirmation ) {
c . UI . Error ( "Entered password and confirmation value did not match." )
return false
}
c . flagNewPassword = strings . TrimSpace ( value )
default :
2022-08-05 16:08:48 -04:00
password , err := parseutil . MustParsePath ( c . flagNewPassword )
switch {
case err == nil :
case errors . Is ( err , parseutil . ErrNotParsed ):
c . UI . Error ( "New password flag must be used with env:// or file:// syntax or left empty for an interactive prompt" )
return false
default :
2022-08-05 13:21:46 -04:00
c . UI . Error ( fmt . Sprintf ( "Error parsing new password flag: %v" , err ))
return false
}
c . flagNewPassword = password
2021-02-23 19:06:39 -05:00
}
}
2021-03-04 20:05:17 -05:00
return true
2021-02-23 19:06:39 -05:00
}
2022-07-21 15:21:31 -07:00
func executeExtraActionsImpl ( c * Command , origResp * api . Response , origItem * accounts . Account , origItems [] * accounts . Account , origError error , accountClient * accounts . Client , version uint32 , opts [] accounts . Option ) ( * api . Response , * accounts . Account , [] * accounts . Account , error ) {
2021-02-23 19:06:39 -05:00
switch c . Func {
case "set-password" :
2022-07-21 15:21:31 -07:00
result , err := accountClient . SetPassword ( c . Context , c . FlagId , c . flagPassword , version , opts ... )
2022-07-27 10:00:00 -07:00
if err != nil {
return nil , nil , nil , err
}
2022-07-21 15:21:31 -07:00
return result . GetResponse (), result . GetItem (), nil , err
2021-02-23 19:06:39 -05:00
case "change-password" :
2022-07-21 15:21:31 -07:00
result , err := accountClient . ChangePassword ( c . Context , c . FlagId , c . flagCurrentPassword , c . flagNewPassword , version , opts ... )
2022-07-27 10:00:00 -07:00
if err != nil {
return nil , nil , nil , err
}
2022-07-21 15:21:31 -07:00
return result . GetResponse (), result . GetItem (), nil , err
2021-02-23 19:06:39 -05:00
}
2022-07-21 15:21:31 -07:00
return origResp , origItem , origItems , origError
2021-02-23 19:06:39 -05:00
}
func ( c * Command ) printListTable ( items [] * accounts . Account ) string {
if len ( items ) == 0 {
return "No accounts found"
}
var output [] string
output = [] string {
"" ,
"Account information:" ,
}
2021-05-04 19:57:35 -05:00
for i , item := range items {
2021-02-23 19:06:39 -05:00
if i > 0 {
output = append ( output , "" )
}
2021-05-04 19:57:35 -05:00
if item . Id != "" {
2021-02-23 19:06:39 -05:00
output = append ( output ,
2021-05-04 19:57:35 -05:00
fmt . Sprintf ( " ID: %s" , item . Id ),
)
} else {
output = append ( output ,
fmt . Sprintf ( " ID: %s" , "(not available)" ),
)
}
if item . Version > 0 {
output = append ( output ,
fmt . Sprintf ( " Version: %d" , item . Version ),
2021-02-23 19:06:39 -05:00
)
}
2021-05-04 19:57:35 -05:00
if item . Type != "" {
2021-02-23 19:06:39 -05:00
output = append ( output ,
2021-05-04 19:57:35 -05:00
fmt . Sprintf ( " Type: %s" , item . Type ),
2021-02-23 19:06:39 -05:00
)
}
2021-05-04 19:57:35 -05:00
if item . Name != "" {
2021-02-23 19:06:39 -05:00
output = append ( output ,
2021-05-04 19:57:35 -05:00
fmt . Sprintf ( " Name: %s" , item . Name ),
2021-02-23 19:06:39 -05:00
)
}
2021-05-04 19:57:35 -05:00
if item . Description != "" {
output = append ( output ,
fmt . Sprintf ( " Description: %s" , item . Description ),
)
}
if len ( item . AuthorizedActions ) > 0 {
2021-02-23 19:06:39 -05:00
output = append ( output ,
" Authorized Actions:" ,
2021-05-04 19:57:35 -05:00
base . WrapSlice ( 6 , item . AuthorizedActions ),
2021-02-23 19:06:39 -05:00
)
}
}
return base . WrapForHelpText ( output )
}
2022-07-21 15:21:31 -07:00
func printItemTable ( item * accounts . Account , resp * api . Response ) string {
2022-10-25 18:29:38 -05:00
nonAttributeMap := map [ string ] any {}
2021-05-04 19:57:35 -05:00
if item . Id != "" {
nonAttributeMap [ "ID" ] = item . Id
}
if item . Version != 0 {
nonAttributeMap [ "Version" ] = item . Version
}
if item . Type != "" {
nonAttributeMap [ "Type" ] = item . Type
}
if ! item . CreatedTime . IsZero () {
nonAttributeMap [ "Created Time" ] = item . CreatedTime . Local (). Format ( time . RFC1123 )
}
if ! item . UpdatedTime . IsZero () {
nonAttributeMap [ "Updated Time" ] = item . UpdatedTime . Local (). Format ( time . RFC1123 )
}
if item . AuthMethodId != "" {
nonAttributeMap [ "Auth Method ID" ] = item . AuthMethodId
2021-02-23 19:06:39 -05:00
}
if item . Name != "" {
nonAttributeMap [ "Name" ] = item . Name
}
if item . Description != "" {
nonAttributeMap [ "Description" ] = item . Description
}
maxLength := base . MaxAttributesLength ( nonAttributeMap , item . Attributes , keySubstMap )
ret := [] string {
"" ,
"Account information:" ,
base . WrapMap ( 2 , maxLength + 2 , nonAttributeMap ),
2021-05-04 19:57:35 -05:00
}
if item . Scope != nil {
ret = append ( ret ,
"" ,
" Scope:" ,
base . ScopeInfoForOutput ( item . Scope , maxLength ),
)
2021-02-23 19:06:39 -05:00
}
2021-06-02 13:01:37 -05:00
if len ( item . ManagedGroupIds ) > 0 {
ret = append ( ret ,
"" ,
" Managed Group IDs:" ,
base . WrapSlice ( 4 , item . ManagedGroupIds ),
)
}
2021-02-23 19:06:39 -05:00
if len ( item . AuthorizedActions ) > 0 {
ret = append ( ret ,
"" ,
" Authorized Actions:" ,
base . WrapSlice ( 4 , item . AuthorizedActions ),
)
}
if len ( item . Attributes ) > 0 {
ret = append ( ret ,
"" ,
" Attributes:" ,
base . WrapMap ( 4 , maxLength , item . Attributes ),
)
}
return base . WrapForHelpText ( ret )
}
var keySubstMap = map [ string ] string {
"login_name" : "Login Name" ,
}