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

@@ -14,6 +14,7 @@ type OidcAuthMethodAttributes struct {
IdpCaCerts []string `json:"idp_ca_certs,omitempty"`
AllowedAudiences []string `json:"allowed_audiences,omitempty"`
ClaimsScopes []string `json:"claims_scopes,omitempty"`
AccountClaimMaps []string `json:"account_claim_maps,omitempty"`
DisableDiscoveredConfigValidation bool `json:"disable_discovered_config_validation,omitempty"`
DryRun bool `json:"dry_run,omitempty"`
}
+24
View File
@@ -87,6 +87,30 @@ func WithRecursive(recurse bool) Option {
}
}
func WithOidcAuthMethodAccountClaimMaps(inAccountClaimMaps []string) Option {
return func(o *options) {
raw, ok := o.postMap["attributes"]
if !ok {
raw = interface{}(map[string]interface{}{})
}
val := raw.(map[string]interface{})
val["account_claim_maps"] = inAccountClaimMaps
o.postMap["attributes"] = val
}
}
func DefaultOidcAuthMethodAccountClaimMaps() Option {
return func(o *options) {
raw, ok := o.postMap["attributes"]
if !ok {
raw = interface{}(map[string]interface{}{})
}
val := raw.(map[string]interface{})
val["account_claim_maps"] = nil
o.postMap["attributes"] = val
}
}
func WithOidcAuthMethodAllowedAudiences(inAllowedAudiences []string) Option {
return func(o *options) {
raw, ok := o.postMap["attributes"]
+100
View File
@@ -0,0 +1,100 @@
package oidc
import (
"fmt"
"github.com/hashicorp/boundary/internal/auth/oidc/store"
"github.com/hashicorp/boundary/internal/errors"
"google.golang.org/protobuf/proto"
)
const (
// defaultAcctClaimMapTableName defines the default table name for an AccountClaimMap
defaultAcctClaimMapTableName = "auth_oidc_account_claim_map"
)
type AccountToClaim string
const (
ToSubClaim AccountToClaim = "sub"
ToEmailClaim AccountToClaim = "email"
ToNameClaim AccountToClaim = "name"
)
func ConvertToAccountToClaim(s string) (AccountToClaim, error) {
const op = "oidc.(AccountToClaim).convertToAccountToClaim"
switch s {
case string(ToSubClaim):
return ToSubClaim, nil
case string(ToEmailClaim):
return ToEmailClaim, nil
case string(ToNameClaim):
return ToNameClaim, nil
default:
return "", errors.New(errors.InvalidParameter, op, fmt.Sprintf("%s is not a valid ToAccountClaim value", s))
}
}
// AccountClaimMap defines optional OIDC scope values that are used to request
// claims, in addition to the default scope of "openid" (see: DefaultClaimsScope).
type AccountClaimMap struct {
*store.AccountClaimMap
tableName string
}
func NewAccountClaimMap(authMethodId, fromClaim string, toClaim AccountToClaim) (*AccountClaimMap, error) {
const op = "oidc.NewAccountClaimMap"
cs := &AccountClaimMap{
AccountClaimMap: &store.AccountClaimMap{
OidcMethodId: authMethodId,
FromClaim: fromClaim,
ToClaim: string(toClaim),
},
}
if err := cs.validate(op); err != nil {
return nil, err
}
return cs, nil
}
// validate the AccountClaimMap. On success, it will return nil.
func (cs *AccountClaimMap) validate(caller errors.Op) error {
if cs.OidcMethodId == "" {
return errors.New(errors.InvalidParameter, caller, "missing oidc auth method id")
}
if cs.FromClaim == "" {
return errors.New(errors.InvalidParameter, caller, "missing from claim")
}
if _, err := ConvertToAccountToClaim(cs.ToClaim); err != nil {
return errors.Wrap(err, caller)
}
return nil
}
// AllocClaimsScope makes an empty one in memory
func AllocAccountClaimMap() AccountClaimMap {
return AccountClaimMap{
AccountClaimMap: &store.AccountClaimMap{},
}
}
// Clone a AccountClaimMap
func (cs *AccountClaimMap) Clone() *AccountClaimMap {
cp := proto.Clone(cs.AccountClaimMap)
return &AccountClaimMap{
AccountClaimMap: cp.(*store.AccountClaimMap),
}
}
// TableName returns the table name.
func (s *AccountClaimMap) TableName() string {
if s.tableName != "" {
return s.tableName
}
return defaultAcctClaimMapTableName
}
// SetTableName sets the table name.
func (s *AccountClaimMap) SetTableName(n string) {
s.tableName = n
}
@@ -0,0 +1,282 @@
package oidc
import (
"context"
"testing"
"github.com/hashicorp/boundary/internal/db"
"github.com/hashicorp/boundary/internal/errors"
"github.com/hashicorp/boundary/internal/iam"
"github.com/hashicorp/boundary/internal/kms"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/proto"
)
func TestAccountClaimMap_Create(t *testing.T) {
t.Parallel()
conn, _ := db.TestSetup(t, "postgres")
wrapper := db.TestWrapper(t)
kmsCache := kms.TestKms(t, conn, wrapper)
org, _ := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
rw := db.New(conn)
databaseWrapper, err := kmsCache.GetWrapper(context.Background(), org.PublicId, kms.KeyPurposeDatabase)
require.NoError(t, err)
testAuthMethod := TestAuthMethod(t, conn, databaseWrapper, org.PublicId, InactiveState, "alice_rp", "my-dogs-name",
WithIssuer(TestConvertToUrls(t, "https://alice.com")[0]), WithApiUrl(TestConvertToUrls(t, "https://api.com")[0]))
type args struct {
authMethodId string
to AccountToClaim
from string
}
tests := []struct {
name string
args args
want *AccountClaimMap
wantErr bool
wantIsErr errors.Code
create bool
wantCreateErr bool
wantCreateIsErr errors.Code
}{
{
name: "valid",
args: args{
authMethodId: testAuthMethod.PublicId,
to: ToSubClaim,
from: "oid",
},
create: true,
want: func() *AccountClaimMap {
want := AllocAccountClaimMap()
want.OidcMethodId = testAuthMethod.PublicId
want.ToClaim = string(ToSubClaim)
want.FromClaim = "oid"
return &want
}(),
},
{
name: "dup", // must follow "valid" test. Url must be be unique for an OidcMethodId
args: args{
authMethodId: testAuthMethod.PublicId,
to: ToSubClaim,
from: "oid",
},
create: true,
want: func() *AccountClaimMap {
want := AllocAccountClaimMap()
want.OidcMethodId = testAuthMethod.PublicId
want.ToClaim = string(ToSubClaim)
want.FromClaim = "oid"
return &want
}(),
wantCreateErr: true,
wantCreateIsErr: errors.NotUnique,
},
{
name: "empty-auth-method",
args: args{
authMethodId: "",
to: ToSubClaim,
from: "oid",
},
wantErr: true,
wantIsErr: errors.InvalidParameter,
},
{
name: "empty-to",
args: args{
authMethodId: testAuthMethod.PublicId,
from: "oid",
},
wantErr: true,
wantIsErr: errors.InvalidParameter,
},
{
name: "empty-from",
args: args{
authMethodId: testAuthMethod.PublicId,
to: ToSubClaim,
},
wantErr: true,
wantIsErr: errors.InvalidParameter,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
got, err := NewAccountClaimMap(tt.args.authMethodId, tt.args.from, tt.args.to)
if tt.wantErr {
require.Error(err)
assert.True(errors.Match(errors.T(tt.wantIsErr), err))
return
}
require.NoError(err)
assert.Equal(tt.want, got)
if tt.create {
ctx := context.Background()
err = rw.Create(ctx, got)
if tt.wantCreateErr {
assert.Error(err)
assert.True(errors.Match(errors.T(tt.wantCreateIsErr), err))
return
} else {
assert.NoError(err)
}
found := AllocAccountClaimMap()
require.NoError(rw.LookupWhere(ctx, &found, "oidc_method_id = ? and to_claim = ?", tt.args.authMethodId, tt.args.to))
assert.Equal(got, &found)
}
})
}
}
func TestAccountClaimMap_Delete(t *testing.T) {
t.Parallel()
conn, _ := db.TestSetup(t, "postgres")
wrapper := db.TestWrapper(t)
kmsCache := kms.TestKms(t, conn, wrapper)
org, _ := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
rw := db.New(conn)
databaseWrapper, err := kmsCache.GetWrapper(context.Background(), org.PublicId, kms.KeyPurposeDatabase)
require.NoError(t, err)
testAuthMethod :=
TestAuthMethod(t, conn, databaseWrapper, org.PublicId, InactiveState, "alice_rp", "my-dogs-name",
WithApiUrl(TestConvertToUrls(t, "https://api.com")[0]), WithAudClaims("alice.com")) // seed an extra callback url to just make sure the delete only gets the right num of rows
testResource := func(authMethodId string, fromClaim string, toClaim AccountToClaim) *AccountClaimMap {
c, err := NewAccountClaimMap(authMethodId, fromClaim, toClaim)
require.NoError(t, err)
return c
}
tests := []struct {
name string
AccountClaimMap *AccountClaimMap
wantRowsDeleted int
overrides func(*AccountClaimMap)
wantErr bool
wantErrMsg string
}{
{
name: "valid",
AccountClaimMap: testResource(testAuthMethod.PublicId, "oid", "sub"),
wantErr: false,
wantRowsDeleted: 1,
},
{
name: "bad-OidcMethodId",
AccountClaimMap: testResource(testAuthMethod.PublicId, "oid", "sub"),
overrides: func(c *AccountClaimMap) { c.OidcMethodId = "bad-id"; c.ToClaim = "sub" },
wantErr: false,
wantRowsDeleted: 0,
},
{
name: "bad-toClaim",
AccountClaimMap: testResource(testAuthMethod.PublicId, "oid", "sub"),
overrides: func(c *AccountClaimMap) { c.ToClaim = "bad-aud" },
wantErr: false,
wantRowsDeleted: 0,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
ctx := context.Background()
_, err := rw.Exec(ctx, "delete from auth_oidc_account_claim_map", nil)
require.NoError(err)
cp := tt.AccountClaimMap.Clone()
require.NoError(rw.Create(ctx, &cp))
if tt.overrides != nil {
tt.overrides(cp)
}
deletedRows, err := rw.Delete(ctx, &cp)
if tt.wantErr {
require.Error(err)
return
}
require.NoError(err)
if tt.wantRowsDeleted == 0 {
assert.Equal(tt.wantRowsDeleted, deletedRows)
return
}
assert.Equal(tt.wantRowsDeleted, deletedRows)
found := AllocAccountClaimMap()
err = rw.LookupWhere(ctx, &found, "oidc_method_id = ? and to_claim = ?", tt.AccountClaimMap.OidcMethodId, tt.AccountClaimMap.ToClaim)
assert.True(errors.IsNotFoundError(err))
})
}
}
func TestAccountClaimMap_Clone(t *testing.T) {
t.Parallel()
conn, _ := db.TestSetup(t, "postgres")
wrapper := db.TestWrapper(t)
kmsCache := kms.TestKms(t, conn, wrapper)
t.Run("valid", func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
org, _ := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
databaseWrapper, err := kmsCache.GetWrapper(context.Background(), org.PublicId, kms.KeyPurposeDatabase)
require.NoError(err)
m := TestAuthMethod(t, conn, databaseWrapper, org.PublicId, InactiveState, "alice_rp", "my-dogs-name",
WithIssuer(TestConvertToUrls(t, "https://alice.com")[0]), WithApiUrl(TestConvertToUrls(t, "https://api.com")[0]))
orig, err := NewAccountClaimMap(m.PublicId, "oid", ToSubClaim)
require.NoError(err)
cp := orig.Clone()
assert.True(proto.Equal(cp.AccountClaimMap, orig.AccountClaimMap))
})
t.Run("not-equal", func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
org, _ := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
databaseWrapper, err := kmsCache.GetWrapper(context.Background(), org.PublicId, kms.KeyPurposeDatabase)
require.NoError(err)
m := TestAuthMethod(t, conn, databaseWrapper, org.PublicId, InactiveState, "alice_rp", "my-dogs-name",
WithIssuer(TestConvertToUrls(t, "https://alice.com")[0]), WithApiUrl(TestConvertToUrls(t, "https://api.com")[0]))
orig, err := NewAccountClaimMap(m.PublicId, "oid", ToSubClaim)
require.NoError(err)
orig2, err := NewAccountClaimMap(m.PublicId, "uid", ToSubClaim)
require.NoError(err)
cp := orig.Clone()
assert.True(!proto.Equal(cp.AccountClaimMap, orig2.AccountClaimMap))
})
}
func TestAccountClaimMap_SetTableName(t *testing.T) {
t.Parallel()
defaultTableName := defaultAcctClaimMapTableName
tests := []struct {
name string
setNameTo string
want string
}{
{
name: "new-name",
setNameTo: "new-name",
want: "new-name",
},
{
name: "reset to default",
setNameTo: "",
want: defaultTableName,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
def := AllocAccountClaimMap()
require.Equal(defaultTableName, def.TableName())
m := AllocAccountClaimMap()
m.SetTableName(tt.setNameTo)
assert.Equal(tt.want, m.TableName())
})
}
}
+69 -9
View File
@@ -17,6 +17,7 @@ import (
wrapping "github.com/hashicorp/go-kms-wrapping"
"github.com/hashicorp/go-kms-wrapping/structwrapping"
"github.com/hashicorp/go-multierror"
kvbuilder "github.com/hashicorp/shared-secure-libs/kv-builder"
"google.golang.org/protobuf/proto"
)
@@ -107,6 +108,12 @@ func NewAuthMethod(scopeId string, clientId string, clientSecret ClientSecret, o
a.SigningAlgs = append(a.SigningAlgs, string(alg))
}
}
if len(opts.withAccountClaimMap) > 0 {
a.AccountClaimMaps = make([]string, 0, len(opts.withAccountClaimMap))
for k, v := range opts.withAccountClaimMap {
a.AccountClaimMaps = append(a.AccountClaimMaps, fmt.Sprintf("%s=%s", k, v))
}
}
if a.OperationalState != string(InactiveState) {
if err := a.isComplete(); err != nil {
return nil, errors.Wrap(err, op, errors.WithMsg("new auth method being created with incomplete data but non-inactive state"))
@@ -270,10 +277,11 @@ func (am *AuthMethod) isComplete() error {
}
type convertedValues struct {
Algs []interface{}
Auds []interface{}
Certs []interface{}
ClaimsScopes []interface{}
Algs []interface{}
Auds []interface{}
Certs []interface{}
ClaimsScopes []interface{}
AccountClaimMaps []interface{}
}
// convertValueObjects converts the embedded value objects. It will return an
@@ -284,7 +292,7 @@ func (am *AuthMethod) convertValueObjects() (*convertedValues, error) {
return nil, errors.New(errors.InvalidPublicId, op, "missing public id")
}
var err error
var addAlgs, addAuds, addCerts, addScopes []interface{}
var addAlgs, addAuds, addCerts, addScopes, addAccountClaimMaps []interface{}
if addAlgs, err = am.convertSigningAlgs(); err != nil {
return nil, errors.Wrap(err, op)
}
@@ -297,11 +305,15 @@ func (am *AuthMethod) convertValueObjects() (*convertedValues, error) {
if addScopes, err = am.convertClaimsScopes(); err != nil {
return nil, errors.Wrap(err, op)
}
if addAccountClaimMaps, err = am.convertAccountClaimMaps(); err != nil {
return nil, errors.Wrap(err, op)
}
return &convertedValues{
Algs: addAlgs,
Auds: addAuds,
Certs: addCerts,
ClaimsScopes: addScopes,
Algs: addAlgs,
Auds: addAuds,
Certs: addCerts,
ClaimsScopes: addScopes,
AccountClaimMaps: addAccountClaimMaps,
}, nil
}
@@ -380,3 +392,51 @@ func (am *AuthMethod) convertClaimsScopes() ([]interface{}, error) {
}
return newInterfaces, nil
}
// convertAccountClaimMaps converts the embedded account claim maps from
// []string to []interface{} where each slice element is a *AccountClaimMap. It
// will return an error if the AuthMethod's public id is not set or it can
// convert the account claim maps.
func (am *AuthMethod) convertAccountClaimMaps() ([]interface{}, error) {
const op = "oidc.(AuthMethod).convertAccountClaimMaps"
if am.PublicId == "" {
return nil, errors.New(errors.InvalidPublicId, op, "missing public id")
}
newInterfaces := make([]interface{}, 0, len(am.AccountClaimMaps))
const (
from = 0
to = 1
)
acms, err := ParseAccountClaimMaps(am.AccountClaimMaps...)
if err != nil {
return nil, errors.Wrap(err, op)
}
for from, to := range acms {
toClaim, err := ConvertToAccountToClaim(to)
if err != nil {
return nil, errors.Wrap(err, op)
}
obj, err := NewAccountClaimMap(am.PublicId, from, toClaim)
if err != nil {
return nil, errors.Wrap(err, op)
}
newInterfaces = append(newInterfaces, obj)
}
return newInterfaces, nil
}
func ParseAccountClaimMaps(m ...string) (map[string]string, error) {
const op = "oidc.parseAccountClaimMaps"
var b kvbuilder.Builder
if err := b.Add(m...); err != nil {
return nil, errors.New(errors.InvalidParameter, op, "error parsing map", errors.WithWrap(err))
}
claimMap := map[string]string{}
for k, v := range b.Map() {
var ok bool
if claimMap[k], ok = v.(string); !ok {
return nil, errors.New(errors.InvalidParameter, op, fmt.Sprintf("account claim map key %s value %q is not a string", k, v))
}
}
return claimMap, nil
}
+78 -9
View File
@@ -3,6 +3,7 @@ package oidc
import (
"context"
"net/url"
"sort"
"testing"
"github.com/hashicorp/boundary/internal/auth/oidc/store"
@@ -573,6 +574,18 @@ func Test_convertValueObjects(t *testing.T) {
testClaimsScopes = append(testClaimsScopes, obj)
}
testClaimMaps := []string{"oid=sub", "display_name=name"}
testAccountClaimMaps := make([]interface{}, 0, len(testClaimMaps))
acms, err := ParseAccountClaimMaps(testClaimMaps...)
require.NoError(t, err)
for from, to := range acms {
toClaim, err := ConvertToAccountToClaim(to)
require.NoError(t, err)
obj, err := NewAccountClaimMap(testPublicId, from, toClaim)
require.NoError(t, err)
testAccountClaimMaps = append(testAccountClaimMaps, obj)
}
tests := []struct {
name string
authMethodId string
@@ -580,6 +593,7 @@ func Test_convertValueObjects(t *testing.T) {
auds []string
certs []string
scopes []string
maps []string
wantValues *convertedValues
wantErrMatch *errors.Template
wantErrContains string
@@ -591,11 +605,13 @@ func Test_convertValueObjects(t *testing.T) {
auds: testAuds,
certs: testCerts,
scopes: testScopes,
maps: testClaimMaps,
wantValues: &convertedValues{
Algs: testSigningAlgs,
Auds: testAudiences,
Certs: testCertificates,
ClaimsScopes: testClaimsScopes,
Algs: testSigningAlgs,
Auds: testAudiences,
Certs: testCertificates,
ClaimsScopes: testClaimsScopes,
AccountClaimMaps: testAccountClaimMaps,
},
},
{
@@ -609,11 +625,12 @@ func Test_convertValueObjects(t *testing.T) {
assert, require := assert.New(t), require.New(t)
am := &AuthMethod{
AuthMethod: &store.AuthMethod{
PublicId: tt.authMethodId,
SigningAlgs: tt.algs,
AudClaims: tt.auds,
Certificates: tt.certs,
ClaimsScopes: tt.scopes,
PublicId: tt.authMethodId,
SigningAlgs: tt.algs,
AudClaims: tt.auds,
Certificates: tt.certs,
ClaimsScopes: tt.scopes,
AccountClaimMaps: tt.maps,
},
}
@@ -649,6 +666,28 @@ func Test_convertValueObjects(t *testing.T) {
assert.Equal(tt.wantValues.ClaimsScopes, convertedScopes)
}
convertedMaps, err := am.convertAccountClaimMaps()
if tt.wantErrMatch != nil {
require.Error(err)
assert.Truef(errors.Match(tt.wantErrMatch, err), "wanted err %q and got: %+v", tt.wantErrMatch.Code, err)
} else {
want := make([]*AccountClaimMap, 0, len(tt.wantValues.AccountClaimMaps))
for _, v := range tt.wantValues.AccountClaimMaps {
want = append(want, v.(*AccountClaimMap))
}
got := make([]*AccountClaimMap, 0, len(convertedMaps))
for _, v := range convertedMaps {
got = append(got, v.(*AccountClaimMap))
}
sort.Slice(want, func(a, b int) bool {
return want[a].ToClaim < want[b].ToClaim
})
sort.Slice(got, func(a, b int) bool {
return got[a].ToClaim < got[b].ToClaim
})
assert.Equal(want, got)
}
values, err := am.convertValueObjects()
if tt.wantErrMatch != nil {
require.Error(err)
@@ -659,7 +698,37 @@ func Test_convertValueObjects(t *testing.T) {
return
}
require.NoError(err)
testSortConverted(t, tt.wantValues)
testSortConverted(t, values)
assert.Equal(tt.wantValues, values)
})
}
}
type converted []interface{}
func (a converted) Len() int { return len(a) }
func (a converted) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a converted) Less(i, j int) bool {
switch a[i].(type) {
case *SigningAlg:
return a[i].(*SigningAlg).GetAlg() < a[j].(*SigningAlg).GetAlg()
case *Certificate:
return a[i].(*Certificate).GetCert() < a[j].(*Certificate).GetCert()
case *AccountClaimMap:
return a[i].(*AccountClaimMap).GetToClaim() < a[j].(*AccountClaimMap).GetToClaim()
case *AudClaim:
return a[i].(*AudClaim).GetAud() < a[j].(*AudClaim).GetAud()
case *ClaimsScope:
return a[i].(*ClaimsScope).GetScope() < a[j].(*ClaimsScope).GetScope()
}
return false
}
func testSortConverted(t *testing.T, c *convertedValues) {
sort.Sort(converted(c.Algs))
sort.Sort(converted(c.Certs))
sort.Sort(converted(c.AccountClaimMaps))
sort.Sort(converted(c.Auds))
sort.Sort(converted(c.ClaimsScopes))
}
+8
View File
@@ -41,6 +41,7 @@ type options struct {
withKeyId string
withIssuer *url.URL
withOperationalState AuthMethodState
withAccountClaimMap map[string]AccountToClaim
}
func getDefaultOptions() options {
@@ -210,3 +211,10 @@ func WithOperationalState(state AuthMethodState) Option {
o.withOperationalState = state
}
}
// WithAccountClaimMap provides an option for specifying an Account Claim map.
func WithAccountClaimMap(acm map[string]AccountToClaim) Option {
return func(o *options) {
o.withAccountClaimMap = acm
}
}
+11
View File
@@ -183,4 +183,15 @@ func Test_GetOpts(t *testing.T) {
opts := getOpts(WithOperationalState(ActivePublicState))
assert.Equal(ActivePublicState, opts.withOperationalState)
})
t.Run("WithAccountClaimMap", func(t *testing.T) {
assert := assert.New(t)
acm := map[string]AccountToClaim{
"oid": ToSubClaim,
"display_name": ToNameClaim,
}
opts := getOpts(WithAccountClaimMap(acm))
testOpts := getDefaultOptions()
testOpts.withAccountClaimMap = acm
assert.Equal(opts, testOpts)
})
}
+36 -10
View File
@@ -32,13 +32,39 @@ func (r *Repository) upsertAccount(ctx context.Context, am *AuthMethod, IdTokenC
if AccessTokenClaims == nil {
return nil, errors.New(errors.InvalidParameter, op, "missing Access Token claims")
}
fromSub, fromName, fromEmail := string(ToSubClaim), string(ToNameClaim), string(ToEmailClaim)
if len(am.AccountClaimMaps) > 0 {
acms, err := ParseAccountClaimMaps(am.AccountClaimMaps...)
if err != nil {
return nil, errors.Wrap(err, op)
}
for from, to := range acms {
toClaim, err := ConvertToAccountToClaim(to)
if err != nil {
return nil, errors.Wrap(err, op)
}
switch toClaim {
case ToSubClaim:
fromSub = from
case ToEmailClaim:
fromEmail = from
case ToNameClaim:
fromName = from
default:
// should never happen, but including it just in case.
return nil, errors.New(errors.InvalidParameter, op, fmt.Sprintf("%s=%s is not a valid account claim map", from, to))
}
}
}
var iss, sub string
var ok bool
if iss, ok = IdTokenClaims["iss"].(string); !ok {
return nil, errors.New(errors.Unknown, op, "issuer is not present in ID Token, which should not be possible")
}
if sub, ok = IdTokenClaims["sub"].(string); !ok {
return nil, errors.New(errors.Unknown, op, "subject is not present in ID Token, which should not be possible")
if sub, ok = IdTokenClaims[fromSub].(string); !ok {
return nil, errors.New(errors.Unknown, op, fmt.Sprintf("mapping 'claim' %s to account subject and it is not present in ID Token", fromSub))
}
pubId, err := newAccountId(am.GetPublicId(), iss, sub)
if err != nil {
@@ -51,22 +77,22 @@ func (r *Repository) upsertAccount(ctx context.Context, am *AuthMethod, IdTokenC
var foundEmail, foundName interface{}
switch {
case AccessTokenClaims["name"] != nil:
foundName = AccessTokenClaims["name"]
case AccessTokenClaims[fromName] != nil:
foundName = AccessTokenClaims[fromName]
columns, values = append(columns, "full_name"), append(values, foundName)
case IdTokenClaims["name"] != nil:
foundName = IdTokenClaims["name"]
case IdTokenClaims[fromName] != nil:
foundName = IdTokenClaims[fromName]
columns, values = append(columns, "full_name"), append(values, foundName)
default:
conflictClauses = append(conflictClauses, "full_name = NULL")
nullMasks = append(nullMasks, NameField)
}
switch {
case AccessTokenClaims["email"] != nil:
foundEmail = AccessTokenClaims["email"]
case AccessTokenClaims[fromEmail] != nil:
foundEmail = AccessTokenClaims[fromEmail]
columns, values = append(columns, "email"), append(values, foundEmail)
case IdTokenClaims["email"] != nil:
foundEmail = IdTokenClaims["email"]
case IdTokenClaims[fromEmail] != nil:
foundEmail = IdTokenClaims[fromEmail]
columns, values = append(columns, "email"), append(values, foundEmail)
default:
conflictClauses = append(conflictClauses, "email = NULL")
@@ -112,7 +112,13 @@ func (r *Repository) CreateAuthMethod(ctx context.Context, am *AuthMethod, opt .
}
msgs = append(msgs, scopesOplogMsgs...)
}
if len(vo.AccountClaimMaps) > 0 {
accountClaimMapsOplogMsgs := make([]*oplog.Message, 0, len(vo.AccountClaimMaps))
if err := w.CreateItems(ctx, vo.AccountClaimMaps, db.NewOplogMsgs(&accountClaimMapsOplogMsgs)); err != nil {
return err
}
msgs = append(msgs, accountClaimMapsOplogMsgs...)
}
metadata := am.oplog(oplog.OpType_OP_TYPE_CREATE)
if err := w.WriteOplogEntryWith(ctx, oplogWrapper, ticket, metadata, msgs); err != nil {
return errors.Wrap(err, op, errors.WithMsg("unable to write oplog"))
@@ -6,6 +6,7 @@ import (
"testing"
"time"
"github.com/google/go-cmp/cmp"
"github.com/hashicorp/boundary/internal/db"
"github.com/hashicorp/boundary/internal/errors"
"github.com/hashicorp/boundary/internal/iam"
@@ -14,6 +15,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/testing/protocmp"
)
func TestRepository_CreateAuthMethod(t *testing.T) {
@@ -59,6 +61,7 @@ func TestRepository_CreateAuthMethod(t *testing.T) {
WithName("alice's restaurant"),
WithDescription("it's a good place to eat"),
WithClaimsScopes("email", "profile"),
WithAccountClaimMap(map[string]AccountToClaim{"display_name": ToNameClaim, "oid": ToSubClaim}),
)
require.NoError(t, err)
require.Equal(t, am.SigningAlgs, convertAlg(algs...))
@@ -92,6 +95,7 @@ func TestRepository_CreateAuthMethod(t *testing.T) {
WithName("alice's restaurant with a twist"),
WithDescription("it's an okay but kinda weird place to eat"),
WithClaimsScopes("email", "profile"),
WithAccountClaimMap(map[string]AccountToClaim{"display_name": ToNameClaim, "oid": ToSubClaim}),
)
require.NoError(t, err)
require.Equal(t, am.SigningAlgs, convertAlg(algs...))
@@ -126,6 +130,7 @@ func TestRepository_CreateAuthMethod(t *testing.T) {
WithName("alice's restaurant is bad"),
WithDescription("their food is awful"),
WithClaimsScopes("email", "profile"),
WithAccountClaimMap(map[string]AccountToClaim{"display_name": ToNameClaim, "oid": ToSubClaim}),
)
require.NoError(t, err)
require.Equal(t, am.SigningAlgs, convertAlg(algs...))
@@ -211,6 +216,14 @@ func TestRepository_CreateAuthMethod(t *testing.T) {
err = db.TestVerifyOplog(t, rw, am.PublicId, db.WithOperation(oplog.OpType_OP_TYPE_CREATE), db.WithCreateNotBefore(10*time.Second))
require.NoErrorf(err, "unexpected error verifying oplog entry: %s", err)
found, err := repo.LookupAuthMethod(ctx, am.PublicId)
require.NoError(err)
found.CreateTime = got.CreateTime
found.UpdateTime = got.UpdateTime
found.Version = got.Version
TestSortAuthMethods(t, []*AuthMethod{found, am})
assert.Empty(cmp.Diff(found.AuthMethod, am.AuthMethod, protocmp.Transform()))
})
}
}
@@ -161,6 +161,9 @@ func (r *Repository) getAuthMethods(ctx context.Context, authMethodId string, sc
if agg.ClaimsScopes != "" {
am.ClaimsScopes = strings.Split(agg.ClaimsScopes, aggregateDelimiter)
}
if agg.AccountClaimMaps != "" {
am.AccountClaimMaps = strings.Split(agg.AccountClaimMaps, aggregateDelimiter)
}
authMethods = append(authMethods, &am)
}
return authMethods, nil
@@ -191,6 +194,7 @@ type authMethodAgg struct {
Auds string
Certs string
ClaimsScopes string
AccountClaimMaps string
}
// TableName returns the table name for gorm
@@ -28,12 +28,14 @@ func TestRepository_LookupAuthMethod(t *testing.T) {
t,
conn, databaseWrapper, org.PublicId, ActivePrivateState,
"alice_rp", "alices-dogs-name",
WithAccountClaimMap(map[string]AccountToClaim{"oid": ToSubClaim, "display_name": ToNameClaim}),
WithApiUrl(TestConvertToUrls(t, "https://alice-active-priv.com/callback")[0]),
WithSigningAlgs(RS256))
amActivePub := TestAuthMethod(
t,
conn, databaseWrapper, org.PublicId, ActivePublicState,
"alice_rp", "alices-dogs-name",
WithAccountClaimMap(map[string]AccountToClaim{"oid": ToSubClaim, "display_name": ToNameClaim}),
WithApiUrl(TestConvertToUrls(t, "https://alice-active-pub.com/callback")[0]),
WithSigningAlgs(RS256),
WithClaimsScopes("email", "profile"),
@@ -100,6 +102,12 @@ func TestRepository_LookupAuthMethod(t *testing.T) {
return
}
require.NoError(err)
if tt.want != nil && tt.want.AccountClaimMaps != nil {
sort.Strings(tt.want.AccountClaimMaps)
}
if got != nil && got.AccountClaimMaps != nil {
sort.Strings(got.AccountClaimMaps)
}
assert.EqualValues(tt.want, got)
})
}
@@ -232,6 +240,7 @@ func TestRepository_getAuthMethods(t *testing.T) {
WithSigningAlgs(RS256, ES256),
WithCertificates(cert1, cert2),
WithClaimsScopes("email", "profile"),
WithAccountClaimMap(map[string]AccountToClaim{"oid": ToSubClaim, "display_name": ToNameClaim}),
)
am1b := TestAuthMethod(t, conn, databaseWrapper, org.PublicId, InactiveState, "alice_rp-2", "alices-cat-name",
WithIssuer(TestConvertToUrls(t, "https://alice.com")[0]), WithApiUrl(TestConvertToUrls(t, "https://api.com")[0]))
@@ -38,6 +38,14 @@ func Test_upsertAccount(t *testing.T) {
WithApiUrl(TestConvertToUrls(t, "https://alice-active-priv.com/callback")[0]),
WithSigningAlgs(RS256))
amWithMapping := TestAuthMethod(
t,
conn, databaseWrapper, org.PublicId, ActivePrivateState,
"alice_rp", "fido",
WithAccountClaimMap(map[string]AccountToClaim{"oid": ToSubClaim}),
WithApiUrl(TestConvertToUrls(t, "https://alice-active-priv.com/callback")[0]),
WithSigningAlgs(RS256))
tests := []struct {
name string
am *AuthMethod
@@ -84,6 +92,17 @@ func Test_upsertAccount(t *testing.T) {
FullName: "alice eve-smith",
}},
},
{
name: "success-map-idTk",
am: amWithMapping,
idClaims: map[string]interface{}{"iss": "https://alice-active-priv.com", "sub": "success-defaults", "oid": "success-map"},
atClaims: map[string]interface{}{},
wantAcct: &Account{Account: &store.Account{
AuthMethodId: amWithMapping.PublicId,
Issuer: "https://alice-active-priv.com",
Subject: "success-map",
}},
},
{
name: "non-existent-auth-method-scope-id",
am: func() *AuthMethod { cp := amActivePriv.Clone(); cp.ScopeId = "non-existent-scope-id"; return cp }(),
@@ -130,7 +149,7 @@ func Test_upsertAccount(t *testing.T) {
idClaims: map[string]interface{}{"iss": "https://alice.com"},
atClaims: map[string]interface{}{},
wantErrMatch: errors.T(errors.Unknown),
wantErrContains: "subject is not present",
wantErrContains: "to account subject and it is not present in ID Token",
},
{
name: "missing-id-token-claims",
@@ -32,6 +32,7 @@ const (
AudClaimsField = "AudClaims"
CertificatesField = "Certificates"
ClaimsScopesField = "ClaimsScopes"
AccountClaimMapsField = "AccountClaimMaps"
)
// UpdateAuthMethod will retrieve the auth method from the repository,
@@ -89,17 +90,18 @@ func (r *Repository) UpdateAuthMethod(ctx context.Context, am *AuthMethod, versi
dbMask, nullFields := dbcommon.BuildUpdatePaths(
map[string]interface{}{
NameField: am.Name,
DescriptionField: am.Description,
IssuerField: am.Issuer,
ClientIdField: am.ClientId,
ClientSecretField: am.ClientSecret,
MaxAgeField: am.MaxAge,
SigningAlgsField: am.SigningAlgs,
ApiUrlField: am.ApiUrl,
AudClaimsField: am.AudClaims,
CertificatesField: am.Certificates,
ClaimsScopesField: am.ClaimsScopes,
NameField: am.Name,
DescriptionField: am.Description,
IssuerField: am.Issuer,
ClientIdField: am.ClientId,
ClientSecretField: am.ClientSecret,
MaxAgeField: am.MaxAge,
SigningAlgsField: am.SigningAlgs,
ApiUrlField: am.ApiUrl,
AudClaimsField: am.AudClaims,
CertificatesField: am.Certificates,
ClaimsScopesField: am.ClaimsScopes,
AccountClaimMapsField: am.AccountClaimMaps,
},
fieldMaskPaths,
nil,
@@ -164,10 +166,30 @@ func (r *Repository) UpdateAuthMethod(ctx context.Context, am *AuthMethod, versi
return nil, db.NoRowsAffected, errors.Wrap(err, op)
}
addMaps, deleteMaps, err := valueObjectChanges(origAm.PublicId, AccountClaimMapsVO, am.AccountClaimMaps, origAm.AccountClaimMaps, dbMask, nullFields)
if err != nil {
return nil, db.NoRowsAffected, errors.Wrap(err, op)
}
// we don't allow updates for "sub" claim maps, because we have no way to
// determine if the updated "from" claim in the map might create collisions
// with any existing account's subject.
for _, rawCm := range addMaps {
cm := rawCm.(*AccountClaimMap)
if cm.ToClaim == string(ToSubClaim) {
return nil, db.NoRowsAffected, errors.New(errors.InvalidParameter, op, fmt.Sprintf("you cannot update account claim map %s=%s for the \"sub\" claim", cm.FromClaim, cm.ToClaim))
}
}
for _, rawCm := range deleteMaps {
cm := rawCm.(*AccountClaimMap)
if cm.ToClaim == string(ToSubClaim) {
return nil, db.NoRowsAffected, errors.New(errors.InvalidParameter, op, fmt.Sprintf("you cannot update account claim map %s=%s for the \"sub\" claim", cm.FromClaim, cm.ToClaim))
}
}
var filteredDbMask, filteredNullFields []string
for _, f := range dbMask {
switch f {
case SigningAlgsField, AudClaimsField, CertificatesField, ClaimsScopesField:
case SigningAlgsField, AudClaimsField, CertificatesField, ClaimsScopesField, AccountClaimMapsField:
continue
default:
filteredDbMask = append(filteredDbMask, f)
@@ -175,7 +197,7 @@ func (r *Repository) UpdateAuthMethod(ctx context.Context, am *AuthMethod, versi
}
for _, f := range nullFields {
switch f {
case SigningAlgsField, AudClaimsField, CertificatesField, ClaimsScopesField:
case SigningAlgsField, AudClaimsField, CertificatesField, ClaimsScopesField, AccountClaimMapsField:
continue
default:
filteredNullFields = append(filteredNullFields, f)
@@ -192,7 +214,9 @@ func (r *Repository) UpdateAuthMethod(ctx context.Context, am *AuthMethod, versi
len(addAuds) == 0 &&
len(deleteAuds) == 0 &&
len(addScopes) == 0 &&
len(deleteScopes) == 0 {
len(deleteScopes) == 0 &&
len(addMaps) == 0 &&
len(deleteMaps) == 0 {
return origAm, db.NoRowsAffected, nil
}
@@ -336,6 +360,25 @@ func (r *Repository) UpdateAuthMethod(ctx context.Context, am *AuthMethod, versi
msgs = append(msgs, addScopesOplogMsgs...)
}
if len(deleteMaps) > 0 {
deleteMapsOplogMsgs := make([]*oplog.Message, 0, len(deleteMaps))
rowsDeleted, err := w.DeleteItems(ctx, deleteMaps, db.NewOplogMsgs(&deleteMapsOplogMsgs))
if err != nil {
return errors.Wrap(err, op, errors.WithMsg("unable to delete account claim maps"))
}
if rowsDeleted != len(deleteMaps) {
return errors.New(errors.MultipleRecords, op, fmt.Sprintf("account claim maps deleted %d did not match request for %d", rowsDeleted, len(deleteMaps)))
}
msgs = append(msgs, deleteMapsOplogMsgs...)
}
if len(addMaps) > 0 {
addMapsOplogMsgs := make([]*oplog.Message, 0, len(addMaps))
if err := w.CreateItems(ctx, addMaps, db.NewOplogMsgs(&addMapsOplogMsgs)); err != nil {
return errors.Wrap(err, op, errors.WithMsg("unable to add account claim maps"))
}
msgs = append(msgs, addMapsOplogMsgs...)
}
metadata := updatedAm.oplog(oplog.OpType_OP_TYPE_UPDATE)
if err := w.WriteOplogEntryWith(ctx, oplogWrapper, ticket, metadata, msgs); err != nil {
return errors.Wrap(err, op, errors.WithMsg("unable to write oplog"))
@@ -371,16 +414,17 @@ func (r *Repository) UpdateAuthMethod(ctx context.Context, am *AuthMethod, versi
type voName string
const (
SigningAlgVO voName = "SigningAlgs"
CertificateVO voName = "Certificates"
AudClaimVO voName = "AudClaims"
ClaimsScopesVO voName = "ClaimsScopes"
SigningAlgVO voName = "SigningAlgs"
CertificateVO voName = "Certificates"
AudClaimVO voName = "AudClaims"
ClaimsScopesVO voName = "ClaimsScopes"
AccountClaimMapsVO voName = "AccountClaimMaps"
)
// validVoName decides if the name is valid
func validVoName(name voName) bool {
switch name {
case SigningAlgVO, CertificateVO, AudClaimVO, ClaimsScopesVO:
case SigningAlgVO, CertificateVO, AudClaimVO, ClaimsScopesVO, AccountClaimMapsVO:
return true
default:
return false
@@ -408,6 +452,25 @@ var supportedFactories = map[voName]factoryFunc{
str := fmt.Sprintf("%s", i)
return NewClaimsScope(publicId, str)
},
AccountClaimMapsVO: func(publicId string, i interface{}) (interface{}, error) {
const op = "oidc.AccountClaimMapsFactory"
str := fmt.Sprintf("%s", i)
acm, err := ParseAccountClaimMaps(str)
if err != nil {
return nil, err
}
if len(acm) > 1 {
return nil, errors.New(errors.InvalidParameter, op, fmt.Sprintf("unable to parse account claim map %s", str))
}
var from, rawTo string
for from, rawTo = range acm {
}
to, err := ConvertToAccountToClaim(rawTo)
if err != nil {
return nil, errors.Wrap(err, op)
}
return NewAccountClaimMap(publicId, from, to)
},
}
// valueObjectChanges takes the new and old list of VOs (value objects) and
@@ -455,38 +518,38 @@ func valueObjectChanges(
var deletes []interface{}
if strutil.StrListContains(nullFields, string(valueObjectName)) {
deletes = make([]interface{}, 0, len(oldVOs))
for _, a := range oldVOs {
alg, err := factory(publicId, a)
for _, v := range oldVOs {
deleteObj, err := factory(publicId, v)
if err != nil {
return nil, nil, errors.Wrap(err, op)
}
deletes = append(deletes, alg)
delete(foundVOs, a)
deletes = append(deletes, deleteObj)
delete(foundVOs, v)
}
}
if strutil.StrListContains(dbMask, string(valueObjectName)) {
adds = make([]interface{}, 0, len(newVOs))
for _, a := range newVOs {
if _, ok := foundVOs[a]; ok {
delete(foundVOs, a)
for _, v := range newVOs {
if _, ok := foundVOs[v]; ok {
delete(foundVOs, v)
continue
}
alg, err := factory(publicId, a)
obj, err := factory(publicId, v)
if err != nil {
return nil, nil, errors.Wrap(err, op)
}
adds = append(adds, alg)
delete(foundVOs, a)
adds = append(adds, obj)
delete(foundVOs, v)
}
}
if len(foundVOs) > 0 {
for a := range foundVOs {
alg, err := factory(publicId, a)
for v := range foundVOs {
obj, err := factory(publicId, v)
if err != nil {
return nil, nil, errors.Wrap(err, op)
}
deletes = append(deletes, alg)
delete(foundVOs, a)
deletes = append(deletes, obj)
delete(foundVOs, v)
}
}
return adds, deletes, nil
@@ -508,6 +571,7 @@ func validateFieldMask(fieldMaskPaths []string) error {
case strings.EqualFold(AudClaimsField, f):
case strings.EqualFold(CertificatesField, f):
case strings.EqualFold(ClaimsScopesField, f):
case strings.EqualFold(AccountClaimMapsField, f):
default:
return errors.New(errors.InvalidParameter, op, fmt.Sprintf("invalid field mask: %s", f))
}
@@ -566,6 +630,14 @@ func applyUpdate(new, orig *AuthMethod, fieldMaskPaths []string) *AuthMethod {
cp.ClaimsScopes = make([]string, 0, len(new.ClaimsScopes))
cp.ClaimsScopes = append(cp.ClaimsScopes, new.ClaimsScopes...)
}
case AccountClaimMapsField:
switch {
case len(new.AccountClaimMaps) == 0:
cp.AccountClaimMaps = nil
default:
cp.AccountClaimMaps = make([]string, 0, len(new.AccountClaimMaps))
cp.AccountClaimMaps = append(cp.AccountClaimMaps, new.AccountClaimMaps...)
}
}
}
return cp
@@ -68,6 +68,7 @@ func Test_UpdateAuthMethod(t *testing.T) {
WithCertificates(tpCert[0]),
WithSigningAlgs(Alg(tpAlg)),
WithClaimsScopes("email", "profile"),
WithAccountClaimMap(map[string]AccountToClaim{"display_name": "name"}),
WithApiUrl(TestConvertToUrls(t, "https://www.alice.com/callback")[0]),
)
},
@@ -78,9 +79,10 @@ func Test_UpdateAuthMethod(t *testing.T) {
am.Description = "the best place to eat"
am.AudClaims = []string{"www.alice.com", "www.alice.com/admin"}
am.ClientSecret = "This is a new secret"
am.AccountClaimMaps = []string{"preferred_name=name"}
return &am
},
fieldMasks: []string{NameField, DescriptionField, AudClaimsField, ClientSecretField},
fieldMasks: []string{NameField, DescriptionField, AudClaimsField, ClientSecretField, AccountClaimMapsField},
version: 1,
want: func(orig, updateWith *AuthMethod) *AuthMethod {
am := orig.Clone()
@@ -90,6 +92,7 @@ func Test_UpdateAuthMethod(t *testing.T) {
am.ClientSecret = updateWith.ClientSecret
am.CtClientSecret = updateWith.CtClientSecret
am.ClientSecretHmac = updateWith.ClientSecretHmac
am.AccountClaimMaps = updateWith.AccountClaimMaps
return am
},
},
@@ -368,6 +371,34 @@ func Test_UpdateAuthMethod(t *testing.T) {
return am
},
},
{
name: "attempt-to-update-sub-account-claim-map",
setup: func() *AuthMethod {
org, _ := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
databaseWrapper, err := kmsCache.GetWrapper(context.Background(), org.PublicId, kms.KeyPurposeDatabase)
require.NoError(t, err)
return TestAuthMethod(t,
conn, databaseWrapper,
org.PublicId,
InactiveState,
"alice-rp", "alice-secret",
WithCertificates(tpCert[0]),
WithSigningAlgs(Alg(tpAlg)),
WithClaimsScopes("email", "profile"),
WithAccountClaimMap(map[string]AccountToClaim{"oid": "sub"}),
WithApiUrl(TestConvertToUrls(t, "https://www.alice.com/callback")[0]),
)
},
updateWith: func(orig *AuthMethod) *AuthMethod {
am := AllocAuthMethod()
am.PublicId = orig.PublicId
am.AccountClaimMaps = []string{"uid=sub"}
return &am
},
fieldMasks: []string{AccountClaimMapsField},
version: 1,
wantErrMatch: errors.T(errors.InvalidParameter),
},
{
name: "nil-authMethod",
setup: func() *AuthMethod { return nil },
@@ -548,6 +579,10 @@ func Test_UpdateAuthMethod(t *testing.T) {
err = db.TestVerifyOplog(t, rw, updateWith.PublicId, db.WithOperation(oplog.OpType_OP_TYPE_UPDATE), db.WithCreateNotBefore(10*time.Second))
require.NoErrorf(err, "unexpected error verifying oplog entry: %s", err)
}
found, err := repo.LookupAuthMethod(ctx, want.PublicId)
require.NoError(err)
TestSortAuthMethods(t, []*AuthMethod{found})
assert.Empty(cmp.Diff(found.AuthMethod, want.AuthMethod, protocmp.Transform()))
}
})
}
+210 -84
View File
@@ -117,6 +117,12 @@ type AuthMethod struct {
// are operatated on as a complete set.
// @inject_tag: `gorm:"-"`
ClaimsScopes []string `protobuf:"bytes,200,rep,name=claims_scopes,json=claimsScopes,proto3" json:"claims_scopes,omitempty" gorm:"-"`
// account_claim_maps are optional claim maps from custom claims to the
// standard claims of sub, name and email. These maps are represented as
// key=value where the key equals the from_claim and the value equals the
// to_claim. For example "oid=sub".
// @inject_tag: `gorm:"-"`
AccountClaimMaps []string `protobuf:"bytes,210,rep,name=account_claim_maps,json=accountClaimMaps,proto3" json:"account_claim_maps,omitempty" gorm:"-"`
}
func (x *AuthMethod) Reset() {
@@ -305,6 +311,13 @@ func (x *AuthMethod) GetClaimsScopes() []string {
return nil
}
func (x *AuthMethod) GetAccountClaimMaps() []string {
if x != nil {
return x.AccountClaimMaps
}
return nil
}
// Account represents an OIDC account
// the scope_id column is not included here as it is used only to ensure
// data integrity in the database between iam users and auth methods.
@@ -735,6 +748,87 @@ func (x *ClaimsScope) GetCreateTime() *timestamp.Timestamp {
return nil
}
// AccountClaimMap entries are optional from/to account claim maps.
type AccountClaimMap struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// @inject_tag: `gorm:"primary_key"`
OidcMethodId string `protobuf:"bytes,10,opt,name=oidc_method_id,json=oidcMethodId,proto3" json:"oidc_method_id,omitempty" gorm:"primary_key"`
// from_claim is the claim from the id_token that you need to map to a
// standard account claim.
// @inject_tag: `gorm:"not_null"`
FromClaim string `protobuf:"bytes,20,opt,name=from_claim,json=fromClaim,proto3" json:"from_claim,omitempty" gorm:"not_null"`
// to_claim is the standard account claim to map the from_claim to. Valid
// values are: sub, name, email
// @inject_tag: `gorm:"column:to_claim;primary_key"`
ToClaim string `protobuf:"bytes,30,opt,name=to_claim,json=toClaim,proto3" json:"to_claim,omitempty" gorm:"column:to_claim;primary_key"`
// The create_time is set by the database.
// @inject_tag: `gorm:"default:current_timestamp"`
CreateTime *timestamp.Timestamp `protobuf:"bytes,40,opt,name=create_time,json=createTime,proto3" json:"create_time,omitempty" gorm:"default:current_timestamp"`
}
func (x *AccountClaimMap) Reset() {
*x = AccountClaimMap{}
if protoimpl.UnsafeEnabled {
mi := &file_controller_storage_auth_oidc_store_v1_oidc_proto_msgTypes[6]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *AccountClaimMap) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*AccountClaimMap) ProtoMessage() {}
func (x *AccountClaimMap) ProtoReflect() protoreflect.Message {
mi := &file_controller_storage_auth_oidc_store_v1_oidc_proto_msgTypes[6]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use AccountClaimMap.ProtoReflect.Descriptor instead.
func (*AccountClaimMap) Descriptor() ([]byte, []int) {
return file_controller_storage_auth_oidc_store_v1_oidc_proto_rawDescGZIP(), []int{6}
}
func (x *AccountClaimMap) GetOidcMethodId() string {
if x != nil {
return x.OidcMethodId
}
return ""
}
func (x *AccountClaimMap) GetFromClaim() string {
if x != nil {
return x.FromClaim
}
return ""
}
func (x *AccountClaimMap) GetToClaim() string {
if x != nil {
return x.ToClaim
}
return ""
}
func (x *AccountClaimMap) GetCreateTime() *timestamp.Timestamp {
if x != nil {
return x.CreateTime
}
return nil
}
var File_controller_storage_auth_oidc_store_v1_oidc_proto protoreflect.FileDescriptor
var file_controller_storage_auth_oidc_store_v1_oidc_proto_rawDesc = []byte{
@@ -751,7 +845,7 @@ var file_controller_storage_auth_oidc_store_v1_oidc_proto_rawDesc = []byte{
0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x73,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x9d, 0x0a, 0x0a, 0x0a, 0x41, 0x75, 0x74, 0x68, 0x4d,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x83, 0x0b, 0x0a, 0x0a, 0x41, 0x75, 0x74, 0x68, 0x4d,
0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f,
0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63,
0x49, 0x64, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d,
@@ -833,78 +927,96 @@ var file_controller_storage_auth_oidc_store_v1_oidc_proto_rawDesc = []byte{
0x0a, 0x0c, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x73, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x12, 0x18,
0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x63, 0x6c, 0x61, 0x69, 0x6d,
0x73, 0x5f, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x52, 0x0c, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x73,
0x53, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x22, 0xcd, 0x03, 0x0a, 0x07, 0x41, 0x63, 0x63, 0x6f, 0x75,
0x6e, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x69, 0x64, 0x18,
0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x64, 0x12,
0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x14,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65,
0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74,
0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70,
0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x4b, 0x0a, 0x0b,
0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28,
0x53, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x12, 0x64, 0x0a, 0x12, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e,
0x74, 0x5f, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x5f, 0x6d, 0x61, 0x70, 0x73, 0x18, 0xd2, 0x01, 0x20,
0x03, 0x28, 0x09, 0x42, 0x35, 0xc2, 0xdd, 0x29, 0x31, 0x0a, 0x10, 0x41, 0x63, 0x63, 0x6f, 0x75,
0x6e, 0x74, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x4d, 0x61, 0x70, 0x73, 0x12, 0x1d, 0x61, 0x74, 0x74,
0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f,
0x63, 0x6c, 0x61, 0x69, 0x6d, 0x5f, 0x6d, 0x61, 0x70, 0x73, 0x52, 0x10, 0x61, 0x63, 0x63, 0x6f,
0x75, 0x6e, 0x74, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x4d, 0x61, 0x70, 0x73, 0x22, 0xcd, 0x03, 0x0a,
0x07, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c,
0x69, 0x63, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x75, 0x62,
0x6c, 0x69, 0x63, 0x49, 0x64, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f,
0x74, 0x69, 0x6d, 0x65, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e,
0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e,
0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d,
0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69,
0x6d, 0x65, 0x12, 0x4b, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d,
0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f,
0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d,
0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74,
0x61, 0x6d, 0x70, 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12,
0x24, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x28, 0x20, 0x01, 0x28, 0x09, 0x42, 0x10, 0xc2,
0xdd, 0x29, 0x0c, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x52,
0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x40, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70,
0x74, 0x69, 0x6f, 0x6e, 0x18, 0x32, 0x20, 0x01, 0x28, 0x09, 0x42, 0x1e, 0xc2, 0xdd, 0x29, 0x1a,
0x0a, 0x0b, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0b, 0x64,
0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63,
0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69,
0x6f, 0x6e, 0x18, 0x3c, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f,
0x6e, 0x12, 0x24, 0x0a, 0x0e, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64,
0x5f, 0x69, 0x64, 0x18, 0x46, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x61, 0x75, 0x74, 0x68, 0x4d,
0x65, 0x74, 0x68, 0x6f, 0x64, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x69, 0x73, 0x73, 0x75, 0x65,
0x72, 0x18, 0x50, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x69, 0x73, 0x73, 0x75, 0x65, 0x72, 0x12,
0x18, 0x0a, 0x07, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x5a, 0x20, 0x01, 0x28, 0x09,
0x52, 0x07, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x66, 0x75, 0x6c,
0x6c, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x64, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x75,
0x6c, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18,
0x6e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x22, 0x91, 0x01, 0x0a,
0x0a, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x41, 0x6c, 0x67, 0x12, 0x24, 0x0a, 0x0e, 0x6f,
0x69, 0x64, 0x63, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20,
0x01, 0x28, 0x09, 0x52, 0x0c, 0x6f, 0x69, 0x64, 0x63, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x49,
0x64, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x6c, 0x67, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03,
0x61, 0x6c, 0x67, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69,
0x6d, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72,
0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69,
0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73,
0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65,
0x22, 0x8f, 0x01, 0x0a, 0x08, 0x41, 0x75, 0x64, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x12, 0x24, 0x0a,
0x0e, 0x6f, 0x69, 0x64, 0x63, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x5f, 0x69, 0x64, 0x18,
0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x6f, 0x69, 0x64, 0x63, 0x4d, 0x65, 0x74, 0x68, 0x6f,
0x64, 0x49, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x75, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09,
0x52, 0x03, 0x61, 0x75, 0x64, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f,
0x74, 0x69, 0x6d, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e,
0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e,
0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d,
0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69,
0x6d, 0x65, 0x22, 0x94, 0x01, 0x0a, 0x0b, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61,
0x74, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x6f, 0x69, 0x64, 0x63, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f,
0x64, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x6f, 0x69, 0x64, 0x63,
0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x65, 0x72, 0x74,
0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x65, 0x72, 0x74, 0x12, 0x4b, 0x0a, 0x0b,
0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28,
0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73,
0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70,
0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x75,
0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x24, 0x0a, 0x04, 0x6e, 0x61, 0x6d,
0x65, 0x18, 0x28, 0x20, 0x01, 0x28, 0x09, 0x42, 0x10, 0xc2, 0xdd, 0x29, 0x0c, 0x0a, 0x04, 0x4e,
0x61, 0x6d, 0x65, 0x12, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12,
0x40, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x32,
0x20, 0x01, 0x28, 0x09, 0x42, 0x1e, 0xc2, 0xdd, 0x29, 0x1a, 0x0a, 0x0b, 0x44, 0x65, 0x73, 0x63,
0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70,
0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f,
0x6e, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x3c, 0x20, 0x01,
0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x24, 0x0a, 0x0e, 0x61,
0x75, 0x74, 0x68, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x46, 0x20,
0x01, 0x28, 0x09, 0x52, 0x0c, 0x61, 0x75, 0x74, 0x68, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x49,
0x64, 0x12, 0x16, 0x0a, 0x06, 0x69, 0x73, 0x73, 0x75, 0x65, 0x72, 0x18, 0x50, 0x20, 0x01, 0x28,
0x09, 0x52, 0x06, 0x69, 0x73, 0x73, 0x75, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x62,
0x6a, 0x65, 0x63, 0x74, 0x18, 0x5a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x75, 0x62, 0x6a,
0x65, 0x63, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x66, 0x75, 0x6c, 0x6c, 0x5f, 0x6e, 0x61, 0x6d, 0x65,
0x18, 0x64, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x75, 0x6c, 0x6c, 0x4e, 0x61, 0x6d, 0x65,
0x12, 0x14, 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x6e, 0x20, 0x01, 0x28, 0x09, 0x52,
0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x22, 0x91, 0x01, 0x0a, 0x0a, 0x53, 0x69, 0x67, 0x6e, 0x69,
0x6e, 0x67, 0x41, 0x6c, 0x67, 0x12, 0x24, 0x0a, 0x0e, 0x6f, 0x69, 0x64, 0x63, 0x5f, 0x6d, 0x65,
0x74, 0x68, 0x6f, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x6f,
0x69, 0x64, 0x63, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x49, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x61,
0x6c, 0x67, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x6c, 0x67, 0x12, 0x4b, 0x0a,
0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x1e, 0x20, 0x01,
0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e,
0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d,
0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a,
0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x22, 0x8f, 0x01, 0x0a, 0x08, 0x41,
0x75, 0x64, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x12, 0x24, 0x0a, 0x0e, 0x6f, 0x69, 0x64, 0x63, 0x5f,
0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52,
0x0c, 0x6f, 0x69, 0x64, 0x63, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x49, 0x64, 0x12, 0x10, 0x0a,
0x03, 0x61, 0x75, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x75, 0x64, 0x12,
0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x1e,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65,
0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74,
0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70,
0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x22, 0x94, 0x01, 0x0a,
0x0b, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x24, 0x0a, 0x0e,
0x6f, 0x69, 0x64, 0x63, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x0a,
0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x6f, 0x69, 0x64, 0x63, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64,
0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x65, 0x72, 0x74, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09,
0x52, 0x04, 0x63, 0x65, 0x72, 0x74, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65,
0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f,
0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63,
0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x22, 0x96, 0x01, 0x0a, 0x0b, 0x43, 0x6c,
0x61, 0x69, 0x6d, 0x73, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x6f, 0x69, 0x64,
0x63, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28,
0x09, 0x52, 0x0c, 0x6f, 0x69, 0x64, 0x63, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x49, 0x64, 0x12,
0x14, 0x0a, 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05,
0x73, 0x63, 0x6f, 0x70, 0x65, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f,
0x74, 0x69, 0x6d, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e,
0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e,
0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d,
0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69,
0x6d, 0x65, 0x22, 0xbe, 0x01, 0x0a, 0x0f, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x43, 0x6c,
0x61, 0x69, 0x6d, 0x4d, 0x61, 0x70, 0x12, 0x24, 0x0a, 0x0e, 0x6f, 0x69, 0x64, 0x63, 0x5f, 0x6d,
0x65, 0x74, 0x68, 0x6f, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c,
0x6f, 0x69, 0x64, 0x63, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a,
0x66, 0x72, 0x6f, 0x6d, 0x5f, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09,
0x52, 0x09, 0x66, 0x72, 0x6f, 0x6d, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x12, 0x19, 0x0a, 0x08, 0x74,
0x6f, 0x5f, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x74,
0x6f, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65,
0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x28, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f,
0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65,
0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69,
0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54,
0x69, 0x6d, 0x65, 0x22, 0x96, 0x01, 0x0a, 0x0b, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x73, 0x53, 0x63,
0x6f, 0x70, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x6f, 0x69, 0x64, 0x63, 0x5f, 0x6d, 0x65, 0x74, 0x68,
0x6f, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x6f, 0x69, 0x64,
0x63, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x63, 0x6f,
0x70, 0x65, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x12,
0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x1e,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65,
0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74,
0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70,
0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x42, 0x3e, 0x5a, 0x3c,
0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69,
0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e,
0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x61, 0x75, 0x74, 0x68, 0x2f, 0x6f, 0x69, 0x64, 0x63,
0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3b, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x33,
0x69, 0x6d, 0x65, 0x42, 0x3e, 0x5a, 0x3c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f,
0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e,
0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x61, 0x75,
0x74, 0x68, 0x2f, 0x6f, 0x69, 0x64, 0x63, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3b, 0x73, 0x74,
0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
@@ -919,7 +1031,7 @@ func file_controller_storage_auth_oidc_store_v1_oidc_proto_rawDescGZIP() []byte
return file_controller_storage_auth_oidc_store_v1_oidc_proto_rawDescData
}
var file_controller_storage_auth_oidc_store_v1_oidc_proto_msgTypes = make([]protoimpl.MessageInfo, 6)
var file_controller_storage_auth_oidc_store_v1_oidc_proto_msgTypes = make([]protoimpl.MessageInfo, 7)
var file_controller_storage_auth_oidc_store_v1_oidc_proto_goTypes = []interface{}{
(*AuthMethod)(nil), // 0: controller.storage.auth.oidc.store.v1.AuthMethod
(*Account)(nil), // 1: controller.storage.auth.oidc.store.v1.Account
@@ -927,22 +1039,24 @@ var file_controller_storage_auth_oidc_store_v1_oidc_proto_goTypes = []interface{
(*AudClaim)(nil), // 3: controller.storage.auth.oidc.store.v1.AudClaim
(*Certificate)(nil), // 4: controller.storage.auth.oidc.store.v1.Certificate
(*ClaimsScope)(nil), // 5: controller.storage.auth.oidc.store.v1.ClaimsScope
(*timestamp.Timestamp)(nil), // 6: controller.storage.timestamp.v1.Timestamp
(*AccountClaimMap)(nil), // 6: controller.storage.auth.oidc.store.v1.AccountClaimMap
(*timestamp.Timestamp)(nil), // 7: controller.storage.timestamp.v1.Timestamp
}
var file_controller_storage_auth_oidc_store_v1_oidc_proto_depIdxs = []int32{
6, // 0: controller.storage.auth.oidc.store.v1.AuthMethod.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
6, // 1: controller.storage.auth.oidc.store.v1.AuthMethod.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
6, // 2: controller.storage.auth.oidc.store.v1.Account.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
6, // 3: controller.storage.auth.oidc.store.v1.Account.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
6, // 4: controller.storage.auth.oidc.store.v1.SigningAlg.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
6, // 5: controller.storage.auth.oidc.store.v1.AudClaim.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
6, // 6: controller.storage.auth.oidc.store.v1.Certificate.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
6, // 7: controller.storage.auth.oidc.store.v1.ClaimsScope.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
8, // [8:8] is the sub-list for method output_type
8, // [8:8] is the sub-list for method input_type
8, // [8:8] is the sub-list for extension type_name
8, // [8:8] is the sub-list for extension extendee
0, // [0:8] is the sub-list for field type_name
7, // 0: controller.storage.auth.oidc.store.v1.AuthMethod.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
7, // 1: controller.storage.auth.oidc.store.v1.AuthMethod.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
7, // 2: controller.storage.auth.oidc.store.v1.Account.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
7, // 3: controller.storage.auth.oidc.store.v1.Account.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
7, // 4: controller.storage.auth.oidc.store.v1.SigningAlg.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
7, // 5: controller.storage.auth.oidc.store.v1.AudClaim.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
7, // 6: controller.storage.auth.oidc.store.v1.Certificate.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
7, // 7: controller.storage.auth.oidc.store.v1.ClaimsScope.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
7, // 8: controller.storage.auth.oidc.store.v1.AccountClaimMap.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
9, // [9:9] is the sub-list for method output_type
9, // [9:9] is the sub-list for method input_type
9, // [9:9] is the sub-list for extension type_name
9, // [9:9] is the sub-list for extension extendee
0, // [0:9] is the sub-list for field type_name
}
func init() { file_controller_storage_auth_oidc_store_v1_oidc_proto_init() }
@@ -1023,6 +1137,18 @@ func file_controller_storage_auth_oidc_store_v1_oidc_proto_init() {
return nil
}
}
file_controller_storage_auth_oidc_store_v1_oidc_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*AccountClaimMap); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
@@ -1030,7 +1156,7 @@ func file_controller_storage_auth_oidc_store_v1_oidc_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_controller_storage_auth_oidc_store_v1_oidc_proto_rawDesc,
NumEnums: 0,
NumMessages: 6,
NumMessages: 7,
NumExtensions: 0,
NumServices: 0,
},
+13 -1
View File
@@ -108,7 +108,16 @@ func TestAuthMethod(
require.NoError(err)
require.Equal(len(opts.withClaimsScopes), len(authMethod.ClaimsScopes))
}
if len(opts.withAccountClaimMap) > 0 {
newAccountClaimMaps := make([]interface{}, 0, len(opts.withAccountClaimMap))
for k, v := range opts.withAccountClaimMap {
acm, err := NewAccountClaimMap(authMethod.PublicId, k, v)
require.NoError(err)
newAccountClaimMaps = append(newAccountClaimMaps, acm)
}
require.NoError(rw.CreateItems(ctx, newAccountClaimMaps))
require.Equal(len(opts.withAccountClaimMap), len(authMethod.AccountClaimMaps))
}
authMethod.OperationalState = string(state)
rowsUpdated, err := rw.Update(ctx, authMethod, []string{OperationalStateField}, nil)
require.NoError(err)
@@ -140,6 +149,9 @@ func TestSortAuthMethods(t *testing.T, methods []*AuthMethod) {
sort.Slice(am.ClaimsScopes, func(a, b int) bool {
return am.ClaimsScopes[a] < am.ClaimsScopes[b]
})
sort.Slice(am.AccountClaimMaps, func(a, b int) bool {
return am.AccountClaimMaps[a] < am.AccountClaimMaps[b]
})
}
}
@@ -27,6 +27,7 @@ type extraOidcCmdVars struct {
flagIdpCaCerts []string
flagAllowedAudiences []string
flagClaimsScopes []string
flagAccountClaimMaps []string
flagDisableDiscoveredConfigValidation bool
flagDryRun bool
}
@@ -42,6 +43,7 @@ const (
caCertFlagName = "idp-ca-cert"
allowedAudienceFlagName = "allowed-audience"
claimsScopes = "claims-scopes"
accountClaimMaps = "account-claim-maps"
stateFlagName = "state"
disableDiscoveredConfigValidationFlagName = "disable-discovered-config-validation"
dryRunFlagName = "dry-run"
@@ -59,6 +61,7 @@ func extraOidcActionsFlagsMapFuncImpl() map[string][]string {
caCertFlagName,
allowedAudienceFlagName,
claimsScopes,
accountClaimMaps,
},
"change-state": {
idFlagName,
@@ -129,6 +132,12 @@ func extraOidcFlagsFuncImpl(c *OidcCommand, set *base.FlagSets, _ *base.FlagSet)
Target: &c.flagClaimsScopes,
Usage: `The optional claims scope requested. May be specified multiple times.`,
})
case accountClaimMaps:
f.StringSliceVar(&base.StringSliceVar{
Name: accountClaimMaps,
Target: &c.flagAccountClaimMaps,
Usage: `The optional account claim maps from custom claims to the standard claims of sub, name and email. These maps are represented as key=value where the key equals the Provider from-claim and the value equals the Boundary to-claim. For example "oid=sub". May be specified multiple times for different to-claims.`,
})
case stateFlagName:
f.StringVar(&base.StringVar{
Name: stateFlagName,
@@ -258,6 +267,13 @@ func extraOidcFlagHandlingFuncImpl(c *OidcCommand, f *base.FlagSets, opts *[]aut
default:
*opts = append(*opts, authmethods.WithOidcAuthMethodClaimsScopes(c.flagClaimsScopes))
}
switch {
case len(c.flagAccountClaimMaps) == 0:
case len(c.flagAccountClaimMaps) == 1 && c.flagAccountClaimMaps[0] == "null":
*opts = append(*opts, authmethods.DefaultOidcAuthMethodAccountClaimMaps())
default:
*opts = append(*opts, authmethods.WithOidcAuthMethodAccountClaimMaps(c.flagAccountClaimMaps))
}
if c.flagDisableDiscoveredConfigValidation {
*opts = append(*opts, authmethods.WithOidcAuthMethodDisableDiscoveredConfigValidation(c.flagDisableDiscoveredConfigValidation))
}
@@ -0,0 +1,94 @@
begin;
-- auth_oidc_account_claim_map entries are the optional claim maps from custom
-- claims to the standard claims of sub, name and email. There can be 0 or more
-- for each parent oidc auth method.
create table auth_oidc_account_claim_map (
create_time wt_timestamp,
oidc_method_id wt_public_id
constraint auth_oidc_method_fkey
references auth_oidc_method(public_id)
on delete cascade
on update cascade,
from_claim text not null
constraint from_claim_must_not_be_empty
check(length(trim(from_claim)) > 0)
constraint from_claim_must_be_less_than_1024_chars
check(length(trim(from_claim)) < 1024),
to_claim text not null
constraint to_claim_valid_values
check (to_claim in ('sub', 'name', 'email')), -- intentionally case-sensitive matching
primary key(oidc_method_id, to_claim)
);
comment on table auth_oidc_account_claim_map is
'auth_oidc_account_claim_map entries are the optional claim maps from custom claims to the standard claims of sub, name and email. There can be 0 or more for each parent oidc auth method.';
create trigger
default_create_time_column
before
insert on auth_oidc_account_claim_map
for each row execute procedure default_create_time();
create trigger
immutable_columns
before
update on auth_oidc_account_claim_map
for each row execute procedure immutable_columns('oidc_method_id', 'from_claim', 'to_claim', 'create_time');
-- we will drop the oidc_auth_method_with_value_obj view, so we can recreate it
-- and add the oidc claim's scopes to the returned set.
drop view oidc_auth_method_with_value_obj;
-- oidc_auth_method_with_value_obj is useful for reading an oidc auth method
-- with its associated value objects (algs, auds, certs, claims scopes and
-- account claim maps) as columns with | delimited values. The use of the
-- postgres string_agg(...) to aggregate the value objects into a column works
-- because we are only pulling in one column from the associated tables and that
-- value is part of the primary key and unique. This view will make things like
-- recursive listing of oidc auth methods fairly straightforward to implement
-- for the oidc repo. The view also includes an is_primary_auth_method bool
create view oidc_auth_method_with_value_obj as
select
case when s.primary_auth_method_id is not null then
true
else false end
as is_primary_auth_method,
am.public_id,
am.scope_id,
am.name,
am.description,
am.create_time,
am.update_time,
am.version,
am.state,
am.api_url,
am.disable_discovered_config_validation,
am.issuer,
am.client_id,
am.client_secret,
am.client_secret_hmac,
am.key_id,
am.max_age,
-- the string_agg(..) column will be null if there are no associated value objects
string_agg(distinct alg.signing_alg_name, '|') as algs,
string_agg(distinct aud.aud_claim, '|') as auds,
string_agg(distinct cert.certificate, '|') as certs,
string_agg(distinct cs.scope, '|') as claims_scopes,
string_agg(distinct concat_ws('=', acm.from_claim, acm.to_claim), '|') as account_claim_maps
from
auth_oidc_method am
left outer join iam_scope s on am.public_id = s.primary_auth_method_id
left outer join auth_oidc_signing_alg alg on am.public_id = alg.oidc_method_id
left outer join auth_oidc_aud_claim aud on am.public_id = aud.oidc_method_id
left outer join auth_oidc_certificate cert on am.public_id = cert.oidc_method_id
left outer join auth_oidc_scope cs on am.public_id = cs.oidc_method_id
left outer join auth_oidc_account_claim_map acm on am.public_id = acm.oidc_method_id
group by am.public_id, is_primary_auth_method; -- there can be only one public_id + is_primary_auth_method, so group by isn't a problem.
comment on view oidc_auth_method_with_value_obj is
'oidc auth method with its associated value objects (algs, auds, certs, scopes) as columns with | delimited values';
commit;
+92 -1
View File
@@ -4,7 +4,7 @@ package schema
func init() {
migrationStates["postgres"] = migrationState{
binarySchemaVersion: 5001,
binarySchemaVersion: 6001,
upMigrations: map[int][]byte{
1: []byte(`
create domain wt_public_id as text
@@ -6087,6 +6087,97 @@ from
group by am.public_id, is_primary_auth_method; -- there can be only one public_id + is_primary_auth_method, so group by isn't a problem.
comment on view oidc_auth_method_with_value_obj is
'oidc auth method with its associated value objects (algs, auds, certs, scopes) as columns with | delimited values';
`),
6001: []byte(`
-- auth_oidc_account_claim_map entries are the optional claim maps from custom
-- claims to the standard claims of sub, name and email. There can be 0 or more
-- for each parent oidc auth method.
create table auth_oidc_account_claim_map (
create_time wt_timestamp,
oidc_method_id wt_public_id
constraint auth_oidc_method_fkey
references auth_oidc_method(public_id)
on delete cascade
on update cascade,
from_claim text not null
constraint from_claim_must_not_be_empty
check(length(trim(from_claim)) > 0)
constraint from_claim_must_be_less_than_1024_chars
check(length(trim(from_claim)) < 1024),
to_claim text not null
constraint to_claim_valid_values
CHECK (to_claim IN ('sub', 'name', 'email')), -- intentionally case-sensitive matching
primary key(oidc_method_id, to_claim)
);
comment on table auth_oidc_account_claim_map is
'auth_oidc_account_claim_map entries are the optional claim maps from custom claims to the standard claims of sub, name and email. There can be 0 or more for each parent oidc auth method.';
create trigger
default_create_time_column
before
insert on auth_oidc_account_claim_map
for each row execute procedure default_create_time();
create trigger
immutable_columns
before
update on auth_oidc_account_claim_map
for each row execute procedure immutable_columns('oidc_method_id', 'from_claim', 'to_claim', 'create_time');
-- we will drop the oidc_auth_method_with_value_obj view, so we can recreate it
-- and add the oidc claim's scopes to the returned set.
drop view oidc_auth_method_with_value_obj;
-- oidc_auth_method_with_value_obj is useful for reading an oidc auth method
-- with its associated value objects (algs, auds, certs, claims scopes and
-- account claim maps) as columns with | delimited values. The use of the
-- postgres string_agg(...) to aggregate the value objects into a column works
-- because we are only pulling in one column from the associated tables and that
-- value is part of the primary key and unique. This view will make things like
-- recursive listing of oidc auth methods fairly straightforward to implement
-- for the oidc repo. The view also includes an is_primary_auth_method bool
create view oidc_auth_method_with_value_obj as
select
case when s.primary_auth_method_id is not null then
true
else false end
as is_primary_auth_method,
am.public_id,
am.scope_id,
am.name,
am.description,
am.create_time,
am.update_time,
am.version,
am.state,
am.api_url,
am.disable_discovered_config_validation,
am.issuer,
am.client_id,
am.client_secret,
am.client_secret_hmac,
am.key_id,
am.max_age,
-- the string_agg(..) column will be null if there are no associated value objects
string_agg(distinct alg.signing_alg_name, '|') as algs,
string_agg(distinct aud.aud_claim, '|') as auds,
string_agg(distinct cert.certificate, '|') as certs,
string_agg(distinct cs.scope, '|') as claims_scopes,
string_agg(distinct concat_ws('=', acm.from_claim, acm.to_claim), '|') as account_claim_maps
from
auth_oidc_method am
left outer join iam_scope s on am.public_id = s.primary_auth_method_id
left outer join auth_oidc_signing_alg alg on am.public_id = alg.oidc_method_id
left outer join auth_oidc_aud_claim aud on am.public_id = aud.oidc_method_id
left outer join auth_oidc_certificate cert on am.public_id = cert.oidc_method_id
left outer join auth_oidc_scope cs on am.public_id = cs.oidc_method_id
left outer join auth_oidc_account_claim_map acm on am.public_id = acm.oidc_method_id
group by am.public_id, is_primary_auth_method; -- there can be only one public_id + is_primary_auth_method, so group by isn't a problem.
comment on view oidc_auth_method_with_value_obj is
'oidc auth method with its associated value objects (algs, auds, certs, scopes) as columns with | delimited values';
`),
},
}
@@ -285,6 +285,11 @@ type OidcAuthMethodAttributes struct {
// Optional claims scopes that will be requested during authentication.
// see: https://openid.net/specs/openid-connect-core-1_0.html#ScopeClaims
ClaimsScopes []string `protobuf:"bytes,112,rep,name=claims_scopes,proto3" json:"claims_scopes,omitempty"`
// account_claim_maps are optional claim maps from custom claims to the
// standard claims of sub, name and email. These maps are represented as
// key=value where the key equals the from_claim and the value equals the
// to_claim. For example "oid=sub".
AccountClaimMaps []string `protobuf:"bytes,113,rep,name=account_claim_maps,proto3" json:"account_claim_maps,omitempty"`
// If the Authorization Server's discovered configuration contains values
// that do not match the configuration set on this auth method, this can be
// set to force the local configuration to override the discovered values.
@@ -419,6 +424,13 @@ func (x *OidcAuthMethodAttributes) GetClaimsScopes() []string {
return nil
}
func (x *OidcAuthMethodAttributes) GetAccountClaimMaps() []string {
if x != nil {
return x.AccountClaimMaps
}
return nil
}
func (x *OidcAuthMethodAttributes) GetDisableDiscoveredConfigValidation() bool {
if x != nil {
return x.DisableDiscoveredConfigValidation
@@ -815,7 +827,7 @@ var file_controller_api_resources_authmethods_v1_auth_method_proto_rawDesc = []b
0x6e, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x5f, 0x6c, 0x65, 0x6e, 0x67, 0x74,
0x68, 0x12, 0x11, 0x4d, 0x69, 0x6e, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x4c, 0x65,
0x6e, 0x67, 0x74, 0x68, 0x52, 0x13, 0x6d, 0x69, 0x6e, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f,
0x72, 0x64, 0x5f, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0xfb, 0x08, 0x0a, 0x18, 0x4f, 0x69,
0x72, 0x64, 0x5f, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0xe6, 0x09, 0x0a, 0x18, 0x4f, 0x69,
0x64, 0x63, 0x41, 0x75, 0x74, 0x68, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x41, 0x74, 0x74, 0x72,
0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18,
0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x59, 0x0a, 0x06,
@@ -879,54 +891,60 @@ var file_controller_api_resources_authmethods_v1_auth_method_proto_rawDesc = []b
0x01, 0xc2, 0xdd, 0x29, 0x28, 0x0a, 0x18, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65,
0x73, 0x2e, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x73, 0x5f, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x12,
0x0c, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x73, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x52, 0x0d, 0x63,
0x6c, 0x61, 0x69, 0x6d, 0x73, 0x5f, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x12, 0x58, 0x0a, 0x24,
0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72,
0x65, 0x64, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61,
0x74, 0x69, 0x6f, 0x6e, 0x18, 0x78, 0x20, 0x01, 0x28, 0x08, 0x42, 0x04, 0xa0, 0xda, 0x29, 0x01,
0x52, 0x24, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76,
0x65, 0x72, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x76, 0x61, 0x6c, 0x69,
0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x07, 0x64, 0x72, 0x79, 0x5f, 0x72, 0x75,
0x6e, 0x18, 0x82, 0x01, 0x20, 0x01, 0x28, 0x08, 0x42, 0x04, 0xa0, 0xda, 0x29, 0x01, 0x52, 0x07,
0x64, 0x72, 0x79, 0x5f, 0x72, 0x75, 0x6e, 0x22, 0x61, 0x0a, 0x27, 0x4f, 0x69, 0x64, 0x63, 0x41,
0x75, 0x74, 0x68, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74,
0x69, 0x63, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x0a,
0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x75, 0x72, 0x6c, 0x12, 0x1a,
0x0a, 0x08, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x09,
0x52, 0x08, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x69, 0x64, 0x22, 0xb7, 0x01, 0x0a, 0x29, 0x4f,
0x69, 0x64, 0x63, 0x41, 0x75, 0x74, 0x68, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x41, 0x75, 0x74,
0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x43, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63,
0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65,
0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x14, 0x0a, 0x05,
0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61,
0x74, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x1e, 0x20, 0x01, 0x28,
0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x2c, 0x0a, 0x11, 0x65, 0x72, 0x72, 0x6f,
0x72, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x28, 0x20,
0x01, 0x28, 0x09, 0x52, 0x11, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x72,
0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f,
0x75, 0x72, 0x69, 0x18, 0x32, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x65, 0x72, 0x72, 0x6f, 0x72,
0x5f, 0x75, 0x72, 0x69, 0x22, 0x5c, 0x0a, 0x2a, 0x4f, 0x69, 0x64, 0x63, 0x41, 0x75, 0x74, 0x68,
0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61,
0x74, 0x65, 0x43, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x73, 0x65, 0x12, 0x2e, 0x0a, 0x12, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x5f, 0x72, 0x65, 0x64, 0x69,
0x72, 0x65, 0x63, 0x74, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12,
0x66, 0x69, 0x6e, 0x61, 0x6c, 0x5f, 0x72, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x5f, 0x75,
0x72, 0x6c, 0x22, 0x44, 0x0a, 0x26, 0x4f, 0x69, 0x64, 0x63, 0x41, 0x75, 0x74, 0x68, 0x4d, 0x65,
0x6c, 0x61, 0x69, 0x6d, 0x73, 0x5f, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x12, 0x69, 0x0a, 0x12,
0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x5f, 0x6d, 0x61,
0x70, 0x73, 0x18, 0x71, 0x20, 0x03, 0x28, 0x09, 0x42, 0x39, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd,
0x29, 0x31, 0x0a, 0x1d, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x61,
0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x5f, 0x6d, 0x61, 0x70,
0x73, 0x12, 0x10, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x4d,
0x61, 0x70, 0x73, 0x52, 0x12, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x63, 0x6c, 0x61,
0x69, 0x6d, 0x5f, 0x6d, 0x61, 0x70, 0x73, 0x12, 0x58, 0x0a, 0x24, 0x64, 0x69, 0x73, 0x61, 0x62,
0x6c, 0x65, 0x5f, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x65, 0x64, 0x5f, 0x63, 0x6f,
0x6e, 0x66, 0x69, 0x67, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18,
0x78, 0x20, 0x01, 0x28, 0x08, 0x42, 0x04, 0xa0, 0xda, 0x29, 0x01, 0x52, 0x24, 0x64, 0x69, 0x73,
0x61, 0x62, 0x6c, 0x65, 0x5f, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x65, 0x64, 0x5f,
0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f,
0x6e, 0x12, 0x1f, 0x0a, 0x07, 0x64, 0x72, 0x79, 0x5f, 0x72, 0x75, 0x6e, 0x18, 0x82, 0x01, 0x20,
0x01, 0x28, 0x08, 0x42, 0x04, 0xa0, 0xda, 0x29, 0x01, 0x52, 0x07, 0x64, 0x72, 0x79, 0x5f, 0x72,
0x75, 0x6e, 0x22, 0x61, 0x0a, 0x27, 0x4f, 0x69, 0x64, 0x63, 0x41, 0x75, 0x74, 0x68, 0x4d, 0x65,
0x74, 0x68, 0x6f, 0x64, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65,
0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08,
0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08,
0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x69, 0x64, 0x22, 0x41, 0x0a, 0x27, 0x4f, 0x69, 0x64, 0x63,
0x41, 0x75, 0x74, 0x68, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e,
0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x0a, 0x20,
0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x42, 0x5d, 0x5a, 0x5b, 0x67,
0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63,
0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, 0x74,
0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f,
0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63,
0x65, 0x73, 0x2f, 0x61, 0x75, 0x74, 0x68, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, 0x3b, 0x61,
0x75, 0x74, 0x68, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x33,
0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a,
0x08, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52,
0x08, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x75, 0x72, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x74, 0x6f, 0x6b,
0x65, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x6f, 0x6b,
0x65, 0x6e, 0x5f, 0x69, 0x64, 0x22, 0xb7, 0x01, 0x0a, 0x29, 0x4f, 0x69, 0x64, 0x63, 0x41, 0x75,
0x74, 0x68, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69,
0x63, 0x61, 0x74, 0x65, 0x43, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75,
0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28,
0x09, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65,
0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x14, 0x0a,
0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72,
0x72, 0x6f, 0x72, 0x12, 0x2c, 0x0a, 0x11, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x64, 0x65, 0x73,
0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x28, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11,
0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f,
0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x75, 0x72, 0x69, 0x18, 0x32,
0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x75, 0x72, 0x69, 0x22,
0x5c, 0x0a, 0x2a, 0x4f, 0x69, 0x64, 0x63, 0x41, 0x75, 0x74, 0x68, 0x4d, 0x65, 0x74, 0x68, 0x6f,
0x64, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x43, 0x61, 0x6c,
0x6c, 0x62, 0x61, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2e, 0x0a,
0x12, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x5f, 0x72, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x5f,
0x75, 0x72, 0x6c, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x66, 0x69, 0x6e, 0x61, 0x6c,
0x5f, 0x72, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x5f, 0x75, 0x72, 0x6c, 0x22, 0x44, 0x0a,
0x26, 0x4f, 0x69, 0x64, 0x63, 0x41, 0x75, 0x74, 0x68, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x41,
0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x74, 0x6f, 0x6b, 0x65, 0x6e,
0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x6f, 0x6b, 0x65, 0x6e,
0x5f, 0x69, 0x64, 0x22, 0x41, 0x0a, 0x27, 0x4f, 0x69, 0x64, 0x63, 0x41, 0x75, 0x74, 0x68, 0x4d,
0x65, 0x74, 0x68, 0x6f, 0x64, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74,
0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16,
0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06,
0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x42, 0x5d, 0x5a, 0x5b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62,
0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62,
0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c,
0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f,
0x61, 0x70, 0x69, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x61, 0x75,
0x74, 0x68, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, 0x3b, 0x61, 0x75, 0x74, 0x68, 0x6d, 0x65,
0x74, 0x68, 0x6f, 0x64, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
@@ -123,6 +123,13 @@ message OidcAuthMethodAttributes {
repeated string claims_scopes = 112
[json_name = "claims_scopes", (custom_options.v1.generate_sdk_option) = true, (custom_options.v1.mask_mapping) = { this: "attributes.claims_scopes" that: "ClaimsScopes" }];
// account_claim_maps are optional claim maps from custom claims to the
// standard claims of sub, name and email. These maps are represented as
// key=value where the key equals the from_claim and the value equals the
// to_claim. For example "oid=sub".
repeated string account_claim_maps = 113
[json_name = "account_claim_maps", (custom_options.v1.generate_sdk_option) = true, (custom_options.v1.mask_mapping) = { this: "attributes.account_claim_maps" that: "AccountClaimMaps" }];
// If the Authorization Server's discovered configuration contains values
// that do not match the configuration set on this auth method, this can be
// set to force the local configuration to override the discovered values.
@@ -117,6 +117,13 @@ message AuthMethod {
// are operatated on as a complete set.
// @inject_tag: `gorm:"-"`
repeated string claims_scopes = 200 [(custom_options.v1.mask_mapping) = { this: "ClaimsScopes" that: "attributes.claims_scopes" }];
// account_claim_maps are optional claim maps from custom claims to the
// standard claims of sub, name and email. These maps are represented as
// key=value where the key equals the from_claim and the value equals the
// to_claim. For example "oid=sub".
// @inject_tag: `gorm:"-"`
repeated string account_claim_maps = 210 [(custom_options.v1.mask_mapping) = { this: "AccountClaimMaps" that: "attributes.account_claim_maps" }];
}
// Account represents an OIDC account
@@ -224,4 +231,24 @@ message ClaimsScope {
// The create_time is set by the database.
// @inject_tag: `gorm:"default:current_timestamp"`
timestamp.v1.Timestamp create_time = 30;
}
// AccountClaimMap entries are optional from/to account claim maps.
message AccountClaimMap {
// @inject_tag: `gorm:"primary_key"`
string oidc_method_id = 10;
// from_claim is the claim from the id_token that you need to map to a
// standard account claim.
// @inject_tag: `gorm:"not_null"`
string from_claim = 20;
// to_claim is the standard account claim to map the from_claim to. Valid
// values are: sub, name, email
// @inject_tag: `gorm:"column:to_claim;primary_key"`
string to_claim = 30;
// The create_time is set by the database.
// @inject_tag: `gorm:"default:current_timestamp"`
timestamp.v1.Timestamp create_time = 40;
}
@@ -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 {