Refactor identity/mfa/method/* endpoints to fix bad OpenAPI (#20879)

* Refactor `identity/mfa/method/*` endpoints to fix bad OpenAPI

There is a problem with how the `identity/mfa/method/*` endpoints are
defined, resulting in incorrect OpenAPI generation.

I raised hashicorp/vault-client-go#180 to track a consequence, and
opened #20873 which explains the problem and adds a log message to
detect it.

This PR is now the fix.

It's actually quite an interesting problem, that has come about through
some particular implementation choices, in Vault's first/only case where
REST API objects are created by writing to the collection URL, and have
their ID allocated by the server, instead of the client.

The triggering cause of the malfunction was trying to have a single
framework.Path struct instance which optionally includes or excludes the
method_id path parameter, and also another framework.Path struct
instance handling list operations.

The fix is to simplify the path regexes, and have one framework.Path
which handles the method_id being present, and one that handles it being
absent.

The diff is somewhat large, because the affected code had been
copy/pasted four times (TOTP, Okta, Duo, PingID) - so I took the
opportunity to fix the duplication, creating appropriate helper methods
so that the quadruplicated code could be re-unified.

* Revise documentation

This update refactors how the documentation presents these endpoints to
users, both for clarity, and to align with the new structure of the
code.

From a user perspective, it clears up some unclear presentation of when
the `method_id` parameter should and should not be present, adds
a missing description of the response to create requests, and changes
the `method_id` parameter name to be used consistently (rather than `id`
in some cases, unlike the actual code/OpenAPI).

* Fix incorrect acronym (review fix)

* Accept suggestion of tweaked grammar in documentation

Co-authored-by: Anton Averchenkov <84287187+averche@users.noreply.github.com>

* Add changelog

---------

Co-authored-by: Anton Averchenkov <84287187+averche@users.noreply.github.com>
This commit is contained in:
Max Bowsher
2023-06-23 18:32:41 +01:00
committed by GitHub
parent a71cdb6165
commit 43ae739971
7 changed files with 391 additions and 417 deletions

3
changelog/20879.txt Normal file
View File

@@ -0,0 +1,3 @@
```release-note:bug
identity/mfa: Fixes to OpenAPI representation and returned error codes for `identity/mfa/method/*` APIs
```

View File

@@ -140,18 +140,23 @@ func (i *IdentityStore) paths() []*framework.Path {
upgradePaths(i),
oidcPaths(i),
oidcProviderPaths(i),
mfaPaths(i),
mfaCommonPaths(i),
mfaTOTPPaths(i),
mfaTOTPExtraPaths(i),
mfaOktaPaths(i),
mfaDuoPaths(i),
mfaPingIDPaths(i),
mfaLoginEnforcementPaths(i),
)
}
func mfaPaths(i *IdentityStore) []*framework.Path {
func mfaCommonPaths(i *IdentityStore) []*framework.Path {
return []*framework.Path{
{
Pattern: "mfa/method" + genericOptionalUUIDRegex("method_id"),
Pattern: "mfa/method/" + uuidRegex("method_id"),
DisplayAttrs: &framework.DisplayAttributes{
OperationPrefix: "mfa",
OperationVerb: "read",
OperationSuffix: "method-configuration|method-configuration",
OperationSuffix: "method",
},
Fields: map[string]*framework.FieldSchema{
"method_id": {
@@ -180,100 +185,144 @@ func mfaPaths(i *IdentityStore) []*framework.Path {
},
},
},
}
}
func makeMFAMethodPaths(
methodType string,
methodTypeForOpenAPIOperationID string,
methodFields map[string]*framework.FieldSchema,
i *IdentityStore,
) []*framework.Path {
methodFieldsIncludingMethodID := make(map[string]*framework.FieldSchema)
for k, v := range methodFields {
methodFieldsIncludingMethodID[k] = v
}
methodFieldsIncludingMethodID["method_id"] = &framework.FieldSchema{
Type: framework.TypeString,
Description: `The unique identifier for this MFA method.`,
}
return []*framework.Path{
{
Pattern: "mfa/method/totp" + genericOptionalUUIDRegex("method_id"),
Pattern: "mfa/method/" + methodType + "/" + uuidRegex("method_id"),
DisplayAttrs: &framework.DisplayAttributes{
OperationPrefix: "mfa",
OperationSuffix: methodTypeForOpenAPIOperationID + "-method",
},
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.`,
},
"max_validation_attempts": {
Type: framework.TypeInt,
Description: `Max number of allowed validation attempts.`,
},
"issuer": {
Type: framework.TypeString,
Description: `The name of the key's issuing organization.`,
},
"period": {
Type: framework.TypeDurationSecond,
Default: 30,
Description: `The length of time used to generate a counter for the TOTP token calculation.`,
},
"key_size": {
Type: framework.TypeInt,
Default: 20,
Description: "Determines the size in bytes of the generated key.",
},
"qr_size": {
Type: framework.TypeInt,
Default: 200,
Description: `The pixel size of the generated square QR code.`,
},
"algorithm": {
Type: framework.TypeString,
Default: "SHA1",
Description: `The hashing algorithm used to generate the TOTP token. Options include SHA1, SHA256 and SHA512.`,
},
"digits": {
Type: framework.TypeInt,
Default: 6,
Description: `The number of digits in the generated TOTP token. This value can either be 6 or 8.`,
},
"skew": {
Type: framework.TypeInt,
Default: 1,
Description: `The number of delay periods that are allowed when validating a TOTP token. This value can either be 0 or 1.`,
},
},
Fields: methodFieldsIncludingMethodID,
Operations: map[logical.Operation]framework.OperationHandler{
logical.ReadOperation: &framework.PathOperation{
Callback: i.handleMFAMethodTOTPRead,
DisplayAttrs: &framework.DisplayAttributes{
OperationVerb: "read",
OperationSuffix: "totp-method-configuration|totp-method-configuration",
Callback: func(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
return i.handleMFAMethodReadCommon(ctx, req, d, methodType)
},
Summary: "Read the current configuration for the given MFA method",
},
logical.UpdateOperation: &framework.PathOperation{
Callback: i.handleMFAMethodTOTPUpdate,
DisplayAttrs: &framework.DisplayAttributes{
OperationVerb: "configure",
OperationSuffix: "totp-method|totp-method",
Callback: func(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
return i.handleMFAMethodWriteCommon(ctx, req, d, methodType)
},
Summary: "Update or create a configuration for the given MFA method",
DisplayAttrs: &framework.DisplayAttributes{
OperationVerb: "update",
},
Summary: "Update the configuration for the given MFA method",
},
logical.DeleteOperation: &framework.PathOperation{
Callback: i.handleMFAMethodTOTPDelete,
DisplayAttrs: &framework.DisplayAttributes{
OperationVerb: "delete",
OperationSuffix: "totp-method|totp-method",
Callback: func(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
return i.handleMFAMethodDeleteCommon(ctx, req, d, methodType)
},
Summary: "Delete a configuration for the given MFA method",
Summary: "Delete the given MFA method",
},
},
},
{
Pattern: "mfa/method/totp/?$",
Pattern: "mfa/method/" + methodType + "/?$",
DisplayAttrs: &framework.DisplayAttributes{
OperationPrefix: "mfa",
OperationVerb: "list",
OperationSuffix: "totp-methods",
},
Fields: methodFields,
Operations: map[logical.Operation]framework.OperationHandler{
logical.ListOperation: &framework.PathOperation{
Callback: i.handleMFAMethodListTOTP,
Summary: "List MFA method configurations for the given MFA method",
Callback: func(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
return i.handleMFAMethodListCommon(ctx, req, d, methodType)
},
DisplayAttrs: &framework.DisplayAttributes{
OperationSuffix: methodTypeForOpenAPIOperationID + "-methods",
},
Summary: "List MFA method configurations for the given MFA method",
},
// Conceptually, it would make more sense to treat this as a CreateOperation, but we have to leave it
// as an UpdateOperation, because the API was originally released that way, and we don't want to change
// the meaning of ACL policies that users have written.
logical.UpdateOperation: &framework.PathOperation{
Callback: func(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
return i.handleMFAMethodWriteCommon(ctx, req, d, methodType)
},
DisplayAttrs: &framework.DisplayAttributes{
OperationVerb: "create",
OperationSuffix: methodTypeForOpenAPIOperationID + "-method",
},
Summary: "Create the given MFA method",
},
},
},
}
}
func mfaTOTPPaths(i *IdentityStore) []*framework.Path {
return makeMFAMethodPaths(
mfaMethodTypeTOTP,
mfaMethodTypeTOTP,
map[string]*framework.FieldSchema{
"method_name": {
Type: framework.TypeString,
Description: `The unique name identifier for this MFA method.`,
},
"max_validation_attempts": {
Type: framework.TypeInt,
Description: `Max number of allowed validation attempts.`,
},
"issuer": {
Type: framework.TypeString,
Description: `The name of the key's issuing organization.`,
},
"period": {
Type: framework.TypeDurationSecond,
Default: 30,
Description: `The length of time used to generate a counter for the TOTP token calculation.`,
},
"key_size": {
Type: framework.TypeInt,
Default: 20,
Description: "Determines the size in bytes of the generated key.",
},
"qr_size": {
Type: framework.TypeInt,
Default: 200,
Description: `The pixel size of the generated square QR code.`,
},
"algorithm": {
Type: framework.TypeString,
Default: "SHA1",
Description: `The hashing algorithm used to generate the TOTP token. Options include SHA1, SHA256 and SHA512.`,
},
"digits": {
Type: framework.TypeInt,
Default: 6,
Description: `The number of digits in the generated TOTP token. This value can either be 6 or 8.`,
},
"skew": {
Type: framework.TypeInt,
Default: 1,
Description: `The number of delay periods that are allowed when validating a TOTP token. This value can either be 0 or 1.`,
},
},
i,
)
}
func mfaTOTPExtraPaths(i *IdentityStore) []*framework.Path {
return []*framework.Path{
{
Pattern: "mfa/method/totp/generate$",
DisplayAttrs: &framework.DisplayAttributes{
@@ -347,230 +396,111 @@ func mfaPaths(i *IdentityStore) []*framework.Path {
},
},
},
{
Pattern: "mfa/method/okta" + genericOptionalUUIDRegex("method_id"),
DisplayAttrs: &framework.DisplayAttributes{
OperationPrefix: "mfa",
}
}
func mfaOktaPaths(i *IdentityStore) []*framework.Path {
return makeMFAMethodPaths(
mfaMethodTypeOkta,
mfaMethodTypeOkta,
map[string]*framework.FieldSchema{
"method_name": {
Type: framework.TypeString,
Description: `The unique name identifier for this MFA method.`,
},
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.",
},
"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.`,
},
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",
},
"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.",
},
},
{
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",
},
i,
)
}
func mfaDuoPaths(i *IdentityStore) []*framework.Path {
return makeMFAMethodPaths(
mfaMethodTypeDuo,
mfaMethodTypeDuo,
map[string]*framework.FieldSchema{
"method_name": {
Type: framework.TypeString,
Description: `The unique name 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.`,
},
},
{
Pattern: "mfa/method/duo" + genericOptionalUUIDRegex("method_id"),
DisplayAttrs: &framework.DisplayAttributes{
OperationPrefix: "mfa",
i,
)
}
func mfaPingIDPaths(i *IdentityStore) []*framework.Path {
return makeMFAMethodPaths(
mfaMethodTypePingID,
// This overridden name helps code generation using the OpenAPI spec choose better method names, that avoid
// treating "Pingid" as a single word:
"ping-id",
map[string]*framework.FieldSchema{
"method_name": {
Type: framework.TypeString,
Description: `The unique name identifier for this MFA method.`,
},
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.`,
},
"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. `,
},
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",
},
"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.",
},
},
i,
)
}
func mfaLoginEnforcementPaths(i *IdentityStore) []*framework.Path {
return []*framework.Path{
{
Pattern: "mfa/login-enforcement/" + framework.GenericNameRegex("name"),
DisplayAttrs: &framework.DisplayAttributes{

View File

@@ -106,8 +106,14 @@ func (b *SystemBackend) loginMFAPaths() []*framework.Path {
}
}
func genericOptionalUUIDRegex(name string) string {
return fmt.Sprintf("(/(?P<%s>[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}))?", name)
// uuidRegex crafts a regex for use in URL paths, somewhat similar to framework.GenericNameRegex, but only accepting
// UUIDs, and only lowercase UUIDs at that.
// It is currently exclusively used for the method_id parameter for MFA methods.
// Think twice before making use of it in any other context, as restricting the valid input in the URL regex results in
// an "unsupported path" error, given input which does not match the regex, which is a fairly unclear way to report an
// invalid parameter value, unless the person seeing the error has an excellent understanding of Vault URL routing.
func uuidRegex(name string) string {
return fmt.Sprintf("(?P<%s>[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})", name)
}
type MFABackend struct {
@@ -181,22 +187,6 @@ func (b *LoginMFABackend) ResetLoginMFAMemDB() error {
return nil
}
func (i *IdentityStore) handleMFAMethodListTOTP(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
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 {
@@ -206,7 +196,7 @@ func (i *IdentityStore) handleMFAMethodListGlobal(ctx context.Context, req *logi
return logical.ListResponseWithInfo(keys, configInfo), nil
}
func (i *IdentityStore) handleMFAMethodList(ctx context.Context, req *logical.Request, d *framework.FieldData, methodType string) (*logical.Response, error) {
func (i *IdentityStore) handleMFAMethodListCommon(ctx context.Context, req *logical.Request, d *framework.FieldData, methodType string) (*logical.Response, error) {
keys, configInfo, err := i.mfaBackend.mfaMethodList(ctx, methodType)
if err != nil {
return nil, err
@@ -215,22 +205,6 @@ func (i *IdentityStore) handleMFAMethodList(ctx context.Context, req *logical.Re
return logical.ListResponseWithInfo(keys, configInfo), nil
}
func (i *IdentityStore) handleMFAMethodTOTPRead(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
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, "")
}
@@ -274,7 +248,7 @@ func (i *IdentityStore) handleMFAMethodReadCommon(ctx context.Context, req *logi
}, nil
}
func (i *IdentityStore) handleMFAMethodUpdateCommon(ctx context.Context, req *logical.Request, d *framework.FieldData, methodType string) (*logical.Response, error) {
func (i *IdentityStore) handleMFAMethodWriteCommon(ctx context.Context, req *logical.Request, d *framework.FieldData, methodType string) (*logical.Response, error) {
var err error
var mConfig *mfa.Config
ns, err := namespace.FromContext(ctx)
@@ -282,7 +256,14 @@ func (i *IdentityStore) handleMFAMethodUpdateCommon(ctx context.Context, req *lo
return nil, err
}
methodID := d.Get("method_id").(string)
// This method handles both the create and update flow. The method_id field is not part of the field schema for the
// create flow, so we need to use GetOk in order to not panic.
var methodID string
methodIDAsInterface, ok := d.GetOk("method_id")
if ok {
methodID = methodIDAsInterface.(string)
}
methodName := d.Get("method_name").(string)
b := i.mfaBackend
@@ -405,38 +386,6 @@ func (i *IdentityStore) handleMFAMethodUpdateCommon(ctx context.Context, req *lo
}
}
func (i *IdentityStore) handleMFAMethodTOTPUpdate(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
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 == "" {

View File

@@ -5,18 +5,16 @@ description: >-
The '/identity/mfa/method/duo' endpoint focuses on managing Duo MFA behaviors in Vault.
---
## Configure Duo MFA Method
## Create Duo MFA Method
This endpoint defines an MFA method of type Duo.
This endpoint creates a new MFA method of type Duo.
| Method | Path |
| :----- | :----------------------------- |
| `POST` | `/identity/mfa/method/duo/:method_id` |
| Method | Path |
|:-------|:---------------------------|
| `POST` | `/identity/mfa/method/duo` |
### Parameters
- `method_id` `(string: "")` - Optional UUID to specify if updating an existing method.
- `method_name` `(string)` - The unique name identifier for this MFA method. Supported from Vault 1.13.0.
- `username_format` `(string)` - A template string for mapping Identity names to MFA methods. Values to substitute should be placed in `{{}}`. For example, `"{{identity.entity.name}}"`. If blank, the Entity's Name field is used as-is.
@@ -39,7 +37,7 @@ This endpoint defines an MFA method of type Duo.
"secret_key": "BIACEUEAXI20BNWTEYXT",
"integration_key": "8C7THtrIigh2rPZQMbguugt8IUftWhMRCOBzbuyz",
"api_hostname": "api-2b5c39f5.duosecurity.com",
"method_name": "ns1_duo",
"method_name": "ns1_duo"
}
```
@@ -53,6 +51,36 @@ $ curl \
http://127.0.0.1:8200/v1/identity/mfa/method/duo
```
### Sample Response
```json
{
"data": {
"method_id": "0888fd69-4ea2-91d7-415e-c4bba548529b"
}
}
```
## Update Duo MFA Method
This endpoint updates the configuration of an MFA method of type Duo.
| Method | Path |
|:-------|:--------------------------------------|
| `POST` | `/identity/mfa/method/duo/:method_id` |
### Parameters
- `method_id` `(string: <required>)` - UUID of the MFA method.
- and all of the parameters documented under the preceding "Create" endpoint.
### Sample Payload
Identical to the preceding "Create" endpoint.
### Sample Request
```shell-session
$ curl \
--header "X-Vault-Token: ..." \
@@ -66,9 +94,9 @@ $ curl \
This endpoint queries the MFA configuration of Duo type for a given method
ID.
| Method | Path |
| :----- | :----------------------------- |
| `GET` | `/identity/mfa/method/duo/:id` |
| Method | Path |
|:-------|:--------------------------------------|
| `GET` | `/identity/mfa/method/duo/:method_id` |
### Parameters
@@ -105,13 +133,13 @@ $ curl \
This endpoint deletes a Duo MFA method. MFA methods can only be deleted if they're not currently in use
by a [login enforcement](/vault/api-docs/secret/identity/mfa/login-enforcement).
| Method | Path |
| :------- | :----------------------------- |
| `DELETE` | `/identity/mfa/method/duo/:id` |
| Method | Path |
|:---------|:--------------------------------------|
| `DELETE` | `/identity/mfa/method/duo/:method_id` |
### Parameters
- `id` `(string: <required>)` - UUID of the MFA method.
- `method_id` `(string: <required>)` - UUID of the MFA method.
### Sample Request
@@ -127,7 +155,7 @@ $ curl \
This endpoint lists Duo MFA methods that are visible in the current namespace or in parent namespaces.
| Method | Path |
| :----- | :------------------------- |
|:-------|:---------------------------|
| `LIST` | `/identity/mfa/method/duo` |
### Sample Request

View File

@@ -5,18 +5,16 @@ description: >-
The '/identity/mfa/method/okta' endpoint focuses on managing Okta MFA behaviors in Vault.
---
## Configure Okta MFA Method
## Create Okta MFA Method
This endpoint defines an MFA method of type Okta.
This endpoint creates a new MFA method of type Okta.
| Method | Path |
| :----- | :------------------------------ |
| `POST` | `/identity/mfa/method/okta/:method_id` |
| Method | Path |
|:-------|:----------------------------|
| `POST` | `/identity/mfa/method/okta` |
### Parameters
- `method_id` `(string: "")` - Optional UUID to specify if updating an existing method.
- `method_name` `(string)` - The unique name identifier for this MFA method. Supported from Vault 1.13.0.
- `username_format` `(string)` - A format string for mapping Identity names to MFA method names. Values to substitute should be placed in `{{}}`. For example, `"{{identity.entity.name}}@example.com"`. If blank, the Entity's Name field is used as-is.
@@ -49,6 +47,36 @@ $ curl \
http://127.0.0.1:8200/v1/identity/mfa/method/okta
```
### Sample Response
```json
{
"data": {
"method_id": "0888fd69-4ea2-91d7-415e-c4bba548529b"
}
}
```
## Update Okta MFA Method
This endpoint updates the configuration of an MFA method of type Okta.
| Method | Path |
|:-------|:---------------------------------------|
| `POST` | `/identity/mfa/method/okta/:method_id` |
### Parameters
- `method_id` `(string: <required>)` - UUID of the MFA method.
- and all of the parameters documented under the preceding "Create" endpoint.
### Sample Payload
Identical to the preceding "Create" endpoint.
### Sample Request
```shell-session
$ curl \
--header "X-Vault-Token: ..." \
@@ -62,13 +90,13 @@ $ curl \
This endpoint queries the MFA configuration of Okta type for a given method
name.
| Method | Path |
| :----- | :------------------------------ |
| `GET` | `/identity/mfa/method/okta/:id` |
| Method | Path |
|:-------|:---------------------------------------|
| `GET` | `/identity/mfa/method/okta/:method_id` |
### Parameters
- `id` `(string: <required>)` UUID of the MFA method.
- `method_id` `(string: <required>)` UUID of the MFA method.
### Sample Request
@@ -99,13 +127,13 @@ $ curl \
This endpoint deletes a Okta MFA method. The MFA methods can only be deleted if they're not currently in use
by a [login enforcement](/vault/api-docs/secret/identity/mfa/login-enforcement).
| Method | Path |
| :------- | :------------------------------ |
| `DELETE` | `/identity/mfa/method/okta/:id` |
| Method | Path |
|:---------|:---------------------------------------|
| `DELETE` | `/identity/mfa/method/okta/:method_id` |
### Parameters
- `id` `(string: <required>)` - UUID of the MFA method.
- `method_id` `(string: <required>)` - UUID of the MFA method.
### Sample Request
@@ -121,7 +149,7 @@ $ curl \
This endpoint lists Okta MFA methods that are visible in the current namespace or in parent namespaces.
| Method | Path |
| :----- | :-------------------------- |
|:-------|:----------------------------|
| `LIST` | `/identity/mfa/method/okta` |
### Sample Request

View File

@@ -5,18 +5,16 @@ description: >-
The '/identity/mfa/method/pingid' endpoint focuses on managing PingID MFA behaviors in Vault.
---
## Configure PingID MFA Method
## Create PingID MFA Method
This endpoint defines an MFA method of type PingID.
This endpoint creates an MFA method of type PingID.
| Method | Path |
| :----- | :-------------------------------- |
| `POST` | `/identity/mfa/method/pingid/:method_id` |
| Method | Path |
|:-------|:------------------------------|
| `POST` | `/identity/mfa/method/pingid` |
### Parameters
- `method_id` `(string: "")` - Optional UUID to specify if updating an existing method.
- `method_name` `(string)` - The unique name identifier for this MFA method. Supported from Vault 1.13.0.
- `username_format` `(string)` - A template string for mapping Identity names to MFA method names. Values to substitute should be placed in `{{}}`. For example, `"{{identity.entity.name}}@example.com"`. If blank, the Entity's Name field is used as-is.
@@ -42,6 +40,26 @@ $ curl \
http://127.0.0.1:8200/v1/identity/mfa/method/pingid
```
## Update PingID MFA Method
This endpoint updates the configuration of an MFA method of type PingID.
| Method | Path |
|:-------|:-----------------------------------------|
| `POST` | `/identity/mfa/method/pingid/:method_id` |
### Parameters
- `method_id` `(string: <required>)` - UUID of the MFA method.
- and all of the parameters documented under the preceding "Create" endpoint.
### Sample Payload
Identical to the preceding "Create" endpoint.
### Sample Request
```shell-session
$ curl \
--header "X-Vault-Token: ..." \
@@ -56,7 +74,7 @@ This endpoint queries the MFA configuration of PingID type for a given method
name.
| Method | Path |
| :----- | :-------------------------------- |
|:-------|:----------------------------------|
| `GET` | `/identity/mfa/method/pingid/:id` |
### Parameters
@@ -94,7 +112,7 @@ This endpoint deletes a PingID MFA method. MFA methods can only be deleted if th
by a [login enforcement](/vault/api-docs/secret/identity/mfa/login-enforcement).
| Method | Path |
| :------- | :-------------------------------- |
|:---------|:----------------------------------|
| `DELETE` | `/identity/mfa/method/pingid/:id` |
### Parameters
@@ -115,7 +133,7 @@ $ curl \
This endpoint lists PingID MFA methods that are visible in the current namespace or in parent namespaces.
| Method | Path |
| :----- | :---------------------------- |
|:-------|:------------------------------|
| `LIST` | `/identity/mfa/method/pingid` |
### Sample Request

View File

@@ -5,18 +5,16 @@ description: >-
The '/identity/mfa/method/totp' endpoint focuses on managing TOTP MFA behaviors in Vault.
---
## Configure TOTP MFA Method
## Create TOTP MFA Method
This endpoint defines an MFA method of type TOTP.
This endpoint creates an MFA method of type TOTP.
| Method | Path |
| :----- | :------------------------------ |
| `POST` | `/identity/mfa/method/totp/:method_id` |
| Method | Path |
|:-------|:----------------------------|
| `POST` | `/identity/mfa/method/totp` |
### Parameters
- `method_id` `(string: "")` - Optional UUID to specify if updating an existing method.
- `method_name` `(string)` - The unique name identifier for this MFA method. Supported from Vault 1.13.0.
- `issuer` `(string: <required>)` - The name of the key's issuing organization.
@@ -53,6 +51,26 @@ $ curl \
http://127.0.0.1:8200/v1/identity/mfa/method/totp
```
## Update TOTP MFA Method
This endpoint updates the configuration of an MFA method of type TOTP.
| Method | Path |
|:-------|:---------------------------------------|
| `POST` | `/identity/mfa/method/totp/:method_id` |
### Parameters
- `method_id` `(string: <required>)` - UUID of the MFA method.
- and all of the parameters documented under the preceding "Create" endpoint.
### Sample Payload
Identical to the preceding "Create" endpoint.
### Sample Request
```shell-session
$ curl \
--header "X-Vault-Token: ..." \
@@ -66,13 +84,13 @@ $ curl \
This endpoint queries the MFA configuration of TOTP type for a given method
ID.
| Method | Path |
| :----- | :------------------------------ |
| `GET` | `/identity/mfa/method/totp/:id` |
| Method | Path |
|:-------|:---------------------------------------|
| `GET` | `/identity/mfa/method/totp/:method_id` |
### Parameters
- `id` `(string: <required>)` UUID of the MFA method.
- `method_id` `(string: <required>)` UUID of the MFA method.
### Sample Request
@@ -107,13 +125,13 @@ $ curl \
This endpoint deletes a TOTP MFA method. MFA methods can only be deleted if they're not currently in use
by a [login enforcement](/vault/api-docs/secret/identity/mfa/login-enforcement).
| Method | Path |
| :------- | :------------------------------ |
| `DELETE` | `/identity/mfa/method/totp/:id` |
| Method | Path |
|:---------|:---------------------------------------|
| `DELETE` | `/identity/mfa/method/totp/:method_id` |
### Parameters
- `id` `(string: <required>)` - UUID of the MFA method.
- `method_id` `(string: <required>)` - UUID of the MFA method.
### Sample Request
@@ -129,7 +147,7 @@ $ curl \
This endpoint lists TOTP MFA methods that are visible in the current namespace or in parent namespaces.
| Method | Path |
| :----- | :-------------------------- |
|:-------|:----------------------------|
| `LIST` | `/identity/mfa/method/totp` |
### Sample Request
@@ -161,7 +179,7 @@ doesn't exist already, using the configuration stored under the given MFA
method ID.
| Method | Path |
| :----- | :----------------------------------- |
|:-------|:-------------------------------------|
| `POST` | `/identity/mfa/method/totp/generate` |
### Parameters
@@ -204,7 +222,7 @@ API which stores the generated secret on the entity ID of the calling token,
the `admin-generate` API stores the generated secret on the given entity ID.
| Method | Path |
| :----- | :----------------------------------------- |
|:-------|:-------------------------------------------|
| `POST` | `/identity/mfa/method/totp/admin-generate` |
### Parameters
@@ -254,7 +272,7 @@ and the `generate` or `admin-generate` APIs should be used to regenerate a new
secret.
| Method | Path |
| :----- | :---------------------------------------- |
|:-------|:------------------------------------------|
| `POST` | `/identity/mfa/method/totp/admin-destroy` |
### Parameters