Skip to content

Commit

Permalink
v0.1.2-beta Release (#104)
Browse files Browse the repository at this point in the history
* #70 Add auto completion tab for fish, bash, zsh and powershell (#83)

* Add auto completion tab for fish, bash, zsh and powershell

* Update README to include instuctions for auto-complete

* Add concurrent sessions to the CLI #88 (#89)

* #96 allow more than 2 certificates in cert chain (#97)

* Revert "#90 add no limit to default cybr conjur list command" (#102)

* Add installation instruction for AWS CloudShell (#101)

* #90 add no limit to default cybr conjur list command (#94)

* Revert "#90 add no limit to default cybr conjur list command (#94)"

This reverts commit 04d2f8c.

Co-authored-by: Quincy Cheng <quincy.cheng@gmail.com>
Co-authored-by: Andrew Copeland <50109276+AndrewCopeland@users.noreply.github.com>

* V0.1.2 beta #47 account move (#93)

* Change platform properties to map[string]string to me consistent with requests.AddAccount

* #44 move an account to a new safe

* Resolves #98 so windows login works correctly (#99)

* Resolves #98 so windows login works correctly

* Use strings.TrimSpace() instead of strings.Replace()

* Add --password flag so CCP can retrieve REST API password #66 (#92)

* Add --password flag to so CCP can retrieve REST API password #66

* Move check to beginning

Co-authored-by: Andrew Copeland <50109276+AndrewCopeland@users.noreply.github.com>
Co-authored-by: Quincy Cheng <quincy.cheng@gmail.com>
  • Loading branch information
3 people authored Mar 30, 2021
1 parent 04d2f8c commit 83c6e4a
Show file tree
Hide file tree
Showing 8 changed files with 184 additions and 19 deletions.
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,25 @@ $ cybr help

All commands are documentated [in the docs/ directory](docs/cybr.md).

## Autocomplete
The `cybr` CLI has a `completion` command that can be used to enable autocomplete for the CLI.
The completion command is dependant on your shell type. Currently the only shells that are supported are: bash, zsh, fish and powershell.

Below is an example on how to enable `cybr` cli auto-completion from a zsh shell.
```bash
# enable shell completetion. Only needs to be performed once.
echo "autoload -U compinit; compinit" >> ~/.zshrc

# create and write the auto-completion script.
# ${fpath[1]} '1' may be different depending on your environment.
cybr completion zsh > "${fpath[1]}/_cybr"
```

If you are using a different shell execute the `completion` command with the `--help` flag and follow instructions for the desired shell type.
```bash
cybr completion --help
```

## Example Source Code

### Logon to the PAS REST API Web Service
Expand Down Expand Up @@ -94,6 +113,7 @@ func main() {
log.Fatalf("Authentication failed. %s", errLogon)
}
fmt.Printf("Session Token:\r\n%s\r\n\r\n", token)
}
```

## Testing
Expand Down
58 changes: 58 additions & 0 deletions cmd/accounts.go
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,57 @@ var reconcileAccountCmd = &cobra.Command{
},
}

var moveAccountCmd = &cobra.Command{
Use: "move",
Short: "Move an account to a different safe",
Long: `Move an account to a different safe
Example Usage:
$ cybr accounts move -i 24_1 -s newSafeName`,
Run: func(cmd *cobra.Command, args []string) {
client, err := pasapi.GetConfigWithLogger(getLogger())
if err != nil {
log.Fatalf("Failed to read configuration file. %s", err)
return
}

account, err := client.GetAccount(AccountID)
if err != nil {
log.Fatalf("%s", err)
}

secret, err := client.GetAccountPassword(AccountID, requests.GetAccountPassword{})
if err != nil {
log.Fatalf("%s", err)
}

newAccount := requests.AddAccount{
Name: account.Name,
Address: account.Address,
UserName: account.UserName,
PlatformID: account.PlatformID,
SafeName: Safe,
SecretType: account.SecretType,
Secret: secret,
PlatformAccountProperties: account.PlatformAccountProperties,
SecretManagement: account.SecretManagement,
}

createdAccount, err := client.AddAccount(newAccount)
if err != nil {
log.Fatalf("%s", err)
}

err = client.DeleteAccount(AccountID)
if err != nil {
log.Fatalf("%s", err)
return
}

prettyprint.PrintJSON(createdAccount)
},
}

func init() {
// Listing an account
listAccountsCmd.Flags().StringVarP(&Search, "search", "s", "", "List of keywords to search for in accounts, separated by a space")
Expand Down Expand Up @@ -365,6 +416,12 @@ func init() {
reconcileAccountCmd.Flags().StringVarP(&AccountID, "account-id", "i", "", "Account ID to reconcile")
reconcileAccountCmd.MarkFlagRequired("account-id")

// move
moveAccountCmd.Flags().StringVarP(&AccountID, "account-id", "i", "", "Account ID to move")
moveAccountCmd.MarkFlagRequired("account-id")
moveAccountCmd.Flags().StringVarP(&Safe, "safe", "s", "", "Safe name in which the account will be moved into")
moveAccountCmd.MarkFlagRequired("safe")

// Add cmd to account cmd
accountsCmd.AddCommand(listAccountsCmd)
accountsCmd.AddCommand(getAccountsCmd)
Expand All @@ -374,6 +431,7 @@ func init() {
accountsCmd.AddCommand(verifyAccountCmd)
accountsCmd.AddCommand(changeAccountCmd)
accountsCmd.AddCommand(reconcileAccountCmd)
accountsCmd.AddCommand(moveAccountCmd)

// Add accounts cmd to root
rootCmd.AddCommand(accountsCmd)
Expand Down
70 changes: 70 additions & 0 deletions cmd/completion.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package cmd

import (
"os"

"github.com/spf13/cobra"
)

var completionCmd = &cobra.Command{
Use: "completion [bash|zsh|fish|powershell]",
Short: "Generate completion script",
Long: `To load completions:
Bash:
$ source <(cybr completion bash)
# To load completions for each session, execute once:
# Linux:
$ cybr completion bash > /etc/bash_completion.d/cybr
# macOS:
$ cybr completion bash > /usr/local/etc/bash_completion.d/cybr
Zsh:
# If shell completion is not already enabled in your environment,
# you will need to enable it. You can execute the following once:
$ echo "autoload -U compinit; compinit" >> ~/.zshrc
# To load completions for each session, execute once:
$ cybr completion zsh > "${fpath[1]}/_cybr"
# You will need to start a new shell for this setup to take effect.
fish:
$ cybr completion fish | source
# To load completions for each session, execute once:
$ cybr completion fish > ~/.config/fish/completions/cybr.fish
PowerShell:
PS> cybr completion powershell | Out-String | Invoke-Expression
# To load completions for every new session, run:
PS> cybr completion powershell > cybr.ps1
# and source this file from your PowerShell profile.
`,
DisableFlagsInUseLine: true,
ValidArgs: []string{"bash", "zsh", "fish", "powershell"},
Args: cobra.ExactValidArgs(1),
Run: func(cmd *cobra.Command, args []string) {
switch args[0] {
case "bash":
cmd.Root().GenBashCompletion(os.Stdout)
case "zsh":
cmd.Root().GenZshCompletion(os.Stdout)
case "fish":
cmd.Root().GenFishCompletion(os.Stdout, true)
case "powershell":
cmd.Root().GenPowerShellCompletion(os.Stdout)
}
},
}

func init() {
rootCmd.AddCommand(completionCmd)
}
21 changes: 18 additions & 3 deletions cmd/logon.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ var (
BaseURL string
// NonInteractive logon
NonInteractive bool
// Password to logon to the PAS REST API
Password string
// ConcurrentSession allow concurrent sessions
ConcurrentSession bool
)

var logonCmd = &cobra.Command{
Expand All @@ -39,6 +43,10 @@ var logonCmd = &cobra.Command{
Run: func(cmd *cobra.Command, args []string) {
password := os.Getenv("PAS_PASSWORD")

if !NonInteractive && Password != "" {
log.Fatalf("An error occured because --non-interactive must be provided when using --password flag")
}

if !NonInteractive {
// Get secret value from STDIN
fmt.Print("Enter password: ")
Expand All @@ -51,6 +59,10 @@ var logonCmd = &cobra.Command{
password = string(byteSecretVal)
}

if Password != "" {
password = Password
}

if password == "" {
log.Fatalf("Provided password is empty")
}
Expand All @@ -62,8 +74,9 @@ var logonCmd = &cobra.Command{
}

credentials := requests.Logon{
Username: Username,
Password: password,
Username: Username,
Password: password,
ConcurrentSession: ConcurrentSession,
}

err := client.Logon(credentials)
Expand Down Expand Up @@ -99,14 +112,16 @@ var logonCmd = &cobra.Command{
}

func init() {
logonCmd.Flags().StringVarP(&Username, "username", "u", "", "Username to logon PAS REST API using")
logonCmd.Flags().StringVarP(&Username, "username", "u", "", "Username to logon to PAS REST API")
logonCmd.MarkFlagRequired("username")
logonCmd.Flags().StringVarP(&AuthenticationType, "auth-type", "a", "", "Authentication method to logon using [cyberark|ldap|radius]")
logonCmd.MarkFlagRequired("authType")
logonCmd.Flags().BoolVarP(&InsecureTLS, "insecure-tls", "i", false, "If detected, TLS will not be verified")
logonCmd.Flags().StringVarP(&BaseURL, "base-url", "b", "", "Base URL to send Logon request to [https://pvwa.example.com]")
logonCmd.MarkFlagRequired("base-url")
logonCmd.Flags().BoolVar(&NonInteractive, "non-interactive", false, "If detected, will retrieve the password from the PAS_PASSWORD environment variable")
logonCmd.Flags().StringVarP(&Password, "password", "p", "", "Password to logon to PAS REST API, only supported when using --non-interactive flag")
logonCmd.Flags().BoolVar(&ConcurrentSession, "concurrent", false, "If detected, will create a concurrent session to the PAS API")

rootCmd.AddCommand(logonCmd)
}
2 changes: 1 addition & 1 deletion pkg/cybr/api/responses/getaccount.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ type GetAccount struct {
PlatformID string `json:"platformId"`
SafeName string `json:"safeName"`
SecretType string `json:"secretType"`
PlatformAccountProperties map[string]interface{} `json:"platformAccountProperties"`
PlatformAccountProperties map[string]string `json:"platformAccountProperties"`
SecretManagement shared.SecretManagement `json:"secretManagement"`
CreatedTime int `json:"createdTime"`
}
6 changes: 0 additions & 6 deletions pkg/cybr/conjur/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"io"
"net/http"
"os"
"path/filepath"
"strings"

"github.com/cyberark/conjur-api-go/conjurapi"
Expand Down Expand Up @@ -77,11 +76,6 @@ func getClientFromEnvironmentVariable() (*conjurapi.Client, *authn.LoginPair, er
return client, &loginPair, err
}

// GetNetRcPath returns path to the ~/.netrc file os-agnostic
func GetNetRcPath(homeDir string) string {
return filepath.FromSlash(fmt.Sprintf("%s/.netrc", homeDir))
}

// GetConjurClient create conjur client and login pair for ~/.conjurrc and ~/.netrc
func GetConjurClient() (*conjurapi.Client, *authn.LoginPair, error) {
client, loginPair, err := getClientFromEnvironmentVariable()
Expand Down
14 changes: 8 additions & 6 deletions pkg/cybr/conjur/conjurrc.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,14 @@ func getPem(url string) (string, error) {
}
defer conn.Close()

if len(conn.ConnectionState().PeerCertificates) != 2 {
if len(conn.ConnectionState().PeerCertificates) == 1 {
return "", fmt.Errorf("Invalid conjur url '%s'. Make sure hostname and port are correct", url)
}
pemCert := string(pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: conn.ConnectionState().PeerCertificates[0].Raw}))
secondPemCert := string(pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: conn.ConnectionState().PeerCertificates[1].Raw}))
pemCert = pemCert + secondPemCert

pemCert := ""
for _, cert := range conn.ConnectionState().PeerCertificates {
pemCert += string(pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}))
}

return pemCert, err
}
Expand All @@ -69,7 +71,7 @@ func createConjurCert(certFileName string, url string) error {
reader := bufio.NewReader(os.Stdin)
fmt.Print(fmt.Sprintf("Replace certificate file '%s' [y]: ", certFileName))
text, _ := reader.ReadString('\n')
answer := strings.Replace(text, "\n", "", -1)
answer := strings.TrimSpace(text)
// overwrite file
if answer == "" || answer == "y" {
err = ioutil.WriteFile(certFileName, []byte(pemCert), 0600)
Expand All @@ -85,7 +87,7 @@ func createConjurRcFile(account string, url string, certFileName string, conjurr
fmt.Print("Replace ~/.conjurrc file [y]: ")
reader := bufio.NewReader(os.Stdin)
text, err := reader.ReadString('\n')
answer := strings.Replace(text, "\n", "", -1)
answer := strings.TrimSpace(text)

// overwrite ~/.conjurrc file
if answer == "" || answer == "y" {
Expand Down
12 changes: 9 additions & 3 deletions pkg/cybr/conjur/netrc.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
)

Expand All @@ -13,6 +14,11 @@ var netrcTemplate string = `machine {{ APPLIANCE_URL }}/authn
password {{ PASSWORD }}
`

// GetNetRcPath returns path to the ~/.conjurrc file os-agnostic
func GetNetRcPath(homeDir string) string {
return filepath.FromSlash(fmt.Sprintf("%s/.netrc", homeDir))
}

// CreateNetRc create a conjur netrc file
func CreateNetRc(username string, password string) error {
// creatr ~/.netrc pas
Expand All @@ -21,19 +27,19 @@ func CreateNetRc(username string, password string) error {
return err
}

conjurrcFileName := fmt.Sprintf("%s/.conjurrc", homeDir)
conjurrcFileName := GetConjurRcPath(homeDir)
url := GetURLFromConjurRc(conjurrcFileName)
if url == "" {
return fmt.Errorf("Failed to get appliance url from '%s'. Run 'cam init' to set this file", conjurrcFileName)
}

// create the ~/.netrc file
netrcFileName := fmt.Sprintf("%s/.netrc", homeDir)
netrcFileName := GetNetRcPath(homeDir)
fmt.Print("Replace ~/.netrc file [y]: ")
// prompt user
reader := bufio.NewReader(os.Stdin)
text, _ := reader.ReadString('\n')
answer := strings.Replace(text, "\n", "", -1)
answer := strings.TrimSpace(text)
if answer == "" || answer == "y" {
// create the ~/.netrc file
netrcContent := strings.Replace(netrcTemplate, "{{ USERNAME }}", username, 1)
Expand Down

0 comments on commit 83c6e4a

Please sign in to comment.