OIDC: add support for account claim maps (#1186)

This commit is contained in:
Jim authored and GitHub committed 2021-05-04 08:41:04 -04:00
1 parent 4ae5003763
commit 2437cdcc8d
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 {