From de439ac5741ab5030db799ba69214ef849167595 Mon Sep 17 00:00:00 2001 From: Konstantin Demin Date: Mon, 1 Jul 2024 20:37:46 +0300 Subject: [PATCH] remove external mfa (pingid, duo, okta) --- builtin/credential/okta/backend.go | 406 ---------- builtin/credential/okta/backend_test.go | 300 ------- builtin/credential/okta/cli.go | 122 --- builtin/credential/okta/cmd/okta/main.go | 34 - builtin/credential/okta/path_config.go | 381 --------- builtin/credential/okta/path_groups.go | 219 ----- builtin/credential/okta/path_groups_test.go | 111 --- builtin/credential/okta/path_login.go | 255 ------ builtin/credential/okta/path_users.go | 173 ---- command/base_predict.go | 1 - command/commands.go | 2 - go.mod | 7 +- go.sum | 8 - helper/builtinplugins/registry.go | 2 - helper/identity/mfa/types.pb.go | 286 ++----- helper/identity/mfa/types.proto | 34 - scripts/gen_openapi.sh | 1 - ui/app/adapters/auth-config/okta.js | 8 - ui/app/adapters/cluster.js | 11 +- ui/app/components/auth-form.js | 50 +- .../cluster/access/mfa/methods/create.js | 2 +- ui/app/controllers/vault/cluster/auth.js | 1 - ui/app/helpers/mountable-auth-methods.js | 7 - ui/app/helpers/supported-auth-backends.js | 8 - .../supported-managed-auth-backends.js | 2 +- ui/app/helpers/tabs-for-auth-section.js | 6 - ui/app/models/auth-config/okta.js | 46 -- ui/app/models/mfa-method.js | 70 -- .../settings/auth/configure/section.js | 1 - ui/app/services/auth.js | 16 - ui/app/templates/components/auth-form.hbs | 8 - .../components/okta-number-challenge.hbs | 43 - .../components/wizard/okta-method.hbs | 16 - .../vault/cluster/access/mfa/index.hbs | 2 +- .../cluster/access/mfa/methods/create.hbs | 4 +- ui/app/templates/vault/cluster/auth.hbs | 19 +- ui/lib/core/icon-mappings.js | 5 - ui/mirage/factories/mfa-duo-method.js | 24 - ui/mirage/factories/mfa-okta-method.js | 23 - ui/mirage/factories/mfa-pingid-method.js | 17 - ui/public/duo.svg | 5 - ui/public/eco/okta.svg | 1 - ui/public/okta.svg | 3 - ui/public/pingid.svg | 11 - .../components/okta-number-challenge-test.js | 74 -- .../identity/login_mfa_duo_test.go | 292 ------- .../identity/login_mfa_okta_test.go | 358 --------- vault/identity_store.go | 224 ------ vault/logical_system_helpers.go | 3 - vault/login_mfa.go | 754 ------------------ vault/request_handling.go | 10 +- 51 files changed, 58 insertions(+), 4408 deletions(-) delete mode 100644 builtin/credential/okta/backend.go delete mode 100644 builtin/credential/okta/backend_test.go delete mode 100644 builtin/credential/okta/cli.go delete mode 100644 builtin/credential/okta/cmd/okta/main.go delete mode 100644 builtin/credential/okta/path_config.go delete mode 100644 builtin/credential/okta/path_groups.go delete mode 100644 builtin/credential/okta/path_groups_test.go delete mode 100644 builtin/credential/okta/path_login.go delete mode 100644 builtin/credential/okta/path_users.go delete mode 100644 ui/app/adapters/auth-config/okta.js delete mode 100644 ui/app/models/auth-config/okta.js delete mode 100644 ui/app/templates/components/okta-number-challenge.hbs delete mode 100644 ui/app/templates/components/wizard/okta-method.hbs delete mode 100644 ui/mirage/factories/mfa-duo-method.js delete mode 100644 ui/mirage/factories/mfa-okta-method.js delete mode 100644 ui/mirage/factories/mfa-pingid-method.js delete mode 100644 ui/public/duo.svg delete mode 100644 ui/public/eco/okta.svg delete mode 100644 ui/public/okta.svg delete mode 100644 ui/public/pingid.svg delete mode 100644 ui/tests/integration/components/okta-number-challenge-test.js delete mode 100644 vault/external_tests/identity/login_mfa_duo_test.go delete mode 100644 vault/external_tests/identity/login_mfa_okta_test.go diff --git a/builtin/credential/okta/backend.go b/builtin/credential/okta/backend.go deleted file mode 100644 index 96507f787..000000000 --- a/builtin/credential/okta/backend.go +++ /dev/null @@ -1,406 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: BUSL-1.1 - -package okta - -import ( - "context" - "fmt" - "net/textproto" - "time" - - "github.com/hashicorp/go-secure-stdlib/strutil" - "github.com/hashicorp/vault/sdk/framework" - "github.com/hashicorp/vault/sdk/helper/cidrutil" - "github.com/hashicorp/vault/sdk/logical" - "github.com/okta/okta-sdk-golang/v2/okta" - "github.com/patrickmn/go-cache" -) - -const ( - operationPrefixOkta = "okta" - mfaPushMethod = "push" - mfaTOTPMethod = "token:software:totp" -) - -func Factory(ctx context.Context, conf *logical.BackendConfig) (logical.Backend, error) { - b := Backend() - if err := b.Setup(ctx, conf); err != nil { - return nil, err - } - return b, nil -} - -func Backend() *backend { - var b backend - b.Backend = &framework.Backend{ - Help: backendHelp, - - PathsSpecial: &logical.Paths{ - Unauthenticated: []string{ - "login/*", - "verify/*", - }, - SealWrapStorage: []string{ - "config", - }, - }, - - Paths: []*framework.Path{ - pathConfig(&b), - pathUsers(&b), - pathGroups(&b), - pathUsersList(&b), - pathGroupsList(&b), - pathLogin(&b), - pathVerify(&b), - }, - - AuthRenew: b.pathLoginRenew, - BackendType: logical.TypeCredential, - } - b.verifyCache = cache.New(5*time.Minute, time.Minute) - - return &b -} - -type backend struct { - *framework.Backend - verifyCache *cache.Cache -} - -func (b *backend) Login(ctx context.Context, req *logical.Request, username, password, totp, nonce, preferredProvider string) ([]string, *logical.Response, []string, error) { - cfg, err := b.Config(ctx, req.Storage) - if err != nil { - return nil, nil, nil, err - } - if cfg == nil { - return nil, logical.ErrorResponse("Okta auth method not configured"), nil, nil - } - - // Check for a CIDR match. - if len(cfg.TokenBoundCIDRs) > 0 { - if req.Connection == nil { - b.Logger().Warn("token bound CIDRs found but no connection information available for validation") - return nil, nil, nil, logical.ErrPermissionDenied - } - if !cidrutil.RemoteAddrIsOk(req.Connection.RemoteAddr, cfg.TokenBoundCIDRs) { - return nil, nil, nil, logical.ErrPermissionDenied - } - } - - shim, err := cfg.OktaClient(ctx) - if err != nil { - return nil, nil, nil, err - } - - type mfaFactor struct { - Id string `json:"id"` - Type string `json:"factorType"` - Provider string `json:"provider"` - Embedded struct { - Challenge struct { - CorrectAnswer *int `json:"correctAnswer"` - } `json:"challenge"` - } `json:"_embedded"` - } - - type embeddedResult struct { - User okta.User `json:"user"` - Factors []mfaFactor `json:"factors"` - Factor *mfaFactor `json:"factor"` - } - - type authResult struct { - Embedded embeddedResult `json:"_embedded"` - Status string `json:"status"` - FactorResult string `json:"factorResult"` - StateToken string `json:"stateToken"` - } - - authReq, err := shim.NewRequest("POST", "authn", map[string]interface{}{ - "username": username, - "password": password, - }) - if err != nil { - return nil, nil, nil, err - } - - var result authResult - rsp, err := shim.Do(authReq, &result) - if err != nil { - if oe, ok := err.(*okta.Error); ok { - return nil, logical.ErrorResponse("Okta auth failed: %v (code=%v)", err, oe.ErrorCode), nil, nil - } - return nil, logical.ErrorResponse(fmt.Sprintf("Okta auth failed: %v", err)), nil, nil - } - if rsp == nil { - return nil, logical.ErrorResponse("okta auth method unexpected failure"), nil, nil - } - - oktaResponse := &logical.Response{ - Data: map[string]interface{}{}, - } - - // More about Okta's Auth transaction state here: - // https://developer.okta.com/docs/api/resources/authn#transaction-state - - // If lockout failures are not configured to be hidden, the status needs to - // be inspected for LOCKED_OUT status. Otherwise, it is handled above by an - // error returned during the authentication request. - switch result.Status { - case "LOCKED_OUT": - if b.Logger().IsDebug() { - b.Logger().Debug("user is locked out", "user", username) - } - return nil, logical.ErrorResponse("okta authentication failed"), nil, nil - - case "PASSWORD_EXPIRED": - if b.Logger().IsDebug() { - b.Logger().Debug("password is expired", "user", username) - } - return nil, logical.ErrorResponse("okta authentication failed"), nil, nil - - case "PASSWORD_WARN": - oktaResponse.AddWarning("Your Okta password is in warning state and needs to be changed soon.") - - case "MFA_ENROLL", "MFA_ENROLL_ACTIVATE": - if !cfg.BypassOktaMFA { - if b.Logger().IsDebug() { - b.Logger().Debug("user must enroll or complete mfa enrollment", "user", username) - } - return nil, logical.ErrorResponse("okta authentication failed: you must complete MFA enrollment to continue"), nil, nil - } - - case "MFA_REQUIRED": - // Per Okta documentation: Users are challenged for MFA (MFA_REQUIRED) - // before the Status of PASSWORD_EXPIRED is exposed (if they have an - // active factor enrollment). This bypass removes visibility - // into the authenticating user's password expiry, but still ensures the - // credentials are valid and the user is not locked out. - // - // API reference: https://developer.okta.com/docs/reference/api/authn/#verify-factor - if cfg.BypassOktaMFA { - result.Status = "SUCCESS" - break - } - - var selectedFactor, totpFactor, pushFactor *mfaFactor - - // Scan for available factors - for _, v := range result.Embedded.Factors { - v := v // create a new copy since we'll be taking the address later - - if preferredProvider != "" && preferredProvider != v.Provider { - continue - } - - if !strutil.StrListContains(b.getSupportedProviders(), v.Provider) { - continue - } - - switch v.Type { - case mfaTOTPMethod: - totpFactor = &v - case mfaPushMethod: - pushFactor = &v - } - } - - // Okta push and totp, and Google totp are currently supported. - // If a totp passcode is provided during login and is supported, - // that will be the preferred method. - switch { - case totpFactor != nil && totp != "": - selectedFactor = totpFactor - case pushFactor != nil && pushFactor.Provider == oktaProvider: - selectedFactor = pushFactor - case totpFactor != nil && totp == "": - return nil, logical.ErrorResponse("'totp' passcode parameter is required to perform MFA"), nil, nil - default: - return nil, logical.ErrorResponse("Okta Verify Push or TOTP or Google TOTP factor is required in order to perform MFA"), nil, nil - } - - requestPath := fmt.Sprintf("authn/factors/%s/verify", selectedFactor.Id) - - payload := map[string]interface{}{ - "stateToken": result.StateToken, - } - if selectedFactor.Type == mfaTOTPMethod { - payload["passCode"] = totp - } - - verifyReq, err := shim.NewRequest("POST", requestPath, payload) - if err != nil { - return nil, nil, nil, err - } - if len(req.Headers["X-Forwarded-For"]) > 0 { - verifyReq.Header.Set("X-Forwarded-For", req.Headers[textproto.CanonicalMIMEHeaderKey("X-Forwarded-For")][0]) - } - - rsp, err := shim.Do(verifyReq, &result) - if err != nil { - return nil, logical.ErrorResponse(fmt.Sprintf("Okta auth failed: %v", err)), nil, nil - } - if rsp == nil { - return nil, logical.ErrorResponse("okta auth backend unexpected failure"), nil, nil - } - for result.Status == "MFA_CHALLENGE" { - switch result.FactorResult { - case "WAITING": - verifyReq, err := shim.NewRequest("POST", requestPath, payload) - if err != nil { - return nil, logical.ErrorResponse(fmt.Sprintf("okta auth failed creating verify request: %v", err)), nil, nil - } - rsp, err := shim.Do(verifyReq, &result) - - // Store number challenge if found - numberChallenge := result.Embedded.Factor.Embedded.Challenge.CorrectAnswer - if numberChallenge != nil { - if nonce == "" { - return nil, logical.ErrorResponse("nonce must be provided during login request when presented with number challenge"), nil, nil - } - - b.verifyCache.SetDefault(nonce, *numberChallenge) - } - - if err != nil { - return nil, logical.ErrorResponse(fmt.Sprintf("Okta auth failed checking loop: %v", err)), nil, nil - } - if rsp == nil { - return nil, logical.ErrorResponse("okta auth backend unexpected failure"), nil, nil - } - - timer := time.NewTimer(1 * time.Second) - select { - case <-timer.C: - // Continue - case <-ctx.Done(): - timer.Stop() - return nil, logical.ErrorResponse("exiting pending mfa challenge"), nil, nil - } - case "REJECTED": - return nil, logical.ErrorResponse("multi-factor authentication denied"), nil, nil - case "TIMEOUT": - return nil, logical.ErrorResponse("failed to complete multi-factor authentication"), nil, nil - case "SUCCESS": - // Allowed - default: - if b.Logger().IsDebug() { - b.Logger().Debug("unhandled result status", "status", result.Status, "factorstatus", result.FactorResult) - } - return nil, logical.ErrorResponse("okta authentication failed"), nil, nil - } - } - - case "SUCCESS": - // Do nothing here - - default: - if b.Logger().IsDebug() { - b.Logger().Debug("unhandled result status", "status", result.Status) - } - return nil, logical.ErrorResponse("okta authentication failed"), nil, nil - } - - // Verify result status again in case a switch case above modifies result - switch { - case result.Status == "SUCCESS", - result.Status == "PASSWORD_WARN", - result.Status == "MFA_REQUIRED" && cfg.BypassOktaMFA, - result.Status == "MFA_ENROLL" && cfg.BypassOktaMFA, - result.Status == "MFA_ENROLL_ACTIVATE" && cfg.BypassOktaMFA: - // Allowed - default: - if b.Logger().IsDebug() { - b.Logger().Debug("authentication returned a non-success status", "status", result.Status) - } - return nil, logical.ErrorResponse("okta authentication failed"), nil, nil - } - - var allGroups []string - // Only query the Okta API for group membership if we have a token - client, oktactx := shim.Client() - if client != nil { - oktaGroups, err := b.getOktaGroups(oktactx, client, &result.Embedded.User) - if err != nil { - return nil, logical.ErrorResponse(fmt.Sprintf("okta failure retrieving groups: %v", err)), nil, nil - } - if len(oktaGroups) == 0 { - errString := fmt.Sprintf( - "no Okta groups found; only policies from locally-defined groups available") - oktaResponse.AddWarning(errString) - } - allGroups = append(allGroups, oktaGroups...) - } - - // Import the custom added groups from okta backend - user, err := b.User(ctx, req.Storage, username) - if err != nil { - if b.Logger().IsDebug() { - b.Logger().Debug("error looking up user", "error", err) - } - } - if err == nil && user != nil && user.Groups != nil { - if b.Logger().IsDebug() { - b.Logger().Debug("adding local groups", "num_local_groups", len(user.Groups), "local_groups", user.Groups) - } - allGroups = append(allGroups, user.Groups...) - } - - // Retrieve policies - var policies []string - for _, groupName := range allGroups { - entry, _, err := b.Group(ctx, req.Storage, groupName) - if err != nil { - if b.Logger().IsDebug() { - b.Logger().Debug("error looking up group policies", "error", err) - } - } - if err == nil && entry != nil && entry.Policies != nil { - policies = append(policies, entry.Policies...) - } - } - - // Merge local Policies into Okta Policies - if user != nil && user.Policies != nil { - policies = append(policies, user.Policies...) - } - - return policies, oktaResponse, allGroups, nil -} - -func (b *backend) getOktaGroups(ctx context.Context, client *okta.Client, user *okta.User) ([]string, error) { - groups, resp, err := client.User.ListUserGroups(ctx, user.Id) - if err != nil { - return nil, err - } - oktaGroups := make([]string, 0, len(groups)) - for _, group := range groups { - oktaGroups = append(oktaGroups, group.Profile.Name) - } - for resp.HasNextPage() { - var nextGroups []*okta.Group - resp, err = resp.Next(ctx, &nextGroups) - if err != nil { - return nil, err - } - for _, group := range nextGroups { - oktaGroups = append(oktaGroups, group.Profile.Name) - } - } - if b.Logger().IsDebug() { - b.Logger().Debug("Groups fetched from Okta", "num_groups", len(oktaGroups), "groups", fmt.Sprintf("%#v", oktaGroups)) - } - return oktaGroups, nil -} - -const backendHelp = ` -The Okta credential provider allows authentication querying, -checking username and password, and associating policies. If an api token is -configured groups are pulled down from Okta. - -Configuration of the connection is done through the "config" and "policies" -endpoints by a user with root access. Authentication is then done -by supplying the two fields for "login". -` diff --git a/builtin/credential/okta/backend_test.go b/builtin/credential/okta/backend_test.go deleted file mode 100644 index b34752465..000000000 --- a/builtin/credential/okta/backend_test.go +++ /dev/null @@ -1,300 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: BUSL-1.1 - -package okta - -import ( - "context" - "fmt" - "os" - "strings" - "testing" - "time" - - log "github.com/hashicorp/go-hclog" - "github.com/hashicorp/vault/helper/testhelpers" - logicaltest "github.com/hashicorp/vault/helper/testhelpers/logical" - "github.com/hashicorp/vault/sdk/helper/logging" - "github.com/hashicorp/vault/sdk/helper/policyutil" - "github.com/hashicorp/vault/sdk/logical" - "github.com/okta/okta-sdk-golang/v2/okta" - "github.com/okta/okta-sdk-golang/v2/okta/query" - "github.com/stretchr/testify/require" -) - -// To run this test, set the following env variables: -// VAULT_ACC=1 -// OKTA_ORG=dev-219337 -// OKTA_API_TOKEN= -// OKTA_USERNAME=test3@example.com -// OKTA_PASSWORD= -// -// You will need to install the Okta client app on your mobile device and -// setup MFA in order to use the Okta web UI. This test does not exercise -// MFA however (which is an enterprise feature), and therefore the test -// user in OKTA_USERNAME should not be configured with it. Currently -// test3@example.com is not a member of testgroup, which is the group with -// the profile that requires MFA. -func TestBackend_Config(t *testing.T) { - if os.Getenv("VAULT_ACC") == "" { - t.SkipNow() - } - - // Ensure each cred is populated. - credNames := []string{ - "OKTA_USERNAME", - "OKTA_PASSWORD", - "OKTA_API_TOKEN", - } - testhelpers.SkipUnlessEnvVarsSet(t, credNames) - - defaultLeaseTTLVal := time.Hour * 12 - maxLeaseTTLVal := time.Hour * 24 - b, err := Factory(context.Background(), &logical.BackendConfig{ - Logger: logging.NewVaultLogger(log.Trace), - System: &logical.StaticSystemView{ - DefaultLeaseTTLVal: defaultLeaseTTLVal, - MaxLeaseTTLVal: maxLeaseTTLVal, - }, - }) - if err != nil { - t.Fatalf("Unable to create backend: %s", err) - } - - username := os.Getenv("OKTA_USERNAME") - password := os.Getenv("OKTA_PASSWORD") - token := os.Getenv("OKTA_API_TOKEN") - groupIDs := createOktaGroups(t, username, token, os.Getenv("OKTA_ORG")) - defer deleteOktaGroups(t, token, os.Getenv("OKTA_ORG"), groupIDs) - - configData := map[string]interface{}{ - "org_name": os.Getenv("OKTA_ORG"), - "base_url": "oktapreview.com", - } - - updatedDuration := time.Hour * 1 - configDataToken := map[string]interface{}{ - "api_token": token, - "token_ttl": "1h", - } - - logicaltest.Test(t, logicaltest.TestCase{ - AcceptanceTest: true, - PreCheck: func() { testAccPreCheck(t) }, - CredentialBackend: b, - Steps: []logicaltest.TestStep{ - testConfigCreate(t, configData), - // 2. Login with bad password, expect failure (E0000004=okta auth failure). - testLoginWrite(t, username, "wrong", "E0000004", 0, nil), - // 3. Make our user belong to two groups and have one user-specific policy. - testAccUserGroups(t, username, "local_grouP,lOcal_group2", []string{"user_policy"}), - // 4. Create the group local_group, assign it a single policy. - testAccGroups(t, "local_groUp", "loCal_group_policy"), - // 5. Login with good password, expect user to have their user-specific - // policy and the policy of the one valid group they belong to. - testLoginWrite(t, username, password, "", defaultLeaseTTLVal, []string{"local_group_policy", "user_policy"}), - // 6. Create the group everyone, assign it two policies. This is a - // magic group name in okta that always exists and which every - // user automatically belongs to. - testAccGroups(t, "everyoNe", "everyone_grouP_policy,eveRy_group_policy2"), - // 7. Login as before, expect same result - testLoginWrite(t, username, password, "", defaultLeaseTTLVal, []string{"local_group_policy", "user_policy"}), - // 8. Add API token so we can lookup groups - testConfigUpdate(t, configDataToken), - testConfigRead(t, token, configData), - // 10. Login should now lookup okta groups; since all okta users are - // in the "everyone" group, that should be returned; since we - // defined policies attached to the everyone group, we should now - // see those policies attached to returned vault token. - testLoginWrite(t, username, password, "", updatedDuration, []string{"everyone_group_policy", "every_group_policy2", "local_group_policy", "user_policy"}), - testAccGroups(t, "locAl_group2", "testgroup_group_policy"), - testLoginWrite(t, username, password, "", updatedDuration, []string{"everyone_group_policy", "every_group_policy2", "local_group_policy", "testgroup_group_policy", "user_policy"}), - }, - }) -} - -func createOktaGroups(t *testing.T, username string, token string, org string) []string { - orgURL := "https://" + org + "." + previewBaseURL - ctx, client, err := okta.NewClient(context.Background(), okta.WithOrgUrl(orgURL), okta.WithToken(token)) - require.Nil(t, err) - - users, _, err := client.User.ListUsers(ctx, &query.Params{ - Q: username, - }) - require.Nil(t, err) - require.Len(t, users, 1) - userID := users[0].Id - var groupIDs []string - - // Verify that login's call to list the groups of the user logging in will page - // through multiple result sets; note here - // https://developer.okta.com/docs/reference/api/groups/#list-groups-with-defaults - // that "If you don't specify a value for limit and don't specify a query, - // only 200 results are returned for most orgs." - for i := 0; i < 201; i++ { - name := fmt.Sprintf("TestGroup%d", i) - groups, _, err := client.Group.ListGroups(ctx, &query.Params{ - Q: name, - }) - require.Nil(t, err) - - var groupID string - if len(groups) == 0 { - group, _, err := client.Group.CreateGroup(ctx, okta.Group{ - Profile: &okta.GroupProfile{ - Name: fmt.Sprintf("TestGroup%d", i), - }, - }) - require.Nil(t, err) - groupID = group.Id - } else { - groupID = groups[0].Id - } - groupIDs = append(groupIDs, groupID) - - _, err = client.Group.AddUserToGroup(ctx, groupID, userID) - require.Nil(t, err) - } - return groupIDs -} - -func deleteOktaGroups(t *testing.T, token string, org string, groupIDs []string) { - orgURL := "https://" + org + "." + previewBaseURL - ctx, client, err := okta.NewClient(context.Background(), okta.WithOrgUrl(orgURL), okta.WithToken(token)) - require.Nil(t, err) - - for _, groupID := range groupIDs { - _, err := client.Group.DeleteGroup(ctx, groupID) - require.Nil(t, err) - } -} - -func testLoginWrite(t *testing.T, username, password, reason string, expectedTTL time.Duration, policies []string) logicaltest.TestStep { - return logicaltest.TestStep{ - Operation: logical.UpdateOperation, - Path: "login/" + username, - ErrorOk: true, - Data: map[string]interface{}{ - "password": password, - }, - Check: func(resp *logical.Response) error { - if resp.IsError() { - if reason == "" || !strings.Contains(resp.Error().Error(), reason) { - return resp.Error() - } - } else if reason != "" { - return fmt.Errorf("expected error containing %q, got no error", reason) - } - - if resp.Auth != nil { - if !policyutil.EquivalentPolicies(resp.Auth.Policies, policies) { - return fmt.Errorf("policy mismatch expected %v but got %v", policies, resp.Auth.Policies) - } - - actualTTL := resp.Auth.LeaseOptions.TTL - if actualTTL != expectedTTL { - return fmt.Errorf("TTL mismatch expected %v but got %v", expectedTTL, actualTTL) - } - } - - return nil - }, - } -} - -func testConfigCreate(t *testing.T, d map[string]interface{}) logicaltest.TestStep { - return logicaltest.TestStep{ - Operation: logical.CreateOperation, - Path: "config", - Data: d, - } -} - -func testConfigUpdate(t *testing.T, d map[string]interface{}) logicaltest.TestStep { - return logicaltest.TestStep{ - Operation: logical.UpdateOperation, - Path: "config", - Data: d, - } -} - -func testConfigRead(t *testing.T, token string, d map[string]interface{}) logicaltest.TestStep { - return logicaltest.TestStep{ - Operation: logical.ReadOperation, - Path: "config", - Check: func(resp *logical.Response) error { - if resp.IsError() { - return resp.Error() - } - - if resp.Data["org_name"] != d["org_name"] { - return fmt.Errorf("org mismatch expected %s but got %s", d["organization"], resp.Data["Org"]) - } - - if resp.Data["base_url"] != d["base_url"] { - return fmt.Errorf("BaseURL mismatch expected %s but got %s", d["base_url"], resp.Data["BaseURL"]) - } - - for _, value := range resp.Data { - if value == token { - return fmt.Errorf("token should not be returned on a read request") - } - } - - return nil - }, - } -} - -func testAccPreCheck(t *testing.T) { - if v := os.Getenv("OKTA_USERNAME"); v == "" { - t.Fatal("OKTA_USERNAME must be set for acceptance tests") - } - - if v := os.Getenv("OKTA_PASSWORD"); v == "" { - t.Fatal("OKTA_PASSWORD must be set for acceptance tests") - } - - if v := os.Getenv("OKTA_ORG"); v == "" { - t.Fatal("OKTA_ORG must be set for acceptance tests") - } - - if v := os.Getenv("OKTA_API_TOKEN"); v == "" { - t.Fatal("OKTA_API_TOKEN must be set for acceptance tests") - } -} - -func testAccUserGroups(t *testing.T, user string, groups interface{}, policies interface{}) logicaltest.TestStep { - return logicaltest.TestStep{ - Operation: logical.UpdateOperation, - Path: "users/" + user, - Data: map[string]interface{}{ - "groups": groups, - "policies": policies, - }, - } -} - -func testAccGroups(t *testing.T, group string, policies interface{}) logicaltest.TestStep { - t.Logf("[testAccGroups] - Registering group %s, policy %s", group, policies) - return logicaltest.TestStep{ - Operation: logical.UpdateOperation, - Path: "groups/" + group, - Data: map[string]interface{}{ - "policies": policies, - }, - } -} - -func testAccLogin(t *testing.T, user, password string, keys []string) logicaltest.TestStep { - return logicaltest.TestStep{ - Operation: logical.UpdateOperation, - Path: "login/" + user, - Data: map[string]interface{}{ - "password": password, - }, - Unauthenticated: true, - - Check: logicaltest.TestCheckAuth(keys), - } -} diff --git a/builtin/credential/okta/cli.go b/builtin/credential/okta/cli.go deleted file mode 100644 index faa7f86f2..000000000 --- a/builtin/credential/okta/cli.go +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: BUSL-1.1 - -package okta - -import ( - "encoding/json" - "fmt" - "os" - "strings" - "time" - - "github.com/hashicorp/go-secure-stdlib/base62" - pwd "github.com/hashicorp/go-secure-stdlib/password" - "github.com/hashicorp/vault/api" -) - -// CLIHandler struct -type CLIHandler struct{} - -// Auth cli method -func (h *CLIHandler) Auth(c *api.Client, m map[string]string) (*api.Secret, error) { - mount, ok := m["mount"] - if !ok { - mount = "okta" - } - - username, ok := m["username"] - if !ok { - return nil, fmt.Errorf("'username' var must be set") - } - password, ok := m["password"] - if !ok { - fmt.Fprintf(os.Stderr, "Password (will be hidden): ") - var err error - password, err = pwd.Read(os.Stdin) - fmt.Fprintf(os.Stderr, "\n") - if err != nil { - return nil, err - } - } - - data := map[string]interface{}{ - "password": password, - } - - // Okta or Google totp code - if totp, ok := m["totp"]; ok { - data["totp"] = totp - } - - // provider is an optional parameter - if provider, ok := m["provider"]; ok { - data["provider"] = provider - } - - nonce := base62.MustRandom(20) - data["nonce"] = nonce - - // Create a done channel to signal termination of the login so that we can - // clean up the goroutine - doneCh := make(chan struct{}) - defer close(doneCh) - - go func() { - for { - timer := time.NewTimer(time.Second) - select { - case <-doneCh: - timer.Stop() - return - case <-timer.C: - } - - resp, _ := c.Logical().Read(fmt.Sprintf("auth/%s/verify/%s", mount, nonce)) - if resp != nil { - fmt.Fprintf(os.Stderr, "In Okta Verify, tap the number %q\n", resp.Data["correct_answer"].(json.Number)) - return - } - } - }() - - path := fmt.Sprintf("auth/%s/login/%s", mount, username) - secret, err := c.Logical().Write(path, data) - if err != nil { - return nil, err - } - if secret == nil { - return nil, fmt.Errorf("empty response from credential provider") - } - - return secret, nil -} - -// Help method for okta cli -func (h *CLIHandler) Help() string { - help := ` -Usage: vault login -method=okta [CONFIG K=V...] - - The Okta auth method allows users to authenticate using Okta. - - Authenticate as "sally": - - $ vault login -method=okta username=sally - Password (will be hidden): - - Authenticate as "bob": - - $ vault login -method=okta username=bob password=password - -Configuration: - - password= - Okta password to use for authentication. If not provided, the CLI will - prompt for this on stdin. - - username= - Okta username to use for authentication. -` - - return strings.TrimSpace(help) -} diff --git a/builtin/credential/okta/cmd/okta/main.go b/builtin/credential/okta/cmd/okta/main.go deleted file mode 100644 index 2b6c3e949..000000000 --- a/builtin/credential/okta/cmd/okta/main.go +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: BUSL-1.1 - -package main - -import ( - "os" - - hclog "github.com/hashicorp/go-hclog" - "github.com/hashicorp/vault/api" - "github.com/hashicorp/vault/builtin/credential/okta" - "github.com/hashicorp/vault/sdk/plugin" -) - -func main() { - apiClientMeta := &api.PluginAPIClientMeta{} - flags := apiClientMeta.FlagSet() - flags.Parse(os.Args[1:]) - - tlsConfig := apiClientMeta.GetTLSConfig() - tlsProviderFunc := api.VaultPluginTLSProvider(tlsConfig) - - if err := plugin.ServeMultiplex(&plugin.ServeOpts{ - BackendFactoryFunc: okta.Factory, - // set the TLSProviderFunc so that the plugin maintains backwards - // compatibility with Vault versions that don’t support plugin AutoMTLS - TLSProviderFunc: tlsProviderFunc, - }); err != nil { - logger := hclog.New(&hclog.LoggerOptions{}) - - logger.Error("plugin shutting down", "error", err) - os.Exit(1) - } -} diff --git a/builtin/credential/okta/path_config.go b/builtin/credential/okta/path_config.go deleted file mode 100644 index 6bdb241b2..000000000 --- a/builtin/credential/okta/path_config.go +++ /dev/null @@ -1,381 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: BUSL-1.1 - -package okta - -import ( - "context" - "fmt" - "net/http" - "net/url" - "strings" - "time" - - oktaold "github.com/chrismalek/oktasdk-go/okta" - "github.com/hashicorp/go-cleanhttp" - "github.com/hashicorp/vault/sdk/framework" - "github.com/hashicorp/vault/sdk/helper/tokenutil" - "github.com/hashicorp/vault/sdk/logical" - oktanew "github.com/okta/okta-sdk-golang/v2/okta" -) - -const ( - defaultBaseURL = "okta.com" - previewBaseURL = "oktapreview.com" -) - -func pathConfig(b *backend) *framework.Path { - p := &framework.Path{ - Pattern: `config`, - - DisplayAttrs: &framework.DisplayAttributes{ - OperationPrefix: operationPrefixOkta, - Action: "Configure", - }, - - Fields: map[string]*framework.FieldSchema{ - "organization": { - Type: framework.TypeString, - Description: "Use org_name instead.", - Deprecated: true, - }, - "org_name": { - Type: framework.TypeString, - Description: "Name of the organization to be used in the Okta API.", - DisplayAttrs: &framework.DisplayAttributes{ - Name: "Organization Name", - }, - }, - "token": { - Type: framework.TypeString, - Description: "Use api_token instead.", - Deprecated: true, - }, - "api_token": { - Type: framework.TypeString, - Description: "Okta API key.", - DisplayAttrs: &framework.DisplayAttributes{ - Name: "API Token", - }, - }, - "base_url": { - Type: framework.TypeString, - Description: `The base domain to use for the Okta API. When not specified in the configuration, "okta.com" is used.`, - DisplayAttrs: &framework.DisplayAttributes{ - Name: "Base URL", - }, - }, - "production": { - Type: framework.TypeBool, - Description: `Use base_url instead.`, - Deprecated: true, - }, - "ttl": { - Type: framework.TypeDurationSecond, - Description: tokenutil.DeprecationText("token_ttl"), - Deprecated: true, - }, - "max_ttl": { - Type: framework.TypeDurationSecond, - Description: tokenutil.DeprecationText("token_max_ttl"), - Deprecated: true, - }, - "bypass_okta_mfa": { - Type: framework.TypeBool, - Description: `When set true, requests by Okta for a MFA check will be bypassed. This also disallows certain status checks on the account, such as whether the password is expired.`, - DisplayAttrs: &framework.DisplayAttributes{ - Name: "Bypass Okta MFA", - }, - }, - }, - - Operations: map[logical.Operation]framework.OperationHandler{ - logical.ReadOperation: &framework.PathOperation{ - Callback: b.pathConfigRead, - DisplayAttrs: &framework.DisplayAttributes{ - OperationSuffix: "configuration", - }, - }, - logical.CreateOperation: &framework.PathOperation{ - Callback: b.pathConfigWrite, - DisplayAttrs: &framework.DisplayAttributes{ - OperationVerb: "configure", - }, - }, - logical.UpdateOperation: &framework.PathOperation{ - Callback: b.pathConfigWrite, - DisplayAttrs: &framework.DisplayAttributes{ - OperationVerb: "configure", - }, - }, - }, - - ExistenceCheck: b.pathConfigExistenceCheck, - - HelpSynopsis: pathConfigHelp, - } - - tokenutil.AddTokenFields(p.Fields) - p.Fields["token_policies"].Description += ". This will apply to all tokens generated by this auth method, in addition to any configured for specific users/groups." - return p -} - -// Config returns the configuration for this backend. -func (b *backend) Config(ctx context.Context, s logical.Storage) (*ConfigEntry, error) { - entry, err := s.Get(ctx, "config") - if err != nil { - return nil, err - } - if entry == nil { - return nil, nil - } - - var result ConfigEntry - if entry != nil { - if err := entry.DecodeJSON(&result); err != nil { - return nil, err - } - } - - if result.TokenTTL == 0 && result.TTL > 0 { - result.TokenTTL = result.TTL - } - if result.TokenMaxTTL == 0 && result.MaxTTL > 0 { - result.TokenMaxTTL = result.MaxTTL - } - - return &result, nil -} - -func (b *backend) pathConfigRead(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { - cfg, err := b.Config(ctx, req.Storage) - if err != nil { - return nil, err - } - if cfg == nil { - return nil, nil - } - - data := map[string]interface{}{ - "organization": cfg.Org, - "org_name": cfg.Org, - "bypass_okta_mfa": cfg.BypassOktaMFA, - } - cfg.PopulateTokenData(data) - - if cfg.BaseURL != "" { - data["base_url"] = cfg.BaseURL - } - if cfg.Production != nil { - data["production"] = *cfg.Production - } - if cfg.TTL > 0 { - data["ttl"] = int64(cfg.TTL.Seconds()) - } - if cfg.MaxTTL > 0 { - data["max_ttl"] = int64(cfg.MaxTTL.Seconds()) - } - - resp := &logical.Response{ - Data: data, - } - - if cfg.BypassOktaMFA { - resp.AddWarning("Okta MFA bypass is configured. In addition to ignoring Okta MFA requests, certain other account statuses will not be seen, such as PASSWORD_EXPIRED. Authentication will succeed in these cases.") - } - - return resp, nil -} - -func (b *backend) pathConfigWrite(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { - cfg, err := b.Config(ctx, req.Storage) - if err != nil { - return nil, err - } - - // Due to the existence check, entry will only be nil if it's a create - // operation, so just create a new one - if cfg == nil { - cfg = &ConfigEntry{} - } - - org, ok := d.GetOk("org_name") - if ok { - cfg.Org = org.(string) - } - if cfg.Org == "" { - org, ok = d.GetOk("organization") - if ok { - cfg.Org = org.(string) - } - } - if cfg.Org == "" && req.Operation == logical.CreateOperation { - return logical.ErrorResponse("org_name is missing"), nil - } - - token, ok := d.GetOk("api_token") - if ok { - cfg.Token = token.(string) - } else if token, ok = d.GetOk("token"); ok { - cfg.Token = token.(string) - } - - baseURLRaw, ok := d.GetOk("base_url") - if ok { - baseURL := baseURLRaw.(string) - _, err = url.Parse(fmt.Sprintf("https://%s,%s", cfg.Org, baseURL)) - if err != nil { - return logical.ErrorResponse(fmt.Sprintf("Error parsing given base_url: %s", err)), nil - } - cfg.BaseURL = baseURL - } - - // We only care about the production flag when base_url is not set. It is - // for compatibility reasons. - if cfg.BaseURL == "" { - productionRaw, ok := d.GetOk("production") - if ok { - production := productionRaw.(bool) - cfg.Production = &production - } - } else { - // clear out old production flag if base_url is set - cfg.Production = nil - } - - bypass, ok := d.GetOk("bypass_okta_mfa") - if ok { - cfg.BypassOktaMFA = bypass.(bool) - } - - if err := cfg.ParseTokenFields(req, d); err != nil { - return logical.ErrorResponse(err.Error()), logical.ErrInvalidRequest - } - - // Handle upgrade cases - { - if err := tokenutil.UpgradeValue(d, "ttl", "token_ttl", &cfg.TTL, &cfg.TokenTTL); err != nil { - return logical.ErrorResponse(err.Error()), nil - } - - if err := tokenutil.UpgradeValue(d, "max_ttl", "token_max_ttl", &cfg.MaxTTL, &cfg.TokenMaxTTL); err != nil { - return logical.ErrorResponse(err.Error()), nil - } - } - - jsonCfg, err := logical.StorageEntryJSON("config", cfg) - if err != nil { - return nil, err - } - if err := req.Storage.Put(ctx, jsonCfg); err != nil { - return nil, err - } - - var resp *logical.Response - if cfg.BypassOktaMFA { - resp = new(logical.Response) - resp.AddWarning("Okta MFA bypass is configured. In addition to ignoring Okta MFA requests, certain other account statuses will not be seen, such as PASSWORD_EXPIRED. Authentication will succeed in these cases.") - } - - return resp, nil -} - -func (b *backend) pathConfigExistenceCheck(ctx context.Context, req *logical.Request, d *framework.FieldData) (bool, error) { - cfg, err := b.Config(ctx, req.Storage) - if err != nil { - return false, err - } - - return cfg != nil, nil -} - -type oktaShim interface { - Client() (*oktanew.Client, context.Context) - NewRequest(method string, url string, body interface{}) (*http.Request, error) - Do(req *http.Request, v interface{}) (interface{}, error) -} - -type oktaShimNew struct { - client *oktanew.Client - ctx context.Context -} - -func (new *oktaShimNew) Client() (*oktanew.Client, context.Context) { - return new.client, new.ctx -} - -func (new *oktaShimNew) NewRequest(method string, url string, body interface{}) (*http.Request, error) { - if !strings.HasPrefix(url, "/") { - url = "/api/v1/" + url - } - return new.client.GetRequestExecutor().NewRequest(method, url, body) -} - -func (new *oktaShimNew) Do(req *http.Request, v interface{}) (interface{}, error) { - return new.client.GetRequestExecutor().Do(new.ctx, req, v) -} - -type oktaShimOld struct { - client *oktaold.Client -} - -func (new *oktaShimOld) Client() (*oktanew.Client, context.Context) { - return nil, nil -} - -func (new *oktaShimOld) NewRequest(method string, url string, body interface{}) (*http.Request, error) { - return new.client.NewRequest(method, url, body) -} - -func (new *oktaShimOld) Do(req *http.Request, v interface{}) (interface{}, error) { - return new.client.Do(req, v) -} - -// OktaClient creates a basic okta client connection -func (c *ConfigEntry) OktaClient(ctx context.Context) (oktaShim, error) { - baseURL := defaultBaseURL - if c.Production != nil { - if !*c.Production { - baseURL = previewBaseURL - } - } - if c.BaseURL != "" { - baseURL = c.BaseURL - } - - if c.Token != "" { - ctx, client, err := oktanew.NewClient(ctx, - oktanew.WithOrgUrl("https://"+c.Org+"."+baseURL), - oktanew.WithToken(c.Token)) - if err != nil { - return nil, err - } - return &oktaShimNew{client, ctx}, nil - } - client, err := oktaold.NewClientWithDomain(cleanhttp.DefaultClient(), c.Org, baseURL, "") - if err != nil { - return nil, err - } - return &oktaShimOld{client}, nil -} - -// ConfigEntry for Okta -type ConfigEntry struct { - tokenutil.TokenParams - - Org string `json:"organization"` - Token string `json:"token"` - BaseURL string `json:"base_url"` - Production *bool `json:"is_production,omitempty"` - TTL time.Duration `json:"ttl"` - MaxTTL time.Duration `json:"max_ttl"` - BypassOktaMFA bool `json:"bypass_okta_mfa"` -} - -const pathConfigHelp = ` -This endpoint allows you to configure the Okta and its -configuration options. - -The Okta organization are the characters at the front of the URL for Okta. -Example https://ORG.okta.com -` diff --git a/builtin/credential/okta/path_groups.go b/builtin/credential/okta/path_groups.go deleted file mode 100644 index 9ae9826d3..000000000 --- a/builtin/credential/okta/path_groups.go +++ /dev/null @@ -1,219 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: BUSL-1.1 - -package okta - -import ( - "context" - "strings" - - "github.com/hashicorp/vault/sdk/framework" - "github.com/hashicorp/vault/sdk/helper/policyutil" - "github.com/hashicorp/vault/sdk/logical" -) - -func pathGroupsList(b *backend) *framework.Path { - return &framework.Path{ - Pattern: "groups/?$", - - DisplayAttrs: &framework.DisplayAttributes{ - OperationPrefix: operationPrefixOkta, - OperationSuffix: "groups", - Navigation: true, - ItemType: "Group", - }, - - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.ListOperation: b.pathGroupList, - }, - - HelpSynopsis: pathGroupHelpSyn, - HelpDescription: pathGroupHelpDesc, - } -} - -func pathGroups(b *backend) *framework.Path { - return &framework.Path{ - Pattern: `groups/(?P.+)`, - - DisplayAttrs: &framework.DisplayAttributes{ - OperationPrefix: operationPrefixOkta, - OperationSuffix: "group", - Action: "Create", - ItemType: "Group", - }, - - Fields: map[string]*framework.FieldSchema{ - "name": { - Type: framework.TypeString, - Description: "Name of the Okta group.", - }, - - "policies": { - Type: framework.TypeCommaStringSlice, - Description: "Comma-separated list of policies associated to the group.", - DisplayAttrs: &framework.DisplayAttributes{ - Description: "A list of policies associated to the group.", - }, - }, - }, - - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.DeleteOperation: b.pathGroupDelete, - logical.ReadOperation: b.pathGroupRead, - logical.UpdateOperation: b.pathGroupWrite, - }, - - HelpSynopsis: pathGroupHelpSyn, - HelpDescription: pathGroupHelpDesc, - } -} - -// We look up groups in a case-insensitive manner since Okta is case-preserving -// but case-insensitive for comparisons -func (b *backend) Group(ctx context.Context, s logical.Storage, n string) (*GroupEntry, string, error) { - canonicalName := n - entry, err := s.Get(ctx, "group/"+n) - if err != nil { - return nil, "", err - } - if entry == nil { - entries, err := groupList(ctx, s) - if err != nil { - return nil, "", err - } - - for _, groupName := range entries { - if strings.EqualFold(groupName, n) { - entry, err = s.Get(ctx, "group/"+groupName) - if err != nil { - return nil, "", err - } - canonicalName = groupName - break - } - } - } - if entry == nil { - return nil, "", nil - } - - var result GroupEntry - if err := entry.DecodeJSON(&result); err != nil { - return nil, "", err - } - - return &result, canonicalName, nil -} - -func (b *backend) pathGroupDelete(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { - name := d.Get("name").(string) - if len(name) == 0 { - return logical.ErrorResponse("'name' must be supplied"), nil - } - - entry, canonicalName, err := b.Group(ctx, req.Storage, name) - if err != nil { - return nil, err - } - if entry != nil { - err := req.Storage.Delete(ctx, "group/"+canonicalName) - if err != nil { - return nil, err - } - } - - return nil, nil -} - -func (b *backend) pathGroupRead(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { - name := d.Get("name").(string) - if len(name) == 0 { - return logical.ErrorResponse("'name' must be supplied"), nil - } - - group, _, err := b.Group(ctx, req.Storage, name) - if err != nil { - return nil, err - } - if group == nil { - return nil, nil - } - - return &logical.Response{ - Data: map[string]interface{}{ - "policies": group.Policies, - }, - }, nil -} - -func (b *backend) pathGroupWrite(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { - name := d.Get("name").(string) - if len(name) == 0 { - return logical.ErrorResponse("'name' must be supplied"), nil - } - - // Check for an existing group, possibly lowercased so that we keep using - // existing user set values - _, canonicalName, err := b.Group(ctx, req.Storage, name) - if err != nil { - return nil, err - } - if canonicalName != "" { - name = canonicalName - } else { - name = strings.ToLower(name) - } - - entry, err := logical.StorageEntryJSON("group/"+name, &GroupEntry{ - Policies: policyutil.ParsePolicies(d.Get("policies")), - }) - if err != nil { - return nil, err - } - if err := req.Storage.Put(ctx, entry); err != nil { - return nil, err - } - - return nil, nil -} - -func (b *backend) pathGroupList(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { - groups, err := groupList(ctx, req.Storage) - if err != nil { - return nil, err - } - - return logical.ListResponse(groups), nil -} - -func groupList(ctx context.Context, s logical.Storage) ([]string, error) { - keys, err := logical.CollectKeysWithPrefix(ctx, s, "group/") - if err != nil { - return nil, err - } - - for i := range keys { - keys[i] = strings.TrimPrefix(keys[i], "group/") - } - - return keys, nil -} - -type GroupEntry struct { - Policies []string -} - -const pathGroupHelpSyn = ` -Manage users allowed to authenticate. -` - -const pathGroupHelpDesc = ` -This endpoint allows you to create, read, update, and delete configuration -for Okta groups that are allowed to authenticate, and associate policies to -them. - -Deleting a group will not revoke auth for prior authenticated users in that -group. To do this, do a revoke on "login/" for -the usernames you want revoked. -` diff --git a/builtin/credential/okta/path_groups_test.go b/builtin/credential/okta/path_groups_test.go deleted file mode 100644 index 7ca8a9415..000000000 --- a/builtin/credential/okta/path_groups_test.go +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: BUSL-1.1 - -package okta - -import ( - "context" - "strings" - "testing" - "time" - - "github.com/go-test/deep" - - log "github.com/hashicorp/go-hclog" - "github.com/hashicorp/vault/sdk/helper/logging" - "github.com/hashicorp/vault/sdk/logical" -) - -func TestGroupsList(t *testing.T) { - b, storage := getBackend(t) - - groups := []string{ - "%20\\", - "foo", - "zfoo", - "🙂", - "foo/nested", - "foo/even/more/nested", - } - - for _, group := range groups { - req := &logical.Request{ - Operation: logical.UpdateOperation, - Path: "groups/" + group, - Storage: storage, - Data: map[string]interface{}{ - "policies": []string{group + "_a", group + "_b"}, - }, - } - - resp, err := b.HandleRequest(context.Background(), req) - if err != nil || (resp != nil && resp.IsError()) { - t.Fatalf("err:%s resp:%#v\n", err, resp) - } - - } - - for _, group := range groups { - for _, upper := range []bool{false, true} { - groupPath := group - if upper { - groupPath = strings.ToUpper(group) - } - req := &logical.Request{ - Operation: logical.ReadOperation, - Path: "groups/" + groupPath, - Storage: storage, - } - - resp, err := b.HandleRequest(context.Background(), req) - if err != nil || (resp != nil && resp.IsError()) { - t.Fatalf("err:%s resp:%#v\n", err, resp) - } - if resp == nil { - t.Fatal("unexpected nil response") - } - - expected := []string{group + "_a", group + "_b"} - - if diff := deep.Equal(resp.Data["policies"].([]string), expected); diff != nil { - t.Fatal(diff) - } - } - } - - req := &logical.Request{ - Operation: logical.ListOperation, - Path: "groups", - Storage: storage, - } - - resp, err := b.HandleRequest(context.Background(), req) - if err != nil || (resp != nil && resp.IsError()) { - t.Fatalf("err:%s resp:%#v\n", err, resp) - } - - if diff := deep.Equal(resp.Data["keys"].([]string), groups); diff != nil { - t.Fatal(diff) - } -} - -func getBackend(t *testing.T) (logical.Backend, logical.Storage) { - defaultLeaseTTLVal := time.Hour * 12 - maxLeaseTTLVal := time.Hour * 24 - - config := &logical.BackendConfig{ - Logger: logging.NewVaultLogger(log.Trace), - - System: &logical.StaticSystemView{ - DefaultLeaseTTLVal: defaultLeaseTTLVal, - MaxLeaseTTLVal: maxLeaseTTLVal, - }, - StorageView: &logical.InmemStorage{}, - } - b, err := Factory(context.Background(), config) - if err != nil { - t.Fatalf("unable to create backend: %v", err) - } - - return b, config.StorageView -} diff --git a/builtin/credential/okta/path_login.go b/builtin/credential/okta/path_login.go deleted file mode 100644 index 5b86545d2..000000000 --- a/builtin/credential/okta/path_login.go +++ /dev/null @@ -1,255 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: BUSL-1.1 - -package okta - -import ( - "context" - "fmt" - "strings" - - "github.com/go-errors/errors" - "github.com/hashicorp/vault/sdk/framework" - "github.com/hashicorp/vault/sdk/helper/policyutil" - "github.com/hashicorp/vault/sdk/helper/strutil" - "github.com/hashicorp/vault/sdk/logical" -) - -const ( - googleProvider = "GOOGLE" - oktaProvider = "OKTA" -) - -func pathLogin(b *backend) *framework.Path { - return &framework.Path{ - Pattern: `login/(?P.+)`, - - DisplayAttrs: &framework.DisplayAttributes{ - OperationPrefix: operationPrefixOkta, - OperationVerb: "login", - }, - - Fields: map[string]*framework.FieldSchema{ - "username": { - Type: framework.TypeString, - Description: "Username to be used for login.", - }, - - "password": { - Type: framework.TypeString, - Description: "Password for this user.", - }, - "totp": { - Type: framework.TypeString, - Description: "TOTP passcode.", - }, - "nonce": { - Type: framework.TypeString, - Description: `Nonce provided if performing login that requires -number verification challenge. Logins through the vault login CLI command will -automatically generate a nonce.`, - }, - "provider": { - Type: framework.TypeString, - Description: "Preferred factor provider.", - }, - }, - - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.UpdateOperation: b.pathLogin, - logical.AliasLookaheadOperation: b.pathLoginAliasLookahead, - }, - - HelpSynopsis: pathLoginSyn, - HelpDescription: pathLoginDesc, - } -} - -func (b *backend) getSupportedProviders() []string { - return []string{googleProvider, oktaProvider} -} - -func (b *backend) pathLoginAliasLookahead(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { - username := d.Get("username").(string) - if username == "" { - return nil, fmt.Errorf("missing username") - } - - return &logical.Response{ - Auth: &logical.Auth{ - Alias: &logical.Alias{ - Name: username, - }, - }, - }, nil -} - -func (b *backend) pathLogin(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { - username := d.Get("username").(string) - password := d.Get("password").(string) - totp := d.Get("totp").(string) - nonce := d.Get("nonce").(string) - preferredProvider := strings.ToUpper(d.Get("provider").(string)) - if preferredProvider != "" && !strutil.StrListContains(b.getSupportedProviders(), preferredProvider) { - return logical.ErrorResponse(fmt.Sprintf("provider %s is not among the supported ones %v", preferredProvider, b.getSupportedProviders())), nil - } - - defer b.verifyCache.Delete(nonce) - - policies, resp, groupNames, err := b.Login(ctx, req, username, password, totp, nonce, preferredProvider) - // Handle an internal error - if err != nil { - return nil, err - } - if resp != nil { - // Handle a logical error - if resp.IsError() { - return resp, nil - } - } else { - resp = &logical.Response{} - } - - cfg, err := b.getConfig(ctx, req) - if err != nil { - return nil, err - } - - auth := &logical.Auth{ - Metadata: map[string]string{ - "username": username, - "policies": strings.Join(policies, ","), - }, - InternalData: map[string]interface{}{ - "password": password, - }, - DisplayName: username, - Alias: &logical.Alias{ - Name: username, - }, - } - cfg.PopulateTokenAuth(auth) - - // Add in configured policies from mappings - if len(policies) > 0 { - auth.Policies = append(auth.Policies, policies...) - } - - resp.Auth = auth - - for _, groupName := range groupNames { - if groupName == "" { - continue - } - resp.Auth.GroupAliases = append(resp.Auth.GroupAliases, &logical.Alias{ - Name: groupName, - }) - } - - return resp, nil -} - -func (b *backend) pathLoginRenew(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { - username := req.Auth.Metadata["username"] - password := req.Auth.InternalData["password"].(string) - - var nonce string - if d != nil { - nonce = d.Get("nonce").(string) - } - - cfg, err := b.getConfig(ctx, req) - if err != nil { - return nil, err - } - - // No TOTP entry is possible on renew. If push MFA is enabled it will still be triggered, however. - // Sending "" as the totp will prompt the push action if it is configured. - loginPolicies, resp, groupNames, err := b.Login(ctx, req, username, password, "", nonce, "") - if err != nil || (resp != nil && resp.IsError()) { - return resp, err - } - - finalPolicies := cfg.TokenPolicies - if len(loginPolicies) > 0 { - finalPolicies = append(finalPolicies, loginPolicies...) - } - if !policyutil.EquivalentPolicies(finalPolicies, req.Auth.TokenPolicies) { - return nil, fmt.Errorf("policies have changed, not renewing") - } - - resp.Auth = req.Auth - resp.Auth.Period = cfg.TokenPeriod - resp.Auth.TTL = cfg.TokenTTL - resp.Auth.MaxTTL = cfg.TokenMaxTTL - - // Remove old aliases - resp.Auth.GroupAliases = nil - - for _, groupName := range groupNames { - resp.Auth.GroupAliases = append(resp.Auth.GroupAliases, &logical.Alias{ - Name: groupName, - }) - } - - return resp, nil -} - -func pathVerify(b *backend) *framework.Path { - return &framework.Path{ - Pattern: `verify/(?P.+)`, - DisplayAttrs: &framework.DisplayAttributes{ - OperationPrefix: operationPrefixOkta, - OperationVerb: "verify", - }, - Fields: map[string]*framework.FieldSchema{ - "nonce": { - Type: framework.TypeString, - Description: `Nonce provided during a login request to -retrieve the number verification challenge for the matching request.`, - }, - }, - Operations: map[logical.Operation]framework.OperationHandler{ - logical.ReadOperation: &framework.PathOperation{ - Callback: b.pathVerify, - }, - }, - } -} - -func (b *backend) pathVerify(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { - nonce := d.Get("nonce").(string) - - correctRaw, ok := b.verifyCache.Get(nonce) - if !ok { - return nil, nil - } - - resp := &logical.Response{ - Data: map[string]interface{}{ - "correct_answer": correctRaw.(int), - }, - } - - return resp, nil -} - -func (b *backend) getConfig(ctx context.Context, req *logical.Request) (*ConfigEntry, error) { - cfg, err := b.Config(ctx, req.Storage) - if err != nil { - return nil, err - } - if cfg == nil { - return nil, errors.New("Okta backend not configured") - } - - return cfg, nil -} - -const pathLoginSyn = ` -Log in with a username and password. -` - -const pathLoginDesc = ` -This endpoint authenticates using a username and password. -` diff --git a/builtin/credential/okta/path_users.go b/builtin/credential/okta/path_users.go deleted file mode 100644 index d66a5ed46..000000000 --- a/builtin/credential/okta/path_users.go +++ /dev/null @@ -1,173 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: BUSL-1.1 - -package okta - -import ( - "context" - - "github.com/hashicorp/vault/sdk/framework" - "github.com/hashicorp/vault/sdk/logical" -) - -func pathUsersList(b *backend) *framework.Path { - return &framework.Path{ - Pattern: "users/?$", - - DisplayAttrs: &framework.DisplayAttributes{ - OperationPrefix: operationPrefixOkta, - OperationSuffix: "users", - Navigation: true, - ItemType: "User", - }, - - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.ListOperation: b.pathUserList, - }, - - HelpSynopsis: pathUserHelpSyn, - HelpDescription: pathUserHelpDesc, - } -} - -func pathUsers(b *backend) *framework.Path { - return &framework.Path{ - Pattern: `users/(?P.+)`, - - DisplayAttrs: &framework.DisplayAttributes{ - OperationPrefix: operationPrefixOkta, - OperationSuffix: "user", - Action: "Create", - ItemType: "User", - }, - - Fields: map[string]*framework.FieldSchema{ - "name": { - Type: framework.TypeString, - Description: "Name of the user.", - }, - - "groups": { - Type: framework.TypeCommaStringSlice, - Description: "List of groups associated with the user.", - }, - - "policies": { - Type: framework.TypeCommaStringSlice, - Description: "List of policies associated with the user.", - }, - }, - - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.DeleteOperation: b.pathUserDelete, - logical.ReadOperation: b.pathUserRead, - logical.UpdateOperation: b.pathUserWrite, - }, - - HelpSynopsis: pathUserHelpSyn, - HelpDescription: pathUserHelpDesc, - } -} - -func (b *backend) User(ctx context.Context, s logical.Storage, n string) (*UserEntry, error) { - entry, err := s.Get(ctx, "user/"+n) - if err != nil { - return nil, err - } - if entry == nil { - return nil, nil - } - - var result UserEntry - if err := entry.DecodeJSON(&result); err != nil { - return nil, err - } - - return &result, nil -} - -func (b *backend) pathUserDelete(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { - name := d.Get("name").(string) - if len(name) == 0 { - return logical.ErrorResponse("Error empty name"), nil - } - - err := req.Storage.Delete(ctx, "user/"+name) - if err != nil { - return nil, err - } - - return nil, nil -} - -func (b *backend) pathUserRead(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { - name := d.Get("name").(string) - if len(name) == 0 { - return logical.ErrorResponse("Error empty name"), nil - } - - user, err := b.User(ctx, req.Storage, name) - if err != nil { - return nil, err - } - if user == nil { - return nil, nil - } - - return &logical.Response{ - Data: map[string]interface{}{ - "groups": user.Groups, - "policies": user.Policies, - }, - }, nil -} - -func (b *backend) pathUserWrite(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { - name := d.Get("name").(string) - if len(name) == 0 { - return logical.ErrorResponse("Error empty name"), nil - } - - groups := d.Get("groups").([]string) - policies := d.Get("policies").([]string) - - // Store it - entry, err := logical.StorageEntryJSON("user/"+name, &UserEntry{ - Groups: groups, - Policies: policies, - }) - if err != nil { - return nil, err - } - if err := req.Storage.Put(ctx, entry); err != nil { - return nil, err - } - - return nil, nil -} - -func (b *backend) pathUserList(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { - users, err := req.Storage.List(ctx, "user/") - if err != nil { - return nil, err - } - return logical.ListResponse(users), nil -} - -type UserEntry struct { - Groups []string - Policies []string -} - -const pathUserHelpSyn = ` -Manage additional groups for users allowed to authenticate. -` - -const pathUserHelpDesc = ` -This endpoint allows you to create, read, update, and delete configuration -for Okta users that are allowed to authenticate, in particular associating -additional groups to them. - -Deleting a user will not revoke their auth. To do this, do a revoke on "login/" for -the usernames you want revoked. -` diff --git a/command/base_predict.go b/command/base_predict.go index ad54526bf..a64fbd7bc 100644 --- a/command/base_predict.go +++ b/command/base_predict.go @@ -107,7 +107,6 @@ func (b *BaseCommand) PredictVaultAvailableAuths() complete.Predictor { "cert", "github", "ldap", - "okta", "plugin", "radius", "userpass", diff --git a/command/commands.go b/command/commands.go index dfd2b5352..7c241f5fb 100644 --- a/command/commands.go +++ b/command/commands.go @@ -33,7 +33,6 @@ import ( credCert "github.com/hashicorp/vault/builtin/credential/cert" credGitHub "github.com/hashicorp/vault/builtin/credential/github" credLdap "github.com/hashicorp/vault/builtin/credential/ldap" - credOkta "github.com/hashicorp/vault/builtin/credential/okta" credToken "github.com/hashicorp/vault/builtin/credential/token" credUserpass "github.com/hashicorp/vault/builtin/credential/userpass" @@ -187,7 +186,6 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) map[string]cli.Co "kerberos": &credKerb.CLIHandler{}, "ldap": &credLdap.CLIHandler{}, "oidc": &credOIDC.CLIHandler{}, - "okta": &credOkta.CLIHandler{}, "pcf": &credCF.CLIHandler{}, // Deprecated. "radius": &credUserpass.CLIHandler{ DefaultMount: "radius", diff --git a/go.mod b/go.mod index 1209a5cf0..1f8a2503f 100644 --- a/go.mod +++ b/go.mod @@ -31,9 +31,7 @@ require ( github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef github.com/axiomhq/hyperloglog v0.0.0-20220105174342-98591331716a github.com/cenkalti/backoff/v3 v3.2.2 - github.com/chrismalek/oktasdk-go v0.0.0-20181212195951-3430665dfaa0 github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf - github.com/duosecurity/duo_api_golang v0.0.0-20190308151101-6c680f768e74 github.com/dustin/go-humanize v1.0.1 github.com/fatih/color v1.16.0 github.com/fatih/structs v1.1.0 @@ -45,7 +43,6 @@ require ( github.com/go-sql-driver/mysql v1.6.0 github.com/go-test/deep v1.1.0 github.com/gocql/gocql v1.0.0 - github.com/golang-jwt/jwt/v4 v4.5.0 github.com/golang/protobuf v1.5.3 github.com/google/go-cmp v0.6.0 github.com/google/go-github v17.0.0+incompatible @@ -136,7 +133,6 @@ require ( github.com/mitchellh/reflectwalk v1.0.2 github.com/natefinch/atomic v0.0.0-20150920032501-a62ce929ffcc github.com/oklog/run v1.1.0 - github.com/okta/okta-sdk-golang/v2 v2.12.1 github.com/ory/dockertest v3.3.5+incompatible github.com/patrickmn/go-cache v2.1.0+incompatible github.com/pires/go-proxyproto v0.6.1 @@ -208,7 +204,6 @@ require ( github.com/bgentry/speakeasy v0.1.0 // indirect github.com/boombuler/barcode v1.0.1 // indirect github.com/cenkalti/backoff v2.2.1+incompatible // indirect - github.com/cenkalti/backoff/v4 v4.2.1 // indirect github.com/centrify/cloud-golang-sdk v0.0.0-20210923165758-a8c48d049166 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible // indirect @@ -253,6 +248,7 @@ require ( github.com/go-ozzo/ozzo-validation v3.6.0+incompatible // indirect github.com/gofrs/uuid v4.3.0+incompatible // indirect github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang-jwt/jwt/v4 v4.5.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/gnostic v0.5.7-v3refs // indirect @@ -298,7 +294,6 @@ require ( github.com/josharian/intern v1.0.0 // indirect github.com/joyent/triton-go v1.7.1-0.20200416154420-6801d15b779f // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/kelseyhightower/envconfig v1.4.0 // indirect github.com/klauspost/pgzip v1.2.5 // indirect github.com/kylelemons/godebug v1.1.0 // indirect github.com/lib/pq v1.10.9 // indirect diff --git a/go.sum b/go.sum index f75c9d71a..7f52e7e7b 100644 --- a/go.sum +++ b/go.sum @@ -1054,8 +1054,6 @@ github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL github.com/checkpoint-restore/go-criu/v4 v4.1.0/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw= github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M= github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E= -github.com/chrismalek/oktasdk-go v0.0.0-20181212195951-3430665dfaa0 h1:CWU8piLyqoi9qXEUwzOh5KFKGgmSU5ZhktJyYcq6ryQ= -github.com/chrismalek/oktasdk-go v0.0.0-20181212195951-3430665dfaa0/go.mod h1:5d8DqS60xkj9k3aXfL3+mXBH0DPYO0FQjcKosxl+b/Q= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -1336,8 +1334,6 @@ github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3 github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 h1:iFaUwBSo5Svw6L7HYpRu/0lE3e0BaElwnNO1qkNQxBY= github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s= github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY= -github.com/duosecurity/duo_api_golang v0.0.0-20190308151101-6c680f768e74 h1:2MIhn2R6oXQbgW5yHfS+d6YqyMfXiu2L55rFZC4UD/M= -github.com/duosecurity/duo_api_golang v0.0.0-20190308151101-6c680f768e74/go.mod h1:UqXY1lYT/ERa4OEAywUqdok1T4RCRdArkhic1Opuavo= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= @@ -2190,8 +2186,6 @@ github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= -github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= -github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= @@ -2426,8 +2420,6 @@ github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQ github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/okta/okta-sdk-golang/v2 v2.12.1 h1:U+smE7trkHSZO8Mval3Ow85dbxawO+pMAr692VZq9gM= -github.com/okta/okta-sdk-golang/v2 v2.12.1/go.mod h1:KRoAArk1H216oiRnQT77UN6JAhBOnOWkK27yA1SM7FQ= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/olekukonko/tablewriter v0.0.0-20180130162743-b8a9be070da4/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= diff --git a/helper/builtinplugins/registry.go b/helper/builtinplugins/registry.go index 2a79701a6..bc0e01ced 100644 --- a/helper/builtinplugins/registry.go +++ b/helper/builtinplugins/registry.go @@ -20,7 +20,6 @@ import ( credCert "github.com/hashicorp/vault/builtin/credential/cert" credGitHub "github.com/hashicorp/vault/builtin/credential/github" credLdap "github.com/hashicorp/vault/builtin/credential/ldap" - credOkta "github.com/hashicorp/vault/builtin/credential/okta" credRadius "github.com/hashicorp/vault/builtin/credential/radius" credUserpass "github.com/hashicorp/vault/builtin/credential/userpass" logicalNomad "github.com/hashicorp/vault/builtin/logical/nomad" @@ -91,7 +90,6 @@ func newRegistry() *registry { "kubernetes": {Factory: credKube.Factory}, "ldap": {Factory: credLdap.Factory}, "oidc": {Factory: credJWT.Factory}, - "okta": {Factory: credOkta.Factory}, "pcf": { Factory: credCF.Factory, DeprecationStatus: consts.Deprecated, diff --git a/helper/identity/mfa/types.pb.go b/helper/identity/mfa/types.pb.go index 9d16644dd..33b9c4887 100644 --- a/helper/identity/mfa/types.pb.go +++ b/helper/identity/mfa/types.pb.go @@ -312,17 +312,6 @@ type DuoConfig struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - - // @inject_tag: sentinel:"-" - IntegrationKey string `protobuf:"bytes,1,opt,name=integration_key,json=integrationKey,proto3" json:"integration_key,omitempty" sentinel:"-"` - // @inject_tag: sentinel:"-" - SecretKey string `protobuf:"bytes,2,opt,name=secret_key,json=secretKey,proto3" json:"secret_key,omitempty" sentinel:"-"` - // @inject_tag: sentinel:"-" - APIHostname string `protobuf:"bytes,3,opt,name=api_hostname,json=apiHostname,proto3" json:"api_hostname,omitempty" sentinel:"-"` - // @inject_tag: sentinel:"-" - PushInfo string `protobuf:"bytes,4,opt,name=push_info,json=pushInfo,proto3" json:"push_info,omitempty" sentinel:"-"` - // @inject_tag: sentinel:"-" - UsePasscode bool `protobuf:"varint,5,opt,name=use_passcode,json=usePasscode,proto3" json:"use_passcode,omitempty" sentinel:"-"` } func (x *DuoConfig) Reset() { @@ -357,58 +346,12 @@ func (*DuoConfig) Descriptor() ([]byte, []int) { return file_helper_identity_mfa_types_proto_rawDescGZIP(), []int{2} } -func (x *DuoConfig) GetIntegrationKey() string { - if x != nil { - return x.IntegrationKey - } - return "" -} - -func (x *DuoConfig) GetSecretKey() string { - if x != nil { - return x.SecretKey - } - return "" -} - -func (x *DuoConfig) GetAPIHostname() string { - if x != nil { - return x.APIHostname - } - return "" -} - -func (x *DuoConfig) GetPushInfo() string { - if x != nil { - return x.PushInfo - } - return "" -} - -func (x *DuoConfig) GetUsePasscode() bool { - if x != nil { - return x.UsePasscode - } - return false -} - // OktaConfig contains Okta configuration parameters required to perform Okta // authentication. type OktaConfig struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - - // @inject_tag: sentinel:"-" - OrgName string `protobuf:"bytes,1,opt,name=org_name,json=orgName,proto3" json:"org_name,omitempty" sentinel:"-"` - // @inject_tag: sentinel:"-" - APIToken string `protobuf:"bytes,2,opt,name=api_token,json=apiToken,proto3" json:"api_token,omitempty" sentinel:"-"` - // @inject_tag: sentinel:"-" - Production bool `protobuf:"varint,3,opt,name=production,proto3" json:"production,omitempty" sentinel:"-"` - // @inject_tag: sentinel:"-" - BaseURL string `protobuf:"bytes,4,opt,name=base_url,json=baseUrl,proto3" json:"base_url,omitempty" sentinel:"-"` - // @inject_tag: sentinel:"-" - PrimaryEmail bool `protobuf:"varint,5,opt,name=primary_email,json=primaryEmail,proto3" json:"primary_email,omitempty" sentinel:"-"` } func (x *OktaConfig) Reset() { @@ -443,61 +386,11 @@ func (*OktaConfig) Descriptor() ([]byte, []int) { return file_helper_identity_mfa_types_proto_rawDescGZIP(), []int{3} } -func (x *OktaConfig) GetOrgName() string { - if x != nil { - return x.OrgName - } - return "" -} - -func (x *OktaConfig) GetAPIToken() string { - if x != nil { - return x.APIToken - } - return "" -} - -func (x *OktaConfig) GetProduction() bool { - if x != nil { - return x.Production - } - return false -} - -func (x *OktaConfig) GetBaseURL() string { - if x != nil { - return x.BaseURL - } - return "" -} - -func (x *OktaConfig) GetPrimaryEmail() bool { - if x != nil { - return x.PrimaryEmail - } - return false -} - // PingIDConfig contains PingID configuration information type PingIDConfig struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - - // @inject_tag: sentinel:"-" - UseBase64Key string `protobuf:"bytes,1,opt,name=use_base64_key,json=useBase64Key,proto3" json:"use_base64_key,omitempty" sentinel:"-"` - // @inject_tag: sentinel:"-" - UseSignature bool `protobuf:"varint,2,opt,name=use_signature,json=useSignature,proto3" json:"use_signature,omitempty" sentinel:"-"` - // @inject_tag: sentinel:"-" - Token string `protobuf:"bytes,3,opt,name=token,proto3" json:"token,omitempty" sentinel:"-"` - // @inject_tag: sentinel:"-" - IDPURL string `protobuf:"bytes,4,opt,name=idp_url,json=idpUrl,proto3" json:"idp_url,omitempty" sentinel:"-"` - // @inject_tag: sentinel:"-" - OrgAlias string `protobuf:"bytes,5,opt,name=org_alias,json=orgAlias,proto3" json:"org_alias,omitempty" sentinel:"-"` - // @inject_tag: sentinel:"-" - AdminURL string `protobuf:"bytes,6,opt,name=admin_url,json=adminUrl,proto3" json:"admin_url,omitempty" sentinel:"-"` - // @inject_tag: sentinel:"-" - AuthenticatorURL string `protobuf:"bytes,7,opt,name=authenticator_url,json=authenticatorUrl,proto3" json:"authenticator_url,omitempty" sentinel:"-"` } func (x *PingIDConfig) Reset() { @@ -532,55 +425,6 @@ func (*PingIDConfig) Descriptor() ([]byte, []int) { return file_helper_identity_mfa_types_proto_rawDescGZIP(), []int{4} } -func (x *PingIDConfig) GetUseBase64Key() string { - if x != nil { - return x.UseBase64Key - } - return "" -} - -func (x *PingIDConfig) GetUseSignature() bool { - if x != nil { - return x.UseSignature - } - return false -} - -func (x *PingIDConfig) GetToken() string { - if x != nil { - return x.Token - } - return "" -} - -func (x *PingIDConfig) GetIDPURL() string { - if x != nil { - return x.IDPURL - } - return "" -} - -func (x *PingIDConfig) GetOrgAlias() string { - if x != nil { - return x.OrgAlias - } - return "" -} - -func (x *PingIDConfig) GetAdminURL() string { - if x != nil { - return x.AdminURL - } - return "" -} - -func (x *PingIDConfig) GetAuthenticatorURL() string { - if x != nil { - return x.AuthenticatorURL - } - return "" -} - // Secret represents all the types of secrets which the entity can hold. // Each MFA type should add a secret type to the oneof block in this message. type Secret struct { @@ -925,88 +769,54 @@ var file_helper_identity_mfa_types_proto_rawDesc = []byte{ 0x71, 0x72, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x36, 0x0a, 0x17, 0x6d, 0x61, 0x78, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x61, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x15, 0x6d, 0x61, 0x78, 0x56, 0x61, 0x6c, 0x69, - 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x73, 0x22, 0xb6, - 0x01, 0x0a, 0x09, 0x44, 0x75, 0x6f, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x27, 0x0a, 0x0f, - 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6b, 0x65, 0x79, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x5f, - 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, 0x63, 0x72, 0x65, - 0x74, 0x4b, 0x65, 0x79, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x70, 0x69, 0x5f, 0x68, 0x6f, 0x73, 0x74, - 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x70, 0x69, 0x48, - 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x75, 0x73, 0x68, 0x5f, - 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x75, 0x73, 0x68, - 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x21, 0x0a, 0x0c, 0x75, 0x73, 0x65, 0x5f, 0x70, 0x61, 0x73, 0x73, - 0x63, 0x6f, 0x64, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x75, 0x73, 0x65, 0x50, - 0x61, 0x73, 0x73, 0x63, 0x6f, 0x64, 0x65, 0x22, 0xa4, 0x01, 0x0a, 0x0a, 0x4f, 0x6b, 0x74, 0x61, - 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x19, 0x0a, 0x08, 0x6f, 0x72, 0x67, 0x5f, 0x6e, 0x61, - 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6f, 0x72, 0x67, 0x4e, 0x61, 0x6d, - 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x61, 0x70, 0x69, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x61, 0x70, 0x69, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1e, - 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x19, - 0x0a, 0x08, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x07, 0x62, 0x61, 0x73, 0x65, 0x55, 0x72, 0x6c, 0x12, 0x23, 0x0a, 0x0d, 0x70, 0x72, 0x69, - 0x6d, 0x61, 0x72, 0x79, 0x5f, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x0c, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x22, 0xef, - 0x01, 0x0a, 0x0c, 0x50, 0x69, 0x6e, 0x67, 0x49, 0x44, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, - 0x24, 0x0a, 0x0e, 0x75, 0x73, 0x65, 0x5f, 0x62, 0x61, 0x73, 0x65, 0x36, 0x34, 0x5f, 0x6b, 0x65, - 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x75, 0x73, 0x65, 0x42, 0x61, 0x73, 0x65, - 0x36, 0x34, 0x4b, 0x65, 0x79, 0x12, 0x23, 0x0a, 0x0d, 0x75, 0x73, 0x65, 0x5f, 0x73, 0x69, 0x67, - 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x75, 0x73, - 0x65, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, - 0x6b, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, - 0x12, 0x17, 0x0a, 0x07, 0x69, 0x64, 0x70, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x06, 0x69, 0x64, 0x70, 0x55, 0x72, 0x6c, 0x12, 0x1b, 0x0a, 0x09, 0x6f, 0x72, 0x67, - 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6f, 0x72, - 0x67, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x5f, - 0x75, 0x72, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x61, 0x64, 0x6d, 0x69, 0x6e, - 0x55, 0x72, 0x6c, 0x12, 0x2b, 0x0a, 0x11, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, - 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, - 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x6f, 0x72, 0x55, 0x72, 0x6c, - 0x22, 0x66, 0x0a, 0x06, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x65, - 0x74, 0x68, 0x6f, 0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0a, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x32, 0x0a, 0x0b, 0x74, - 0x6f, 0x74, 0x70, 0x5f, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x0f, 0x2e, 0x6d, 0x66, 0x61, 0x2e, 0x54, 0x4f, 0x54, 0x50, 0x53, 0x65, 0x63, 0x72, 0x65, - 0x74, 0x48, 0x00, 0x52, 0x0a, 0x74, 0x6f, 0x74, 0x70, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x42, - 0x07, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0xd6, 0x01, 0x0a, 0x0a, 0x54, 0x4f, 0x54, - 0x50, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x69, 0x73, 0x73, 0x75, 0x65, - 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x69, 0x73, 0x73, 0x75, 0x65, 0x72, 0x12, - 0x16, 0x0a, 0x06, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, - 0x06, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x6c, 0x67, 0x6f, 0x72, - 0x69, 0x74, 0x68, 0x6d, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x61, 0x6c, 0x67, 0x6f, - 0x72, 0x69, 0x74, 0x68, 0x6d, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x69, 0x67, 0x69, 0x74, 0x73, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x64, 0x69, 0x67, 0x69, 0x74, 0x73, 0x12, 0x12, 0x0a, - 0x04, 0x73, 0x6b, 0x65, 0x77, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x73, 0x6b, 0x65, - 0x77, 0x12, 0x19, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x06, 0x20, - 0x01, 0x28, 0x0d, 0x52, 0x07, 0x6b, 0x65, 0x79, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x21, 0x0a, 0x0c, - 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x08, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0b, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, - 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, - 0x79, 0x22, 0xc1, 0x02, 0x0a, 0x14, 0x4d, 0x46, 0x41, 0x45, 0x6e, 0x66, 0x6f, 0x72, 0x63, 0x65, - 0x6d, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x21, - 0x0a, 0x0c, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, - 0x64, 0x12, 0x24, 0x0a, 0x0e, 0x6d, 0x66, 0x61, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x5f, - 0x69, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x6d, 0x66, 0x61, 0x4d, 0x65, - 0x74, 0x68, 0x6f, 0x64, 0x49, 0x64, 0x73, 0x12, 0x32, 0x0a, 0x15, 0x61, 0x75, 0x74, 0x68, 0x5f, - 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x73, - 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x13, 0x61, 0x75, 0x74, 0x68, 0x4d, 0x65, 0x74, 0x68, - 0x6f, 0x64, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x61, - 0x75, 0x74, 0x68, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x73, - 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x61, 0x75, 0x74, 0x68, 0x4d, 0x65, 0x74, 0x68, - 0x6f, 0x64, 0x54, 0x79, 0x70, 0x65, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x69, 0x64, 0x65, 0x6e, 0x74, - 0x69, 0x74, 0x79, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x06, 0x20, - 0x03, 0x28, 0x09, 0x52, 0x10, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x47, 0x72, 0x6f, - 0x75, 0x70, 0x49, 0x64, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, - 0x79, 0x5f, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x07, 0x20, 0x03, - 0x28, 0x09, 0x52, 0x11, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x45, 0x6e, 0x74, 0x69, - 0x74, 0x79, 0x49, 0x64, 0x73, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x02, 0x69, 0x64, 0x42, 0x30, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, - 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x76, 0x61, - 0x75, 0x6c, 0x74, 0x2f, 0x68, 0x65, 0x6c, 0x70, 0x65, 0x72, 0x2f, 0x69, 0x64, 0x65, 0x6e, 0x74, - 0x69, 0x74, 0x79, 0x2f, 0x6d, 0x66, 0x61, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x73, 0x22, 0x0b, + 0x0a, 0x09, 0x44, 0x75, 0x6f, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x0c, 0x0a, 0x0a, 0x4f, + 0x6b, 0x74, 0x61, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x0e, 0x0a, 0x0c, 0x50, 0x69, 0x6e, + 0x67, 0x49, 0x44, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x66, 0x0a, 0x06, 0x53, 0x65, 0x63, + 0x72, 0x65, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x5f, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, + 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x32, 0x0a, 0x0b, 0x74, 0x6f, 0x74, 0x70, 0x5f, 0x73, 0x65, 0x63, + 0x72, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6d, 0x66, 0x61, 0x2e, + 0x54, 0x4f, 0x54, 0x50, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x48, 0x00, 0x52, 0x0a, 0x74, 0x6f, + 0x74, 0x70, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x42, 0x07, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x22, 0xd6, 0x01, 0x0a, 0x0a, 0x54, 0x4f, 0x54, 0x50, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, + 0x12, 0x16, 0x0a, 0x06, 0x69, 0x73, 0x73, 0x75, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x06, 0x69, 0x73, 0x73, 0x75, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x65, 0x72, 0x69, + 0x6f, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, + 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x09, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x12, 0x16, + 0x0a, 0x06, 0x64, 0x69, 0x67, 0x69, 0x74, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, + 0x64, 0x69, 0x67, 0x69, 0x74, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x6b, 0x65, 0x77, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x73, 0x6b, 0x65, 0x77, 0x12, 0x19, 0x0a, 0x08, 0x6b, 0x65, + 0x79, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x6b, 0x65, + 0x79, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x63, 0x63, + 0x6f, 0x75, 0x6e, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, + 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x22, 0xc1, 0x02, 0x0a, 0x14, 0x4d, + 0x46, 0x41, 0x45, 0x6e, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x6e, 0x61, 0x6d, 0x65, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6e, + 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x64, 0x12, 0x24, 0x0a, 0x0e, 0x6d, 0x66, + 0x61, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, + 0x28, 0x09, 0x52, 0x0c, 0x6d, 0x66, 0x61, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x49, 0x64, 0x73, + 0x12, 0x32, 0x0a, 0x15, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x5f, + 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, + 0x13, 0x61, 0x75, 0x74, 0x68, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x41, 0x63, 0x63, 0x65, 0x73, + 0x73, 0x6f, 0x72, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x6d, 0x65, 0x74, + 0x68, 0x6f, 0x64, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, + 0x0f, 0x61, 0x75, 0x74, 0x68, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x54, 0x79, 0x70, 0x65, 0x73, + 0x12, 0x2c, 0x0a, 0x12, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x67, 0x72, 0x6f, + 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x69, 0x64, + 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, 0x12, 0x2e, + 0x0a, 0x13, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x65, 0x6e, 0x74, 0x69, 0x74, + 0x79, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x11, 0x69, 0x64, 0x65, + 0x6e, 0x74, 0x69, 0x74, 0x79, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x49, 0x64, 0x73, 0x12, 0x0e, + 0x0a, 0x02, 0x69, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x42, 0x30, + 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, + 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x2f, 0x68, 0x65, 0x6c, + 0x70, 0x65, 0x72, 0x2f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2f, 0x6d, 0x66, 0x61, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/helper/identity/mfa/types.proto b/helper/identity/mfa/types.proto index 412233998..3d8cd7cb6 100644 --- a/helper/identity/mfa/types.proto +++ b/helper/identity/mfa/types.proto @@ -60,49 +60,15 @@ message TOTPConfig { // DuoConfig represents the configuration information required to perform // Duo authentication. message DuoConfig { - // @inject_tag: sentinel:"-" - string integration_key = 1; - // @inject_tag: sentinel:"-" - string secret_key = 2; - // @inject_tag: sentinel:"-" - string api_hostname = 3; - // @inject_tag: sentinel:"-" - string push_info = 4; - // @inject_tag: sentinel:"-" - bool use_passcode = 5; } // OktaConfig contains Okta configuration parameters required to perform Okta // authentication. message OktaConfig { - // @inject_tag: sentinel:"-" - string org_name = 1; - // @inject_tag: sentinel:"-" - string api_token = 2; - // @inject_tag: sentinel:"-" - bool production = 3; - // @inject_tag: sentinel:"-" - string base_url = 4; - // @inject_tag: sentinel:"-" - bool primary_email = 5; } // PingIDConfig contains PingID configuration information message PingIDConfig { - // @inject_tag: sentinel:"-" - string use_base64_key = 1; - // @inject_tag: sentinel:"-" - bool use_signature = 2; - // @inject_tag: sentinel:"-" - string token = 3; - // @inject_tag: sentinel:"-" - string idp_url = 4; - // @inject_tag: sentinel:"-" - string org_alias = 5; - // @inject_tag: sentinel:"-" - string admin_url = 6; - // @inject_tag: sentinel:"-" - string authenticator_url = 7; } // Secret represents all the types of secrets which the entity can hold. diff --git a/scripts/gen_openapi.sh b/scripts/gen_openapi.sh index 798c70f48..e00d6ee34 100755 --- a/scripts/gen_openapi.sh +++ b/scripts/gen_openapi.sh @@ -59,7 +59,6 @@ vault auth enable "jwt" vault auth enable "kerberos" vault auth enable "kubernetes" vault auth enable "ldap" -vault auth enable "okta" vault auth enable "radius" vault auth enable "userpass" diff --git a/ui/app/adapters/auth-config/okta.js b/ui/app/adapters/auth-config/okta.js deleted file mode 100644 index 1a1c1a672..000000000 --- a/ui/app/adapters/auth-config/okta.js +++ /dev/null @@ -1,8 +0,0 @@ -/** - * Copyright (c) HashiCorp, Inc. - * SPDX-License-Identifier: BUSL-1.1 - */ - -import AuthConfig from './_base'; - -export default AuthConfig.extend(); diff --git a/ui/app/adapters/cluster.js b/ui/app/adapters/cluster.js index e9f1136e9..8941f4253 100644 --- a/ui/app/adapters/cluster.js +++ b/ui/app/adapters/cluster.js @@ -124,8 +124,6 @@ export default ApplicationAdapter.extend({ }; } else if (backend === 'jwt' || backend === 'oidc') { options.data = { role, jwt }; - } else if (backend === 'okta') { - options.data = { password, nonce }; } else { options.data = token ? { token, password } : { password }; } @@ -140,13 +138,7 @@ export default ApplicationAdapter.extend({ mfa_payload: mfa_constraints.reduce((obj, { selectedMethod, passcode }) => { let payload = []; if (passcode) { - // duo requires passcode= prepended to the actual passcode - // this isn't a great UX so we add it behind the scenes to fulfill the requirement - // check if user added passcode= to avoid duplication - payload = - selectedMethod.type === 'duo' && !passcode.includes('passcode=') - ? [`passcode=${passcode}`] - : [passcode]; + payload = [passcode]; } obj[selectedMethod.id] = payload; return obj; @@ -173,7 +165,6 @@ export default ApplicationAdapter.extend({ oidc: 'login', userpass: `login/${encodeURIComponent(username)}`, ldap: `login/${encodeURIComponent(username)}`, - okta: `login/${encodeURIComponent(username)}`, radius: `login/${encodeURIComponent(username)}`, token: 'lookup-self', }; diff --git a/ui/app/components/auth-form.js b/ui/app/components/auth-form.js index ccd8f0bf3..e09f7d73b 100644 --- a/ui/app/components/auth-form.js +++ b/ui/app/components/auth-form.js @@ -30,10 +30,6 @@ const BACKENDS = supportedAuthBackends(); * @param {string} namespace- The currently active namespace. * @param {string} selectedAuth - The auth method that is currently selected in the dropdown. * @param {function} onSuccess - Fired on auth success. - * @param {function} [setOktaNumberChallenge] - Sets whether we are waiting for okta number challenge to be used to sign in. - * @param {boolean} [waitingForOktaNumberChallenge=false] - Determines if we are waiting for the Okta Number Challenge to sign in. - * @param {function} [setCancellingAuth] - Sets whether we are cancelling or not the login authentication for Okta Number Challenge. - * @param {boolean} [cancelAuthForOktaNumberChallenge=false] - Determines if we are cancelling the login authentication for the Okta Number Challenge. */ const DEFAULTS = { @@ -60,9 +56,6 @@ export default Component.extend(DEFAULTS, { oldNamespace: null, authMethods: BACKENDS, - // number answer for okta number challenge if applicable - oktaNumberChallengeAnswer: null, - didReceiveAttrs() { this._super(...arguments); const { @@ -72,13 +65,10 @@ export default Component.extend(DEFAULTS, { namespace: ns, selectedAuth: newMethod, oldSelectedAuth: oldMethod, - cancelAuthForOktaNumberChallenge: cancelAuth, } = this; // if we are cancelling the login then we reset the number challenge answer and cancel the current authenticate and polling tasks if (cancelAuth) { - this.set('oktaNumberChallengeAnswer', null); this.authenticate.cancelAll(); - this.pollForOktaNumberChallenge.cancelAll(); } next(() => { if (!token && (oldNS === null || oldNS !== ns)) { @@ -237,11 +227,7 @@ export default Component.extend(DEFAULTS, { cluster: { id: clusterId }, } = this; try { - if (backendType === 'okta') { - this.pollForOktaNumberChallenge.perform(data.nonce, data.path); - } else { - this.delayAuthMessageReminder.perform(); - } + this.delayAuthMessageReminder.perform(); const authResponse = yield this.auth.authenticate({ clusterId, backend: backendType, @@ -258,28 +244,6 @@ export default Component.extend(DEFAULTS, { }) ), - pollForOktaNumberChallenge: task(function* (nonce, mount) { - // yield for 1s to wait to see if there is a login error before polling - yield timeout(1000); - if (this.error) { - return; - } - let response = null; - this.setOktaNumberChallenge(true); - this.setCancellingAuth(false); - // keep polling /auth/okta/verify/:nonce API every 1s until a response is given with the correct number for the Okta Number Challenge - while (response === null) { - // when testing, the polling loop causes promises to be rejected making acceptance tests fail - // so disable the poll in tests - if (Ember.testing) { - return; - } - yield timeout(1000); - response = yield this.auth.getOktaNumberChallengeAnswer(nonce, mount); - } - this.set('oktaNumberChallengeAnswer', response); - }), - delayAuthMessageReminder: task(function* () { if (Ember.testing) { yield timeout(0); @@ -311,14 +275,6 @@ export default Component.extend(DEFAULTS, { if (this.customPath || backend.id) { data.path = this.customPath || backend.id; } - // add nonce field for okta backend - if (backend.type === 'okta') { - data.nonce = uuidv4(); - // add a default path of okta if it doesn't exist to be used for Okta Number Challenge - if (!data.path) { - data.path = 'okta'; - } - } return this.authenticate.unlinked().perform(backend.type, data); }, handleError(e) { @@ -327,9 +283,5 @@ export default Component.extend(DEFAULTS, { error: e ? this.auth.handleError(e) : null, }); }, - returnToLoginFromOktaNumberChallenge() { - this.setOktaNumberChallenge(false); - this.set('oktaNumberChallengeAnswer', null); - }, }, }); diff --git a/ui/app/controllers/vault/cluster/access/mfa/methods/create.js b/ui/app/controllers/vault/cluster/access/mfa/methods/create.js index 03471884f..827f9db53 100644 --- a/ui/app/controllers/vault/cluster/access/mfa/methods/create.js +++ b/ui/app/controllers/vault/cluster/access/mfa/methods/create.js @@ -16,7 +16,7 @@ export default class MfaMethodCreateController extends Controller { @service router; queryParams = ['type']; - methodNames = ['TOTP', 'Duo', 'Okta', 'PingID']; + methodNames = ['TOTP']; @tracked type = null; @tracked method = null; diff --git a/ui/app/controllers/vault/cluster/auth.js b/ui/app/controllers/vault/cluster/auth.js index 7156ade99..d9b3b8d4c 100644 --- a/ui/app/controllers/vault/cluster/auth.js +++ b/ui/app/controllers/vault/cluster/auth.js @@ -90,7 +90,6 @@ export default Controller.extend({ }, cancelAuthentication() { this.set('cancelAuth', true); - this.set('waitingForOktaNumberChallenge', false); }, }, }); diff --git a/ui/app/helpers/mountable-auth-methods.js b/ui/app/helpers/mountable-auth-methods.js index 157dbde9c..a82310734 100644 --- a/ui/app/helpers/mountable-auth-methods.js +++ b/ui/app/helpers/mountable-auth-methods.js @@ -53,13 +53,6 @@ const MOUNTABLE_AUTH_METHODS = [ glyph: 'auth', category: 'infra', }, - { - displayName: 'Okta', - value: 'okta', - type: 'okta', - category: 'infra', - glyph: 'okta-color', - }, { displayName: 'RADIUS', value: 'radius', diff --git a/ui/app/helpers/supported-auth-backends.js b/ui/app/helpers/supported-auth-backends.js index 65c987aeb..5778385c4 100644 --- a/ui/app/helpers/supported-auth-backends.js +++ b/ui/app/helpers/supported-auth-backends.js @@ -36,14 +36,6 @@ const SUPPORTED_AUTH_BACKENDS = [ displayNamePath: 'metadata.username', formAttributes: ['username', 'password'], }, - { - type: 'okta', - typeDisplay: 'Okta', - description: 'Authenticate with your Okta username and password.', - tokenPath: 'client_token', - displayNamePath: 'metadata.username', - formAttributes: ['username', 'password'], - }, { type: 'jwt', typeDisplay: 'JWT', diff --git a/ui/app/helpers/supported-managed-auth-backends.js b/ui/app/helpers/supported-managed-auth-backends.js index 43d3ed0d7..3651e3ce9 100644 --- a/ui/app/helpers/supported-managed-auth-backends.js +++ b/ui/app/helpers/supported-managed-auth-backends.js @@ -7,7 +7,7 @@ import { helper as buildHelper } from '@ember/component/helper'; // The UI supports management of these auth methods (i.e. configuring roles or users) // otherwise only configuration of the method is supported. -const MANAGED_AUTH_BACKENDS = ['cert', 'kubernetes', 'ldap', 'okta', 'radius', 'userpass']; +const MANAGED_AUTH_BACKENDS = ['cert', 'kubernetes', 'ldap', 'radius', 'userpass']; export function supportedManagedAuthBackends() { return MANAGED_AUTH_BACKENDS; diff --git a/ui/app/helpers/tabs-for-auth-section.js b/ui/app/helpers/tabs-for-auth-section.js index 1f25bfe98..b8a73fdbd 100644 --- a/ui/app/helpers/tabs-for-auth-section.js +++ b/ui/app/helpers/tabs-for-auth-section.js @@ -38,12 +38,6 @@ const TABS_FOR_SETTINGS = { routeParams: ['vault.cluster.settings.auth.configure.section', 'configuration'], }, ], - okta: [ - { - label: 'Configuration', - routeParams: ['vault.cluster.settings.auth.configure.section', 'configuration'], - }, - ], radius: [ { label: 'Configuration', diff --git a/ui/app/models/auth-config/okta.js b/ui/app/models/auth-config/okta.js deleted file mode 100644 index 11a18b1e0..000000000 --- a/ui/app/models/auth-config/okta.js +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Copyright (c) HashiCorp, Inc. - * SPDX-License-Identifier: BUSL-1.1 - */ - -import { attr } from '@ember-data/model'; -import { computed } from '@ember/object'; -import AuthConfig from '../auth-config'; -import fieldToAttrs from 'vault/utils/field-to-attrs'; -import { combineFieldGroups } from 'vault/utils/openapi-to-attrs'; - -export default AuthConfig.extend({ - useOpenAPI: true, - orgName: attr('string', { - helpText: 'Name of the organization to be used in the Okta API', - }), - apiToken: attr('string', { - helpText: - 'Okta API token. This is required to query Okta for user group membership. If this is not supplied only locally configured groups will be enabled.', - }), - baseUrl: attr('string', { - helpText: - 'If set, will be used as the base domain for API requests. Examples are okta.com, oktapreview.com, and okta-emea.com', - }), - bypassOktaMfa: attr('boolean', { - defaultValue: false, - helpText: - "Useful if using Vault's built-in MFA mechanisms. Will also cause certain other statuses to be ignored, such as PASSWORD_EXPIRED", - }), - - fieldGroups: computed('newFields', function () { - let groups = [ - { - default: ['orgName'], - }, - { - Options: ['apiToken', 'baseUrl', 'bypassOktaMfa'], - }, - ]; - if (this.newFields) { - groups = combineFieldGroups(groups, this.newFields, []); - } - - return fieldToAttrs(this, groups); - }), -}); diff --git a/ui/app/models/mfa-method.js b/ui/app/models/mfa-method.js index d3b58c30b..11e1756df 100644 --- a/ui/app/models/mfa-method.js +++ b/ui/app/models/mfa-method.js @@ -11,25 +11,11 @@ import { isPresent } from '@ember/utils'; const METHOD_PROPS = { common: [], - duo: ['username_format', 'secret_key', 'integration_key', 'api_hostname', 'push_info', 'use_passcode'], - okta: ['username_format', 'mount_accessor', 'org_name', 'api_token', 'base_url', 'primary_email'], totp: ['issuer', 'period', 'key_size', 'qr_size', 'algorithm', 'digits', 'skew', 'max_validation_attempts'], - pingid: [ - 'username_format', - 'settings_file_base64', - 'use_signature', - 'idp_url', - 'admin_url', - 'authenticator_url', - 'org_alias', - ], }; const REQUIRED_PROPS = { - duo: ['secret_key', 'integration_key', 'api_hostname'], - okta: ['org_name', 'api_token'], totp: ['issuer'], - pingid: ['settings_file_base64'], }; const validators = Object.keys(REQUIRED_PROPS).reduce((obj, type) => { @@ -61,62 +47,6 @@ export default class MfaMethod extends Model { namespace_id; @attr('string') mount_accessor; - // PING ID - @attr('string', { - label: 'Settings file', - subText: 'A base-64 encoded third party setting file retrieved from the PingIDs configuration page.', - }) - settings_file_base64; - @attr('boolean') use_signature; - @attr('string') idp_url; - @attr('string') admin_url; - @attr('string') authenticator_url; - @attr('string') org_alias; - - // OKTA - @attr('string', { - label: 'Organization name', - subText: 'Name of the organization to be used in the Okta API.', - }) - org_name; - @attr('string', { - label: 'Okta API key', - }) - api_token; - @attr('string', { - label: 'Base URL', - subText: - 'If set, will be used as the base domain for API requests. Example are okta.com, oktapreview.com and okta-emea.com.', - }) - base_url; - @attr('boolean') primary_email; - - // DUO - @attr('string', { - label: 'Duo secret key', - sensitive: true, - }) - secret_key; - @attr('string', { - label: 'Duo integration key', - sensitive: true, - }) - integration_key; - @attr('string', { - label: 'Duo API hostname', - }) - api_hostname; - @attr('string', { - label: 'Duo push information', - subText: 'Additional information displayed to the user when the push is presented to them.', - }) - push_info; - @attr('boolean', { - label: 'Passcode reminder', - subText: 'If this is turned on, the user is reminded to use the passcode upon MFA validation.', - }) - use_passcode; - // TOTP @attr('string', { label: 'Issuer', diff --git a/ui/app/routes/vault/cluster/settings/auth/configure/section.js b/ui/app/routes/vault/cluster/settings/auth/configure/section.js index fae17938a..8473fdb5e 100644 --- a/ui/app/routes/vault/cluster/settings/auth/configure/section.js +++ b/ui/app/routes/vault/cluster/settings/auth/configure/section.js @@ -22,7 +22,6 @@ export default Route.extend(UnloadModelRoute, { 'oidc-configuration': 'auth-config/oidc', 'kubernetes-configuration': 'auth-config/kubernetes', 'ldap-configuration': 'auth-config/ldap', - 'okta-configuration': 'auth-config/okta', 'radius-configuration': 'auth-config/radius', }; return MODELS[`${backendType}-${section}`]; diff --git a/ui/app/services/auth.js b/ui/app/services/auth.js index bee06f3d0..7cc653a85 100644 --- a/ui/app/services/auth.js +++ b/ui/app/services/auth.js @@ -482,20 +482,4 @@ export default Service.extend({ this.set('tokens', tokenNames); }, - getOktaNumberChallengeAnswer(nonce, mount) { - const url = `/v1/auth/${mount}/verify/${nonce}`; - return this.ajax(url, 'GET', {}).then( - (resp) => { - return resp.data.correct_answer; - }, - (e) => { - // if error status is 404, return and keep polling for a response - if (e.status === 404) { - return null; - } else { - throw e; - } - } - ); - }, }); diff --git a/ui/app/templates/components/auth-form.hbs b/ui/app/templates/components/auth-form.hbs index b51cfabd6..23328c727 100644 --- a/ui/app/templates/components/auth-form.hbs +++ b/ui/app/templates/components/auth-form.hbs @@ -4,13 +4,6 @@ ~}}
- {{#if (and this.waitingForOktaNumberChallenge (not this.cancelAuthForOktaNumberChallenge))}} - - {{else}} {{#if this.hasMethodsWithPath}}
- {{/if}} \ No newline at end of file diff --git a/ui/app/templates/components/okta-number-challenge.hbs b/ui/app/templates/components/okta-number-challenge.hbs deleted file mode 100644 index 790833362..000000000 --- a/ui/app/templates/components/okta-number-challenge.hbs +++ /dev/null @@ -1,43 +0,0 @@ -{{! - Copyright (c) HashiCorp, Inc. - SPDX-License-Identifier: BUSL-1.1 -~}} - -
-
-
-

- To finish signing in, you will need to complete an additional MFA step.

- {{#if @hasError}} -
- - -
- {{else if @correctAnswer}} -
-

Okta - verification

-

Select the following number to complete verification:

-

{{@correctAnswer}}

-
- {{else}} -
-
- -
-

Please wait...

-
-
-
- {{/if}} -
-
-
\ No newline at end of file diff --git a/ui/app/templates/components/wizard/okta-method.hbs b/ui/app/templates/components/wizard/okta-method.hbs deleted file mode 100644 index 03a387835..000000000 --- a/ui/app/templates/components/wizard/okta-method.hbs +++ /dev/null @@ -1,16 +0,0 @@ -{{! - Copyright (c) HashiCorp, Inc. - SPDX-License-Identifier: BUSL-1.1 -~}} - - -

- The Okta Auth Method allows authentication using Okta and user/password credentials. This allows Vault to be integrated - into environments using Okta. -

-
\ No newline at end of file diff --git a/ui/app/templates/vault/cluster/access/mfa/index.hbs b/ui/app/templates/vault/cluster/access/mfa/index.hbs index 33de27ad7..21c8e3ea6 100644 --- a/ui/app/templates/vault/cluster/access/mfa/index.hbs +++ b/ui/app/templates/vault/cluster/access/mfa/index.hbs @@ -32,7 +32,7 @@

Step 1: - Set up an MFA configuration using one of the methods; TOTP, Okta, Duo or Pingid. + Set up an MFA configuration using TOTP method.

Step 2: diff --git a/ui/app/templates/vault/cluster/access/mfa/methods/create.hbs b/ui/app/templates/vault/cluster/access/mfa/methods/create.hbs index 5c35a4bac..04374dead 100644 --- a/ui/app/templates/vault/cluster/access/mfa/methods/create.hbs +++ b/ui/app/templates/vault/cluster/access/mfa/methods/create.hbs @@ -67,11 +67,11 @@

-

+

{{methodName}}

diff --git a/ui/app/templates/vault/cluster/auth.hbs b/ui/app/templates/vault/cluster/auth.hbs index 2192c4390..30c8ce205 100644 --- a/ui/app/templates/vault/cluster/auth.hbs +++ b/ui/app/templates/vault/cluster/auth.hbs @@ -37,13 +37,9 @@ - {{else if this.waitingForOktaNumberChallenge}} - {{/if}}

- {{if (or this.mfaAuthData this.waitingForOktaNumberChallenge) "Authenticate" "Sign in to Vault"}} + {{if this.mfaAuthData "Authenticate" "Sign in to Vault"}}

{{/if}} @@ -120,19 +116,6 @@ @onSuccess={{action "onMfaSuccess"}} @onError={{fn (mut this.mfaErrors)}} /> - {{else}} - {{/if}} diff --git a/ui/lib/core/icon-mappings.js b/ui/lib/core/icon-mappings.js index 7eb4750e2..0f38d8029 100644 --- a/ui/lib/core/icon-mappings.js +++ b/ui/lib/core/icon-mappings.js @@ -21,12 +21,9 @@ export const localIconMap = { kmip: 'unlock', kv: 'key-values', ldap: 'user', - okta: 'okta-color', radius: 'user', ssh: 'terminal-screen', totp: 'history', - duo: null, - pingid: null, transit: 'swap-horizontal', userpass: 'identity-user', stopwatch: 'clock', @@ -138,8 +135,6 @@ export const structureIconMap = { 'logo-kubernetes-monochrome': 'kubernetes', 'logo-microsoft-color': 'microsoft-color', 'logo-microsoft-monochrome': 'microsoft', - 'logo-okta-color': 'okta-color', - 'logo-okta-monochrome': 'okta', 'logo-slack-color': 'slack-color', 'logo-slack-monochrome': 'slack', 'logo-vmware-color': 'vmware-color', diff --git a/ui/mirage/factories/mfa-duo-method.js b/ui/mirage/factories/mfa-duo-method.js deleted file mode 100644 index bea96f591..000000000 --- a/ui/mirage/factories/mfa-duo-method.js +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Copyright (c) HashiCorp, Inc. - * SPDX-License-Identifier: BUSL-1.1 - */ - -import { Factory } from 'ember-cli-mirage'; - -export default Factory.extend({ - api_hostname: 'api-foobar.duosecurity.com', - mount_accessor: '', - name: '', // returned but cannot be set at this time - namespace_id: 'root', - pushinfo: '', - type: 'duo', - use_passcode: false, - username_template: '', - - afterCreate(record) { - if (record.name) { - console.warn('Endpoint ignored these unrecognized parameters: [name]'); // eslint-disable-line - record.name = ''; - } - }, -}); diff --git a/ui/mirage/factories/mfa-okta-method.js b/ui/mirage/factories/mfa-okta-method.js deleted file mode 100644 index 29b3347da..000000000 --- a/ui/mirage/factories/mfa-okta-method.js +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Copyright (c) HashiCorp, Inc. - * SPDX-License-Identifier: BUSL-1.1 - */ - -import { Factory } from 'ember-cli-mirage'; - -export default Factory.extend({ - base_url: 'okta.com', - mount_accessor: '', - name: '', // returned but cannot be set at this time - namespace_id: 'root', - org_name: 'dev-foobar', - type: 'okta', - username_template: '', // returned but cannot be set at this time - - afterCreate(record) { - if (record.name) { - console.warn('Endpoint ignored these unrecognized parameters: [name]'); // eslint-disable-line - record.name = ''; - } - }, -}); diff --git a/ui/mirage/factories/mfa-pingid-method.js b/ui/mirage/factories/mfa-pingid-method.js deleted file mode 100644 index 7b87465db..000000000 --- a/ui/mirage/factories/mfa-pingid-method.js +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (c) HashiCorp, Inc. - * SPDX-License-Identifier: BUSL-1.1 - */ - -import { Factory } from 'ember-cli-mirage'; - -export default Factory.extend({ - use_signature: true, - idp_url: 'https://foobar.pingidentity.com/pingid', - admin_url: 'https://foobar.pingidentity.com/pingid', - authenticator_url: 'https://authenticator.pingone.com/pingid/ppm', - org_alias: 'foobarbaz', - type: 'pingid', - username_template: '', - namespace_id: 'root', -}); diff --git a/ui/public/duo.svg b/ui/public/duo.svg deleted file mode 100644 index 72a97e5d1..000000000 --- a/ui/public/duo.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/ui/public/eco/okta.svg b/ui/public/eco/okta.svg deleted file mode 100644 index 16d288e29..000000000 --- a/ui/public/eco/okta.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/ui/public/okta.svg b/ui/public/okta.svg deleted file mode 100644 index 6b6e8906e..000000000 --- a/ui/public/okta.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/ui/public/pingid.svg b/ui/public/pingid.svg deleted file mode 100644 index 99b33fefc..000000000 --- a/ui/public/pingid.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/ui/tests/integration/components/okta-number-challenge-test.js b/ui/tests/integration/components/okta-number-challenge-test.js deleted file mode 100644 index 80b00bb8a..000000000 --- a/ui/tests/integration/components/okta-number-challenge-test.js +++ /dev/null @@ -1,74 +0,0 @@ -/** - * Copyright (c) HashiCorp, Inc. - * SPDX-License-Identifier: BUSL-1.1 - */ - -import { module, test } from 'qunit'; -import { setupRenderingTest } from 'ember-qunit'; -import { render, click } from '@ember/test-helpers'; -import { hbs } from 'ember-cli-htmlbars'; - -module('Integration | Component | okta-number-challenge', function (hooks) { - setupRenderingTest(hooks); - - hooks.beforeEach(function () { - this.oktaNumberChallengeAnswer = null; - this.hasError = false; - }); - - test('it should render correct descriptions', async function (assert) { - await render(hbs``); - - assert - .dom('[data-test-okta-number-challenge-description]') - .includesText( - 'To finish signing in, you will need to complete an additional MFA step.', - 'Correct description renders' - ); - assert - .dom('[data-test-okta-number-challenge-loading]') - .includesText('Please wait...', 'Correct loading description renders'); - }); - - test('it should show correct number for okta number challenge', async function (assert) { - this.set('oktaNumberChallengeAnswer', 1); - await render(hbs``); - assert - .dom('[data-test-okta-number-challenge-description]') - .includesText( - 'To finish signing in, you will need to complete an additional MFA step.', - 'Correct description renders' - ); - assert - .dom('[data-test-okta-number-challenge-verification-type]') - .includesText('Okta verification', 'Correct verification type renders'); - - assert - .dom('[data-test-okta-number-challenge-verification-description]') - .includesText( - 'Select the following number to complete verification:', - 'Correct verification description renders' - ); - assert - .dom('[data-test-okta-number-challenge-answer]') - .includesText('1', 'Correct okta number challenge answer renders'); - }); - - test('it should show error screen', async function (assert) { - this.set('hasError', true); - await render( - hbs`` - ); - assert - .dom('[data-test-okta-number-challenge-description]') - .includesText( - 'To finish signing in, you will need to complete an additional MFA step.', - 'Correct description renders' - ); - assert - .dom('[data-test-error]') - .includesText('There was a problem', 'Displays error that there was a problem'); - await click('[data-test-return-from-okta-number-challenge]'); - assert.true(this.returnToLogin, 'onReturnToLogin was triggered'); - }); -}); diff --git a/vault/external_tests/identity/login_mfa_duo_test.go b/vault/external_tests/identity/login_mfa_duo_test.go deleted file mode 100644 index 40c9e77b1..000000000 --- a/vault/external_tests/identity/login_mfa_duo_test.go +++ /dev/null @@ -1,292 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: BUSL-1.1 - -package identity - -import ( - "context" - "fmt" - "net/http" - "reflect" - "testing" - - "github.com/hashicorp/vault/api" - "github.com/hashicorp/vault/builtin/credential/userpass" - vaulthttp "github.com/hashicorp/vault/http" - "github.com/hashicorp/vault/sdk/logical" - "github.com/hashicorp/vault/vault" -) - -var identityMFACoreConfigDUO = &vault.CoreConfig{ - CredentialBackends: map[string]logical.Factory{ - "userpass": userpass.Factory, - }, -} - -var ( - secret_key = "" - integration_key = "" - api_hostname = "" -) - -func TestInteg_PolicyMFADUO(t *testing.T) { - t.Skip("This test requires manual intervention and DUO verify on cellphone is needed") - cluster := vault.NewTestCluster(t, identityMFACoreConfigDUO, &vault.TestClusterOptions{ - HandlerFunc: vaulthttp.Handler, - }) - cluster.Start() - defer cluster.Cleanup() - - client := cluster.Cores[0].Client - - // Enable Userpass authentication - err := client.Sys().EnableAuthWithOptions("userpass", &api.EnableAuthOptions{ - Type: "userpass", - }) - if err != nil { - t.Fatalf("failed to enable userpass auth: %v", err) - } - - err = mfaGeneratePolicyDUOTest(client) - if err != nil { - t.Fatalf("DUO verification failed") - } -} - -func mfaGeneratePolicyDUOTest(client *api.Client) error { - var err error - - rules := ` -path "secret/foo" { - capabilities = ["read"] - mfa_methods = ["my_duo"] -} - ` - - auths, err := client.Sys().ListAuth() - if err != nil { - return fmt.Errorf("failed to list auth mount") - } - mountAccessor := auths["userpass/"].Accessor - - err = client.Sys().PutPolicy("mfa_policy", rules) - if err != nil { - return fmt.Errorf("failed to create mfa_policy: %v", err) - } - - _, err = client.Logical().Write("auth/userpass/users/vaultmfa", map[string]interface{}{ - "password": "testpassword", - "policies": "mfa_policy", - }) - if err != nil { - return fmt.Errorf("failed to configure userpass backend: %v", err) - } - - secret, err := client.Logical().Write("auth/userpass/login/vaultmfa", map[string]interface{}{ - "password": "testpassword", - }) - if err != nil { - return fmt.Errorf("failed to login using userpass auth: %v", err) - } - - userpassToken := secret.Auth.ClientToken - - secret, err = client.Logical().Write("auth/token/lookup", map[string]interface{}{ - "token": userpassToken, - }) - if err != nil { - return fmt.Errorf("failed to lookup userpass authenticated token: %v", err) - } - - // entityID := secret.Data["entity_id"].(string) - - mfaConfigData := map[string]interface{}{ - "mount_accessor": mountAccessor, - "secret_key": secret_key, - "integration_key": integration_key, - "api_hostname": api_hostname, - } - _, err = client.Logical().Write("sys/mfa/method/duo/my_duo", mfaConfigData) - if err != nil { - return fmt.Errorf("failed to persist TOTP MFA configuration: %v", err) - } - - // Write some data in the path that requires TOTP MFA - genericData := map[string]interface{}{ - "somedata": "which can only be read if MFA succeeds", - } - _, err = client.Logical().Write("secret/foo", genericData) - if err != nil { - return fmt.Errorf("failed to store data in generic backend: %v", err) - } - - // Replace the token in client with the one that has access to MFA - // required path - originalToken := client.Token() - defer client.SetToken(originalToken) - client.SetToken(userpassToken) - - // Create a GET request and set the MFA header containing the generated - // TOTP passcode - secretRequest := client.NewRequest("GET", "/v1/secret/foo") - secretRequest.Headers = make(http.Header) - // mfaHeaderValue := "my_duo:" + totpPasscode - // secretRequest.Headers.Add("X-Vault-MFA", mfaHeaderValue) - - // Make the request - resp, err := client.RawRequest(secretRequest) - if resp != nil { - defer resp.Body.Close() - } - if resp != nil && resp.StatusCode == 403 { - return fmt.Errorf("failed to read the secret") - } - if err != nil { - return fmt.Errorf("failed to read the secret: %v", err) - } - - // It should be possible to access the secret - secret, err = api.ParseSecret(resp.Body) - if err != nil { - return fmt.Errorf("failed to parse the secret: %v", err) - } - if !reflect.DeepEqual(secret.Data, genericData) { - return fmt.Errorf("bad: generic data; expected: %#v\nactual: %#v", genericData, secret.Data) - } - return nil -} - -func TestInteg_LoginMFADUO(t *testing.T) { - t.Skip("This test requires manual intervention and DUO verify on cellphone is needed") - cluster := vault.NewTestCluster(t, identityMFACoreConfigDUO, &vault.TestClusterOptions{ - HandlerFunc: vaulthttp.Handler, - }) - cluster.Start() - defer cluster.Cleanup() - - client := cluster.Cores[0].Client - - // Enable Userpass authentication - err := client.Sys().EnableAuthWithOptions("userpass", &api.EnableAuthOptions{ - Type: "userpass", - }) - if err != nil { - t.Fatalf("failed to enable userpass auth: %v", err) - } - - err = mfaGenerateLoginDUOTest(client) - if err != nil { - t.Fatalf("DUO verification failed. error: %s", err) - } -} - -func mfaGenerateLoginDUOTest(client *api.Client) error { - var err error - - auths, err := client.Sys().ListAuth() - if err != nil { - return fmt.Errorf("failed to list auth mount") - } - mountAccessor := auths["userpass/"].Accessor - - _, err = client.Logical().Write("auth/userpass/users/vaultmfa", map[string]interface{}{ - "password": "testpassword", - }) - if err != nil { - return fmt.Errorf("failed to configure userpass backend: %v", err) - } - secret, err := client.Logical().Write("identity/entity", map[string]interface{}{ - "name": "test", - }) - if err != nil { - return fmt.Errorf("failed to create an entity") - } - entityID := secret.Data["id"].(string) - - _, err = client.Logical().Write("identity/entity-alias", map[string]interface{}{ - "name": "vaultmfa", - "canonical_id": entityID, - "mount_accessor": mountAccessor, - }) - if err != nil { - return fmt.Errorf("failed to create an entity alias") - } - - var methodID string - // login MFA - { - // create a config - mfaConfigData := map[string]interface{}{ - "username_format": fmt.Sprintf("{{identity.entity.aliases.%s.name}}", mountAccessor), - "secret_key": secret_key, - "integration_key": integration_key, - "api_hostname": api_hostname, - } - resp, err := client.Logical().Write("identity/mfa/method/duo", mfaConfigData) - - if err != nil || (resp == nil) { - return fmt.Errorf("bad: resp: %#v\n err: %v", resp, err) - } - - methodID = resp.Data["method_id"].(string) - if methodID == "" { - return fmt.Errorf("method ID is empty") - } - - // creating MFAEnforcementConfig - _, err = client.Logical().Write("identity/mfa/login-enforcement/randomName", map[string]interface{}{ - "auth_method_accessors": []string{mountAccessor}, - "auth_method_types": []string{"userpass"}, - "identity_entity_ids": []string{entityID}, - "name": "randomName", - "mfa_method_ids": []string{methodID}, - }) - if err != nil { - return fmt.Errorf("failed to configure MFAEnforcementConfig: %v", err) - } - } - secret, err = client.Logical().Write("auth/userpass/login/vaultmfa", map[string]interface{}{ - "password": "testpassword", - }) - if err != nil { - return fmt.Errorf("failed to login using userpass auth: %v", err) - } - - if secret.Auth == nil || secret.Auth.MFARequirement == nil { - return fmt.Errorf("two phase login returned nil MFARequirement") - } - if secret.Auth.MFARequirement.MFARequestID == "" { - return fmt.Errorf("MFARequirement contains empty MFARequestID") - } - if secret.Auth.MFARequirement.MFAConstraints == nil || len(secret.Auth.MFARequirement.MFAConstraints) == 0 { - return fmt.Errorf("MFAConstraints is nil or empty") - } - mfaConstraints, ok := secret.Auth.MFARequirement.MFAConstraints["randomName"] - if !ok { - return fmt.Errorf("failed to find the mfaConstrains") - } - if mfaConstraints.Any == nil || len(mfaConstraints.Any) == 0 { - return fmt.Errorf("") - } - for _, mfaAny := range mfaConstraints.Any { - if mfaAny.ID != methodID || mfaAny.Type != "duo" { - return fmt.Errorf("invalid mfa constraints") - } - } - - // validation - secret, err = client.Sys().MFAValidateWithContext(context.Background(), - secret.Auth.MFARequirement.MFARequestID, - map[string]interface{}{ - methodID: []string{}, - }) - if err != nil { - return fmt.Errorf("MFA failed: %v", err) - } - - if secret.Auth.ClientToken == "" { - return fmt.Errorf("MFA was not enforced") - } - - return nil -} diff --git a/vault/external_tests/identity/login_mfa_okta_test.go b/vault/external_tests/identity/login_mfa_okta_test.go deleted file mode 100644 index 4e6adb577..000000000 --- a/vault/external_tests/identity/login_mfa_okta_test.go +++ /dev/null @@ -1,358 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: BUSL-1.1 - -package identity - -import ( - "context" - "fmt" - "reflect" - "testing" - - "github.com/hashicorp/vault/api" - "github.com/hashicorp/vault/builtin/credential/okta" - "github.com/hashicorp/vault/builtin/credential/userpass" - vaulthttp "github.com/hashicorp/vault/http" - "github.com/hashicorp/vault/sdk/logical" - "github.com/hashicorp/vault/vault" -) - -var ( - org_name = "" - api_token = "" -) - -var identityOktaMFACoreConfig = &vault.CoreConfig{ - CredentialBackends: map[string]logical.Factory{ - "userpass": userpass.Factory, - "okta": okta.Factory, - }, -} - -func TestOktaEngineMFA(t *testing.T) { - t.Skip("This test requires manual intervention and OKTA verify on cellphone is needed") - cluster := vault.NewTestCluster(t, identityOktaMFACoreConfig, &vault.TestClusterOptions{ - HandlerFunc: vaulthttp.Handler, - }) - cluster.Start() - defer cluster.Cleanup() - - client := cluster.Cores[0].Client - - // Enable Okta engine - err := client.Sys().EnableAuthWithOptions("okta", &api.EnableAuthOptions{ - Type: "okta", - }) - if err != nil { - t.Fatalf("failed to enable okta auth: %v", err) - } - - _, err = client.Logical().Write("auth/okta/config", map[string]interface{}{ - "base_url": "okta.com", - "org_name": org_name, - "api_token": api_token, - }) - if err != nil { - t.Fatalf("error configuring okta mount: %v", err) - } - - _, err = client.Logical().Write("auth/okta/groups/testgroup", map[string]interface{}{ - "policies": "default", - }) - if err != nil { - t.Fatalf("error configuring okta group, %v", err) - } - - _, err = client.Logical().Write("auth/okta/login/", map[string]interface{}{ - "password": "", - }) - if err != nil { - t.Fatalf("error configuring okta group, %v", err) - } -} - -func TestInteg_PolicyMFAOkta(t *testing.T) { - t.Skip("This test requires manual intervention and OKTA verify on cellphone is needed") - cluster := vault.NewTestCluster(t, identityOktaMFACoreConfig, &vault.TestClusterOptions{ - HandlerFunc: vaulthttp.Handler, - }) - cluster.Start() - defer cluster.Cleanup() - - client := cluster.Cores[0].Client - - // Enable Userpass authentication - err := client.Sys().EnableAuthWithOptions("userpass", &api.EnableAuthOptions{ - Type: "userpass", - }) - if err != nil { - t.Fatalf("failed to enable userpass auth: %v", err) - } - - err = mfaGenerateOktaPolicyMFATest(client) - if err != nil { - t.Fatalf("Okta failed: %s", err) - } -} - -func mfaGenerateOktaPolicyMFATest(client *api.Client) error { - var err error - - rules := ` -path "secret/foo" { - capabilities = ["read"] - mfa_methods = ["my_okta"] -} - ` - - err = client.Sys().PutPolicy("mfa_policy", rules) - if err != nil { - return fmt.Errorf("failed to create mfa_policy: %v", err) - } - - // listing auth mounts to find the mount accessor for the userpass - auths, err := client.Sys().ListAuth() - if err != nil { - return fmt.Errorf("error listing auth mounts") - } - mountAccessor := auths["userpass/"].Accessor - - // creating a user in userpass - _, err = client.Logical().Write("auth/userpass/users/testuser", map[string]interface{}{ - "password": "testpassword", - }) - if err != nil { - return fmt.Errorf("failed to configure userpass backend: %v", err) - } - - // creating an identity with email metadata to be used for MFA validation - secret, err := client.Logical().Write("identity/entity", map[string]interface{}{ - "name": "test-entity", - "policies": "mfa_policy", - "metadata": map[string]string{ - "email": "", - }, - }) - if err != nil { - return fmt.Errorf("failed to create an entity") - } - entityID := secret.Data["id"].(string) - - // assigning the entity ID to the testuser alias - _, err = client.Logical().Write("identity/entity-alias", map[string]interface{}{ - "name": "testuser", - "canonical_id": entityID, - "mount_accessor": mountAccessor, - }) - if err != nil { - return fmt.Errorf("failed to create an entity alias") - } - - mfaConfigData := map[string]interface{}{ - "mount_accessor": mountAccessor, - "org_name": org_name, - "api_token": api_token, - "primary_email": true, - "username_format": "{{entity.metadata.email}}", - } - _, err = client.Logical().Write("sys/mfa/method/okta/my_okta", mfaConfigData) - if err != nil { - return fmt.Errorf("failed to persist TOTP MFA configuration: %v", err) - } - - // Write some data in the path that requires TOTP MFA - genericData := map[string]interface{}{ - "somedata": "which can only be read if MFA succeeds", - } - _, err = client.Logical().Write("secret/foo", genericData) - if err != nil { - return fmt.Errorf("failed to store data in generic backend: %v", err) - } - - // Replace the token in client with the one that has access to MFA - // required path - originalToken := client.Token() - defer client.SetToken(originalToken) - - // login to the testuser - secret, err = client.Logical().Write("auth/userpass/login/testuser", map[string]interface{}{ - "password": "testpassword", - }) - if err != nil { - return fmt.Errorf("failed to login using userpass auth: %v", err) - } - - userpassToken := secret.Auth.ClientToken - client.SetToken(userpassToken) - - secret, err = client.Logical().Read("secret/foo") - if err != nil { - return fmt.Errorf("failed to read the secret: %v", err) - } - - // It should be possible to access the secret - // secret, err = api.ParseSecret(resp.Body) - if err != nil { - return fmt.Errorf("failed to parse the secret: %v", err) - } - if !reflect.DeepEqual(secret.Data, genericData) { - return fmt.Errorf("bad: generic data; expected: %#v\nactual: %#v", genericData, secret.Data) - } - return nil -} - -func TestInteg_LoginMFAOkta(t *testing.T) { - t.Skip("This test requires manual intervention and OKTA verify on cellphone is needed") - cluster := vault.NewTestCluster(t, identityOktaMFACoreConfig, &vault.TestClusterOptions{ - HandlerFunc: vaulthttp.Handler, - }) - cluster.Start() - defer cluster.Cleanup() - - client := cluster.Cores[0].Client - - // Enable Userpass authentication - err := client.Sys().EnableAuthWithOptions("userpass", &api.EnableAuthOptions{ - Type: "userpass", - }) - if err != nil { - t.Fatalf("failed to enable userpass auth: %v", err) - } - - err = mfaGenerateOktaLoginMFATest(client) - if err != nil { - t.Fatalf("Okta failed: %s", err) - } -} - -func mfaGenerateOktaLoginMFATest(client *api.Client) error { - var err error - - auths, err := client.Sys().ListAuth() - if err != nil { - return fmt.Errorf("failed to list auth mounts") - } - mountAccessor := auths["userpass/"].Accessor - - _, err = client.Logical().Write("auth/userpass/users/testuser", map[string]interface{}{ - "password": "testpassword", - }) - if err != nil { - return fmt.Errorf("failed to configure userpass backend: %v", err) - } - - secret, err := client.Logical().Write("identity/entity", map[string]interface{}{ - "name": "test-entity", - "metadata": map[string]string{ - "email": "", - }, - }) - if err != nil { - return fmt.Errorf("failed to create an entity") - } - entityID := secret.Data["id"].(string) - - _, err = client.Logical().Write("identity/entity-alias", map[string]interface{}{ - "name": "testuser", - "canonical_id": entityID, - "mount_accessor": mountAccessor, - }) - if err != nil { - return fmt.Errorf("failed to create an entity alias") - } - - var methodID string - var userpassToken string - // login MFA - { - // create a config - mfaConfigData := map[string]interface{}{ - "mount_accessor": mountAccessor, - "org_name": org_name, - "api_token": api_token, - "primary_email": true, - "username_format": "{{identity.entity.metadata.email}}", - } - resp, err := client.Logical().Write("identity/mfa/method/okta", mfaConfigData) - - if err != nil || (resp == nil) { - return fmt.Errorf("bad: resp: %#v\n err: %v", resp, err) - } - - methodID = resp.Data["method_id"].(string) - if methodID == "" { - return fmt.Errorf("method ID is empty") - } - // creating MFAEnforcementConfig - _, err = client.Logical().Write("identity/mfa/login-enforcement/randomName", map[string]interface{}{ - "auth_method_accessors": []string{mountAccessor}, - "auth_method_types": []string{"userpass"}, - "identity_entity_ids": []string{entityID}, - "name": "randomName", - "mfa_method_ids": []string{methodID}, - }) - if err != nil { - return fmt.Errorf("failed to configure MFAEnforcementConfig: %v", err) - } - } - - secret, err = client.Logical().Write("auth/userpass/login/testuser", map[string]interface{}{ - "password": "testpassword", - }) - if err != nil { - return fmt.Errorf("failed to login using userpass auth: %v", err) - } - - if secret.Auth == nil || secret.Auth.MFARequirement == nil { - return fmt.Errorf("two phase login returned nil MFARequirement") - } - if secret.Auth.MFARequirement.MFARequestID == "" { - return fmt.Errorf("MFARequirement contains empty MFARequestID") - } - if secret.Auth.MFARequirement.MFAConstraints == nil || len(secret.Auth.MFARequirement.MFAConstraints) == 0 { - return fmt.Errorf("MFAConstraints is nil or empty") - } - mfaConstraints, ok := secret.Auth.MFARequirement.MFAConstraints["randomName"] - if !ok { - return fmt.Errorf("failed to find the mfaConstrains") - } - if mfaConstraints.Any == nil || len(mfaConstraints.Any) == 0 { - return fmt.Errorf("") - } - for _, mfaAny := range mfaConstraints.Any { - if mfaAny.ID != methodID || mfaAny.Type != "okta" { - return fmt.Errorf("invalid mfa constraints") - } - } - - // validation - secret, err = client.Sys().MFAValidateWithContext(context.Background(), - secret.Auth.MFARequirement.MFARequestID, - map[string]interface{}{ - methodID: []string{}, - }, - ) - if err != nil { - return fmt.Errorf("MFA failed: %v", err) - } - - userpassToken = secret.Auth.ClientToken - if secret.Auth.ClientToken == "" { - return fmt.Errorf("MFA was not enforced") - } - - client.SetToken(client.Token()) - secret, err = client.Logical().Write("auth/token/lookup", map[string]interface{}{ - "token": userpassToken, - }) - if err != nil { - return fmt.Errorf("failed to lookup userpass authenticated token: %v", err) - } - - entityIDCheck := secret.Data["entity_id"].(string) - if entityIDCheck != entityID { - return fmt.Errorf("different entityID assigned") - } - - return nil -} diff --git a/vault/identity_store.go b/vault/identity_store.go index c16786c56..7e1036fd0 100644 --- a/vault/identity_store.go +++ b/vault/identity_store.go @@ -347,230 +347,6 @@ func mfaPaths(i *IdentityStore) []*framework.Path { }, }, }, - { - Pattern: "mfa/method/okta" + genericOptionalUUIDRegex("method_id"), - DisplayAttrs: &framework.DisplayAttributes{ - OperationPrefix: "mfa", - }, - Fields: map[string]*framework.FieldSchema{ - "method_name": { - Type: framework.TypeString, - Description: `The unique name identifier for this MFA method.`, - }, - "method_id": { - Type: framework.TypeString, - Description: `The unique identifier for this MFA method.`, - }, - "username_format": { - Type: framework.TypeString, - Description: `A template string for mapping Identity names to MFA method names. Values to substitute should be placed in {{}}. For example, "{{entity.name}}@example.com". If blank, the Entity's name field will be used as-is.`, - }, - "org_name": { - Type: framework.TypeString, - Description: "Name of the organization to be used in the Okta API.", - }, - "api_token": { - Type: framework.TypeString, - Description: "Okta API key.", - }, - "base_url": { - Type: framework.TypeString, - Description: `The base domain to use for the Okta API. When not specified in the configuration, "okta.com" is used.`, - }, - "primary_email": { - Type: framework.TypeBool, - Description: `If true, the username will only match the primary email for the account. Defaults to false.`, - }, - "production": { - Type: framework.TypeBool, - Description: "(DEPRECATED) Use base_url instead.", - }, - }, - Operations: map[logical.Operation]framework.OperationHandler{ - logical.ReadOperation: &framework.PathOperation{ - Callback: i.handleMFAMethodOKTARead, - DisplayAttrs: &framework.DisplayAttributes{ - OperationVerb: "read", - OperationSuffix: "okta-method-configuration|okta-method-configuration", - }, - Summary: "Read the current configuration for the given MFA method", - }, - logical.UpdateOperation: &framework.PathOperation{ - Callback: i.handleMFAMethodOKTAUpdate, - DisplayAttrs: &framework.DisplayAttributes{ - OperationVerb: "configure", - OperationSuffix: "okta-method|okta-method", - }, - Summary: "Update or create a configuration for the given MFA method", - }, - logical.DeleteOperation: &framework.PathOperation{ - Callback: i.handleMFAMethodOKTADelete, - DisplayAttrs: &framework.DisplayAttributes{ - OperationVerb: "delete", - OperationSuffix: "okta-method|okta-method", - }, - Summary: "Delete a configuration for the given MFA method", - }, - }, - }, - { - Pattern: "mfa/method/okta/?$", - Operations: map[logical.Operation]framework.OperationHandler{ - logical.ListOperation: &framework.PathOperation{ - Callback: i.handleMFAMethodListOkta, - DisplayAttrs: &framework.DisplayAttributes{ - OperationPrefix: "mfa", - OperationVerb: "list", - OperationSuffix: "okta-methods", - }, - Summary: "List MFA method configurations for the given MFA method", - }, - }, - }, - { - Pattern: "mfa/method/duo" + genericOptionalUUIDRegex("method_id"), - DisplayAttrs: &framework.DisplayAttributes{ - OperationPrefix: "mfa", - }, - Fields: map[string]*framework.FieldSchema{ - "method_name": { - Type: framework.TypeString, - Description: `The unique name identifier for this MFA method.`, - }, - "method_id": { - Type: framework.TypeString, - Description: `The unique identifier for this MFA method.`, - }, - "username_format": { - Type: framework.TypeString, - Description: `A template string for mapping Identity names to MFA method names. Values to subtitute should be placed in {{}}. For example, "{{alias.name}}@example.com". Currently-supported mappings: alias.name: The name returned by the mount configured via the mount_accessor parameter If blank, the Alias's name field will be used as-is. `, - }, - "secret_key": { - Type: framework.TypeString, - Description: "Secret key for Duo.", - }, - "integration_key": { - Type: framework.TypeString, - Description: "Integration key for Duo.", - }, - "api_hostname": { - Type: framework.TypeString, - Description: "API host name for Duo.", - }, - "push_info": { - Type: framework.TypeString, - Description: "Push information for Duo.", - }, - "use_passcode": { - Type: framework.TypeBool, - Description: `If true, the user is reminded to use the passcode upon MFA validation. This option does not enforce using the passcode. Defaults to false.`, - }, - }, - Operations: map[logical.Operation]framework.OperationHandler{ - logical.ReadOperation: &framework.PathOperation{ - Callback: i.handleMFAMethodDuoRead, - DisplayAttrs: &framework.DisplayAttributes{ - OperationVerb: "read", - OperationSuffix: "duo-method-configuration|duo-method-configuration", - }, - Summary: "Read the current configuration for the given MFA method", - }, - logical.UpdateOperation: &framework.PathOperation{ - Callback: i.handleMFAMethodDuoUpdate, - DisplayAttrs: &framework.DisplayAttributes{ - OperationVerb: "configure", - OperationSuffix: "duo-method|duo-method", - }, - Summary: "Update or create a configuration for the given MFA method", - }, - logical.DeleteOperation: &framework.PathOperation{ - Callback: i.handleMFAMethodDUODelete, - DisplayAttrs: &framework.DisplayAttributes{ - OperationVerb: "delete", - OperationSuffix: "duo-method|duo-method", - }, - Summary: "Delete a configuration for the given MFA method", - }, - }, - }, - { - Pattern: "mfa/method/duo/?$", - Operations: map[logical.Operation]framework.OperationHandler{ - logical.ListOperation: &framework.PathOperation{ - Callback: i.handleMFAMethodListDuo, - DisplayAttrs: &framework.DisplayAttributes{ - OperationPrefix: "mfa", - OperationVerb: "list", - OperationSuffix: "duo-methods", - }, - Summary: "List MFA method configurations for the given MFA method", - }, - }, - }, - { - Pattern: "mfa/method/pingid" + genericOptionalUUIDRegex("method_id"), - DisplayAttrs: &framework.DisplayAttributes{ - OperationPrefix: "mfa", - }, - Fields: map[string]*framework.FieldSchema{ - "method_name": { - Type: framework.TypeString, - Description: `The unique name identifier for this MFA method.`, - }, - "method_id": { - Type: framework.TypeString, - Description: `The unique identifier for this MFA method.`, - }, - "username_format": { - Type: framework.TypeString, - Description: `A template string for mapping Identity names to MFA method names. Values to subtitute should be placed in {{}}. For example, "{{alias.name}}@example.com". Currently-supported mappings: alias.name: The name returned by the mount configured via the mount_accessor parameter If blank, the Alias's name field will be used as-is. `, - }, - "settings_file_base64": { - Type: framework.TypeString, - Description: "The settings file provided by Ping, Base64-encoded. This must be a settings file suitable for third-party clients, not the PingID SDK or PingFederate.", - }, - }, - Operations: map[logical.Operation]framework.OperationHandler{ - logical.ReadOperation: &framework.PathOperation{ - Callback: i.handleMFAMethodPingIDRead, - DisplayAttrs: &framework.DisplayAttributes{ - OperationVerb: "read", - OperationSuffix: "ping-id-method-configuration|ping-id-method-configuration", - }, - Summary: "Read the current configuration for the given MFA method", - }, - logical.UpdateOperation: &framework.PathOperation{ - Callback: i.handleMFAMethodPingIDUpdate, - DisplayAttrs: &framework.DisplayAttributes{ - OperationVerb: "configure", - OperationSuffix: "ping-id-method|ping-id-method", - }, - Summary: "Update or create a configuration for the given MFA method", - }, - logical.DeleteOperation: &framework.PathOperation{ - Callback: i.handleMFAMethodPingIDDelete, - DisplayAttrs: &framework.DisplayAttributes{ - OperationVerb: "delete", - OperationSuffix: "ping-id-method|ping-id-method", - }, - Summary: "Delete a configuration for the given MFA method", - }, - }, - }, - { - Pattern: "mfa/method/pingid/?$", - Operations: map[logical.Operation]framework.OperationHandler{ - logical.ListOperation: &framework.PathOperation{ - Callback: i.handleMFAMethodListPingID, - DisplayAttrs: &framework.DisplayAttributes{ - OperationPrefix: "mfa", - OperationVerb: "list", - OperationSuffix: "ping-id-methods", - }, - Summary: "List MFA method configurations for the given MFA method", - }, - }, - }, { Pattern: "mfa/login-enforcement/" + framework.GenericNameRegex("name"), DisplayAttrs: &framework.DisplayAttributes{ diff --git a/vault/logical_system_helpers.go b/vault/logical_system_helpers.go index 1f013a12c..eb08b4ed3 100644 --- a/vault/logical_system_helpers.go +++ b/vault/logical_system_helpers.go @@ -188,9 +188,6 @@ var ( "mfa/method/totp/" + framework.GenericNameRegex("name") + "/admin-generate$": {parameters: []string{"name"}, operations: []logical.Operation{logical.UpdateOperation}}, "mfa/method/totp/" + framework.GenericNameRegex("name") + "/admin-destroy$": {parameters: []string{"name"}, operations: []logical.Operation{logical.UpdateOperation}}, "mfa/method/totp/" + framework.GenericNameRegex("name"): {parameters: []string{"name"}, operations: []logical.Operation{logical.DeleteOperation, logical.ReadOperation, logical.UpdateOperation}}, - "mfa/method/okta/" + framework.GenericNameRegex("name"): {parameters: []string{"name"}, operations: []logical.Operation{logical.DeleteOperation, logical.ReadOperation, logical.UpdateOperation}}, - "mfa/method/duo/" + framework.GenericNameRegex("name"): {parameters: []string{"name"}, operations: []logical.Operation{logical.DeleteOperation, logical.ReadOperation, logical.UpdateOperation}}, - "mfa/method/pingid/" + framework.GenericNameRegex("name"): {parameters: []string{"name"}, operations: []logical.Operation{logical.DeleteOperation, logical.ReadOperation, logical.UpdateOperation}}, })...) // control-group paths diff --git a/vault/login_mfa.go b/vault/login_mfa.go index c03b9f1fc..b096b925b 100644 --- a/vault/login_mfa.go +++ b/vault/login_mfa.go @@ -7,22 +7,15 @@ import ( "bytes" "context" "encoding/base64" - "errors" "fmt" "image/png" - "io/ioutil" "net/http" - "net/url" "strings" "sync" "time" - duoapi "github.com/duosecurity/duo_api_golang" - "github.com/duosecurity/duo_api_golang/authapi" - "github.com/golang-jwt/jwt/v4" "github.com/golang/protobuf/proto" "github.com/hashicorp/errwrap" - "github.com/hashicorp/go-cleanhttp" "github.com/hashicorp/go-hclog" "github.com/hashicorp/go-memdb" "github.com/hashicorp/go-multierror" @@ -31,15 +24,11 @@ import ( "github.com/hashicorp/vault/helper/identity/mfa" "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/sdk/framework" - "github.com/hashicorp/vault/sdk/helper/identitytpl" "github.com/hashicorp/vault/sdk/helper/jsonutil" - "github.com/hashicorp/vault/sdk/helper/parseutil" "github.com/hashicorp/vault/sdk/helper/strutil" "github.com/hashicorp/vault/sdk/logical" "github.com/hashicorp/vault/vault/quotas" "github.com/mitchellh/mapstructure" - "github.com/okta/okta-sdk-golang/v2/okta" - "github.com/okta/okta-sdk-golang/v2/okta/query" "github.com/patrickmn/go-cache" otplib "github.com/pquerna/otp" totplib "github.com/pquerna/otp/totp" @@ -47,9 +36,6 @@ import ( const ( mfaMethodTypeTOTP = "totp" - mfaMethodTypeDuo = "duo" - mfaMethodTypeOkta = "okta" - mfaMethodTypePingID = "pingid" memDBLoginMFAConfigsTable = "login_mfa_configs" memDBMFALoginEnforcementsTable = "login_enforcements" mfaTOTPKeysPrefix = systemBarrierPrefix + "mfa/totpkeys/" @@ -185,18 +171,6 @@ func (i *IdentityStore) handleMFAMethodListTOTP(ctx context.Context, req *logica return i.handleMFAMethodList(ctx, req, d, mfaMethodTypeTOTP) } -func (i *IdentityStore) handleMFAMethodListDuo(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { - return i.handleMFAMethodList(ctx, req, d, mfaMethodTypeDuo) -} - -func (i *IdentityStore) handleMFAMethodListOkta(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { - return i.handleMFAMethodList(ctx, req, d, mfaMethodTypeOkta) -} - -func (i *IdentityStore) handleMFAMethodListPingID(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { - return i.handleMFAMethodList(ctx, req, d, mfaMethodTypePingID) -} - func (i *IdentityStore) handleMFAMethodListGlobal(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { keys, configInfo, err := i.mfaBackend.mfaMethodList(ctx, "") if err != nil { @@ -219,18 +193,6 @@ func (i *IdentityStore) handleMFAMethodTOTPRead(ctx context.Context, req *logica return i.handleMFAMethodReadCommon(ctx, req, d, mfaMethodTypeTOTP) } -func (i *IdentityStore) handleMFAMethodOKTARead(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { - return i.handleMFAMethodReadCommon(ctx, req, d, mfaMethodTypeOkta) -} - -func (i *IdentityStore) handleMFAMethodDuoRead(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { - return i.handleMFAMethodReadCommon(ctx, req, d, mfaMethodTypeDuo) -} - -func (i *IdentityStore) handleMFAMethodPingIDRead(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { - return i.handleMFAMethodReadCommon(ctx, req, d, mfaMethodTypePingID) -} - func (i *IdentityStore) handleMFAMethodReadGlobal(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { return i.handleMFAMethodReadCommon(ctx, req, d, "") } @@ -360,24 +322,6 @@ func (i *IdentityStore) handleMFAMethodUpdateCommon(ctx context.Context, req *lo return logical.ErrorResponse(err.Error()), nil } - case mfaMethodTypeOkta: - err = parseOktaConfig(mConfig, d) - if err != nil { - return logical.ErrorResponse(err.Error()), nil - } - - case mfaMethodTypeDuo: - err = parseDuoConfig(mConfig, d) - if err != nil { - return logical.ErrorResponse(err.Error()), nil - } - - case mfaMethodTypePingID: - err = parsePingIDConfig(mConfig, d) - if err != nil { - return logical.ErrorResponse(err.Error()), nil - } - default: return logical.ErrorResponse(fmt.Sprintf("unrecognized type %q", methodType)), nil } @@ -409,34 +353,10 @@ func (i *IdentityStore) handleMFAMethodTOTPUpdate(ctx context.Context, req *logi return i.handleMFAMethodUpdateCommon(ctx, req, d, mfaMethodTypeTOTP) } -func (i *IdentityStore) handleMFAMethodOKTAUpdate(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { - return i.handleMFAMethodUpdateCommon(ctx, req, d, mfaMethodTypeOkta) -} - -func (i *IdentityStore) handleMFAMethodDuoUpdate(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { - return i.handleMFAMethodUpdateCommon(ctx, req, d, mfaMethodTypeDuo) -} - -func (i *IdentityStore) handleMFAMethodPingIDUpdate(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { - return i.handleMFAMethodUpdateCommon(ctx, req, d, mfaMethodTypePingID) -} - func (i *IdentityStore) handleMFAMethodTOTPDelete(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { return i.handleMFAMethodDeleteCommon(ctx, req, d, mfaMethodTypeTOTP) } -func (i *IdentityStore) handleMFAMethodOKTADelete(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { - return i.handleMFAMethodDeleteCommon(ctx, req, d, mfaMethodTypeOkta) -} - -func (i *IdentityStore) handleMFAMethodDUODelete(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { - return i.handleMFAMethodDeleteCommon(ctx, req, d, mfaMethodTypeDuo) -} - -func (i *IdentityStore) handleMFAMethodPingIDDelete(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { - return i.handleMFAMethodDeleteCommon(ctx, req, d, mfaMethodTypePingID) -} - func (i *IdentityStore) handleMFAMethodDeleteCommon(ctx context.Context, req *logical.Request, d *framework.FieldData, methodType string) (*logical.Response, error) { methodID := d.Get("method_id").(string) if methodID == "" { @@ -1229,91 +1149,6 @@ func (b *MFABackend) handleMFAGenerateTOTP(ctx context.Context, mConfig *mfa.Con }, nil } -func parseDuoConfig(mConfig *mfa.Config, d *framework.FieldData) error { - secretKey := d.Get("secret_key").(string) - if secretKey == "" { - return fmt.Errorf("secret_key is empty") - } - - integrationKey := d.Get("integration_key").(string) - if integrationKey == "" { - return fmt.Errorf("integration_key is empty") - } - - apiHostname := d.Get("api_hostname").(string) - if apiHostname == "" { - return fmt.Errorf("api_hostname is empty") - } - - config := &mfa.DuoConfig{ - SecretKey: secretKey, - IntegrationKey: integrationKey, - APIHostname: apiHostname, - PushInfo: d.Get("push_info").(string), - UsePasscode: d.Get("use_passcode").(bool), - } - - mConfig.Config = &mfa.Config_DuoConfig{ - DuoConfig: config, - } - - return nil -} - -func parsePingIDConfig(mConfig *mfa.Config, d *framework.FieldData) error { - fileString := d.Get("settings_file_base64").(string) - if fileString == "" { - return fmt.Errorf("settings_file_base64 is empty") - } - - fileBytes, err := base64.StdEncoding.DecodeString(fileString) - if err != nil { - return err - } - - config := &mfa.PingIDConfig{} - for _, line := range strings.Split(string(fileBytes), "\n") { - if strings.HasPrefix(line, "#") { - continue - } - if strings.TrimSpace(line) == "" { - continue - } - splitLine := strings.SplitN(line, "=", 2) - if len(splitLine) != 2 { - return fmt.Errorf("pingid settings file contains a non-empty non-comment line that is not in key=value format: %q", line) - } - switch splitLine[0] { - case "use_base64_key": - config.UseBase64Key = splitLine[1] - case "use_signature": - result, err := parseutil.ParseBool(splitLine[1]) - if err != nil { - return errors.New("error parsing use_signature value in pingid settings file") - } - config.UseSignature = result - case "token": - config.Token = splitLine[1] - case "idp_url": - config.IDPURL = splitLine[1] - case "org_alias": - config.OrgAlias = splitLine[1] - case "admin_url": - config.AdminURL = splitLine[1] - case "authenticator_url": - config.AuthenticatorURL = splitLine[1] - default: - return fmt.Errorf("unknown key %q in pingid settings file", splitLine[0]) - } - } - - mConfig.Config = &mfa.Config_PingIDConfig{ - PingIDConfig: config, - } - - return nil -} - func (b *LoginMFABackend) mfaConfigReadByMethodID(id string) (map[string]interface{}, error) { mConfig, err := b.MemDBMFAConfigByID(id) if err != nil { @@ -1483,30 +1318,6 @@ func (b *MFABackend) mfaConfigToMap(mConfig *mfa.Config) (map[string]interface{} respData["qr_size"] = totpConfig.QRSize respData["algorithm"] = otplib.Algorithm(totpConfig.Algorithm).String() respData["max_validation_attempts"] = totpConfig.MaxValidationAttempts - case *mfa.Config_OktaConfig: - oktaConfig := mConfig.GetOktaConfig() - respData["org_name"] = oktaConfig.OrgName - if oktaConfig.BaseURL != "" { - respData["base_url"] = oktaConfig.BaseURL - } else { - respData["production"] = oktaConfig.Production - } - respData["mount_accessor"] = mConfig.MountAccessor - respData["username_format"] = mConfig.UsernameFormat - case *mfa.Config_DuoConfig: - duoConfig := mConfig.GetDuoConfig() - respData["api_hostname"] = duoConfig.APIHostname - respData["pushinfo"] = duoConfig.PushInfo - respData["mount_accessor"] = mConfig.MountAccessor - respData["username_format"] = mConfig.UsernameFormat - respData["use_passcode"] = duoConfig.UsePasscode - case *mfa.Config_PingIDConfig: - pingConfig := mConfig.GetPingIDConfig() - respData["use_signature"] = pingConfig.UseSignature - respData["idp_url"] = pingConfig.IDPURL - respData["org_alias"] = pingConfig.OrgAlias - respData["admin_url"] = pingConfig.AdminURL - respData["authenticator_url"] = pingConfig.AuthenticatorURL default: return nil, fmt.Errorf("invalid method type %q was persisted, underlying type: %T", mConfig.Type, mConfig.Config) } @@ -1607,63 +1418,6 @@ func parseTOTPConfig(mConfig *mfa.Config, d *framework.FieldData) error { return nil } -func parseOktaConfig(mConfig *mfa.Config, d *framework.FieldData) error { - if mConfig == nil { - return errors.New("config is nil") - } - - if d == nil { - return errors.New("field data is nil") - } - - oktaConfig := &mfa.OktaConfig{} - - orgName := d.Get("org_name").(string) - if orgName == "" { - return errors.New("org_name must be set") - } - oktaConfig.OrgName = orgName - - token := d.Get("api_token").(string) - if token == "" { - return errors.New("api_token must be set") - } - oktaConfig.APIToken = token - - productionRaw, productionOk := d.GetOk("production") - if productionOk { - oktaConfig.Production = productionRaw.(bool) - } else { - oktaConfig.Production = true - } - - baseURLRaw, ok := d.GetOk("base_url") - if ok { - oktaConfig.BaseURL = baseURLRaw.(string) - } else { - // Only set if not using legacy production flag - if !productionOk { - oktaConfig.BaseURL = "okta.com" - } - } - - primaryEmailOnly := d.Get("primary_email").(bool) - if primaryEmailOnly { - oktaConfig.PrimaryEmail = true - } - - _, err := url.Parse(fmt.Sprintf("https://%s,%s", oktaConfig.OrgName, oktaConfig.BaseURL)) - if err != nil { - return errwrap.Wrapf("error parsing given base_url: {{err}}", err) - } - - mConfig.Config = &mfa.Config_OktaConfig{ - OktaConfig: oktaConfig, - } - - return nil -} - func (c *Core) validateLoginMFA(ctx context.Context, eConfig *mfa.MFAEnforcementConfig, entity *identity.Entity, requestConnRemoteAddr string, mfaCredsMap logical.MFACreds) error { sanitizedMfaCreds, err := c.loginMFABackend.sanitizeMFACredsWithLoginEnforcementMethodIDs(ctx, mfaCredsMap, eConfig.MFAMethodIDs) if err != nil { @@ -1708,31 +1462,6 @@ func (c *Core) validateLoginMFAInternal(ctx context.Context, methodID string, en return fmt.Errorf("MFA method configuration not present") } - var finalUsername string - switch mConfig.Type { - case mfaMethodTypeDuo, mfaMethodTypeOkta, mfaMethodTypePingID: - if mConfig.UsernameFormat == "" { - finalUsername = entity.Name - } else { - directGroups, inheritedGroups, err := c.identityStore.groupsByEntityID(entity.ID) - if err != nil { - return fmt.Errorf("failed to fetch group memberships: %w", err) - } - groups := append(directGroups, inheritedGroups...) - - _, finalUsername, err = identitytpl.PopulateString(identitytpl.PopulateStringInput{ - Mode: identitytpl.ACLTemplating, - String: mConfig.UsernameFormat, - Entity: identity.ToSDKEntity(entity), - Groups: identity.ToSDKGroups(groups), - NamespaceID: entity.NamespaceID, - }) - if err != nil { - return err - } - } - } - mfaFactors, err := parseMfaFactors(mfaCreds) if err != nil { return fmt.Errorf("failed to parse MFA factor, %w", err) @@ -1751,15 +1480,6 @@ func (c *Core) validateLoginMFAInternal(ctx context.Context, methodID string, en return c.validateTOTP(ctx, mfaFactors, entityMFASecret, mConfig.ID, entity.ID, c.loginMFABackend.usedCodes, mConfig.GetTOTPConfig().MaxValidationAttempts) - case mfaMethodTypeOkta: - return c.validateOkta(ctx, mConfig, finalUsername) - - case mfaMethodTypeDuo: - return c.validateDuo(ctx, mfaFactors, mConfig, finalUsername, reqConnectionRemoteAddress) - - case mfaMethodTypePingID: - return c.validatePingID(ctx, mConfig, finalUsername) - default: return fmt.Errorf("unrecognized MFA type %q", mConfig.Type) } @@ -1906,468 +1626,6 @@ func parseMfaFactors(creds []string) (*MFAFactor, error) { return mfaFactor, nil } -func (c *Core) validateDuo(ctx context.Context, mfaFactors *MFAFactor, mConfig *mfa.Config, username, reqConnectionRemoteAddr string) error { - duoConfig := mConfig.GetDuoConfig() - if duoConfig == nil { - return fmt.Errorf("failed to get Duo configuration for method %q", mConfig.Name) - } - - var passcode string - if mfaFactors != nil { - passcode = mfaFactors.passcode - } - - client := duoapi.NewDuoApi( - duoConfig.IntegrationKey, - duoConfig.SecretKey, - duoConfig.APIHostname, - duoConfig.PushInfo, - ) - - authClient := authapi.NewAuthApi(*client) - check, err := authClient.Check() - if err != nil { - return err - } - if check == nil { - return errors.New("Duo api check returned nil, possibly bad integration key") - } - var message string - var messageDetail string - if check.StatResult.Message != nil { - message = *check.StatResult.Message - } - if check.StatResult.Message_Detail != nil { - messageDetail = *check.StatResult.Message_Detail - } - if check.StatResult.Stat != "OK" { - return fmt.Errorf("check against Duo failed; message (if given): %q; message detail (if given): %q", message, messageDetail) - } - - preauth, err := authClient.Preauth(authapi.PreauthUsername(username), authapi.PreauthIpAddr(reqConnectionRemoteAddr)) - if err != nil { - return errwrap.Wrapf("failed to perform Duo preauth: {{err}}", err) - } - if preauth == nil { - return fmt.Errorf("failed to perform Duo preauth") - } - if preauth.StatResult.Stat != "OK" { - return fmt.Errorf("failed to perform Duo preauth: %q - %q", *preauth.StatResult.Message, *preauth.StatResult.Message_Detail) - } - - switch preauth.Response.Result { - case "allow": - return nil - case "deny": - return fmt.Errorf(preauth.Response.Status_Msg) - case "enroll": - return fmt.Errorf(fmt.Sprintf("%q - %q", preauth.Response.Status_Msg, preauth.Response.Enroll_Portal_Url)) - case "auth": - break - default: - return fmt.Errorf("invalid response from Duo preauth: %q", preauth.Response.Result) - } - - options := []func(*url.Values){} - factor := "push" - if passcode != "" { - factor = "passcode" - options = append(options, authapi.AuthPasscode(passcode)) - } else { - options = append(options, authapi.AuthDevice("auto")) - if duoConfig.PushInfo != "" { - options = append(options, authapi.AuthPushinfo(duoConfig.PushInfo)) - } - } - - options = append(options, authapi.AuthIpAddr(reqConnectionRemoteAddr)) - options = append(options, authapi.AuthUsername(username)) - options = append(options, authapi.AuthAsync()) - - result, err := authClient.Auth(factor, options...) - if err != nil { - return errwrap.Wrapf("failed to authenticate with Duo: {{err}}", err) - } - if result.StatResult.Stat != "OK" { - return fmt.Errorf("failed to authenticate with Duo: %q - %q", *result.StatResult.Message, *result.StatResult.Message_Detail) - } - if result.Response.Txid == "" { - return fmt.Errorf("failed to get transaction ID for Duo authentication") - } - - for { - // AuthStatus does the long polling until there is a status update. So - // there is no need to wait for a second before we invoke this API. - statusResult, err := authClient.AuthStatus(result.Response.Txid) - if err != nil { - return errwrap.Wrapf("failed to get authentication status from Duo: {{err}}", err) - } - if statusResult == nil { - return errwrap.Wrapf("failed to get authentication status from Duo: {{err}}", err) - } - if statusResult.StatResult.Stat != "OK" { - return fmt.Errorf("failed to get authentication status from Duo: %q - %q", *statusResult.StatResult.Message, *statusResult.StatResult.Message_Detail) - } - - switch statusResult.Response.Result { - case "deny": - return fmt.Errorf("duo authentication failed: %q", statusResult.Response.Status_Msg) - case "allow": - return nil - } - timer := time.NewTimer(time.Second) - - select { - case <-ctx.Done(): - timer.Stop() - return fmt.Errorf("duo push verification operation canceled") - case <-timer.C: - } - } -} - -func (c *Core) validateOkta(ctx context.Context, mConfig *mfa.Config, username string) error { - oktaConfig := mConfig.GetOktaConfig() - if oktaConfig == nil { - return fmt.Errorf("failed to get Okta configuration for method %q", mConfig.Name) - } - - baseURL := oktaConfig.BaseURL - if baseURL == "" { - baseURL = "okta.com" - } - orgURL, err := url.Parse(fmt.Sprintf("https://%s.%s", oktaConfig.OrgName, baseURL)) - if err != nil { - return err - } - - ctx, client, err := okta.NewClient(ctx, - okta.WithToken(oktaConfig.APIToken), - okta.WithOrgUrl(orgURL.String()), - // Do not use cache or polling MFA will not refresh - okta.WithCache(false), - ) - if err != nil { - return fmt.Errorf("error creating client: %s", err) - } - - filterField := "profile.login" - if oktaConfig.PrimaryEmail { - filterField = "profile.email" - } - filterQuery := fmt.Sprintf("%s eq %q", filterField, username) - filter := query.NewQueryParams(query.WithFilter(filterQuery)) - - users, _, err := client.User.ListUsers(ctx, filter) - if err != nil { - return err - } - switch { - case len(users) == 0: - return fmt.Errorf("no users found for e-mail address") - case len(users) > 1: - return fmt.Errorf("more than one user found for e-mail address") - } - - user := users[0] - - factors, _, err := client.UserFactor.ListFactors(ctx, user.Id) - if err != nil { - return err - } - - if len(factors) == 0 { - return fmt.Errorf("no MFA factors found for user") - } - - var factorFound bool - var userFactor *okta.UserFactor - for _, factor := range factors { - if factor.IsUserFactorInstance() { - userFactor = factor.(*okta.UserFactor) - if userFactor.FactorType == "push" { - factorFound = true - break - } - } - } - - if !factorFound { - return fmt.Errorf("no push-type MFA factor found for user") - } - - result, _, err := client.UserFactor.VerifyFactor(ctx, user.Id, userFactor.Id, okta.VerifyFactorRequest{}, userFactor, nil) - if err != nil { - return err - } - - if result.FactorResult != "WAITING" { - return fmt.Errorf("expected WAITING status for push status, got %q", result.FactorResult) - } - - // Parse links to get polling link - type linksObj struct { - Poll struct { - Href string `mapstructure:"href"` - } `mapstructure:"poll"` - } - links := new(linksObj) - if err := mapstructure.WeakDecode(result.Links, links); err != nil { - return err - } - // Strip the org URL from the fully qualified poll URL - url, err := url.Parse(strings.Replace(links.Poll.Href, orgURL.String(), "", 1)) - if err != nil { - return err - } - - for { - // Okta provides an SDK method `GetFactorTransactionStatus` but does not provide the transaction id in - // the VerifyFactor respone. This code effectively reimplements that method. - rq := client.CloneRequestExecutor() - req, err := rq.WithAccept("application/json").WithContentType("application/json").NewRequest("GET", url.String(), nil) - if err != nil { - return err - } - var result *okta.VerifyUserFactorResponse - _, err = rq.Do(ctx, req, &result) - if err != nil { - return err - } - - switch result.FactorResult { - case "WAITING": - case "SUCCESS": - return nil - case "REJECTED": - return fmt.Errorf("push verification explicitly rejected") - case "TIMEOUT": - return fmt.Errorf("push verification timed out") - default: - return fmt.Errorf("unknown status code") - } - timer := time.NewTimer(time.Second) - - select { - case <-ctx.Done(): - timer.Stop() - return fmt.Errorf("push verification operation canceled") - case <-timer.C: - } - } -} - -func (c *Core) validatePingID(ctx context.Context, mConfig *mfa.Config, username string) error { - pingConfig := mConfig.GetPingIDConfig() - if pingConfig == nil { - return fmt.Errorf("failed to get PingID configuration for method %q", mConfig.Name) - } - - signingKey, err := base64.StdEncoding.DecodeString(pingConfig.UseBase64Key) - if err != nil { - return errwrap.Wrapf("failed decoding pingid signing key: {{err}}", err) - } - - client := cleanhttp.DefaultClient() - - createRequest := func(reqPath string, reqBody map[string]interface{}) (*http.Request, error) { - // Construct the token - token := &jwt.Token{ - Method: jwt.SigningMethodHS256, - Header: map[string]interface{}{ - "alg": "HS256", - "org_alias": pingConfig.OrgAlias, - "token": pingConfig.Token, - }, - Claims: jwt.MapClaims{ - "reqHeader": map[string]interface{}{ - "locale": "en", - "orgAlias": pingConfig.OrgAlias, - "secretKey": pingConfig.Token, - "timestamp": time.Now().Format("2006-01-02 15:04:05.000"), - "version": "4.9", - }, - "reqBody": reqBody, - }, - } - signedToken, err := token.SignedString(signingKey) - if err != nil { - return nil, errwrap.Wrapf("failed signing pingid request token: {{err}}", err) - } - - // Construct the URL - if !strings.HasPrefix(reqPath, "/") { - reqPath = "/" + reqPath - } - reqURL, err := url.Parse(pingConfig.IDPURL + reqPath) - if err != nil { - return nil, errwrap.Wrapf("failed to parse pingid request url: {{err}}", err) - } - - // Construct the request; WithContext is done here since it's a shallow - // copy - req := &http.Request{} - req = req.WithContext(ctx) - req.Method = "POST" - req.URL = reqURL - req.Body = ioutil.NopCloser(bytes.NewBufferString(signedToken)) - if req.Header == nil { - req.Header = make(http.Header) - } - req.Header.Set("Content-Type", "application/json") - return req, nil - } - - do := func(req *http.Request) (*jwt.Token, error) { - // Run the request and fetch the response - resp, err := client.Do(req) - if err != nil { - return nil, err - } - if resp == nil { - return nil, fmt.Errorf("nil response from pingid") - } - if resp.Body == nil { - return nil, fmt.Errorf("nil body in pingid response") - } - bodyBytes := bytes.NewBuffer(nil) - _, err = bodyBytes.ReadFrom(resp.Body) - resp.Body.Close() - if err != nil { - return nil, errwrap.Wrapf("error reading pingid response: {{err}}", err) - } - - // Parse the body, which is a JWT. Ensure that it's using HMAC signing - // and return the signing key in the func for validation - token, err := jwt.Parse(bodyBytes.String(), func(token *jwt.Token) (interface{}, error) { - if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { - return nil, fmt.Errorf("unexpected signing method %q from pingid response", token.Header["alg"]) - } - return signingKey, nil - }) - if err != nil { - return nil, errwrap.Wrapf("error parsing pingid response: {{err}}", err) - } - - // Check if parameters are as expected - if _, ok := token.Header["token"]; !ok { - return nil, fmt.Errorf("%q header not found", "token") - } - if headerTokenStr, ok := token.Header["token"].(string); !ok || headerTokenStr != pingConfig.Token { - return nil, fmt.Errorf("invalid token in ping response") - } - - // validate org alias - // This was originally 'org_alias', but it appears to now be returned as 'orgAlias'. Official - // Ping docs are sparse on the header details. We now prefer orgAlias but will still handle - // org_alias. - oa := token.Header["orgAlias"] - if oa == nil { - if oa = token.Header["org_alias"]; oa == nil { - return nil, fmt.Errorf("neither orgAlias nor org_alias headers were found") - } - } - - if headerOrgAliasStr, ok := oa.(string); !ok || headerOrgAliasStr != pingConfig.OrgAlias { - return nil, fmt.Errorf("invalid org_alias in ping response") - } - return token, nil - } - - type deviceDetails struct { - PushEnabled bool `mapstructure:"pushEnabled"` - DeviceID int64 `mapstructure:"deviceId"` - } - - type respBody struct { - SessionID string `mapstructure:"sessionId"` - ErrorID int64 `mapstructure:"errorId"` - ErrorMsg string `mapstructure:"errorMsg"` - UserDevices []deviceDetails `mapstructure:"userDevices"` - } - - type apiResponse struct { - ResponseBody respBody `mapstructure:"responseBody"` - } - - /* - // Normally we don't leave in commented code, however: - // Explicitly setting the device ID didn't work even when the device was - // push enabled (said the application was not installed on the device), so - // instead trigger default behavior, which does work, even when there's - // only one device and the deviceid matched :-/ - // We're leaving this here because if we support other types we'll likely - // still need it, and if we get device ID selection working we'll want it. - - req, err := createRequest("rest/4/startauthentication/do", map[string]interface{}{ - "spAlias": "web", - "userName": username, - }) - if err != nil { - return err - } - token, err := do(req) - if err != nil { - return err - } - - // We get back a map from the JWT library so use mapstructure - var startResp apiResponse - err = mapstructure.Decode(token.Claims, &startResp) - if err != nil { - return err - } - - // Look for at least one push-enabled method - body := startResp.ResponseBody - var foundPush bool - switch { - case body.ErrorID != 30007: - return fmt.Errorf("only pingid push authentication is currently supported") - - case len(body.UserDevices) == 0: - return fmt.Errorf("no user mfa devices returned from pingid") - - default: - for _, dev := range body.UserDevices { - if dev.PushEnabled { - foundPush = true - break - } - } - - if !foundPush { - return fmt.Errorf("no push enabled device id found from pingid") - } - } - */ - req, err := createRequest("rest/4/authonline/do", map[string]interface{}{ - "spAlias": "web", - "userName": username, - "authType": "CONFIRM", - }) - if err != nil { - return err - } - token, err := do(req) - if err != nil { - return err - } - - // Ensure a success response - var authResp apiResponse - err = mapstructure.Decode(token.Claims, &authResp) - if err != nil { - return err - } - - if authResp.ResponseBody.ErrorID != 200 { - return errors.New(authResp.ResponseBody.ErrorMsg) - } - - return nil -} - func (c *Core) validateTOTP(ctx context.Context, mfaFactors *MFAFactor, entityMethodSecret *mfa.Secret, configID, entityID string, usedCodes *cache.Cache, maximumValidationAttempts uint32) error { if mfaFactors == nil || mfaFactors.passcode == "" { return fmt.Errorf("MFA credentials not supplied") @@ -3016,16 +2274,4 @@ var mfaHelp = map[string][2]string{ "Defines or updates a TOTP MFA method.", "", }, - "okta-method": { - "Defines or updates an Okta MFA method.", - "", - }, - "duo-method": { - "Defines or updates a Duo MFA method.", - "", - }, - "pingid-method": { - "Defines or updates a PingID MFA method.", - "", - }, } diff --git a/vault/request_handling.go b/vault/request_handling.go index 35e3a4535..9471aafb6 100644 --- a/vault/request_handling.go +++ b/vault/request_handling.go @@ -2170,18 +2170,10 @@ func (c *Core) buildMfaEnforcementResponse(eConfig *mfa.MFAEnforcementConfig) (* if err != nil { return nil, fmt.Errorf("failed to get methodID %s from MFA config table, error: %v", methodID, err) } - var duoUsePasscode bool - if mConfig.Type == mfaMethodTypeDuo { - duoConf, ok := mConfig.Config.(*mfa.Config_DuoConfig) - if !ok { - return nil, fmt.Errorf("invalid MFA configuration type") - } - duoUsePasscode = duoConf.DuoConfig.UsePasscode - } mfaMethod := &logical.MFAMethodID{ Type: mConfig.Type, ID: methodID, - UsesPasscode: mConfig.Type == mfaMethodTypeTOTP || duoUsePasscode, + UsesPasscode: mConfig.Type == mfaMethodTypeTOTP, Name: mConfig.Name, } mfaAny.Any = append(mfaAny.Any, mfaMethod)