@@ -5,6 +5,7 @@ package vault
55
66import (
77 "context"
8+ "errors"
89 "fmt"
910 "strings"
1011
@@ -78,6 +79,14 @@ This field is deprecated, use canonical_id.`,
7879 Type : framework .TypeKVPairs ,
7980 Description : "User provided key-value pairs" ,
8081 },
82+ "external_id" : {
83+ Type : framework .TypeString ,
84+ Description : "Unique external identifier from external IdP." ,
85+ },
86+ "issuer" : {
87+ Type : framework .TypeString ,
88+ Description : "Issuer name associated with this alias." ,
89+ },
8190 },
8291
8392 Operations : map [logical.Operation ]framework.OperationHandler {
@@ -151,6 +160,14 @@ This field is deprecated, use canonical_id.`,
151160 Type : framework .TypeKVPairs ,
152161 Description : "User provided key-value pairs" ,
153162 },
163+ "external_id" : {
164+ Type : framework .TypeString ,
165+ Description : "Unique external identifier from external IdP." ,
166+ },
167+ "issuer" : {
168+ Type : framework .TypeString ,
169+ Description : "Issuer name associated with this alias." ,
170+ },
154171 }
155172}
156173
@@ -194,6 +211,12 @@ func (i *IdentityStore) handleAliasCreateUpdate() framework.OperationFunc {
194211 }
195212 }
196213
214+ // Get external_id if provided
215+ externalID := d .Get ("external_id" ).(string )
216+
217+ // Get issuer if provided
218+ issuer := d .Get ("issuer" ).(string )
219+
197220 i .lock .Lock ()
198221 defer i .lock .Unlock ()
199222
@@ -225,16 +248,24 @@ func (i *IdentityStore) handleAliasCreateUpdate() framework.OperationFunc {
225248 }
226249 name = alias .Name
227250 mountAccessor = alias .MountAccessor
251+ // Issuer can't be modified after initial creation
252+ issuer = alias .Issuer
253+ // ExternalID can't be modified after initial creation
254+ externalID = alias .ExternalID
228255 case mountAccessor == "" :
229256 // No change to mount accessor
230257 mountAccessor = alias .MountAccessor
231258 case name == "" :
232259 // No change to mount name
233260 name = alias .Name
261+ case externalID == "" :
262+ externalID = alias .ExternalID
263+ case issuer == "" :
264+ issuer = alias .Issuer
234265 default :
235266 // mountAccessor, name and customMetadata provided
236267 }
237- return i .handleAliasUpdate (ctx , canonicalID , name , mountAccessor , alias , customMetadata )
268+ return i .handleAliasUpdate (ctx , canonicalID , name , mountAccessor , externalID , issuer , alias , customMetadata )
238269 }
239270 }
240271
@@ -254,26 +285,33 @@ func (i *IdentityStore) handleAliasCreateUpdate() framework.OperationFunc {
254285 localMount := mountEntry .Local
255286
256287 // Look up the alias by factors; if it's found it's an update
257- return i .handleAliasCreateUpdateCommon (ctx , ns , mountAccessor , name , canonicalID , customMetadata , localMount , "" )
288+ return i .handleAliasCreateUpdateCommon (ctx , ns , mountAccessor , name , canonicalID , externalID , issuer , customMetadata , localMount , "" )
258289 }
259290}
260291
261- func (i * IdentityStore ) handleAliasCreateUpdateCommon (ctx context.Context , ns * namespace.Namespace , mountAccessor string , name string , canonicalID string , customMetadata map [string ]string , localMount bool , scimClientID string ) (* logical.Response , error ) {
292+ func (i * IdentityStore ) handleAliasCreateUpdateCommon (ctx context.Context , ns * namespace.Namespace , mountAccessor string , name string , canonicalID string , externalID string , issuer string , customMetadata map [string ]string , localMount bool , scimClientID string ) (* logical.Response , error ) {
262293 alias , err := i .MemDBAliasByFactors (mountAccessor , name , true , false )
263294 if err != nil {
264295 return nil , err
265296 }
297+
266298 if alias != nil {
267299 if alias .NamespaceID != ns .ID {
268300 return logical .ErrorResponse ("cannot modify aliases across namespaces" ), logical .ErrPermissionDenied
269301 }
270- return i .handleAliasUpdate (ctx , canonicalID , name , mountAccessor , alias , customMetadata )
302+ return i .handleAliasUpdate (ctx , canonicalID , name , mountAccessor , externalID , issuer , alias , customMetadata )
271303 }
272304 // At this point we know it's a new creation request
273- return i .handleAliasCreate (ctx , canonicalID , name , mountAccessor , localMount , customMetadata , scimClientID )
305+ return i .handleAliasCreate (ctx , canonicalID , name , mountAccessor , externalID , issuer , localMount , customMetadata , scimClientID )
306+ }
307+
308+ // doesAliasHaveExternalIdIssuerMountAccessor returns true if the given alias has
309+ // the given externalId, issuer,and mount accessor
310+ func (i * IdentityStore ) doesAliasHaveExternalIdIssuerMountAccessor (alias * identity.Alias , externalId , issuer , mountAccessor string ) bool {
311+ return alias .MountAccessor == mountAccessor && alias .ExternalID == externalId && alias .Issuer == issuer
274312}
275313
276- func (i * IdentityStore ) handleAliasCreate (ctx context.Context , canonicalID , name , mountAccessor string , local bool , customMetadata map [string ]string , scimClientID string ) (* logical.Response , error ) {
314+ func (i * IdentityStore ) handleAliasCreate (ctx context.Context , canonicalID , name , mountAccessor , externalID , issuer string , local bool , customMetadata map [string ]string , scimClientID string ) (* logical.Response , error ) {
277315 ns , err := namespace .FromContext (ctx )
278316 if err != nil {
279317 return nil , err
@@ -301,6 +339,17 @@ func (i *IdentityStore) handleAliasCreate(ctx context.Context, canonicalID, name
301339 }
302340 }
303341
342+ // Validate that an alias doesn't already exist with passed in issuer and external_id
343+ if issuer != "" && externalID != "" {
344+ existingAlias , err := i .MemDBAliasByIssuerAndExternalId (issuer , externalID , false )
345+ if err != nil && ! errors .Is (err , ErrNoAliasFound ) {
346+ return nil , err
347+ }
348+ if existingAlias != nil {
349+ return logical .ErrorResponse ("alias already exists for issuer and external_id" ), nil
350+ }
351+ }
352+
304353 persist := false
305354 // If the request was not forwarded, then this is the active node of the
306355 // primary. Create the entity here itself.
@@ -314,19 +363,26 @@ func (i *IdentityStore) handleAliasCreate(ctx context.Context, canonicalID, name
314363 }
315364
316365 for _ , currentAlias := range entity .Aliases {
366+ if i .doesAliasHaveExternalIdIssuerMountAccessor (currentAlias , externalID , issuer , mountAccessor ) {
367+ return logical .ErrorResponse ("alias already exists for requested entity, external ID, and issuer" ), nil
368+ }
369+
317370 if currentAlias .MountAccessor == mountAccessor {
318- return logical .ErrorResponse ("Alias already exists for requested entity and mount accessor" ), nil
371+ return logical .ErrorResponse ("alias already exists for requested entity and mount accessor" ), nil
319372 }
320373 }
321374
322375 var alias * identity.Alias
376+
323377 switch local {
324378 case true :
325379 alias , err = i .processLocalAlias (ctx , & logical.Alias {
326380 MountAccessor : mountAccessor ,
327381 Name : name ,
328382 Local : local ,
329383 CustomMetadata : customMetadata ,
384+ Issuer : issuer ,
385+ ExternalID : externalID ,
330386 }, entity , false )
331387 if err != nil {
332388 return nil , err
@@ -338,6 +394,8 @@ func (i *IdentityStore) handleAliasCreate(ctx context.Context, canonicalID, name
338394 CustomMetadata : customMetadata ,
339395 CanonicalID : entity .ID ,
340396 ScimClientID : scimClientID ,
397+ Issuer : issuer ,
398+ ExternalID : externalID ,
341399 }
342400 err = i .sanitizeAlias (ctx , alias )
343401 if err != nil {
@@ -362,10 +420,14 @@ func (i *IdentityStore) handleAliasCreate(ctx context.Context, canonicalID, name
362420 }, nil
363421}
364422
365- func (i * IdentityStore ) handleAliasUpdate (ctx context.Context , canonicalID , name , mountAccessor string , alias * identity.Alias , customMetadata map [string ]string ) (* logical.Response , error ) {
423+ func (i * IdentityStore ) handleAliasUpdate (ctx context.Context , canonicalID , name , mountAccessor , externalID , issuer string , alias * identity.Alias , customMetadata map [string ]string ) (* logical.Response , error ) {
424+ // Fast return if nothing to be updated
366425 if name == alias .Name &&
367426 mountAccessor == alias .MountAccessor &&
368- (canonicalID == alias .CanonicalID || canonicalID == "" ) && (strutil .EqualStringMaps (customMetadata , alias .CustomMetadata )) {
427+ (canonicalID == alias .CanonicalID || canonicalID == "" ) &&
428+ (strutil .EqualStringMaps (customMetadata , alias .CustomMetadata )) &&
429+ (externalID == alias .ExternalID ) &&
430+ (issuer == alias .Issuer ) {
369431 // Nothing to do; return nil to be idempotent
370432 return nil , nil
371433 }
@@ -391,14 +453,15 @@ func (i *IdentityStore) handleAliasUpdate(ctx context.Context, canonicalID, name
391453 if mountAccessor != alias .MountAccessor && (canonicalID == "" || canonicalID == alias .CanonicalID ) {
392454 for _ , currentAlias := range currentEntity .Aliases {
393455 if currentAlias .MountAccessor == mountAccessor {
394- return logical .ErrorResponse ("Alias cannot be updated as the entity already has an alias for the given 'mount_accessor' " ), nil
456+ return logical .ErrorResponse ("alias cannot be updated as the entity already has an alias for the given 'mount_accessor' " ), nil
395457 }
396458 }
397459 }
398- // If we're changing one or the other or both of these, make sure that
399- // there isn't a matching alias already, and make sure it's in the same
400- // namespace.
401- if name != alias .Name || mountAccessor != alias .MountAccessor || ! strutil .EqualStringMaps (customMetadata , alias .CustomMetadata ) {
460+ // If we're changing these, make sure that there isn't a matching alias already,
461+ // and make sure it's in the same namespace.
462+ if name != alias .Name || mountAccessor != alias .MountAccessor ||
463+ ! strutil .EqualStringMaps (customMetadata , alias .CustomMetadata ) ||
464+ issuer != alias .Issuer || externalID != alias .ExternalID {
402465 // Check here to see if such an alias already exists, if so bail
403466 mountEntry := i .router .MatchingMountByAccessor (mountAccessor )
404467 if mountEntry == nil {
@@ -418,6 +481,13 @@ func (i *IdentityStore) handleAliasUpdate(ctx context.Context, canonicalID, name
418481 return logical .ErrorResponse ("alias with combination of mount accessor and name already exists" ), nil
419482 }
420483
484+ // Bail if issuer and/or external_id differ. These fields are not to be changed after alias creation.
485+ if existingAlias != nil {
486+ if existingAlias .Issuer != issuer || existingAlias .ExternalID != externalID {
487+ return logical .ErrorResponse ("changes to issuer and/or external_id prohibited" ), nil
488+ }
489+ }
490+
421491 // Update the values in the alias
422492 alias .Name = name
423493 alias .MountAccessor = mountAccessor
@@ -450,7 +520,7 @@ func (i *IdentityStore) handleAliasUpdate(ctx context.Context, canonicalID, name
450520 // Check if the entity the alias is being updated to, already has an alias for the mount
451521 for _ , alias := range newEntity .Aliases {
452522 if alias .MountAccessor == mountAccessor {
453- return logical .ErrorResponse ("Alias cannot be updated as the given entity already has an alias for this mount " ), nil
523+ return logical .ErrorResponse ("alias cannot be updated as the given entity already has an alias for this mount " ), nil
454524 }
455525 }
456526
@@ -484,6 +554,8 @@ func (i *IdentityStore) handleAliasUpdate(ctx context.Context, canonicalID, name
484554 Name : name ,
485555 Local : mountValidationResp .MountLocal ,
486556 CustomMetadata : customMetadata ,
557+ Issuer : issuer ,
558+ ExternalID : externalID ,
487559 }, newEntity , true )
488560 if err != nil {
489561 return nil , err
@@ -555,6 +627,8 @@ func (i *IdentityStore) handleAliasReadCommon(ctx context.Context, alias *identi
555627 respData ["merged_from_canonical_ids" ] = alias .MergedFromCanonicalIDs
556628 respData ["namespace_id" ] = alias .NamespaceID
557629 respData ["local" ] = alias .Local
630+ respData ["issuer" ] = alias .Issuer
631+ respData ["external_id" ] = alias .ExternalID
558632
559633 if mountValidationResp := i .router .ValidateMountByAccessor (alias .MountAccessor ); mountValidationResp != nil {
560634 respData ["mount_path" ] = mountValidationResp .MountPath
0 commit comments