Skip to content

Commit

Permalink
Automated from: 1304921
Browse files Browse the repository at this point in the history
  • Loading branch information
thycotic-rd committed Apr 16, 2021
1 parent 94ab521 commit f37a7c9
Show file tree
Hide file tree
Showing 21 changed files with 1,954 additions and 78 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ bin/*
/svc_acct_creds_1
docs/mkdocsenv/
docs/site/
dsv
dsv.exe

inittests/initenv/
inittests/__pycache__/
Expand Down
67 changes: 42 additions & 25 deletions auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"reflect"
"runtime"
"strings"
"text/template"
"time"

"thy/paths"
Expand Down Expand Up @@ -173,7 +174,7 @@ func (a *authenticator) getTokenForAuthType(at AuthType, useCache bool) (*TokenR
if keySuffix == "" {
keySuffix = cst.DefaultProfile
}
} else if at == Oidc {
} else if at == Oidc || at == FederatedThyOne {
profile := viper.GetString(cst.Profile)
keySuffix = viper.GetString(keyName)
if profile != "" && profile != cst.DefaultProfile {
Expand Down Expand Up @@ -271,7 +272,7 @@ func (a *authenticator) getTokenForAuthType(at AuthType, useCache bool) (*TokenR
data = requestBody{
GrantType: authTypeToGrantType[at],
}
if at == Password || at == FederatedThyOne {
if at == Password {
err := setupDataForPasswordAuth(&data)
if err != nil {
return nil, errors.New(err)
Expand All @@ -294,10 +295,16 @@ func (a *authenticator) getTokenForAuthType(at AuthType, useCache bool) (*TokenR
}
data.RefreshToken = refreshToken
}
} else if at == Oidc {
} else if at == Oidc || at == FederatedThyOne {
data.Provider = viper.GetString(cst.AuthProvider)
data.CallbackHost = viper.GetString(cst.Callback)
data.CallbackUrl = fmt.Sprintf("http://%s/callback", viper.GetString(cst.Callback))

callback := viper.GetString(cst.Callback)
if callback == "" {
callback = cst.DefaultCallback
}

data.CallbackHost = callback
data.CallbackUrl = fmt.Sprintf("http://%s/callback", callback)
}
}

Expand Down Expand Up @@ -345,7 +352,7 @@ func (a *authenticator) fetchTokenVault(at AuthType, data requestBody) (*TokenRe
if err := data.ValidateForAuthType(at); err != nil {
return nil, errors.New(err)
}
if at == Oidc {
if at == Oidc || at == FederatedThyOne {
ui := cli.BasicUi{
Writer: os.Stdout,
Reader: os.Stdin,
Expand Down Expand Up @@ -394,9 +401,9 @@ func (a *authenticator) fetchTokenVault(at AuthType, data requestBody) (*TokenRe
}
data.State = ar.state
data.AuthorizationCode = ar.authCode
ui.Info(fmt.Sprintf("Received response from oidc provider, submitting authorization code to %s", cst.ProductName))
ui.Info(fmt.Sprintf("Received response from %s provider, submitting authorization code to %s", at, cst.ProductName))
case <-time.After(5 * time.Minute):
ui.Info(fmt.Sprintf("Timeout occurred waiting for callback from oidc provider"))
ui.Info(fmt.Sprintf("Timeout occurred waiting for callback from %s provider", at))
return nil, errors.NewS("no callback occurred after redirect")
}
}
Expand All @@ -410,10 +417,9 @@ func (a *authenticator) fetchTokenVault(at AuthType, data requestBody) (*TokenRe
return &response, nil
}

//
// handleOidcAuth handles OIDC and Thycotic One auths
func (a *authenticator) handleOidcAuth(doneCh chan<- AuthResponse) http.HandlerFunc {
return func(w http.ResponseWriter, req *http.Request) {

b, err := ioutil.ReadAll(req.Body)
if err != nil {
w.Write([]byte(err.Error()))
Expand All @@ -434,7 +440,28 @@ func (a *authenticator) handleOidcAuth(doneCh chan<- AuthResponse) http.HandlerF
return
}

w.Write([]byte(youDidIt))
tmpl, err := template.New("youDidIt").Parse(youDidIt)
if err != nil {
w.Write([]byte(err.Error()))
doneCh <- AuthResponse{
err: errors.New(err),
message: "error in html parse template",
}
}

vars := map[string]interface{}{
"providerName": viper.GetString(cst.AuthType),
}

err = tmpl.Execute(w, vars)
if err != nil {
w.Write([]byte(err.Error()))
doneCh <- AuthResponse{
err: errors.New(err),
message: "error in html template execute",
}
}

doneCh <- AuthResponse{
err: nil,
message: "success",
Expand Down Expand Up @@ -564,7 +591,7 @@ func (r *requestBody) ValidateForAuthType(at AuthType) error {

var authTypeToGrantType = map[AuthType]string{
Password: "password",
FederatedThyOne: "password",
FederatedThyOne: "oidc",
ClientCredential: "client_credentials",
Certificate: "certificate",
Refresh: "refresh_token",
Expand Down Expand Up @@ -600,20 +627,10 @@ var paramSpecDict = map[AuthType][]paramSpec{
},
},
FederatedThyOne: {
{PropName: "Password",
ArgName: cst.Password,
IsKey: false,
RequestVar: true,
},
{PropName: "Username",
ArgName: cst.Username,
{PropName: "AuthType",
ArgName: cst.AuthType,
IsKey: true,
RequestVar: true,
},
{PropName: "Provider",
ArgName: cst.AuthProvider,
IsKey: false,
RequestVar: true,
RequestVar: false,
},
},
ClientCredential: {
Expand Down
4 changes: 2 additions & 2 deletions auth/html_responses.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const youDidIt = `<!DOCTYPE html>
<head>
<meta charset="utf-8">
<title>OIDC Sign In Complete</title>
<title>{{.providerName}} Sign In Complete</title>
<style>
/*
Expand Down Expand Up @@ -190,7 +190,7 @@ hr {
<div class="container">
<div class="row">
<div class="two-thirds column" style="margin-top: 25%">
<h2>OIDC Provider Sign In Complete</h2>
<h2>{{.providerName}} Provider Sign In Complete</h2>
<h5>Return to the CLI to verify sign in to DevOps Secrets Vault finished.</h5>
</div>
</div>
Expand Down
30 changes: 22 additions & 8 deletions cicd-integration/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,12 @@ var certPath = strings.Join([]string{"cicd-integration", "data", "cert.pem"}, st
var privateKeyPath = strings.Join([]string{"cicd-integration", "data", "key.pem"}, string(filepath.Separator))
var csrPath = strings.Join([]string{"cicd-integration", "data", "csr.pem"}, string(filepath.Separator))

var manualKeyPath = "thekey:first"
var manualPrivateKey = "MnI1dTh4L0E/RChHK0tiUGVTaFZtWXEzczZ2OXkkQiY="
var manualKeyNonce = "S1NzeHdFcHB6b1Bz"
var plaintext = "hello there"
var ciphertext = "8Tns2mbY/w6YHoICfiDGQM+rDlQzwrZWpqK7"

func addConfigArg(args []string) []string {
args = append(args, "--config")
args = append(args, configPath)
Expand Down Expand Up @@ -360,7 +366,7 @@ func init() {

{"role-get-pass", []string{"role", "read", roleName}, outputPattern(fmt.Sprintf(`"name":\s*"%s"`, roleName))},
{"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-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")},
Expand Down Expand Up @@ -419,15 +425,22 @@ func init() {
{"home-rollback", []string{"home", "rollback", homeSecretPath}, outputPattern(`"version": "2"`)},
{"home-get-by-version", []string{"home", "read", homeSecretPath, "version", "2"}, outputPattern(`"version": "2"`)},

// EaaS-Manual
{"crypto-manual-key-upload", []string{"crypto", "manual", "key-upload", "--path", manualKeyPath, "--private-key", manualPrivateKey, "--nonce", manualKeyNonce, "--scheme", "symmetric"}, outputPattern(`"version": "0"`)},
{"crypto-manual-key-read", []string{"crypto", "manual", "key-read", "--path", manualKeyPath}, outputPattern(`"version": "0"`)},
{"crypto-manual-encrypt", []string{"crypto", "manual", "encrypt", "--path", manualKeyPath, "--data", plaintext}, outputPattern(`"version": "0"`)},
{"crypto-manual-decrypt", []string{"crypto", "manual", "decrypt", "--path", manualKeyPath, "--data", ciphertext}, outputPattern(`"data": "hello there"`)},
{"crypto-manual-key-update", []string{"crypto", "manual", "key-update", "--path", manualKeyPath, "--private-key", manualPrivateKey}, outputPattern(`"version": "1"`)},

// Pool
{"pool", []string{"pool", "create", "--name", "mypool"}, outputPattern(`"name": "mypool"`)},
{"pool", []string{"pool", "read", "--name", "mypool"}, outputPattern(`"name": "mypool"`)},
{"pool-create", []string{"pool", "create", "--name", "mypool"}, outputPattern(`"name": "mypool"`)},
{"pool-read", []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()},
{"engine-create-fail", []string{"engine", "create", "--name", "myengine", "--pool-name", "bad-pool"}, outputPattern(`specified pool doesn't exist`)},
{"engine-create-pass", []string{"engine", "create", "--name", "myengine", "--pool-name", "mypool"}, outputPattern(`"name": "myengine"`)},
{"engine-read", []string{"engine", "read", "--name", "myengine"}, outputPattern(`"name": "myengine"`)},
{"engine-delete", []string{"engine", "delete", "myengine"}, outputEmpty()},

// Whoami
{"whoami", []string{"whoami", ""}, outputPattern(`users:` + adminUser)},
Expand Down Expand Up @@ -476,7 +489,8 @@ 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()},
{"pool-delete", []string{"pool", "delete", "mypool"}, outputEmpty()},
{"crypto-manual-key-delete", []string{"crypto", "manual", "key-delete", "--path", manualKeyPath, "--force"}, outputEmpty()},
}
}

Expand Down
40 changes: 33 additions & 7 deletions commands/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,14 @@ func GetAuthCmd() (cli.Command, error) {
auth.NewAuthenticatorDefault,
store.GetStore, nil}.handleAuth,
NoPreAuth: true,
SynopsisText: fmt.Sprintf("%s", cst.NounAuth),
SynopsisText: cst.NounAuth,
HelpText: fmt.Sprintf(`Authenticate with %[2]s
Usage:
• auth --profile staging
• auth --auth-username %[3]s --auth-password %[4]s
• auth --auth-type %[7]s --auth-client-id=%[5]s --auth-client-secret=%[6]s
`, cst.NounAuth, cst.ProductName, cst.ExampleUser, cst.ExamplePassword, cst.ExampleAuthClientID, cst.ExampleAuthClientSecret, string(auth.FederatedAws)),
• auth --auth-type %[5]s --auth-client-id %[6]s --domain %[7]s --auth-client-secret %[8]s
`, cst.NounAuth, cst.ProductName, cst.ExampleUser, cst.ExamplePassword, cst.ExampleAuthType, cst.ExampleAuthClientID, cst.ExampleDomain, cst.ExampleAuthClientSecret, string(auth.FederatedAws)),
})
}

Expand Down Expand Up @@ -100,16 +100,42 @@ Usage:

func (ac AuthCommand) handleAuth(args []string) int {
var data []byte
token, err := ac.token().GetToken()
if err == nil {
data, err = errors.Convert(format.JsonMarshal(token))
token, apiErr := ac.token().GetToken()
if apiErr == nil {
data, apiErr = errors.Convert(format.JsonMarshal(token))
}

outClient := ac.outClient
if outClient == nil {
outClient = format.NewDefaultOutClient()
}

outClient.WriteResponse(data, err)
// Ask the user for the password, in case if the stored user information is not set to be able to auth AND username is set AND password is not via flags
if strings.Contains(apiErr.Error(), auth.KeyfileNotFoundError.Error()) && viper.GetString(cst.Username) != "" && viper.GetString(cst.Password) == "" {
ui := &PasswordUi{
cli.BasicUi{
Writer: os.Stdout,
Reader: os.Stdin,
ErrorWriter: os.Stderr,
},
}

if password, err := getStringAndValidate(ui, "Please enter password", false, nil, true, false); err != nil {
outClient.WriteResponse(data, errors.New(err))

return 0
} else {
viper.Set(cst.Password, password)
}

token, apiErr = ac.token().GetToken()
if apiErr == nil {
data, apiErr = errors.Convert(format.JsonMarshal(token))
}
}

outClient.WriteResponse(data, apiErr)

return 0
}

Expand Down
1 change: 1 addition & 0 deletions commands/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ func BasePredictorWrappers() cli.PredictorWrappers {
preds.LongFlag(cst.AuthProvider): cli.PredictorWrapper{complete.PredictAnything, preds.NewFlagValue(preds.Params{Name: cst.AuthProvider, Usage: "Authentication provider name for federated authentication", Global: true, Hidden: true}), false},
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.DomainName): cli.PredictorWrapper{complete.PredictAnything, preds.NewFlagValue(preds.Params{Name: cst.DomainName, Usage: "Domain 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", Hidden: true}), false},
// we could get away with just one of beautify / plain but gets tricky because we want the default to be beautify,
Expand Down
53 changes: 27 additions & 26 deletions commands/cli-config.go
Original file line number Diff line number Diff line change
Expand Up @@ -548,8 +548,8 @@ func handleCliConfigInitCmd(args []string) int {
}
AddNode(&cfg, jsonish{cst.Type: authType}, profile, cst.NounAuth)
if storeType != store.None {
if auth.AuthType(authType) == auth.Password || auth.AuthType(authType) == auth.FederatedThyOne {
var passwordMessage, userMessage, authProviderMessage string
if auth.AuthType(authType) == auth.Password {
var passwordMessage, userMessage string
var confirmRequired bool
tenant := viper.GetString(cst.Tenant)
if setupRequired {
Expand Down Expand Up @@ -589,23 +589,6 @@ func handleCliConfigInitCmd(args []string) int {
viper.Set(cst.Password, password)
}
}

if auth.AuthType(authType) == auth.FederatedThyOne {
authProvider = cst.DefaultThyOneName

if authProvider == "" && isDevDomain {
authProviderMessage = fmt.Sprintf("Thycotic One authentication provider name (default %s):", cst.DefaultThyOneName)
if authProvider, err = getStringAndValidateDefault(ui, authProviderMessage, cst.DefaultThyOneName, true, nil, false, false); err != nil {
return 1

}
}

viper.Set(cst.AuthProvider, authProvider)

AddNode(&cfg, jsonish{cst.DataProvider: authProvider}, profile, cst.NounAuth)
}

} else if auth.AuthType(authType) == auth.ClientCredential {
if id, err := getStringAndValidate(ui, "Please enter client id for client auth:", false, nil, false, false); err != nil {
return 1
Expand Down Expand Up @@ -636,18 +619,34 @@ func handleCliConfigInitCmd(args []string) int {
}
AddNode(&cfg, jsonish{cst.NounAwsProfile: awsProfile}, profile, cst.NounAuth)
viper.Set(cst.AwsProfile, awsProfile)
} else if auth.AuthType(authType) == auth.Oidc {
if authProvider == "" {
if authProvider, err = getStringAndValidateDefault(ui, fmt.Sprintf("Please enter auth provider name (default: %s):", cst.DefaultThyOneName), cst.DefaultThyOneName, true, nil, false, false); err != nil {
return 1
} else if auth.AuthType(authType) == auth.Oidc || auth.AuthType(authType) == auth.FederatedThyOne {
if auth.AuthType(authType) == auth.Oidc {
if authProvider == "" {
authProviderMessage := fmt.Sprintf("Please enter auth provider name (default: %s):", cst.DefaultThyOneName)
if authProvider, err = getStringAndValidateDefault(ui, authProviderMessage, cst.DefaultThyOneName, true, nil, false, false); err != nil {
return 1
}
}
} else {
authProvider = cst.DefaultThyOneName

if isDevDomain {
authProviderMessage := fmt.Sprintf("Thycotic One authentication provider name (default %s):", cst.DefaultThyOneName)
if authProvider, err = getStringAndValidateDefault(ui, authProviderMessage, cst.DefaultThyOneName, true, nil, false, false); err != nil {
return 1

}
}
viper.Set(cst.AuthProvider, authProvider)
}

viper.Set(cst.AuthProvider, authProvider)
AddNode(&cfg, jsonish{cst.DataProvider: authProvider}, profile, cst.NounAuth)

var callback string
if callback = viper.GetString(cst.Callback); callback == "" {
callback = cst.DefaultCallback
}

viper.Set(cst.Callback, callback)
AddNode(&cfg, jsonish{cst.DataCallback: callback}, profile, cst.NounAuth)
}
Expand All @@ -672,13 +671,15 @@ func handleCliConfigInitCmd(args []string) int {
ui.Output(authError.Error())
return 1
}

ui.Output("Failed to authenticate, restoring previous config.")
ui.Output("Please check your credentials, or tenant name, or domain name and try again.")
return 1

}

// Store encryption key file (for auth types password and thy-one).
if auth.AuthType(authType) == auth.Password || auth.AuthType(authType) == auth.FederatedThyOne {
// Store encryption key file (for auth type password).
if auth.AuthType(authType) == auth.Password {
st, apiError := store.GetStore(string(storeType))
if apiError != nil {
ui.Error(apiError.Error())
Expand Down
Loading

0 comments on commit f37a7c9

Please sign in to comment.