Skip to content

Commit

Permalink
Initial Automated Sync
Browse files Browse the repository at this point in the history
  • Loading branch information
thycotic-rd committed Dec 8, 2020
1 parent 8064629 commit 478e865
Show file tree
Hide file tree
Showing 27 changed files with 1,093 additions and 205 deletions.
15 changes: 15 additions & 0 deletions cicd-integration/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,8 @@ func init() {
{"user-read-fail", []string{"user", "read", user1}, outputPattern("will be removed")},
{"user-restore", []string{"user", "restore", user1}, outputEmpty()},
{"user-read-pass-verify-restore", []string{"user", "read", user1}, outputPattern(`"userName": "mrmittens"`)},
{"user-create-provider-missing", []string{"user", "create", "--username", "bob", "--external-id", "1234"}, outputPattern("must specify both provider and external ID")},
{"user-create-external-id-missing", []string{"user", "create", "--username", "bob", "--provider", "aws-dev"}, outputPattern("must specify both provider and external ID")},

// group operations
{"group-help", []string{"group", ""}, outputPattern(`Execute an action on a group.*`)},
Expand All @@ -364,6 +366,8 @@ func init() {
{"role-get-implicit-pass", []string{"role", roleName}, outputPattern(fmt.Sprintf(`"name":\s*"%s"`, roleName))},
{"role-search-find-pass", []string{"role", "search", roleName[:3], "data.[0].name"}, outputPattern(roleName)},
{"role-search-none-pass", []string{"role", "search", "abcdef"}, outputPattern(`"data": null`)},
{"role-create-provider-missing", []string{"role", "create", "--name", "bob", "--external-id", "1234"}, outputPattern("must specify both provider and external ID")},
{"role-create-external-id-missing", []string{"role", "create", "--name", "bob", "--provider", "aws-dev"}, outputPattern("must specify both provider and external ID")},

// client operations
{"client-help", []string{"client", ""}, outputPattern(`Execute an action on a client.*`)},
Expand Down Expand Up @@ -426,6 +430,16 @@ func init() {
{"home-rollback", []string{"home", "rollback", homeSecretPath}, outputPattern(`"version": "2"`)},
{"home-get-by-version", []string{"home", "read", homeSecretPath, "version", "2"}, outputPattern(`"version": "2"`)},

// Pool
{"pool", []string{"pool", "create", "--name", "mypool"}, outputPattern(`"name": "mypool"`)},
{"pool", []string{"pool", "read", "--name", "mypool"}, outputPattern(`"name": "mypool"`)},

// Engine
{"engine", []string{"engine", "create", "--name", "myengine", "--pool-name", "bad-pool"}, outputPattern(`specified pool doesn't exist`)},
{"engine", []string{"engine", "create", "--name", "myengine", "--pool-name", "mypool"}, outputPattern(`"name": "myengine"`)},
{"engine", []string{"engine", "read", "--name", "myengine"}, outputPattern(`"name": "myengine"`)},
{"engine", []string{"engine", "delete", "myengine"}, outputEmpty()},

// Whoami
{"whoami", []string{"whoami", ""}, outputPattern(`users:` + adminUser)},

Expand Down Expand Up @@ -473,6 +487,7 @@ func init() {
{"rootCA-secret-delete", []string{"secret", "delete", "--path", existingRootSecret, "--force"}, outputEmpty()},
{"leafCA-secret-delete", []string{"secret", "delete", "--path", leafSecretPath, "--force"}, outputEmpty()},
{"home-secret-delete", []string{"home", "delete", homeSecretPath, "--force"}, outputEmpty()},
{"pool", []string{"pool", "delete", "mypool"}, outputEmpty()},
}
}

Expand Down
78 changes: 75 additions & 3 deletions commands/auth_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
type AuthProvider struct {
request requests.Client
outClient format.OutClient
edit func([]byte, dataFunc, *errors.ApiError, bool) ([]byte, *errors.ApiError)
}

func (p AuthProvider) Run(args []string) int {
Expand Down Expand Up @@ -65,7 +66,7 @@ func GetAuthProviderCmd() (cli.Command, error) {
if name == "" {
return cli.RunResultHelp
}
return AuthProvider{requests.NewHttpClient(), nil}.handleAuthProviderReadCmd(args)
return AuthProvider{requests.NewHttpClient(), nil, nil}.handleAuthProviderReadCmd(args)
},
SynopsisText: "manage 3rd party authentication providers",
HelpText: fmt.Sprintf("Execute an action on an %s from %s", cst.NounAuthProvider, cst.ProductName),
Expand Down Expand Up @@ -154,6 +155,7 @@ Usage:
preds.LongFlag(cst.ThyOneAuthClientBaseUri): cli.PredictorWrapper{complete.PredictAnything, preds.NewFlagValue(preds.Params{Name: cst.ThyOneAuthClientBaseUri, Usage: fmt.Sprintf("Thycotic One base URI")}), false},
preds.LongFlag(cst.ThyOneAuthClientID): cli.PredictorWrapper{complete.PredictAnything, preds.NewFlagValue(preds.Params{Name: cst.ThyOneAuthClientID, Usage: fmt.Sprintf("Thycotic One client ID")}), false},
preds.LongFlag(cst.ThyOneAuthClientSecret): cli.PredictorWrapper{complete.PredictAnything, preds.NewFlagValue(preds.Params{Name: cst.ThyOneAuthClientSecret, Usage: fmt.Sprintf("Thycotic One client secret")}), false},
preds.LongFlag(cst.SendWelcomeEmail): cli.PredictorWrapper{complete.PredictAnything, preds.NewFlagValue(preds.Params{Name: cst.SendWelcomeEmail, Usage: fmt.Sprintf("Whether to send welcome email for thycotic-one users linked to the auth provider (true or false)")}), false},
},
MinNumberArgs: 0,
})
Expand Down Expand Up @@ -182,11 +184,30 @@ Usage:
preds.LongFlag(cst.ThyOneAuthClientBaseUri): cli.PredictorWrapper{complete.PredictAnything, preds.NewFlagValue(preds.Params{Name: cst.ThyOneAuthClientBaseUri, Usage: fmt.Sprintf("Thycotic One base URI")}), false},
preds.LongFlag(cst.ThyOneAuthClientID): cli.PredictorWrapper{complete.PredictAnything, preds.NewFlagValue(preds.Params{Name: cst.ThyOneAuthClientID, Usage: fmt.Sprintf("Thycotic One client ID")}), false},
preds.LongFlag(cst.ThyOneAuthClientSecret): cli.PredictorWrapper{complete.PredictAnything, preds.NewFlagValue(preds.Params{Name: cst.ThyOneAuthClientSecret, Usage: fmt.Sprintf("Thycotic One client secret")}), false},
preds.LongFlag(cst.SendWelcomeEmail): cli.PredictorWrapper{complete.PredictAnything, preds.NewFlagValue(preds.Params{Name: cst.SendWelcomeEmail, Usage: fmt.Sprintf("Whether to send welcome email for thycotic-one users linked to the auth provider (true or false)")}), false},
},
MinNumberArgs: 0,
})
}

