OIDC: add support for account claim maps (#1186)
This commit is contained in:
28 files changed
+1456
-200
No files matched your search
@@ -678,6 +678,7 @@ func toAuthMethodProto(in auth.AuthMethod, opt ...handlers.Option) (*pb.AuthMeth
|
||||
SigningAlgorithms: i.GetSigningAlgs(),
|
||||
AllowedAudiences: i.GetAudClaims(),
|
||||
ClaimsScopes: i.GetClaimsScopes(),
|
||||
AccountClaimMaps: i.GetAccountClaimMaps(),
|
||||
}
|
||||
if i.DisableDiscoveredConfigValidation {
|
||||
attrs.DisableDiscoveredConfigValidation = true
|
||||
@@ -814,6 +815,19 @@ func validateCreateRequest(req *pbs.CreateAuthMethodRequest) error {
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(attrs.GetAccountClaimMaps()) > 0 {
|
||||
acm, err := oidc.ParseAccountClaimMaps(attrs.GetAccountClaimMaps()...)
|
||||
if err != nil {
|
||||
badFields[accountClaimMapsField] = fmt.Sprintf("Contains invalid map %q", err.Error())
|
||||
}
|
||||
foundTo := make(map[string]bool, len(attrs.GetAccountClaimMaps()))
|
||||
for from, rawTo := range acm {
|
||||
if foundTo[rawTo] {
|
||||
badFields[accountClaimMapsField] = fmt.Sprintf("%s=%s contains invalid map - multiple maps to the same Boundary to-claim %s", from, rawTo, rawTo)
|
||||
}
|
||||
foundTo[rawTo] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
default:
|
||||
badFields[typeField] = fmt.Sprintf("This is a required field and must be %q.", auth.PasswordSubtype.String())
|
||||
@@ -922,6 +936,30 @@ func validateUpdateRequest(req *pbs.UpdateAuthMethodRequest) error {
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(attrs.GetAccountClaimMaps()) > 0 {
|
||||
acm, err := oidc.ParseAccountClaimMaps(attrs.GetAccountClaimMaps()...)
|
||||
if err != nil {
|
||||
badFields[accountClaimMapsField] = fmt.Sprintf("Contains invalid map %q", err.Error())
|
||||
} else {
|
||||
foundTo := make(map[string]bool, len(attrs.GetAccountClaimMaps()))
|
||||
for from, rawTo := range acm {
|
||||
if foundTo[rawTo] {
|
||||
badFields[accountClaimMapsField] = fmt.Sprintf("%s=%s contains invalid map - multiple maps to the same Boundary to-claim %s", from, rawTo, rawTo)
|
||||
}
|
||||
foundTo[rawTo] = true
|
||||
|
||||
to, err := oidc.ConvertToAccountToClaim(rawTo)
|
||||
if err != nil {
|
||||
badFields[accountClaimMapsField] = fmt.Sprintf("%s=%s contains invalid map %q", from, rawTo, err.Error())
|
||||
break
|
||||
}
|
||||
if to == oidc.ToSubClaim {
|
||||
badFields[accountClaimMapsField] = fmt.Sprintf("%s=%s contains invalid map: not allowed to update sub claim maps", from, rawTo)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
default:
|
||||
badFields["id"] = "Incorrectly formatted identifier."
|
||||
}
|
||||
|
||||
@@ -572,6 +572,10 @@ func TestCreate(t *testing.T) {
|
||||
lv, _ := structpb.NewList([]interface{}{"email", "profile"})
|
||||
return structpb.NewListValue(lv)
|
||||
}(),
|
||||
"account_claim_maps": func() *structpb.Value {
|
||||
lv, _ := structpb.NewList([]interface{}{"display_name=name", "oid=sub"})
|
||||
return structpb.NewListValue(lv)
|
||||
}(),
|
||||
}},
|
||||
}},
|
||||
idPrefix: oidc.AuthMethodPrefix + "_",
|
||||
@@ -600,6 +604,10 @@ func TestCreate(t *testing.T) {
|
||||
lv, _ := structpb.NewList([]interface{}{"email", "profile"})
|
||||
return structpb.NewListValue(lv)
|
||||
}(),
|
||||
"account_claim_maps": func() *structpb.Value {
|
||||
lv, _ := structpb.NewList([]interface{}{"display_name=name", "oid=sub"})
|
||||
return structpb.NewListValue(lv)
|
||||
}(),
|
||||
}},
|
||||
AuthorizedActions: oidcAuthorizedActions,
|
||||
AuthorizedCollectionActions: authorizedCollectionActions,
|
||||
@@ -989,7 +997,7 @@ func TestCreate(t *testing.T) {
|
||||
delete(tc.res.Item.Attributes.Fields, "callback_url")
|
||||
}
|
||||
}
|
||||
assert.Empty(cmp.Diff(got, tc.res, protocmp.Transform()), "CreateAuthMethod(%q) got response %q, wanted %q", tc.req, got, tc.res)
|
||||
assert.Empty(cmp.Diff(got, tc.res, protocmp.Transform(), protocmp.SortRepeatedFields(got)), "CreateAuthMethod(%q) got response %q, wanted %q", tc.req, got, tc.res)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -42,6 +42,7 @@ const (
|
||||
roundtripPayloadAttributesField = "attributes.roundtrip_payload"
|
||||
codeField = "attributes.code"
|
||||
claimsScopesField = "attributes.claims_scopes"
|
||||
accountClaimMapsField = "attributes.account_claim_maps"
|
||||
)
|
||||
|
||||
var oidcMaskManager handlers.MaskManager
|
||||
@@ -472,6 +473,28 @@ func toStorageOidcAuthMethod(scopeId string, in *pb.AuthMethod) (out *oidc.AuthM
|
||||
opts = append(opts, oidc.WithClaimsScopes(attrs.GetClaimsScopes()...))
|
||||
}
|
||||
|
||||
if len(attrs.GetAccountClaimMaps()) > 0 {
|
||||
claimsMap := make(map[string]oidc.AccountToClaim, len(attrs.GetAccountClaimMaps()))
|
||||
for _, v := range attrs.GetAccountClaimMaps() {
|
||||
acm, err := oidc.ParseAccountClaimMaps(v)
|
||||
if err != nil {
|
||||
return nil, false, false, errors.Wrap(err, op)
|
||||
}
|
||||
if len(acm) > 1 {
|
||||
return nil, false, false, errors.New(errors.InvalidParameter, op, fmt.Sprintf("unable to parse account claim map %s", v))
|
||||
}
|
||||
var from, rawTo string
|
||||
for from, rawTo = range acm {
|
||||
}
|
||||
to, err := oidc.ConvertToAccountToClaim(rawTo)
|
||||
if err != nil {
|
||||
return nil, false, false, errors.Wrap(err, op)
|
||||
}
|
||||
claimsMap[from] = to
|
||||
}
|
||||
opts = append(opts, oidc.WithAccountClaimMap(claimsMap))
|
||||
}
|
||||
|
||||
u, err := oidc.NewAuthMethod(scopeId, clientId, clientSecret, opts...)
|
||||
if err != nil {
|
||||
return nil, false, false, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to build auth method: %v.", err)
|
||||
|
||||
@@ -905,6 +905,64 @@ func TestUpdate_OIDC(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Change Account Claim Maps",
|
||||
req: &pbs.UpdateAuthMethodRequest{
|
||||
UpdateMask: &field_mask.FieldMask{
|
||||
Paths: []string{"attributes.account_claim_maps"},
|
||||
},
|
||||
Item: &pb.AuthMethod{
|
||||
Attributes: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
"account_claim_maps": func() *structpb.Value {
|
||||
lv, _ := structpb.NewList([]interface{}{"display_name=name"})
|
||||
return structpb.NewListValue(lv)
|
||||
}(),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
res: &pbs.UpdateAuthMethodResponse{
|
||||
Item: &pb.AuthMethod{
|
||||
ScopeId: o.GetPublicId(),
|
||||
Name: &wrapperspb.StringValue{Value: "default"},
|
||||
Description: &wrapperspb.StringValue{Value: "default"},
|
||||
Type: auth.OidcSubtype.String(),
|
||||
Attributes: &structpb.Struct{
|
||||
Fields: func() map[string]*structpb.Value {
|
||||
f := defaultReadAttributeFields()
|
||||
f["account_claim_maps"] = func() *structpb.Value {
|
||||
lv, _ := structpb.NewList([]interface{}{"display_name=name"})
|
||||
return structpb.NewListValue(lv)
|
||||
}()
|
||||
return f
|
||||
}(),
|
||||
},
|
||||
Scope: defaultScopeInfo,
|
||||
AuthorizedActions: oidcAuthorizedActions,
|
||||
AuthorizedCollectionActions: authorizedCollectionActions,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Attempt to Change Account Claim Maps for sub",
|
||||
req: &pbs.UpdateAuthMethodRequest{
|
||||
UpdateMask: &field_mask.FieldMask{
|
||||
Paths: []string{"attributes.account_claim_maps"},
|
||||
},
|
||||
Item: &pb.AuthMethod{
|
||||
Attributes: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
"account_claim_maps": func() *structpb.Value {
|
||||
lv, _ := structpb.NewList([]interface{}{"oid=sub"})
|
||||
return structpb.NewListValue(lv)
|
||||
}(),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "Unset Issuer Is Incomplete",
|
||||
req: &pbs.UpdateAuthMethodRequest{
|
||||
@@ -996,7 +1054,6 @@ func TestUpdate_OIDC(t *testing.T) {
|
||||
tc.res.Item.Id = am.GetId()
|
||||
tc.res.Item.CreatedTime = am.GetCreatedTime()
|
||||
}
|
||||
|
||||
got, gErr := tested.UpdateAuthMethod(auth.DisabledAuthTestContext(iamRepoFn, o.GetPublicId()), tc.req)
|
||||
// TODO: When handlers move to domain errors remove wantErr and rely errors.Match here.
|
||||
if tc.err != nil || tc.wantErr {
|
||||
|
||||
Reference in new issue
Block a user