mirror of
https://github.com/hashicorp/vault.git
synced 2026-05-05 20:36:26 +02:00
[VAULT-38601] Modify response to MFA enforced requests to enable TOTP self-enrollment (#8723) (#8746)
Co-authored-by: Kuba Wieczorek <kuba.wieczorek@hashicorp.com>
This commit is contained in:
parent
3594d6d6b1
commit
ae0e5e160f
@ -298,6 +298,11 @@ type MFAMethodID struct {
|
||||
ID string `json:"id,omitempty"`
|
||||
UsesPasscode bool `json:"uses_passcode,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
// SelfEnrollmentEnabled indicates whether the user does not yet have an MFA
|
||||
// secret for this method and self-enrollment is enabled for it. Clients (like the UI) can use
|
||||
// this to determine whether to offer the user a way to generate an MFA secret
|
||||
// for this method.
|
||||
SelfEnrollmentEnabled bool `json:"self_enrollment_enabled,omitempty"`
|
||||
}
|
||||
|
||||
type MFAConstraintAny struct {
|
||||
|
||||
@ -306,13 +306,14 @@ func (x *Group) GetNamespaceID() string {
|
||||
}
|
||||
|
||||
type MFAMethodID struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"`
|
||||
ID string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"`
|
||||
UsesPasscode bool `protobuf:"varint,3,opt,name=uses_passcode,json=usesPasscode,proto3" json:"uses_passcode,omitempty"`
|
||||
Name string `protobuf:"bytes,4,opt,name=name,proto3" json:"name,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"`
|
||||
ID string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"`
|
||||
UsesPasscode bool `protobuf:"varint,3,opt,name=uses_passcode,json=usesPasscode,proto3" json:"uses_passcode,omitempty"`
|
||||
Name string `protobuf:"bytes,4,opt,name=name,proto3" json:"name,omitempty"`
|
||||
SelfEnrollmentEnabled bool `protobuf:"varint,5,opt,name=self_enrollment_enabled,json=selfEnrollmentEnabled,proto3" json:"self_enrollment_enabled,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *MFAMethodID) Reset() {
|
||||
@ -373,6 +374,13 @@ func (x *MFAMethodID) GetName() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *MFAMethodID) GetSelfEnrollmentEnabled() bool {
|
||||
if x != nil {
|
||||
return x.SelfEnrollmentEnabled
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type MFAConstraintAny struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Any []*MFAMethodID `protobuf:"bytes,1,rep,name=any,proto3" json:"any,omitempty"`
|
||||
@ -531,35 +539,39 @@ var file_sdk_logical_identity_proto_rawDesc = string([]byte{
|
||||
0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01,
|
||||
0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
|
||||
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38,
|
||||
0x01, 0x22, 0x6a, 0x0a, 0x0b, 0x4d, 0x46, 0x41, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x49, 0x44,
|
||||
0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04,
|
||||
0x74, 0x79, 0x70, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
|
||||
0x52, 0x02, 0x69, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x75, 0x73, 0x65, 0x73, 0x5f, 0x70, 0x61, 0x73,
|
||||
0x73, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x75, 0x73, 0x65,
|
||||
0x73, 0x50, 0x61, 0x73, 0x73, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d,
|
||||
0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x3a, 0x0a,
|
||||
0x10, 0x4d, 0x46, 0x41, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, 0x41, 0x6e,
|
||||
0x79, 0x12, 0x26, 0x0a, 0x03, 0x61, 0x6e, 0x79, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14,
|
||||
0x2e, 0x6c, 0x6f, 0x67, 0x69, 0x63, 0x61, 0x6c, 0x2e, 0x4d, 0x46, 0x41, 0x4d, 0x65, 0x74, 0x68,
|
||||
0x6f, 0x64, 0x49, 0x44, 0x52, 0x03, 0x61, 0x6e, 0x79, 0x22, 0xea, 0x01, 0x0a, 0x0e, 0x4d, 0x46,
|
||||
0x41, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x24, 0x0a, 0x0e,
|
||||
0x6d, 0x66, 0x61, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01,
|
||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x6d, 0x66, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
|
||||
0x49, 0x64, 0x12, 0x54, 0x0a, 0x0f, 0x6d, 0x66, 0x61, 0x5f, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x72,
|
||||
0x61, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x6c, 0x6f,
|
||||
0x67, 0x69, 0x63, 0x61, 0x6c, 0x2e, 0x4d, 0x46, 0x41, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65,
|
||||
0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x4d, 0x66, 0x61, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69,
|
||||
0x6e, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0e, 0x6d, 0x66, 0x61, 0x43, 0x6f, 0x6e,
|
||||
0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, 0x73, 0x1a, 0x5c, 0x0a, 0x13, 0x4d, 0x66, 0x61, 0x43,
|
||||
0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12,
|
||||
0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65,
|
||||
0x79, 0x12, 0x2f, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b,
|
||||
0x32, 0x19, 0x2e, 0x6c, 0x6f, 0x67, 0x69, 0x63, 0x61, 0x6c, 0x2e, 0x4d, 0x46, 0x41, 0x43, 0x6f,
|
||||
0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, 0x41, 0x6e, 0x79, 0x52, 0x05, 0x76, 0x61, 0x6c,
|
||||
0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x28, 0x5a, 0x26, 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, 0x73, 0x64, 0x6b, 0x2f, 0x6c, 0x6f, 0x67, 0x69, 0x63, 0x61, 0x6c,
|
||||
0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
0x01, 0x22, 0xa2, 0x01, 0x0a, 0x0b, 0x4d, 0x46, 0x41, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x49,
|
||||
0x44, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||
0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28,
|
||||
0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x75, 0x73, 0x65, 0x73, 0x5f, 0x70, 0x61,
|
||||
0x73, 0x73, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x75, 0x73,
|
||||
0x65, 0x73, 0x50, 0x61, 0x73, 0x73, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61,
|
||||
0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x36,
|
||||
0x0a, 0x17, 0x73, 0x65, 0x6c, 0x66, 0x5f, 0x65, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x6d, 0x65, 0x6e,
|
||||
0x74, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52,
|
||||
0x15, 0x73, 0x65, 0x6c, 0x66, 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x6d, 0x65, 0x6e, 0x74, 0x45,
|
||||
0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0x3a, 0x0a, 0x10, 0x4d, 0x46, 0x41, 0x43, 0x6f, 0x6e,
|
||||
0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, 0x41, 0x6e, 0x79, 0x12, 0x26, 0x0a, 0x03, 0x61, 0x6e,
|
||||
0x79, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6c, 0x6f, 0x67, 0x69, 0x63, 0x61,
|
||||
0x6c, 0x2e, 0x4d, 0x46, 0x41, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x49, 0x44, 0x52, 0x03, 0x61,
|
||||
0x6e, 0x79, 0x22, 0xea, 0x01, 0x0a, 0x0e, 0x4d, 0x46, 0x41, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72,
|
||||
0x65, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x24, 0x0a, 0x0e, 0x6d, 0x66, 0x61, 0x5f, 0x72, 0x65, 0x71,
|
||||
0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x6d,
|
||||
0x66, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x54, 0x0a, 0x0f, 0x6d,
|
||||
0x66, 0x61, 0x5f, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x02,
|
||||
0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x6c, 0x6f, 0x67, 0x69, 0x63, 0x61, 0x6c, 0x2e, 0x4d,
|
||||
0x46, 0x41, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x4d, 0x66,
|
||||
0x61, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72,
|
||||
0x79, 0x52, 0x0e, 0x6d, 0x66, 0x61, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74,
|
||||
0x73, 0x1a, 0x5c, 0x0a, 0x13, 0x4d, 0x66, 0x61, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69,
|
||||
0x6e, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18,
|
||||
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2f, 0x0a, 0x05, 0x76, 0x61,
|
||||
0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6c, 0x6f, 0x67, 0x69,
|
||||
0x63, 0x61, 0x6c, 0x2e, 0x4d, 0x46, 0x41, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e,
|
||||
0x74, 0x41, 0x6e, 0x79, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42,
|
||||
0x28, 0x5a, 0x26, 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, 0x73, 0x64,
|
||||
0x6b, 0x2f, 0x6c, 0x6f, 0x67, 0x69, 0x63, 0x61, 0x6c, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
||||
0x33,
|
||||
})
|
||||
|
||||
var (
|
||||
|
||||
@ -83,6 +83,7 @@ message MFAMethodID {
|
||||
string id = 2;
|
||||
bool uses_passcode = 3;
|
||||
string name = 4;
|
||||
bool self_enrollment_enabled = 5;
|
||||
}
|
||||
|
||||
message MFAConstraintAny {
|
||||
|
||||
@ -1273,7 +1273,7 @@ func (b *LoginMFABackend) mfaConfigReadByMethodID(id string) (map[string]interfa
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return b.mfaConfigToMap(mConfig, true)
|
||||
return b.mfaConfigToMap(mConfig, constants.IsEnterprise, true)
|
||||
}
|
||||
|
||||
func (b *LoginMFABackend) mfaMethodList(ctx context.Context, methodType string) ([]string, map[string]interface{}, error) {
|
||||
@ -1333,7 +1333,7 @@ func (b *LoginMFABackend) mfaMethodList(ctx context.Context, methodType string)
|
||||
}
|
||||
|
||||
keys = append(keys, config.ID)
|
||||
configInfoEntry, err := b.mfaConfigToMap(config, true)
|
||||
configInfoEntry, err := b.mfaConfigToMap(config, constants.IsEnterprise, true)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to convert config to map: %w", err)
|
||||
}
|
||||
@ -1423,7 +1423,7 @@ func (b *LoginMFABackend) mfaLoginEnforcementConfigToMap(eConfig *mfa.MFAEnforce
|
||||
// method endpoints. The `isLoginMFA` parameter indicates whether the
|
||||
// configuration is for login MFA, which includes additional fields on the
|
||||
// shared mfa.Config object for the TOTP type MFA method.
|
||||
func (b *MFABackend) mfaConfigToMap(mConfig *mfa.Config, isLoginMFA bool) (map[string]interface{}, error) {
|
||||
func (b *MFABackend) mfaConfigToMap(mConfig *mfa.Config, isEnterprise, isLoginMFA bool) (map[string]interface{}, error) {
|
||||
respData := make(map[string]interface{})
|
||||
|
||||
switch mConfig.Config.(type) {
|
||||
@ -1437,9 +1437,9 @@ func (b *MFABackend) mfaConfigToMap(mConfig *mfa.Config, isLoginMFA bool) (map[s
|
||||
respData["qr_size"] = totpConfig.QRSize
|
||||
respData["algorithm"] = otplib.Algorithm(totpConfig.Algorithm).String()
|
||||
respData["max_validation_attempts"] = totpConfig.MaxValidationAttempts
|
||||
if isLoginMFA {
|
||||
if isEnterprise && isLoginMFA {
|
||||
// Login MFA and policy (i.e. enterprise step-up) MFA share the same protobuf message for TOTPConfig,
|
||||
// but the login MFA has an additional field for self-enrollment.
|
||||
// but the login MFA has an additional field for self-enrollment, which is an enterprise feature.
|
||||
respData["enable_self_enrollment"] = totpConfig.GetEnableSelfEnrollment()
|
||||
}
|
||||
case *mfa.Config_OktaConfig:
|
||||
|
||||
@ -79,6 +79,7 @@ func TestMFAConfigToMap(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
config *mfa.Config
|
||||
expectedResult map[string]any
|
||||
isEnterprise bool
|
||||
isLoginMFA bool
|
||||
}{
|
||||
"totp-with-login-mfa": {
|
||||
@ -114,7 +115,43 @@ func TestMFAConfigToMap(t *testing.T) {
|
||||
"skew": uint32(1),
|
||||
"type": "totp",
|
||||
},
|
||||
isLoginMFA: true,
|
||||
isEnterprise: true,
|
||||
isLoginMFA: true,
|
||||
},
|
||||
"totp-with-login-mfa-non-ent": {
|
||||
config: &mfa.Config{
|
||||
Type: mfaMethodTypeTOTP,
|
||||
Config: &mfa.Config_TOTPConfig{
|
||||
TOTPConfig: &mfa.TOTPConfig{
|
||||
Issuer: "TestIssuer",
|
||||
Period: 30,
|
||||
Digits: 6,
|
||||
Skew: 1,
|
||||
KeySize: 20,
|
||||
QRSize: 200,
|
||||
Algorithm: int32(otplib.AlgorithmSHA1),
|
||||
MaxValidationAttempts: 5,
|
||||
EnableSelfEnrollment: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedResult: map[string]interface{}{
|
||||
"algorithm": "SHA1",
|
||||
"digits": int32(6),
|
||||
"id": "",
|
||||
"issuer": "TestIssuer",
|
||||
"key_size": uint32(20),
|
||||
"max_validation_attempts": uint32(5),
|
||||
"name": "",
|
||||
"namespace_id": "",
|
||||
"namespace_path": "/",
|
||||
"period": uint32(30),
|
||||
"qr_size": int32(200),
|
||||
"skew": uint32(1),
|
||||
"type": "totp",
|
||||
},
|
||||
isEnterprise: false,
|
||||
isLoginMFA: true,
|
||||
},
|
||||
"totp-ent-step-up-mfa-self-enrollment-false": {
|
||||
config: &mfa.Config{
|
||||
@ -148,7 +185,8 @@ func TestMFAConfigToMap(t *testing.T) {
|
||||
"skew": uint32(1),
|
||||
"type": "totp",
|
||||
},
|
||||
isLoginMFA: false,
|
||||
isEnterprise: true,
|
||||
isLoginMFA: false,
|
||||
},
|
||||
"totp-ent-step-up-mfa-self-enrollment-true": {
|
||||
config: &mfa.Config{
|
||||
@ -182,7 +220,8 @@ func TestMFAConfigToMap(t *testing.T) {
|
||||
"skew": uint32(1),
|
||||
"type": "totp",
|
||||
},
|
||||
isLoginMFA: false,
|
||||
isEnterprise: true,
|
||||
isLoginMFA: false,
|
||||
},
|
||||
"okta-prod": {
|
||||
config: &mfa.Config{
|
||||
@ -257,9 +296,8 @@ func TestMFAConfigToMap(t *testing.T) {
|
||||
"namespace_id": "",
|
||||
"namespace_path": "/",
|
||||
},
|
||||
isLoginMFA: false,
|
||||
},
|
||||
"pingid": {
|
||||
"ping-id": {
|
||||
config: &mfa.Config{
|
||||
Type: mfaMethodTypePingID,
|
||||
Config: &mfa.Config_PingIDConfig{
|
||||
@ -284,7 +322,6 @@ func TestMFAConfigToMap(t *testing.T) {
|
||||
"namespace_id": "",
|
||||
"namespace_path": "/",
|
||||
},
|
||||
isLoginMFA: false,
|
||||
},
|
||||
}
|
||||
backend := &MFABackend{}
|
||||
@ -292,7 +329,7 @@ func TestMFAConfigToMap(t *testing.T) {
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
actualResult, err := backend.mfaConfigToMap(tc.config, tc.isLoginMFA)
|
||||
actualResult, err := backend.mfaConfigToMap(tc.config, tc.isEnterprise, tc.isLoginMFA)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, tc.expectedResult, actualResult)
|
||||
})
|
||||
@ -308,7 +345,7 @@ func TestMfaConfigToMap_InvalidType(t *testing.T) {
|
||||
Config: nil,
|
||||
}
|
||||
|
||||
_, err := backend.mfaConfigToMap(mConfig, true)
|
||||
_, err := backend.mfaConfigToMap(mConfig, true, true)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "invalid method type")
|
||||
}
|
||||
|
||||
@ -26,6 +26,7 @@ import (
|
||||
"github.com/hashicorp/go-sockaddr"
|
||||
"github.com/hashicorp/go-uuid"
|
||||
"github.com/hashicorp/vault/command/server"
|
||||
"github.com/hashicorp/vault/helper/constants"
|
||||
"github.com/hashicorp/vault/helper/identity"
|
||||
"github.com/hashicorp/vault/helper/identity/mfa"
|
||||
"github.com/hashicorp/vault/helper/metricsutil"
|
||||
@ -1874,7 +1875,7 @@ func (c *Core) handleLoginRequest(ctx context.Context, req *logical.Request) (re
|
||||
MFAConstraints: make(map[string]*logical.MFAConstraintAny),
|
||||
}
|
||||
for _, eConfig := range matchedMfaEnforcementList {
|
||||
mfaAny, err := c.buildMfaEnforcementResponse(eConfig)
|
||||
mfaAny, err := c.buildMfaEnforcementResponse(eConfig, entity)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@ -2456,7 +2457,13 @@ func (c *Core) getUserLockoutFromConfig(mountType string) UserLockoutConfig {
|
||||
return defaultUserLockoutConfig
|
||||
}
|
||||
|
||||
func (c *Core) buildMfaEnforcementResponse(eConfig *mfa.MFAEnforcementConfig) (*logical.MFAConstraintAny, error) {
|
||||
func (c *Core) buildMfaEnforcementResponse(eConfig *mfa.MFAEnforcementConfig, entity *identity.Entity) (*logical.MFAConstraintAny, error) {
|
||||
if eConfig == nil {
|
||||
return nil, fmt.Errorf("MFA enforcement config is nil")
|
||||
}
|
||||
if entity == nil {
|
||||
return nil, fmt.Errorf("entity is nil")
|
||||
}
|
||||
mfaAny := &logical.MFAConstraintAny{
|
||||
Any: []*logical.MFAMethodID{},
|
||||
}
|
||||
@ -2469,15 +2476,35 @@ func (c *Core) buildMfaEnforcementResponse(eConfig *mfa.MFAEnforcementConfig) (*
|
||||
if mConfig.Type == mfaMethodTypeDuo {
|
||||
duoConf, ok := mConfig.Config.(*mfa.Config_DuoConfig)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid MFA configuration type")
|
||||
return nil, fmt.Errorf("invalid MFA configuration type, expected DuoConfig")
|
||||
}
|
||||
duoUsePasscode = duoConf.DuoConfig.UsePasscode
|
||||
}
|
||||
|
||||
allowSelfEnrollment := false
|
||||
if mConfig.Type == mfaMethodTypeTOTP && constants.IsEnterprise {
|
||||
totpConf, ok := mConfig.Config.(*mfa.Config_TOTPConfig)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid MFA configuration type, expected TOTPConfig")
|
||||
}
|
||||
enrollmentEnabled := totpConf.TOTPConfig.GetEnableSelfEnrollment()
|
||||
_, entityHasMFASecretForMethodID := entity.MFASecrets[methodID]
|
||||
if enrollmentEnabled && !entityHasMFASecretForMethodID {
|
||||
// If enable_self_enrollment setting on the TOTP MFA method config is set to
|
||||
// true and the entity does not have an MFA secret yet, we will allow
|
||||
// self-service enrollment.
|
||||
allowSelfEnrollment = true
|
||||
}
|
||||
}
|
||||
|
||||
mfaMethod := &logical.MFAMethodID{
|
||||
Type: mConfig.Type,
|
||||
ID: methodID,
|
||||
UsesPasscode: mConfig.Type == mfaMethodTypeTOTP || duoUsePasscode,
|
||||
Name: mConfig.Name,
|
||||
// This will be used by the client to determine whether it should offer the user
|
||||
// a way to generate an MFA secret for this method.
|
||||
SelfEnrollmentEnabled: allowSelfEnrollment,
|
||||
}
|
||||
mfaAny.Any = append(mfaAny.Any, mfaMethod)
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user