func GetAuthProviderEditCmd() (cli.Command, error) {
return NewCommand(CommandArgs{
Path: []string{cst.Config, cst.NounAuthProvider, cst.Edit},
RunFunc: AuthProvider{request: requests.NewHttpClient(), outClient: nil, edit: EditData}.handleAuthProviderEdit,
SynopsisText: fmt.Sprintf("%s %s %s (<name> | --name|-n)", cst.NounConfig, cst.NounAuthProvider, cst.Edit),
HelpText: fmt.Sprintf(`Edit an auth provider
Usage:
• %[1]s %[2]s %[4]s %[3]s
• %[1]s %[2]s %[4]s --name %[3]s
`, cst.NounConfig, cst.NounAuthProvider, cst.ExampleAuthProviderName, cst.Edit),
FlagsPredictor: cli.PredictorWrappers{
preds.LongFlag(cst.DataName): cli.PredictorWrapper{complete.PredictAnything, preds.NewFlagValue(preds.Params{Name: cst.DataName, Shorthand: "n", Usage: fmt.Sprintf("Target %s to an %s", cst.Path, cst.NounAuthProvider)}), false},
},
MinNumberArgs: 1,
})
}

func GetAuthProviderRollbackCmd() (cli.Command, error) {
return NewCommand(CommandArgs{
Path: []string{cst.Config, cst.NounAuthProvider, cst.Rollback},
Expand Down Expand Up @@ -325,7 +346,7 @@ func (p AuthProvider) handleAuthProviderUpsertWorkflow(args []string) int {
{"aws", "AWS"},
{"azure", "Azure"},
{"gcp", "GCP"},
{"thycoticone", "Thycotic One"},
{cst.ThyOne, "Thycotic One"},
}, false, false); err != nil {
ui.Error(err.Error())
return utils.GetExecStatus(err)
Expand Down Expand Up @@ -380,7 +401,7 @@ func (p AuthProvider) handleAuthProviderUpsertWorkflow(args []string) int {
}
params.Properties = props
}
case "thycoticone":
case cst.ThyOne:
if url, err := getStringAndValidate(
ui, "Base URL:", false, nil, false, false); err != nil {
ui.Error(err.Error())
Expand All @@ -402,6 +423,16 @@ func (p AuthProvider) handleAuthProviderUpsertWorkflow(args []string) int {
} else {
params.Properties.ClientSecret = clientSecret
}
if sendWelcomeEmail, err := getStringAndValidate(
ui, "Send welcome email (true or false):", true, nil, false, false); err != nil {
ui.Error(err.Error())
return utils.GetExecStatus(err)
} else {
sendWelcomeEmail, parseErr := strconv.ParseBool(sendWelcomeEmail)
if parseErr == nil {
params.Properties.SendWelcomeEmail = sendWelcomeEmail
}
}
default:
ui.Error("Unsupported auth provider type.")
return 1
Expand Down Expand Up @@ -454,6 +485,11 @@ func (p AuthProvider) handleAuthProviderUpsert(args []string) int {
},
}
}
sendWelcomeEmail, parseErr := strconv.ParseBool(viper.GetString(cst.SendWelcomeEmail))
if parseErr == nil && model.Type == cst.ThyOne {
model.Properties.SendWelcomeEmail = sendWelcomeEmail
}

resp, apiErr := p.submitAuthProvider(model)
p.outClient.WriteResponse(resp, apiErr)
return utils.GetExecStatus(err)
Expand Down Expand Up @@ -526,6 +562,42 @@ func (p AuthProvider) handleAuthProviderSearchCommand(args []string) int {
return utils.GetExecStatus(err)
}

func (p AuthProvider) handleAuthProviderEdit(args []string) int {
if p.outClient == nil {
p.outClient = format.NewDefaultOutClient()
}

var err *errors.ApiError
var resp []byte

path, status := getAuthProviderParams(args)
if status != 0 {
return status
}
baseType := strings.Join([]string{cst.Config, cst.NounAuth}, "/")
uri := paths.CreateResourceURI(baseType, paths.ProcessPath(path), "", true, nil, false)

resp, err = p.request.DoRequest("GET", uri, nil)
if err != nil {
p.outClient.WriteResponse(resp, err)
return utils.GetExecStatus(err)
}

saveFunc := dataFunc(func(data []byte) (resp []byte, err *errors.ApiError) {
var model authProvider
if mErr := json.Unmarshal(data, &model); mErr != nil {
return nil, errors.New(mErr).Grow("invalid format for auth provider")
}
if model.Type != cst.ThyOne {
model.Properties.SendWelcomeEmail = false
}
_, err = p.request.DoRequest("PUT", uri, &model)
return nil, err
})
resp, err = p.edit(resp, saveFunc, nil, false)
p.outClient.WriteResponse(resp, err)
return utils.GetExecStatus(err)
}
func getAuthProviderParams(args []string) (name string, status int) {
status = 0
name = viper.GetString(cst.DataName)
Expand Down
9 changes: 5 additions & 4 deletions commands/auth_provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ package cmd

import (
e "errors"
"github.com/spf13/viper"
"github.com/stretchr/testify/assert"
"testing"
cst "thy/constants"
"thy/errors"
"thy/fake"

"github.com/spf13/viper"
"github.com/stretchr/testify/assert"
)

func TestHandleAuthProviderReadCommand(t *testing.T) {
Expand Down Expand Up @@ -128,7 +129,7 @@ func TestHandleAuthProviderUpsertCommand(t *testing.T) {
return tt.out, tt.expectedErr
}

p := &AuthProvider{req, writer}
p := &AuthProvider{req, writer, nil}
viper.Set(cst.LastCommandKey, tt.method)

if len(tt.dataAction) == 2 {
Expand Down Expand Up @@ -193,7 +194,7 @@ func TestHandleAuthProviderDeleteCmd(t *testing.T) {
return tt.out, tt.expectedErr
}

p := &AuthProvider{req, writer}
p := &AuthProvider{req, writer, nil}
_ = p.handleAuthProviderDeleteCmd(tt.args)
if tt.expectedErr == nil {
assert.Equal(t, data, tt.out)
Expand Down
7 changes: 5 additions & 2 deletions commands/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,10 @@ func BasePredictorWrappers() cli.PredictorWrappers {
preds.LongFlag(cst.Profile): cli.PredictorWrapper{complete.PredictAnything, preds.NewFlagValue(preds.Params{Name: cst.Profile, Usage: "Configuration Profile [default:default]", Global: true}), false},
preds.LongFlag(cst.Tenant): cli.PredictorWrapper{complete.PredictAnything, preds.NewFlagValue(preds.Params{Name: cst.Tenant, Shorthand: "t", Usage: "Tenant used for auth", Global: true}), false},
preds.LongFlag(cst.Encoding): cli.PredictorWrapper{preds.EncodingTypePredictor{}, preds.NewFlagValue(preds.Params{Name: cst.Encoding, Shorthand: "e", Usage: "Output encoding (json|yaml) [default:json]", Global: true}), false},
preds.LongFlag(cst.Beautify): cli.PredictorWrapper{complete.PredictAnything, preds.NewFlagValue(preds.Params{Name: cst.Beautify, Shorthand: "b", Usage: "Should beautify output", Global: true, ValueType: "bool"}), false},
preds.LongFlag(cst.Beautify): cli.PredictorWrapper{complete.PredictAnything, preds.NewFlagValue(preds.Params{Name: cst.Beautify, Shorthand: "b", Usage: "Should beautify output", Global: true, ValueType: "bool", Hidden: true}), false},
// we could get away with just one of beautify / plain but gets tricky because we want the default to be beautify,
// unless it's a read operation
preds.LongFlag(cst.Plain): cli.PredictorWrapper{complete.PredictAnything, preds.NewFlagValue(preds.Params{Name: cst.Plain, Usage: "Should not beautify output (overrides beautify)", Global: true, ValueType: "bool", Hidden: true}), false},
preds.LongFlag(cst.Plain): cli.PredictorWrapper{complete.PredictAnything, preds.NewFlagValue(preds.Params{Name: cst.Plain, Usage: "Should not beautify output (overrides beautify)", Global: true, ValueType: "bool"}), false},
preds.LongFlag(cst.Verbose): cli.PredictorWrapper{complete.PredictAnything, preds.NewFlagValue(preds.Params{Name: cst.Verbose, Shorthand: "v", Usage: "Verbose output [default:false]", Global: true, ValueType: "bool"}), false},
preds.LongFlag(cst.Config): cli.PredictorWrapper{complete.PredictAnything, preds.NewFlagValue(preds.Params{Name: cst.Config, Shorthand: "c", Usage: fmt.Sprintf("Config file path [default:%s%s.thy.yaml]", homePath, string(os.PathSeparator)), Global: true}), false},
preds.LongFlag(cst.Filter): cli.PredictorWrapper{complete.PredictAnything, preds.NewFlagValue(preds.Params{Name: cst.Filter, Shorthand: "f", Usage: "Filter in jq (stedolan.github.io/jq)", Global: true}), false},
Expand Down Expand Up @@ -136,6 +136,9 @@ func (c *baseCommand) preRun(args []string) int {

configureFormattingOptions()

// Set profile name to lower case globally.
viper.Set(cst.Profile, strings.ToLower(viper.GetString(cst.Profile)))

if upd, err := version.CheckLatestVersion(); err != nil {
log.Println(err)
} else if upd != nil {
Expand Down
15 changes: 9 additions & 6 deletions commands/cli-config.go
Original file line number Diff line number Diff line change
Expand Up @@ -336,13 +336,14 @@ func handleCliConfigInitCmd(args []string) int {
cfgPath = config.GetCliConfigPath()
}

profile := viper.GetString(cst.Profile)
profile := strings.ToLower(viper.GetString(cst.Profile))
if profile == "" {
profile = cst.DefaultProfile
} else {
// If not default profile, that means the user specified --profile [name], so the intent is to add a profile to the config.
addProfile = true
}
viper.Set(cst.Profile, profile)

if cfgPath != "" {
if cfgInfo, err := os.Stat(cfgPath); err == nil && cfgInfo != nil {
Expand Down Expand Up @@ -383,16 +384,18 @@ func handleCliConfigInitCmd(args []string) int {

// If default profile, that means the user did not specify --profile [name], so ask for profile name.
if addProfile && profile == cst.DefaultProfile {
if p, err := getStringAndValidate(ui, "Please enter profile name:", false, nil, false, false); err != nil {
var p string
if p, err = getStringAndValidate(ui, "Please enter profile name:", false, nil, false, false); err != nil {
return 1
} else if err := viper.ReadInConfig(p); err == nil {
}
p = strings.ToLower(p)
if err := viper.ReadInConfig(p); err == nil {
msg := fmt.Sprintf("Profile %q already exists in the config.", p)
ui.Info(msg)
return 1
} else {
profile = p
viper.Set(cst.Profile, profile)
}
profile = p
viper.Set(cst.Profile, profile)
}
}
} else if profile != cst.DefaultProfile {
Expand Down
Loading

0 comments on commit 478e865

Please sign in to comment.