2017-10-11 17:21:20 +00:00
package vault
import (
2018-01-19 06:44:44 +00:00
"context"
2018-10-19 19:47:26 +00:00
"errors"
2017-10-11 17:21:20 +00:00
"fmt"
"strings"
"sync"
2020-04-23 18:31:22 +00:00
"time"
2017-10-11 17:21:20 +00:00
2020-04-23 18:31:22 +00:00
metrics "github.com/armon/go-metrics"
2017-10-11 17:21:20 +00:00
"github.com/golang/protobuf/ptypes"
2018-04-05 15:49:21 +00:00
"github.com/hashicorp/errwrap"
2017-10-11 17:21:20 +00:00
memdb "github.com/hashicorp/go-memdb"
uuid "github.com/hashicorp/go-uuid"
"github.com/hashicorp/vault/helper/identity"
2018-09-18 03:03:00 +00:00
"github.com/hashicorp/vault/helper/identity/mfa"
"github.com/hashicorp/vault/helper/namespace"
2017-10-11 17:21:20 +00:00
"github.com/hashicorp/vault/helper/storagepacker"
2019-04-13 07:44:06 +00:00
"github.com/hashicorp/vault/sdk/helper/consts"
2019-04-12 21:54:35 +00:00
"github.com/hashicorp/vault/sdk/helper/strutil"
"github.com/hashicorp/vault/sdk/logical"
2017-10-11 17:21:20 +00:00
)
2018-10-19 19:47:26 +00:00
var (
errDuplicateIdentityName = errors . New ( "duplicate identity name" )
)
2019-02-08 21:32:06 +00:00
func ( c * Core ) SetLoadCaseSensitiveIdentityStore ( caseSensitive bool ) {
c . loadCaseSensitiveIdentityStore = caseSensitive
}
2018-01-19 06:44:44 +00:00
func ( c * Core ) loadIdentityStoreArtifacts ( ctx context . Context ) error {
2017-10-11 17:21:20 +00:00
if c . identityStore == nil {
2018-04-25 03:10:22 +00:00
c . logger . Warn ( "identity store is not setup, skipping loading" )
return nil
2017-10-11 17:21:20 +00:00
}
2018-10-19 19:47:26 +00:00
loadFunc := func ( context . Context ) error {
err := c . identityStore . loadEntities ( ctx )
if err != nil {
return err
}
return c . identityStore . loadGroups ( ctx )
}
2019-02-08 21:32:06 +00:00
if ! c . loadCaseSensitiveIdentityStore {
// Load everything when memdb is set to operate on lower cased names
err := loadFunc ( ctx )
switch {
case err == nil :
// If it succeeds, all is well
return nil
case err != nil && ! errwrap . Contains ( err , errDuplicateIdentityName . Error ( ) ) :
return err
}
2017-10-11 17:21:20 +00:00
}
2018-10-19 19:47:26 +00:00
c . identityStore . logger . Warn ( "enabling case sensitive identity names" )
// Set identity store to operate on case sensitive identity names
c . identityStore . disableLowerCasedNames = true
// Swap the memdb instance by the one which operates on case sensitive
// names, hence obviating the need to unload anything that's already
// loaded.
2019-02-08 21:32:06 +00:00
if err := c . identityStore . resetDB ( ctx ) ; err != nil {
2017-10-11 17:21:20 +00:00
return err
}
2018-10-19 19:47:26 +00:00
// Attempt to load identity artifacts once more after memdb is reset to
// accept case sensitive names
return loadFunc ( ctx )
}
func ( i * IdentityStore ) sanitizeName ( name string ) string {
if i . disableLowerCasedNames {
return name
}
return strings . ToLower ( name )
2017-10-11 17:21:20 +00:00
}
2018-01-19 06:44:44 +00:00
func ( i * IdentityStore ) loadGroups ( ctx context . Context ) error {
2017-10-11 17:21:20 +00:00
i . logger . Debug ( "identity loading groups" )
2018-01-19 06:44:44 +00:00
existing , err := i . groupPacker . View ( ) . List ( ctx , groupBucketsPrefix )
2017-10-11 17:21:20 +00:00
if err != nil {
2018-04-05 15:49:21 +00:00
return errwrap . Wrapf ( "failed to scan for groups: {{err}}" , err )
2017-10-11 17:21:20 +00:00
}
2018-04-03 00:46:59 +00:00
i . logger . Debug ( "groups collected" , "num_existing" , len ( existing ) )
2017-10-11 17:21:20 +00:00
for _ , key := range existing {
2019-05-07 19:29:51 +00:00
bucket , err := i . groupPacker . GetBucket ( groupBucketsPrefix + key )
2017-10-11 17:21:20 +00:00
if err != nil {
return err
}
if bucket == nil {
continue
}
for _ , item := range bucket . Items {
group , err := i . parseGroupFromBucketItem ( item )
if err != nil {
return err
}
if group == nil {
continue
}
2019-04-10 19:05:57 +00:00
ns , err := NamespaceByID ( ctx , group . NamespaceID , i . core )
if err != nil {
return err
}
if ns == nil {
// Remove dangling groups
if ! ( i . core . ReplicationState ( ) . HasState ( consts . ReplicationPerformanceSecondary ) || i . core . perfStandby ) {
2019-02-26 21:11:16 +00:00
// Group's namespace doesn't exist anymore but the group
// from the namespace still exists.
i . logger . Warn ( "deleting group and its any existing aliases" , "name" , group . Name , "namespace_id" , group . NamespaceID )
2019-05-01 17:47:41 +00:00
err = i . groupPacker . DeleteItem ( ctx , group . ID )
2019-02-26 21:11:16 +00:00
if err != nil {
return err
}
}
2019-04-10 19:05:57 +00:00
continue
2019-02-26 21:11:16 +00:00
}
2019-04-10 19:05:57 +00:00
nsCtx := namespace . ContextWithNamespace ( context . Background ( ) , ns )
2019-02-26 21:11:16 +00:00
2018-10-19 19:47:26 +00:00
// Ensure that there are no groups with duplicate names
2019-04-10 19:05:57 +00:00
groupByName , err := i . MemDBGroupByName ( nsCtx , group . Name , false )
2018-10-19 19:47:26 +00:00
if err != nil {
return err
}
if groupByName != nil {
i . logger . Warn ( errDuplicateIdentityName . Error ( ) , "group_name" , group . Name , "conflicting_group_name" , groupByName . Name , "action" , "merge the contents of duplicated groups into one and delete the other" )
if ! i . disableLowerCasedNames {
return errDuplicateIdentityName
}
}
2018-04-03 00:46:59 +00:00
if i . logger . IsDebug ( ) {
i . logger . Debug ( "loading group" , "name" , group . Name , "id" , group . ID )
2017-10-11 17:21:20 +00:00
}
txn := i . db . Txn ( true )
2018-11-16 01:07:45 +00:00
// Before pull#5786, entity memberships in groups were not getting
// updated when respective entities were deleted. This is here to
// check that the entity IDs in the group are indeed valid, and if
// not remove them.
persist := false
for _ , memberEntityID := range group . MemberEntityIDs {
entity , err := i . MemDBEntityByID ( memberEntityID , false )
if err != nil {
2020-03-25 16:42:23 +00:00
txn . Abort ( )
2018-11-16 01:07:45 +00:00
return err
}
if entity == nil {
persist = true
group . MemberEntityIDs = strutil . StrListDelete ( group . MemberEntityIDs , memberEntityID )
}
}
2019-05-01 17:47:41 +00:00
err = i . UpsertGroupInTxn ( ctx , txn , group , persist )
2017-10-11 17:21:20 +00:00
if err != nil {
2017-11-01 18:14:21 +00:00
txn . Abort ( )
2018-04-05 15:49:21 +00:00
return errwrap . Wrapf ( "failed to update group in memdb: {{err}}" , err )
2017-10-11 17:21:20 +00:00
}
txn . Commit ( )
}
}
if i . logger . IsInfo ( ) {
2018-04-03 00:46:59 +00:00
i . logger . Info ( "groups restored" )
2017-10-11 17:21:20 +00:00
}
return nil
}
2018-01-19 06:44:44 +00:00
func ( i * IdentityStore ) loadEntities ( ctx context . Context ) error {
2017-10-11 17:21:20 +00:00
// Accumulate existing entities
2018-04-03 00:46:59 +00:00
i . logger . Debug ( "loading entities" )
2018-01-19 06:44:44 +00:00
existing , err := i . entityPacker . View ( ) . List ( ctx , storagepacker . StoragePackerBucketsPrefix )
2017-10-11 17:21:20 +00:00
if err != nil {
2018-04-05 15:49:21 +00:00
return errwrap . Wrapf ( "failed to scan for entities: {{err}}" , err )
2017-10-11 17:21:20 +00:00
}
2018-04-03 00:46:59 +00:00
i . logger . Debug ( "entities collected" , "num_existing" , len ( existing ) )
2017-10-11 17:21:20 +00:00
// Make the channels used for the worker pool
broker := make ( chan string )
quit := make ( chan bool )
// Buffer these channels to prevent deadlocks
errs := make ( chan error , len ( existing ) )
result := make ( chan * storagepacker . Bucket , len ( existing ) )
// Use a wait group
wg := & sync . WaitGroup { }
// Create 64 workers to distribute work to
for j := 0 ; j < consts . ExpirationRestoreWorkerCount ; j ++ {
wg . Add ( 1 )
go func ( ) {
defer wg . Done ( )
for {
select {
2019-05-07 19:29:51 +00:00
case key , ok := <- broker :
2017-10-11 17:21:20 +00:00
// broker has been closed, we are done
if ! ok {
return
}
2019-05-07 19:29:51 +00:00
bucket , err := i . entityPacker . GetBucket ( storagepacker . StoragePackerBucketsPrefix + key )
2017-10-11 17:21:20 +00:00
if err != nil {
errs <- err
continue
}
// Write results out to the result channel
result <- bucket
// quit early
case <- quit :
return
}
}
} ( )
}
// Distribute the collected keys to the workers in a go routine
wg . Add ( 1 )
go func ( ) {
defer wg . Done ( )
2019-05-07 19:29:51 +00:00
for j , key := range existing {
2017-10-11 17:21:20 +00:00
if j % 500 == 0 {
2018-04-03 00:46:59 +00:00
i . logger . Debug ( "entities loading" , "progress" , j )
2017-10-11 17:21:20 +00:00
}
select {
case <- quit :
return
default :
2019-05-07 19:29:51 +00:00
broker <- key
2017-10-11 17:21:20 +00:00
}
}
// Close the broker, causing worker routines to exit
close ( broker )
} ( )
// Restore each key by pulling from the result chan
for j := 0 ; j < len ( existing ) ; j ++ {
select {
case err := <- errs :
// Close all go routines
close ( quit )
return err
case bucket := <- result :
// If there is no entry, nothing to restore
if bucket == nil {
continue
}
for _ , item := range bucket . Items {
2018-04-03 02:17:33 +00:00
entity , err := i . parseEntityFromBucketItem ( ctx , item )
2017-10-11 17:21:20 +00:00
if err != nil {
return err
}
if entity == nil {
continue
}
2019-04-10 19:05:57 +00:00
ns , err := NamespaceByID ( ctx , entity . NamespaceID , i . core )
if err != nil {
return err
}
if ns == nil {
// Remove dangling entities
if ! ( i . core . ReplicationState ( ) . HasState ( consts . ReplicationPerformanceSecondary ) || i . core . perfStandby ) {
2019-02-26 21:11:16 +00:00
// Entity's namespace doesn't exist anymore but the
// entity from the namespace still exists.
i . logger . Warn ( "deleting entity and its any existing aliases" , "name" , entity . Name , "namespace_id" , entity . NamespaceID )
2019-05-01 17:47:41 +00:00
err = i . entityPacker . DeleteItem ( ctx , entity . ID )
2019-02-26 21:11:16 +00:00
if err != nil {
return err
}
}
2019-04-10 19:05:57 +00:00
continue
2019-02-26 21:11:16 +00:00
}
2019-04-10 19:05:57 +00:00
nsCtx := namespace . ContextWithNamespace ( context . Background ( ) , ns )
2019-02-26 21:11:16 +00:00
2018-10-19 19:47:26 +00:00
// Ensure that there are no entities with duplicate names
2019-04-10 19:05:57 +00:00
entityByName , err := i . MemDBEntityByName ( nsCtx , entity . Name , false )
2018-10-19 19:47:26 +00:00
if err != nil {
return nil
}
if entityByName != nil {
i . logger . Warn ( errDuplicateIdentityName . Error ( ) , "entity_name" , entity . Name , "conflicting_entity_name" , entityByName . Name , "action" , "merge the duplicate entities into one" )
if ! i . disableLowerCasedNames {
return errDuplicateIdentityName
}
}
2017-10-11 17:21:20 +00:00
// Only update MemDB and don't hit the storage again
2019-04-10 19:05:57 +00:00
err = i . upsertEntity ( nsCtx , entity , nil , false )
2017-10-11 17:21:20 +00:00
if err != nil {
2018-04-05 15:49:21 +00:00
return errwrap . Wrapf ( "failed to update entity in MemDB: {{err}}" , err )
2017-10-11 17:21:20 +00:00
}
}
}
}
// Let all go routines finish
wg . Wait ( )
if i . logger . IsInfo ( ) {
2018-04-03 00:46:59 +00:00
i . logger . Info ( "entities restored" )
2017-10-11 17:21:20 +00:00
}
return nil
}
// upsertEntityInTxn either creates or updates an existing entity. The
// operations will be updated in both MemDB and storage. If 'persist' is set to
2017-11-02 20:05:48 +00:00
// false, then storage will not be updated. When an alias is transferred from
2017-10-11 17:21:20 +00:00
// one entity to another, both the source and destination entities should get
// updated, in which case, callers should send in both entity and
// previousEntity.
2018-09-18 03:03:00 +00:00
func ( i * IdentityStore ) upsertEntityInTxn ( ctx context . Context , txn * memdb . Txn , entity * identity . Entity , previousEntity * identity . Entity , persist bool ) error {
2020-04-23 18:31:22 +00:00
defer metrics . MeasureSince ( [ ] string { "identity" , "upsert_entity_txn" } , time . Now ( ) )
2017-10-11 17:21:20 +00:00
var err error
if txn == nil {
2019-06-14 16:53:00 +00:00
return errors . New ( "txn is nil" )
2017-10-11 17:21:20 +00:00
}
if entity == nil {
2019-06-14 16:53:00 +00:00
return errors . New ( "entity is nil" )
}
if entity . NamespaceID == "" {
entity . NamespaceID = namespace . RootNamespaceID
}
if previousEntity != nil && previousEntity . NamespaceID != entity . NamespaceID {
return errors . New ( "entity and previous entity are not in the same namespace" )
2017-10-11 17:21:20 +00:00
}
2018-10-19 19:47:26 +00:00
aliasFactors := make ( [ ] string , len ( entity . Aliases ) )
for index , alias := range entity . Aliases {
2017-10-11 17:21:20 +00:00
// Verify that alias is not associated to a different one already
2017-11-02 20:05:48 +00:00
aliasByFactors , err := i . MemDBAliasByFactors ( alias . MountAccessor , alias . Name , false , false )
2017-10-11 17:21:20 +00:00
if err != nil {
return err
}
2019-06-14 16:53:00 +00:00
if alias . NamespaceID == "" {
alias . NamespaceID = namespace . RootNamespaceID
}
2018-11-08 18:04:24 +00:00
switch {
case aliasByFactors == nil :
2019-06-14 16:53:00 +00:00
// Not found, no merging needed, just check namespace
if alias . NamespaceID != entity . NamespaceID {
return errors . New ( "alias and entity are not in the same namespace" )
}
2018-11-08 18:04:24 +00:00
case aliasByFactors . CanonicalID == entity . ID :
// Lookup found the same entity, so it's already attached to the
// right place
2019-06-14 16:53:00 +00:00
if aliasByFactors . NamespaceID != entity . NamespaceID {
return errors . New ( "alias from factors and entity are not in the same namespace" )
}
2018-11-08 18:04:24 +00:00
case previousEntity != nil && aliasByFactors . CanonicalID == previousEntity . ID :
// previousEntity isn't upserted yet so may still contain the old
// alias reference in memdb if it was just changed; validate
// whether or not it's _actually_ still tied to the entity
var found bool
for _ , prevEntAlias := range previousEntity . Aliases {
if prevEntAlias . ID == alias . ID {
found = true
break
}
}
// If we didn't find the alias still tied to previousEntity, we
// shouldn't use the merging logic and should bail
if ! found {
break
}
// Otherwise it's still tied to previousEntity and fall through
2019-06-14 16:53:00 +00:00
// into merging. We don't need a namespace check here as existing
// checks when creating the aliases should ensure that all line up.
2018-11-08 18:04:24 +00:00
fallthrough
2019-06-14 16:53:00 +00:00
2018-11-08 18:04:24 +00:00
default :
i . logger . Warn ( "alias is already tied to a different entity; these entities are being merged" , "alias_id" , alias . ID , "other_entity_id" , aliasByFactors . CanonicalID , "entity_aliases" , entity . Aliases , "alias_by_factors" , aliasByFactors )
2019-02-08 21:32:06 +00:00
respErr , intErr := i . mergeEntity ( ctx , txn , entity , [ ] string { aliasByFactors . CanonicalID } , true , false , true , persist )
2018-08-09 20:37:36 +00:00
switch {
case respErr != nil :
return respErr
case intErr != nil :
return intErr
}
2019-02-08 21:32:06 +00:00
2018-08-09 20:37:36 +00:00
// The entity and aliases will be loaded into memdb and persisted
// as a result of the merge so we are done here
return nil
2017-10-11 17:21:20 +00:00
}
2018-10-19 19:47:26 +00:00
if strutil . StrListContains ( aliasFactors , i . sanitizeName ( alias . Name ) + alias . MountAccessor ) {
i . logger . Warn ( errDuplicateIdentityName . Error ( ) , "alias_name" , alias . Name , "mount_accessor" , alias . MountAccessor , "entity_name" , entity . Name , "action" , "delete one of the duplicate aliases" )
if ! i . disableLowerCasedNames {
return errDuplicateIdentityName
}
}
2017-10-11 17:21:20 +00:00
// Insert or update alias in MemDB using the transaction created above
2017-11-02 20:05:48 +00:00
err = i . MemDBUpsertAliasInTxn ( txn , alias , false )
2017-10-11 17:21:20 +00:00
if err != nil {
return err
}
2018-10-19 19:47:26 +00:00
aliasFactors [ index ] = i . sanitizeName ( alias . Name ) + alias . MountAccessor
2017-10-11 17:21:20 +00:00
}
// If previous entity is set, update it in MemDB and persist it
2019-02-08 21:32:06 +00:00
if previousEntity != nil {
2017-11-02 20:05:48 +00:00
err = i . MemDBUpsertEntityInTxn ( txn , previousEntity )
2017-10-11 17:21:20 +00:00
if err != nil {
return err
}
2019-02-08 21:32:06 +00:00
if persist {
// Persist the previous entity object
marshaledPreviousEntity , err := ptypes . MarshalAny ( previousEntity )
if err != nil {
return err
}
2019-05-01 17:47:41 +00:00
err = i . entityPacker . PutItem ( ctx , & storagepacker . Item {
2019-02-08 21:32:06 +00:00
ID : previousEntity . ID ,
Message : marshaledPreviousEntity ,
} )
if err != nil {
return err
}
2017-10-11 17:21:20 +00:00
}
}
// Insert or update entity in MemDB using the transaction created above
2017-11-02 20:05:48 +00:00
err = i . MemDBUpsertEntityInTxn ( txn , entity )
2017-10-11 17:21:20 +00:00
if err != nil {
return err
}
if persist {
entityAsAny , err := ptypes . MarshalAny ( entity )
if err != nil {
return err
}
item := & storagepacker . Item {
ID : entity . ID ,
Message : entityAsAny ,
}
// Persist the entity object
2019-05-01 17:47:41 +00:00
err = i . entityPacker . PutItem ( ctx , item )
2017-10-11 17:21:20 +00:00
if err != nil {
return err
}
}
return nil
}
// upsertEntity either creates or updates an existing entity. The operations
// will be updated in both MemDB and storage. If 'persist' is set to false,
2017-11-02 20:05:48 +00:00
// then storage will not be updated. When an alias is transferred from one
2017-10-11 17:21:20 +00:00
// entity to another, both the source and destination entities should get
// updated, in which case, callers should send in both entity and
// previousEntity.
2018-09-18 03:03:00 +00:00
func ( i * IdentityStore ) upsertEntity ( ctx context . Context , entity * identity . Entity , previousEntity * identity . Entity , persist bool ) error {
2020-04-23 18:31:22 +00:00
defer metrics . MeasureSince ( [ ] string { "identity" , "upsert_entity" } , time . Now ( ) )
2017-10-11 17:21:20 +00:00
// Create a MemDB transaction to update both alias and entity
txn := i . db . Txn ( true )
defer txn . Abort ( )
2018-09-18 03:03:00 +00:00
err := i . upsertEntityInTxn ( ctx , txn , entity , previousEntity , persist )
2018-06-24 11:54:00 +00:00
if err != nil {
return err
}
txn . Commit ( )
return nil
}
2017-11-02 20:05:48 +00:00
func ( i * IdentityStore ) MemDBUpsertAliasInTxn ( txn * memdb . Txn , alias * identity . Alias , groupAlias bool ) error {
2017-10-11 17:21:20 +00:00
if txn == nil {
return fmt . Errorf ( "nil txn" )
}
if alias == nil {
return fmt . Errorf ( "alias is nil" )
}
2018-09-18 03:03:00 +00:00
if alias . NamespaceID == "" {
alias . NamespaceID = namespace . RootNamespaceID
}
2017-11-02 20:05:48 +00:00
tableName := entityAliasesTable
if groupAlias {
tableName = groupAliasesTable
}
aliasRaw , err := txn . First ( tableName , "id" , alias . ID )
2017-10-11 17:21:20 +00:00
if err != nil {
2018-04-05 15:49:21 +00:00
return errwrap . Wrapf ( "failed to lookup alias from memdb using alias ID: {{err}}" , err )
2017-10-11 17:21:20 +00:00
}
if aliasRaw != nil {
2017-11-02 20:05:48 +00:00
err = txn . Delete ( tableName , aliasRaw )
2017-10-11 17:21:20 +00:00
if err != nil {
2018-04-05 15:49:21 +00:00
return errwrap . Wrapf ( "failed to delete alias from memdb: {{err}}" , err )
2017-10-11 17:21:20 +00:00
}
}
2017-11-02 20:05:48 +00:00
if err := txn . Insert ( tableName , alias ) ; err != nil {
2018-04-05 15:49:21 +00:00
return errwrap . Wrapf ( "failed to update alias into memdb: {{err}}" , err )
2017-10-11 17:21:20 +00:00
}
return nil
}
2017-11-02 20:05:48 +00:00
func ( i * IdentityStore ) MemDBAliasByIDInTxn ( txn * memdb . Txn , aliasID string , clone bool , groupAlias bool ) ( * identity . Alias , error ) {
2017-10-11 17:21:20 +00:00
if aliasID == "" {
return nil , fmt . Errorf ( "missing alias ID" )
}
if txn == nil {
return nil , fmt . Errorf ( "txn is nil" )
}
2017-11-02 20:05:48 +00:00
tableName := entityAliasesTable
if groupAlias {
tableName = groupAliasesTable
}
aliasRaw , err := txn . First ( tableName , "id" , aliasID )
2017-10-11 17:21:20 +00:00
if err != nil {
2018-04-05 15:49:21 +00:00
return nil , errwrap . Wrapf ( "failed to fetch alias from memdb using alias ID: {{err}}" , err )
2017-10-11 17:21:20 +00:00
}
if aliasRaw == nil {
return nil , nil
}
alias , ok := aliasRaw . ( * identity . Alias )
if ! ok {
return nil , fmt . Errorf ( "failed to declare the type of fetched alias" )
}
if clone {
return alias . Clone ( )
}
return alias , nil
}
2017-11-02 20:05:48 +00:00
func ( i * IdentityStore ) MemDBAliasByID ( aliasID string , clone bool , groupAlias bool ) ( * identity . Alias , error ) {
2017-10-11 17:21:20 +00:00
if aliasID == "" {
return nil , fmt . Errorf ( "missing alias ID" )
}
txn := i . db . Txn ( false )
2017-11-02 20:05:48 +00:00
return i . MemDBAliasByIDInTxn ( txn , aliasID , clone , groupAlias )
2017-10-11 17:21:20 +00:00
}
2017-11-02 20:05:48 +00:00
func ( i * IdentityStore ) MemDBAliasByFactors ( mountAccessor , aliasName string , clone bool , groupAlias bool ) ( * identity . Alias , error ) {
2017-10-11 17:21:20 +00:00
if aliasName == "" {
return nil , fmt . Errorf ( "missing alias name" )
}
if mountAccessor == "" {
return nil , fmt . Errorf ( "missing mount accessor" )
}
2018-02-09 15:40:56 +00:00
txn := i . db . Txn ( false )
return i . MemDBAliasByFactorsInTxn ( txn , mountAccessor , aliasName , clone , groupAlias )
}
func ( i * IdentityStore ) MemDBAliasByFactorsInTxn ( txn * memdb . Txn , mountAccessor , aliasName string , clone bool , groupAlias bool ) ( * identity . Alias , error ) {
if txn == nil {
return nil , fmt . Errorf ( "nil txn" )
}
if aliasName == "" {
return nil , fmt . Errorf ( "missing alias name" )
}
if mountAccessor == "" {
return nil , fmt . Errorf ( "missing mount accessor" )
}
2017-11-02 20:05:48 +00:00
tableName := entityAliasesTable
if groupAlias {
tableName = groupAliasesTable
}
aliasRaw , err := txn . First ( tableName , "factors" , mountAccessor , aliasName )
2017-10-11 17:21:20 +00:00
if err != nil {
2018-04-05 15:49:21 +00:00
return nil , errwrap . Wrapf ( "failed to fetch alias from memdb using factors: {{err}}" , err )
2017-10-11 17:21:20 +00:00
}
if aliasRaw == nil {
return nil , nil
}
alias , ok := aliasRaw . ( * identity . Alias )
if ! ok {
return nil , fmt . Errorf ( "failed to declare the type of fetched alias" )
}
if clone {
return alias . Clone ( )
}
return alias , nil
}
2017-11-02 20:05:48 +00:00
func ( i * IdentityStore ) MemDBDeleteAliasByIDInTxn ( txn * memdb . Txn , aliasID string , groupAlias bool ) error {
2017-10-11 17:21:20 +00:00
if aliasID == "" {
return nil
}
if txn == nil {
return fmt . Errorf ( "txn is nil" )
}
2017-11-02 20:05:48 +00:00
alias , err := i . MemDBAliasByIDInTxn ( txn , aliasID , false , groupAlias )
2017-10-11 17:21:20 +00:00
if err != nil {
return err
}
if alias == nil {
return nil
}
2017-11-02 20:05:48 +00:00
tableName := entityAliasesTable
if groupAlias {
tableName = groupAliasesTable
}
err = txn . Delete ( tableName , alias )
2017-10-11 17:21:20 +00:00
if err != nil {
2018-04-05 15:49:21 +00:00
return errwrap . Wrapf ( "failed to delete alias from memdb: {{err}}" , err )
2017-10-11 17:21:20 +00:00
}
return nil
}
2017-11-02 20:05:48 +00:00
func ( i * IdentityStore ) MemDBAliases ( ws memdb . WatchSet , groupAlias bool ) ( memdb . ResultIterator , error ) {
2017-10-11 17:21:20 +00:00
txn := i . db . Txn ( false )
2017-11-02 20:05:48 +00:00
tableName := entityAliasesTable
if groupAlias {
tableName = groupAliasesTable
}
iter , err := txn . Get ( tableName , "id" )
2017-10-11 17:21:20 +00:00
if err != nil {
return nil , err
}
ws . Add ( iter . WatchCh ( ) )
return iter , nil
}
2017-11-02 20:05:48 +00:00
func ( i * IdentityStore ) MemDBUpsertEntityInTxn ( txn * memdb . Txn , entity * identity . Entity ) error {
2017-10-11 17:21:20 +00:00
if txn == nil {
return fmt . Errorf ( "nil txn" )
}
if entity == nil {
return fmt . Errorf ( "entity is nil" )
}
2018-09-18 03:03:00 +00:00
if entity . NamespaceID == "" {
entity . NamespaceID = namespace . RootNamespaceID
}
2017-11-02 20:05:48 +00:00
entityRaw , err := txn . First ( entitiesTable , "id" , entity . ID )
2017-10-11 17:21:20 +00:00
if err != nil {
2018-04-05 15:49:21 +00:00
return errwrap . Wrapf ( "failed to lookup entity from memdb using entity id: {{err}}" , err )
2017-10-11 17:21:20 +00:00
}
if entityRaw != nil {
2017-11-02 20:05:48 +00:00
err = txn . Delete ( entitiesTable , entityRaw )
2017-10-11 17:21:20 +00:00
if err != nil {
2018-04-05 15:49:21 +00:00
return errwrap . Wrapf ( "failed to delete entity from memdb: {{err}}" , err )
2017-10-11 17:21:20 +00:00
}
}
2017-11-02 20:05:48 +00:00
if err := txn . Insert ( entitiesTable , entity ) ; err != nil {
2018-04-05 15:49:21 +00:00
return errwrap . Wrapf ( "failed to update entity into memdb: {{err}}" , err )
2017-10-11 17:21:20 +00:00
}
return nil
}
2017-11-02 20:05:48 +00:00
func ( i * IdentityStore ) MemDBEntityByIDInTxn ( txn * memdb . Txn , entityID string , clone bool ) ( * identity . Entity , error ) {
2017-10-11 17:21:20 +00:00
if entityID == "" {
return nil , fmt . Errorf ( "missing entity id" )
}
if txn == nil {
return nil , fmt . Errorf ( "txn is nil" )
}
2017-11-02 20:05:48 +00:00
entityRaw , err := txn . First ( entitiesTable , "id" , entityID )
2017-10-11 17:21:20 +00:00
if err != nil {
2018-04-05 15:49:21 +00:00
return nil , errwrap . Wrapf ( "failed to fetch entity from memdb using entity id: {{err}}" , err )
2017-10-11 17:21:20 +00:00
}
if entityRaw == nil {
return nil , nil
}
entity , ok := entityRaw . ( * identity . Entity )
if ! ok {
return nil , fmt . Errorf ( "failed to declare the type of fetched entity" )
}
if clone {
return entity . Clone ( )
}
return entity , nil
}
2017-11-02 20:05:48 +00:00
func ( i * IdentityStore ) MemDBEntityByID ( entityID string , clone bool ) ( * identity . Entity , error ) {
2017-10-11 17:21:20 +00:00
if entityID == "" {
return nil , fmt . Errorf ( "missing entity id" )
}
txn := i . db . Txn ( false )
2017-11-02 20:05:48 +00:00
return i . MemDBEntityByIDInTxn ( txn , entityID , clone )
2017-10-11 17:21:20 +00:00
}
2018-09-18 03:03:00 +00:00
func ( i * IdentityStore ) MemDBEntityByName ( ctx context . Context , entityName string , clone bool ) ( * identity . Entity , error ) {
2017-10-11 17:21:20 +00:00
if entityName == "" {
return nil , fmt . Errorf ( "missing entity name" )
}
2018-09-25 19:28:28 +00:00
txn := i . db . Txn ( false )
2018-10-19 19:47:26 +00:00
return i . MemDBEntityByNameInTxn ( ctx , txn , entityName , clone )
2018-09-25 19:28:28 +00:00
}
2018-10-19 19:47:26 +00:00
func ( i * IdentityStore ) MemDBEntityByNameInTxn ( ctx context . Context , txn * memdb . Txn , entityName string , clone bool ) ( * identity . Entity , error ) {
2018-09-25 19:28:28 +00:00
if entityName == "" {
return nil , fmt . Errorf ( "missing entity name" )
}
2018-09-18 03:03:00 +00:00
ns , err := namespace . FromContext ( ctx )
if err != nil {
return nil , err
}
entityRaw , err := txn . First ( entitiesTable , "name" , ns . ID , entityName )
2017-10-11 17:21:20 +00:00
if err != nil {
2018-04-05 15:49:21 +00:00
return nil , errwrap . Wrapf ( "failed to fetch entity from memdb using entity name: {{err}}" , err )
2017-10-11 17:21:20 +00:00
}
if entityRaw == nil {
return nil , nil
}
entity , ok := entityRaw . ( * identity . Entity )
if ! ok {
return nil , fmt . Errorf ( "failed to declare the type of fetched entity" )
}
if clone {
return entity . Clone ( )
}
return entity , nil
}
2019-05-07 19:29:51 +00:00
func ( i * IdentityStore ) MemDBEntitiesByBucketKeyInTxn ( txn * memdb . Txn , bucketKey string ) ( [ ] * identity . Entity , error ) {
2017-10-11 17:21:20 +00:00
if txn == nil {
return nil , fmt . Errorf ( "nil txn" )
}
2019-05-07 19:29:51 +00:00
if bucketKey == "" {
return nil , fmt . Errorf ( "empty bucket key" )
2017-10-11 17:21:20 +00:00
}
2019-05-07 19:29:51 +00:00
entitiesIter , err := txn . Get ( entitiesTable , "bucket_key" , bucketKey )
2017-10-11 17:21:20 +00:00
if err != nil {
2018-04-05 15:49:21 +00:00
return nil , errwrap . Wrapf ( "failed to lookup entities using bucket entry key hash: {{err}}" , err )
2017-10-11 17:21:20 +00:00
}
var entities [ ] * identity . Entity
for entity := entitiesIter . Next ( ) ; entity != nil ; entity = entitiesIter . Next ( ) {
entities = append ( entities , entity . ( * identity . Entity ) )
}
return entities , nil
}
2018-06-24 11:45:53 +00:00
func ( i * IdentityStore ) MemDBEntityByMergedEntityID ( mergedEntityID string , clone bool ) ( * identity . Entity , error ) {
2017-10-11 17:21:20 +00:00
if mergedEntityID == "" {
return nil , fmt . Errorf ( "missing merged entity id" )
}
2018-06-24 11:45:53 +00:00
txn := i . db . Txn ( false )
2017-10-11 17:21:20 +00:00
2017-11-02 20:05:48 +00:00
entityRaw , err := txn . First ( entitiesTable , "merged_entity_ids" , mergedEntityID )
2017-10-11 17:21:20 +00:00
if err != nil {
2018-04-05 15:49:21 +00:00
return nil , errwrap . Wrapf ( "failed to fetch entity from memdb using merged entity id: {{err}}" , err )
2017-10-11 17:21:20 +00:00
}
if entityRaw == nil {
return nil , nil
}
entity , ok := entityRaw . ( * identity . Entity )
if ! ok {
return nil , fmt . Errorf ( "failed to declare the type of fetched entity" )
}
if clone {
return entity . Clone ( )
}
return entity , nil
}
2017-11-02 20:05:48 +00:00
func ( i * IdentityStore ) MemDBEntityByAliasIDInTxn ( txn * memdb . Txn , aliasID string , clone bool ) ( * identity . Entity , error ) {
2017-10-11 17:21:20 +00:00
if aliasID == "" {
return nil , fmt . Errorf ( "missing alias ID" )
}
if txn == nil {
return nil , fmt . Errorf ( "txn is nil" )
}
2017-11-02 20:05:48 +00:00
alias , err := i . MemDBAliasByIDInTxn ( txn , aliasID , false , false )
2017-10-11 17:21:20 +00:00
if err != nil {
return nil , err
}
if alias == nil {
return nil , nil
}
2017-11-02 20:05:48 +00:00
return i . MemDBEntityByIDInTxn ( txn , alias . CanonicalID , clone )
2017-10-11 17:21:20 +00:00
}
2017-11-02 20:05:48 +00:00
func ( i * IdentityStore ) MemDBEntityByAliasID ( aliasID string , clone bool ) ( * identity . Entity , error ) {
2017-10-11 17:21:20 +00:00
if aliasID == "" {
return nil , fmt . Errorf ( "missing alias ID" )
}
txn := i . db . Txn ( false )
2017-11-02 20:05:48 +00:00
return i . MemDBEntityByAliasIDInTxn ( txn , aliasID , clone )
2017-10-11 17:21:20 +00:00
}
2017-11-02 20:05:48 +00:00
func ( i * IdentityStore ) MemDBDeleteEntityByID ( entityID string ) error {
2017-10-11 17:21:20 +00:00
if entityID == "" {
return nil
}
txn := i . db . Txn ( true )
defer txn . Abort ( )
2017-11-02 20:05:48 +00:00
err := i . MemDBDeleteEntityByIDInTxn ( txn , entityID )
2017-10-11 17:21:20 +00:00
if err != nil {
return err
}
txn . Commit ( )
return nil
}
2017-11-02 20:05:48 +00:00
func ( i * IdentityStore ) MemDBDeleteEntityByIDInTxn ( txn * memdb . Txn , entityID string ) error {
2017-10-11 17:21:20 +00:00
if entityID == "" {
return nil
}
if txn == nil {
return fmt . Errorf ( "txn is nil" )
}
2017-11-02 20:05:48 +00:00
entity , err := i . MemDBEntityByIDInTxn ( txn , entityID , false )
2017-10-11 17:21:20 +00:00
if err != nil {
return err
}
if entity == nil {
return nil
}
2017-11-02 20:05:48 +00:00
err = txn . Delete ( entitiesTable , entity )
2017-10-11 17:21:20 +00:00
if err != nil {
2018-04-05 15:49:21 +00:00
return errwrap . Wrapf ( "failed to delete entity from memdb: {{err}}" , err )
2017-10-11 17:21:20 +00:00
}
return nil
}
2018-09-18 03:03:00 +00:00
func ( i * IdentityStore ) sanitizeAlias ( ctx context . Context , alias * identity . Alias ) error {
2017-10-11 17:21:20 +00:00
var err error
if alias == nil {
return fmt . Errorf ( "alias is nil" )
}
2017-11-02 20:05:48 +00:00
// Alias must always be tied to a canonical object
if alias . CanonicalID == "" {
return fmt . Errorf ( "missing canonical ID" )
2017-10-11 17:21:20 +00:00
}
// Alias must have a name
if alias . Name == "" {
return fmt . Errorf ( "missing alias name %q" , alias . Name )
}
// Alias metadata should always be map[string]string
err = validateMetadata ( alias . Metadata )
if err != nil {
2018-04-05 15:49:21 +00:00
return errwrap . Wrapf ( "invalid alias metadata: {{err}}" , err )
2017-10-11 17:21:20 +00:00
}
// Create an ID if there isn't one already
if alias . ID == "" {
alias . ID , err = uuid . GenerateUUID ( )
if err != nil {
return fmt . Errorf ( "failed to generate alias ID" )
}
}
2018-09-18 03:03:00 +00:00
if alias . NamespaceID == "" {
ns , err := namespace . FromContext ( ctx )
if err != nil {
return err
}
alias . NamespaceID = ns . ID
}
ns , err := namespace . FromContext ( ctx )
if err != nil {
return err
}
if ns . ID != alias . NamespaceID {
2019-06-14 16:53:00 +00:00
return errors . New ( "alias belongs to a different namespace" )
2018-09-18 03:03:00 +00:00
}
2017-10-11 17:21:20 +00:00
// Set the creation and last update times
if alias . CreationTime == nil {
alias . CreationTime = ptypes . TimestampNow ( )
alias . LastUpdateTime = alias . CreationTime
} else {
alias . LastUpdateTime = ptypes . TimestampNow ( )
}
return nil
}
2018-09-18 03:03:00 +00:00
func ( i * IdentityStore ) sanitizeEntity ( ctx context . Context , entity * identity . Entity ) error {
2017-10-11 17:21:20 +00:00
var err error
if entity == nil {
return fmt . Errorf ( "entity is nil" )
}
// Create an ID if there isn't one already
if entity . ID == "" {
entity . ID , err = uuid . GenerateUUID ( )
if err != nil {
return fmt . Errorf ( "failed to generate entity id" )
}
2019-05-07 19:29:51 +00:00
// Set the storage bucket key in entity
entity . BucketKey = i . entityPacker . BucketKey ( entity . ID )
2017-10-11 17:21:20 +00:00
}
2018-09-18 03:03:00 +00:00
ns , err := namespace . FromContext ( ctx )
if err != nil {
return err
}
if entity . NamespaceID == "" {
entity . NamespaceID = ns . ID
}
if ns . ID != entity . NamespaceID {
2019-06-14 16:53:00 +00:00
return errors . New ( "entity does not belong to this namespace" )
2018-09-18 03:03:00 +00:00
}
2017-10-11 17:21:20 +00:00
// Create a name if there isn't one already
if entity . Name == "" {
2018-09-18 03:03:00 +00:00
entity . Name , err = i . generateName ( ctx , "entity" )
2017-10-11 17:21:20 +00:00
if err != nil {
return fmt . Errorf ( "failed to generate entity name" )
}
}
// Entity metadata should always be map[string]string
err = validateMetadata ( entity . Metadata )
if err != nil {
2018-04-05 15:49:21 +00:00
return errwrap . Wrapf ( "invalid entity metadata: {{err}}" , err )
2017-10-11 17:21:20 +00:00
}
// Set the creation and last update times
if entity . CreationTime == nil {
entity . CreationTime = ptypes . TimestampNow ( )
entity . LastUpdateTime = entity . CreationTime
} else {
entity . LastUpdateTime = ptypes . TimestampNow ( )
}
2018-09-18 03:03:00 +00:00
// Ensure that MFASecrets is non-nil at any time. This is useful when MFA
// secret generation procedures try to append MFA info to entity.
if entity . MFASecrets == nil {
entity . MFASecrets = make ( map [ string ] * mfa . Secret )
}
2017-10-11 17:21:20 +00:00
return nil
}
2019-06-18 20:42:54 +00:00
func ( i * IdentityStore ) sanitizeAndUpsertGroup ( ctx context . Context , group * identity . Group , previousGroup * identity . Group , memberGroupIDs [ ] string ) error {
2017-10-11 17:21:20 +00:00
var err error
if group == nil {
return fmt . Errorf ( "group is nil" )
}
// Create an ID if there isn't one already
if group . ID == "" {
group . ID , err = uuid . GenerateUUID ( )
if err != nil {
return fmt . Errorf ( "failed to generate group id" )
}
// Set the hash value of the storage bucket key in group
2019-05-07 19:29:51 +00:00
group . BucketKey = i . groupPacker . BucketKey ( group . ID )
2017-10-11 17:21:20 +00:00
}
2018-09-18 03:03:00 +00:00
if group . NamespaceID == "" {
ns , err := namespace . FromContext ( ctx )
if err != nil {
return err
}
group . NamespaceID = ns . ID
}
ns , err := namespace . FromContext ( ctx )
if err != nil {
return err
}
if ns . ID != group . NamespaceID {
2019-06-14 16:53:00 +00:00
return errors . New ( "group does not belong to this namespace" )
2018-09-18 03:03:00 +00:00
}
2017-10-11 17:21:20 +00:00
// Create a name if there isn't one already
if group . Name == "" {
2018-09-18 03:03:00 +00:00
group . Name , err = i . generateName ( ctx , "group" )
2017-10-11 17:21:20 +00:00
if err != nil {
return fmt . Errorf ( "failed to generate group name" )
}
}
// Entity metadata should always be map[string]string
err = validateMetadata ( group . Metadata )
if err != nil {
2018-04-05 15:49:21 +00:00
return errwrap . Wrapf ( "invalid group metadata: {{err}}" , err )
2017-10-11 17:21:20 +00:00
}
// Set the creation and last update times
if group . CreationTime == nil {
group . CreationTime = ptypes . TimestampNow ( )
group . LastUpdateTime = group . CreationTime
} else {
group . LastUpdateTime = ptypes . TimestampNow ( )
}
// Remove duplicate entity IDs and check if all IDs are valid
group . MemberEntityIDs = strutil . RemoveDuplicates ( group . MemberEntityIDs , false )
for _ , entityID := range group . MemberEntityIDs {
2018-06-24 11:45:53 +00:00
entity , err := i . MemDBEntityByID ( entityID , false )
2017-10-11 17:21:20 +00:00
if err != nil {
2018-06-24 11:45:53 +00:00
return errwrap . Wrapf ( fmt . Sprintf ( "failed to validate entity ID %q: {{err}}" , entityID ) , err )
}
if entity == nil {
return fmt . Errorf ( "invalid entity ID %q" , entityID )
2017-10-11 17:21:20 +00:00
}
}
txn := i . db . Txn ( true )
defer txn . Abort ( )
2019-04-05 13:12:39 +00:00
var currentMemberGroupIDs [ ] string
var currentMemberGroups [ ] * identity . Group
// If there are no member group IDs supplied, then it shouldn't be
// processed. If an empty set of member group IDs are supplied, then it
// should be processed. Hence the nil check instead of the length check.
if memberGroupIDs == nil {
goto ALIAS
}
2017-10-11 17:21:20 +00:00
memberGroupIDs = strutil . RemoveDuplicates ( memberGroupIDs , false )
2018-10-04 16:27:29 +00:00
2018-10-04 17:38:41 +00:00
// For those group member IDs that are removed from the list, remove current
// group ID as their respective ParentGroupID.
2018-10-04 16:27:29 +00:00
// Get the current MemberGroups IDs for this group
2019-04-05 13:12:39 +00:00
currentMemberGroups , err = i . MemDBGroupsByParentGroupID ( group . ID , false )
2018-10-04 16:27:29 +00:00
if err != nil {
return err
}
for _ , currentMemberGroup := range currentMemberGroups {
currentMemberGroupIDs = append ( currentMemberGroupIDs , currentMemberGroup . ID )
}
2018-10-04 17:38:41 +00:00
// Update parent group IDs in the removed members
2018-10-04 16:27:29 +00:00
for _ , currentMemberGroupID := range currentMemberGroupIDs {
2018-10-04 17:38:41 +00:00
if strutil . StrListContains ( memberGroupIDs , currentMemberGroupID ) {
continue
}
2018-10-04 16:27:29 +00:00
currentMemberGroup , err := i . MemDBGroupByID ( currentMemberGroupID , true )
if err != nil {
return err
}
if currentMemberGroup == nil {
return fmt . Errorf ( "invalid member group ID %q" , currentMemberGroupID )
}
2018-10-04 17:38:41 +00:00
// Remove group ID from the parent group IDs
currentMemberGroup . ParentGroupIDs = strutil . StrListDelete ( currentMemberGroup . ParentGroupIDs , group . ID )
2018-10-04 16:27:29 +00:00
2019-05-01 17:47:41 +00:00
err = i . UpsertGroupInTxn ( ctx , txn , currentMemberGroup , true )
2018-10-04 16:27:29 +00:00
if err != nil {
return err
}
}
2017-10-11 17:21:20 +00:00
// After the group lock is held, make membership updates to all the
// relevant groups
for _ , memberGroupID := range memberGroupIDs {
2017-11-02 20:05:48 +00:00
memberGroup , err := i . MemDBGroupByID ( memberGroupID , true )
2017-10-11 17:21:20 +00:00
if err != nil {
return err
}
if memberGroup == nil {
return fmt . Errorf ( "invalid member group ID %q" , memberGroupID )
}
// Skip if memberGroupID is already a member of group.ID
if strutil . StrListContains ( memberGroup . ParentGroupIDs , group . ID ) {
continue
}
// Ensure that adding memberGroupID does not lead to cyclic
// relationships
2018-06-24 11:45:53 +00:00
// Detect self loop
if group . ID == memberGroupID {
return fmt . Errorf ( "member group ID %q is same as the ID of the group" , group . ID )
}
groupByID , err := i . MemDBGroupByID ( group . ID , true )
2017-10-11 17:21:20 +00:00
if err != nil {
return err
}
2018-06-24 11:45:53 +00:00
// If group is nil, that means that a group doesn't already exist and its
// okay to add any group as its member group.
if groupByID != nil {
// If adding the memberGroupID to groupID creates a cycle, then groupID must
// be a hop in that loop. Start a DFS traversal from memberGroupID and see if
// it reaches back to groupID. If it does, then it's a loop.
// Created a visited set
visited := make ( map [ string ] bool )
cycleDetected , err := i . detectCycleDFS ( visited , groupByID . ID , memberGroupID )
if err != nil {
return fmt . Errorf ( "failed to perform cyclic relationship detection for member group ID %q" , memberGroupID )
}
if cycleDetected {
return fmt . Errorf ( "cyclic relationship detected for member group ID %q" , memberGroupID )
}
}
2017-10-11 17:21:20 +00:00
memberGroup . ParentGroupIDs = append ( memberGroup . ParentGroupIDs , group . ID )
2018-06-24 11:45:53 +00:00
// This technically is not upsert. It is only update, only the method
// name is upsert here.
2019-05-01 17:47:41 +00:00
err = i . UpsertGroupInTxn ( ctx , txn , memberGroup , true )
2017-10-11 17:21:20 +00:00
if err != nil {
// Ideally we would want to revert the whole operation in case of
// errors while persisting in member groups. But there is no
// storage transaction support yet. When we do have it, this will need
// an update.
return err
2018-06-24 11:45:53 +00:00
}
2017-10-11 17:21:20 +00:00
}
2019-04-05 13:12:39 +00:00
ALIAS :
2018-06-24 11:45:53 +00:00
// Sanitize the group alias
if group . Alias != nil {
group . Alias . CanonicalID = group . ID
2018-09-18 03:03:00 +00:00
err = i . sanitizeAlias ( ctx , group . Alias )
2018-06-24 11:45:53 +00:00
if err != nil {
return err
}
2017-10-11 17:21:20 +00:00
}
2019-06-18 20:42:54 +00:00
// If previousGroup is not nil, we are moving the alias from the previous
// group to the new one. As a result we need to upsert both in the context
// of this same transaction.
if previousGroup != nil {
err = i . UpsertGroupInTxn ( ctx , txn , previousGroup , true )
if err != nil {
return err
}
}
2019-05-01 17:47:41 +00:00
err = i . UpsertGroupInTxn ( ctx , txn , group , true )
2017-10-11 17:21:20 +00:00
if err != nil {
2018-06-24 11:45:53 +00:00
return err
2017-10-11 17:21:20 +00:00
}
2018-06-24 11:45:53 +00:00
txn . Commit ( )
2017-10-11 17:21:20 +00:00
return nil
}
func ( i * IdentityStore ) deleteAliasesInEntityInTxn ( txn * memdb . Txn , entity * identity . Entity , aliases [ ] * identity . Alias ) error {
if entity == nil {
return fmt . Errorf ( "entity is nil" )
}
if txn == nil {
return fmt . Errorf ( "txn is nil" )
}
var remainList [ ] * identity . Alias
var removeList [ ] * identity . Alias
for _ , item := range aliases {
for _ , alias := range entity . Aliases {
if alias . ID == item . ID {
removeList = append ( removeList , alias )
} else {
remainList = append ( remainList , alias )
}
}
}
// Remove identity indices from aliases table for those that needs to
// be removed
for _ , alias := range removeList {
2018-10-18 14:53:12 +00:00
err := i . MemDBDeleteAliasByIDInTxn ( txn , alias . ID , false )
2017-10-11 17:21:20 +00:00
if err != nil {
return err
}
}
// Update the entity with remaining items
entity . Aliases = remainList
return nil
}
// validateMeta validates a set of key/value pairs from the agent config
func validateMetadata ( meta map [ string ] string ) error {
if len ( meta ) > metaMaxKeyPairs {
return fmt . Errorf ( "metadata cannot contain more than %d key/value pairs" , metaMaxKeyPairs )
}
for key , value := range meta {
if err := validateMetaPair ( key , value ) ; err != nil {
2018-04-05 15:49:21 +00:00
return errwrap . Wrapf ( fmt . Sprintf ( "failed to load metadata pair (%q, %q): {{err}}" , key , value ) , err )
2017-10-11 17:21:20 +00:00
}
}
return nil
}
// validateMetaPair checks that the given key/value pair is in a valid format
func validateMetaPair ( key , value string ) error {
if key == "" {
return fmt . Errorf ( "key cannot be blank" )
}
if ! metaKeyFormatRegEx ( key ) {
return fmt . Errorf ( "key contains invalid characters" )
}
if len ( key ) > metaKeyMaxLength {
return fmt . Errorf ( "key is too long (limit: %d characters)" , metaKeyMaxLength )
}
if strings . HasPrefix ( key , metaKeyReservedPrefix ) {
return fmt . Errorf ( "key prefix %q is reserved for internal use" , metaKeyReservedPrefix )
}
if len ( value ) > metaValueMaxLength {
return fmt . Errorf ( "value is too long (limit: %d characters)" , metaValueMaxLength )
}
return nil
}
2018-09-18 03:03:00 +00:00
func ( i * IdentityStore ) MemDBGroupByNameInTxn ( ctx context . Context , txn * memdb . Txn , groupName string , clone bool ) ( * identity . Group , error ) {
2017-10-11 17:21:20 +00:00
if groupName == "" {
return nil , fmt . Errorf ( "missing group name" )
}
if txn == nil {
return nil , fmt . Errorf ( "txn is nil" )
}
2018-09-18 03:03:00 +00:00
ns , err := namespace . FromContext ( ctx )
if err != nil {
return nil , err
}
groupRaw , err := txn . First ( groupsTable , "name" , ns . ID , groupName )
2017-10-11 17:21:20 +00:00
if err != nil {
2018-04-05 15:49:21 +00:00
return nil , errwrap . Wrapf ( "failed to fetch group from memdb using group name: {{err}}" , err )
2017-10-11 17:21:20 +00:00
}
if groupRaw == nil {
return nil , nil
}
group , ok := groupRaw . ( * identity . Group )
if ! ok {
return nil , fmt . Errorf ( "failed to declare the type of fetched group" )
}
if clone {
return group . Clone ( )
}
return group , nil
}
2018-09-18 03:03:00 +00:00
func ( i * IdentityStore ) MemDBGroupByName ( ctx context . Context , groupName string , clone bool ) ( * identity . Group , error ) {
2017-10-11 17:21:20 +00:00
if groupName == "" {
return nil , fmt . Errorf ( "missing group name" )
}
txn := i . db . Txn ( false )
2018-09-18 03:03:00 +00:00
return i . MemDBGroupByNameInTxn ( ctx , txn , groupName , clone )
2017-11-02 20:05:48 +00:00
}
2019-05-01 17:47:41 +00:00
func ( i * IdentityStore ) UpsertGroup ( ctx context . Context , group * identity . Group , persist bool ) error {
2020-04-23 18:31:22 +00:00
defer metrics . MeasureSince ( [ ] string { "identity" , "upsert_group" } , time . Now ( ) )
2017-11-02 20:05:48 +00:00
txn := i . db . Txn ( true )
defer txn . Abort ( )
2019-05-01 17:47:41 +00:00
err := i . UpsertGroupInTxn ( ctx , txn , group , true )
2017-11-02 20:05:48 +00:00
if err != nil {
return err
}
txn . Commit ( )
2018-06-24 11:45:53 +00:00
2017-11-02 20:05:48 +00:00
return nil
2017-10-11 17:21:20 +00:00
}
2019-05-01 17:47:41 +00:00
func ( i * IdentityStore ) UpsertGroupInTxn ( ctx context . Context , txn * memdb . Txn , group * identity . Group , persist bool ) error {
2020-04-23 18:31:22 +00:00
defer metrics . MeasureSince ( [ ] string { "identity" , "upsert_group_txn" } , time . Now ( ) )
2017-10-11 17:21:20 +00:00
var err error
if txn == nil {
return fmt . Errorf ( "txn is nil" )
}
if group == nil {
return fmt . Errorf ( "group is nil" )
}
// Increment the modify index of the group
group . ModifyIndex ++
2018-10-01 17:06:10 +00:00
// Clear the old alias from memdb
groupClone , err := i . MemDBGroupByID ( group . ID , true )
if err != nil {
return err
}
if groupClone != nil && groupClone . Alias != nil {
err = i . MemDBDeleteAliasByIDInTxn ( txn , groupClone . Alias . ID , true )
if err != nil {
return err
}
}
// Add the new alias to memdb
2018-09-06 16:17:44 +00:00
if group . Alias != nil {
err = i . MemDBUpsertAliasInTxn ( txn , group . Alias , true )
if err != nil {
return err
}
}
2018-09-18 03:03:00 +00:00
2017-10-11 17:21:20 +00:00
// Insert or update group in MemDB using the transaction created above
2017-11-02 20:05:48 +00:00
err = i . MemDBUpsertGroupInTxn ( txn , group )
2017-10-11 17:21:20 +00:00
if err != nil {
return err
}
if persist {
groupAsAny , err := ptypes . MarshalAny ( group )
if err != nil {
return err
}
item := & storagepacker . Item {
ID : group . ID ,
Message : groupAsAny ,
}
2018-09-18 03:03:00 +00:00
sent , err := sendGroupUpgrade ( i , group )
2017-10-11 17:21:20 +00:00
if err != nil {
return err
}
2018-09-18 03:03:00 +00:00
if ! sent {
2019-05-01 17:47:41 +00:00
if err := i . groupPacker . PutItem ( ctx , item ) ; err != nil {
2018-09-18 03:03:00 +00:00
return err
}
}
2017-10-11 17:21:20 +00:00
}
return nil
}
2017-11-02 20:05:48 +00:00
func ( i * IdentityStore ) MemDBUpsertGroupInTxn ( txn * memdb . Txn , group * identity . Group ) error {
2017-10-11 17:21:20 +00:00
if txn == nil {
return fmt . Errorf ( "nil txn" )
}
if group == nil {
return fmt . Errorf ( "group is nil" )
}
2018-09-18 03:03:00 +00:00
if group . NamespaceID == "" {
group . NamespaceID = namespace . RootNamespaceID
}
2017-11-02 20:05:48 +00:00
groupRaw , err := txn . First ( groupsTable , "id" , group . ID )
2017-10-11 17:21:20 +00:00
if err != nil {
2018-04-05 15:49:21 +00:00
return errwrap . Wrapf ( "failed to lookup group from memdb using group id: {{err}}" , err )
2017-10-11 17:21:20 +00:00
}
if groupRaw != nil {
2017-11-02 20:05:48 +00:00
err = txn . Delete ( groupsTable , groupRaw )
2017-10-11 17:21:20 +00:00
if err != nil {
2018-04-05 15:49:21 +00:00
return errwrap . Wrapf ( "failed to delete group from memdb: {{err}}" , err )
2017-10-11 17:21:20 +00:00
}
}
2017-11-02 20:05:48 +00:00
if err := txn . Insert ( groupsTable , group ) ; err != nil {
2018-04-05 15:49:21 +00:00
return errwrap . Wrapf ( "failed to update group into memdb: {{err}}" , err )
2017-10-11 17:21:20 +00:00
}
return nil
}
2017-11-02 20:05:48 +00:00
func ( i * IdentityStore ) MemDBDeleteGroupByIDInTxn ( txn * memdb . Txn , groupID string ) error {
2017-10-11 17:21:20 +00:00
if groupID == "" {
return nil
}
if txn == nil {
return fmt . Errorf ( "txn is nil" )
}
2017-11-02 20:05:48 +00:00
group , err := i . MemDBGroupByIDInTxn ( txn , groupID , false )
2017-10-11 17:21:20 +00:00
if err != nil {
return err
}
if group == nil {
return nil
}
err = txn . Delete ( "groups" , group )
if err != nil {
2018-04-05 15:49:21 +00:00
return errwrap . Wrapf ( "failed to delete group from memdb: {{err}}" , err )
2017-10-11 17:21:20 +00:00
}
return nil
}
2017-11-02 20:05:48 +00:00
func ( i * IdentityStore ) MemDBGroupByIDInTxn ( txn * memdb . Txn , groupID string , clone bool ) ( * identity . Group , error ) {
2017-10-11 17:21:20 +00:00
if groupID == "" {
return nil , fmt . Errorf ( "missing group ID" )
}
if txn == nil {
return nil , fmt . Errorf ( "txn is nil" )
}
2017-11-02 20:05:48 +00:00
groupRaw , err := txn . First ( groupsTable , "id" , groupID )
2017-10-11 17:21:20 +00:00
if err != nil {
2018-04-05 15:49:21 +00:00
return nil , errwrap . Wrapf ( "failed to fetch group from memdb using group ID: {{err}}" , err )
2017-10-11 17:21:20 +00:00
}
if groupRaw == nil {
return nil , nil
}
group , ok := groupRaw . ( * identity . Group )
if ! ok {
return nil , fmt . Errorf ( "failed to declare the type of fetched group" )
}
if clone {
return group . Clone ( )
}
return group , nil
}
2017-11-02 20:05:48 +00:00
func ( i * IdentityStore ) MemDBGroupByID ( groupID string , clone bool ) ( * identity . Group , error ) {
2017-10-11 17:21:20 +00:00
if groupID == "" {
return nil , fmt . Errorf ( "missing group ID" )
}
txn := i . db . Txn ( false )
2017-11-02 20:05:48 +00:00
return i . MemDBGroupByIDInTxn ( txn , groupID , clone )
2017-10-11 17:21:20 +00:00
}
2017-11-02 20:05:48 +00:00
func ( i * IdentityStore ) MemDBGroupsByParentGroupIDInTxn ( txn * memdb . Txn , memberGroupID string , clone bool ) ( [ ] * identity . Group , error ) {
2017-10-11 17:21:20 +00:00
if memberGroupID == "" {
return nil , fmt . Errorf ( "missing member group ID" )
}
2017-11-02 20:05:48 +00:00
groupsIter , err := txn . Get ( groupsTable , "parent_group_ids" , memberGroupID )
2017-10-11 17:21:20 +00:00
if err != nil {
2018-04-05 15:49:21 +00:00
return nil , errwrap . Wrapf ( "failed to lookup groups using member group ID: {{err}}" , err )
2017-10-11 17:21:20 +00:00
}
var groups [ ] * identity . Group
for group := groupsIter . Next ( ) ; group != nil ; group = groupsIter . Next ( ) {
entry := group . ( * identity . Group )
if clone {
entry , err = entry . Clone ( )
if err != nil {
return nil , err
}
}
groups = append ( groups , entry )
}
return groups , nil
}
2017-11-02 20:05:48 +00:00
func ( i * IdentityStore ) MemDBGroupsByParentGroupID ( memberGroupID string , clone bool ) ( [ ] * identity . Group , error ) {
2017-10-11 17:21:20 +00:00
if memberGroupID == "" {
return nil , fmt . Errorf ( "missing member group ID" )
}
txn := i . db . Txn ( false )
2017-11-02 20:05:48 +00:00
return i . MemDBGroupsByParentGroupIDInTxn ( txn , memberGroupID , clone )
2017-10-11 17:21:20 +00:00
}
2017-11-02 20:05:48 +00:00
func ( i * IdentityStore ) MemDBGroupsByMemberEntityID ( entityID string , clone bool , externalOnly bool ) ( [ ] * identity . Group , error ) {
txn := i . db . Txn ( false )
defer txn . Abort ( )
return i . MemDBGroupsByMemberEntityIDInTxn ( txn , entityID , clone , externalOnly )
}
func ( i * IdentityStore ) MemDBGroupsByMemberEntityIDInTxn ( txn * memdb . Txn , entityID string , clone bool , externalOnly bool ) ( [ ] * identity . Group , error ) {
2017-10-11 17:21:20 +00:00
if entityID == "" {
return nil , fmt . Errorf ( "missing entity ID" )
}
2017-11-02 20:05:48 +00:00
groupsIter , err := txn . Get ( groupsTable , "member_entity_ids" , entityID )
2017-10-11 17:21:20 +00:00
if err != nil {
2018-04-05 15:49:21 +00:00
return nil , errwrap . Wrapf ( "failed to lookup groups using entity ID: {{err}}" , err )
2017-10-11 17:21:20 +00:00
}
var groups [ ] * identity . Group
for group := groupsIter . Next ( ) ; group != nil ; group = groupsIter . Next ( ) {
entry := group . ( * identity . Group )
2017-11-02 20:05:48 +00:00
if externalOnly && entry . Type == groupTypeInternal {
continue
}
2017-10-11 17:21:20 +00:00
if clone {
entry , err = entry . Clone ( )
if err != nil {
return nil , err
}
}
groups = append ( groups , entry )
}
return groups , nil
}
2018-09-18 03:03:00 +00:00
func ( i * IdentityStore ) groupPoliciesByEntityID ( entityID string ) ( map [ string ] [ ] string , error ) {
2017-10-11 17:21:20 +00:00
if entityID == "" {
return nil , fmt . Errorf ( "empty entity ID" )
}
2017-11-02 20:05:48 +00:00
groups , err := i . MemDBGroupsByMemberEntityID ( entityID , false , false )
2017-10-11 17:21:20 +00:00
if err != nil {
return nil , err
}
visited := make ( map [ string ] bool )
2018-09-18 03:03:00 +00:00
policies := make ( map [ string ] [ ] string )
2017-10-11 17:21:20 +00:00
for _ , group := range groups {
2018-09-18 03:03:00 +00:00
err := i . collectPoliciesReverseDFS ( group , visited , policies )
2017-10-11 17:21:20 +00:00
if err != nil {
return nil , err
}
}
2018-09-18 03:03:00 +00:00
return policies , nil
2017-10-11 17:21:20 +00:00
}
2017-11-06 18:01:48 +00:00
func ( i * IdentityStore ) groupsByEntityID ( entityID string ) ( [ ] * identity . Group , [ ] * identity . Group , error ) {
2017-10-11 17:21:20 +00:00
if entityID == "" {
2017-11-06 18:01:48 +00:00
return nil , nil , fmt . Errorf ( "empty entity ID" )
2017-10-11 17:21:20 +00:00
}
2017-11-06 18:01:48 +00:00
groups , err := i . MemDBGroupsByMemberEntityID ( entityID , true , false )
2017-10-11 17:21:20 +00:00
if err != nil {
2017-11-06 18:01:48 +00:00
return nil , nil , err
2017-10-11 17:21:20 +00:00
}
visited := make ( map [ string ] bool )
var tGroups [ ] * identity . Group
for _ , group := range groups {
2017-11-03 15:26:22 +00:00
gGroups , err := i . collectGroupsReverseDFS ( group , visited , nil )
2017-10-11 17:21:20 +00:00
if err != nil {
2017-11-06 18:01:48 +00:00
return nil , nil , err
2017-10-11 17:21:20 +00:00
}
2017-11-03 15:26:22 +00:00
tGroups = append ( tGroups , gGroups ... )
2017-10-11 17:21:20 +00:00
}
// Remove duplicates
groupMap := make ( map [ string ] * identity . Group )
for _ , group := range tGroups {
groupMap [ group . ID ] = group
}
2017-11-03 15:26:22 +00:00
tGroups = make ( [ ] * identity . Group , 0 , len ( groupMap ) )
2017-10-11 17:21:20 +00:00
for _ , group := range groupMap {
tGroups = append ( tGroups , group )
}
2017-11-06 18:01:48 +00:00
diff := diffGroups ( groups , tGroups )
// For sanity
// There should not be any group that gets deleted
if len ( diff . Deleted ) != 0 {
return nil , nil , fmt . Errorf ( "failed to diff group memberships" )
}
return diff . Unmodified , diff . New , nil
2017-10-11 17:21:20 +00:00
}
func ( i * IdentityStore ) collectGroupsReverseDFS ( group * identity . Group , visited map [ string ] bool , groups [ ] * identity . Group ) ( [ ] * identity . Group , error ) {
if group == nil {
return nil , fmt . Errorf ( "nil group" )
}
// If traversal for a groupID is performed before, skip it
if visited [ group . ID ] {
return groups , nil
}
visited [ group . ID ] = true
groups = append ( groups , group )
// Traverse all the parent groups
for _ , parentGroupID := range group . ParentGroupIDs {
2017-11-02 20:05:48 +00:00
parentGroup , err := i . MemDBGroupByID ( parentGroupID , false )
2017-10-11 17:21:20 +00:00
if err != nil {
return nil , err
}
2018-06-27 14:16:12 +00:00
if parentGroup == nil {
continue
}
2018-10-29 14:38:34 +00:00
groups , err = i . collectGroupsReverseDFS ( parentGroup , visited , groups )
2017-10-11 17:21:20 +00:00
if err != nil {
return nil , fmt . Errorf ( "failed to collect group at parent group ID %q" , parentGroup . ID )
}
}
return groups , nil
}
2018-09-18 03:03:00 +00:00
func ( i * IdentityStore ) collectPoliciesReverseDFS ( group * identity . Group , visited map [ string ] bool , policies map [ string ] [ ] string ) error {
2017-10-11 17:21:20 +00:00
if group == nil {
2018-09-18 03:03:00 +00:00
return fmt . Errorf ( "nil group" )
2017-10-11 17:21:20 +00:00
}
// If traversal for a groupID is performed before, skip it
if visited [ group . ID ] {
2018-09-18 03:03:00 +00:00
return nil
2017-10-11 17:21:20 +00:00
}
visited [ group . ID ] = true
2018-09-18 03:03:00 +00:00
policies [ group . NamespaceID ] = append ( policies [ group . NamespaceID ] , group . Policies ... )
2017-10-11 17:21:20 +00:00
// Traverse all the parent groups
for _ , parentGroupID := range group . ParentGroupIDs {
2017-11-02 20:05:48 +00:00
parentGroup , err := i . MemDBGroupByID ( parentGroupID , false )
2017-10-11 17:21:20 +00:00
if err != nil {
2018-09-18 03:03:00 +00:00
return err
2017-10-11 17:21:20 +00:00
}
2018-06-27 14:16:12 +00:00
if parentGroup == nil {
continue
}
2018-09-18 03:03:00 +00:00
err = i . collectPoliciesReverseDFS ( parentGroup , visited , policies )
2017-10-11 17:21:20 +00:00
if err != nil {
2018-09-18 03:03:00 +00:00
return fmt . Errorf ( "failed to collect policies at parent group ID %q" , parentGroup . ID )
2017-10-11 17:21:20 +00:00
}
}
2018-09-18 03:03:00 +00:00
return nil
2017-10-11 17:21:20 +00:00
}
func ( i * IdentityStore ) detectCycleDFS ( visited map [ string ] bool , startingGroupID , groupID string ) ( bool , error ) {
// If the traversal reaches the startingGroupID, a loop is detected
if startingGroupID == groupID {
return true , nil
}
// If traversal for a groupID is performed before, skip it
if visited [ groupID ] {
return false , nil
}
visited [ groupID ] = true
2017-11-02 20:05:48 +00:00
group , err := i . MemDBGroupByID ( groupID , true )
2017-10-11 17:21:20 +00:00
if err != nil {
return false , err
}
if group == nil {
return false , nil
}
// Fetch all groups in which groupID is present as a ParentGroupID. In
// other words, find all the subgroups of groupID.
2017-11-02 20:05:48 +00:00
memberGroups , err := i . MemDBGroupsByParentGroupID ( groupID , false )
2017-10-11 17:21:20 +00:00
if err != nil {
return false , err
}
// DFS traverse the member groups
for _ , memberGroup := range memberGroups {
cycleDetected , err := i . detectCycleDFS ( visited , startingGroupID , memberGroup . ID )
if err != nil {
return false , fmt . Errorf ( "failed to perform cycle detection at member group ID %q" , memberGroup . ID )
}
if cycleDetected {
return true , fmt . Errorf ( "cycle detected at member group ID %q" , memberGroup . ID )
}
}
return false , nil
}
func ( i * IdentityStore ) memberGroupIDsByID ( groupID string ) ( [ ] string , error ) {
var memberGroupIDs [ ] string
2017-11-02 20:05:48 +00:00
memberGroups , err := i . MemDBGroupsByParentGroupID ( groupID , false )
2017-10-11 17:21:20 +00:00
if err != nil {
return nil , err
}
for _ , memberGroup := range memberGroups {
memberGroupIDs = append ( memberGroupIDs , memberGroup . ID )
}
return memberGroupIDs , nil
}
2018-09-18 03:03:00 +00:00
func ( i * IdentityStore ) generateName ( ctx context . Context , entryType string ) ( string , error ) {
2017-10-11 17:21:20 +00:00
var name string
OUTER :
for {
randBytes , err := uuid . GenerateRandomBytes ( 4 )
if err != nil {
return "" , err
}
name = fmt . Sprintf ( "%s_%s" , entryType , fmt . Sprintf ( "%08x" , randBytes [ 0 : 4 ] ) )
switch entryType {
case "entity" :
2018-09-18 03:03:00 +00:00
entity , err := i . MemDBEntityByName ( ctx , name , false )
2017-10-11 17:21:20 +00:00
if err != nil {
return "" , err
}
if entity == nil {
break OUTER
}
case "group" :
2018-09-18 03:03:00 +00:00
group , err := i . MemDBGroupByName ( ctx , name , false )
2017-10-11 17:21:20 +00:00
if err != nil {
return "" , err
}
if group == nil {
break OUTER
}
default :
return "" , fmt . Errorf ( "unrecognized type %q" , entryType )
}
}
return name , nil
}
2019-05-07 19:29:51 +00:00
func ( i * IdentityStore ) MemDBGroupsByBucketKeyInTxn ( txn * memdb . Txn , bucketKey string ) ( [ ] * identity . Group , error ) {
2017-10-11 17:21:20 +00:00
if txn == nil {
return nil , fmt . Errorf ( "nil txn" )
}
2019-05-07 19:29:51 +00:00
if bucketKey == "" {
return nil , fmt . Errorf ( "empty bucket key" )
2017-10-11 17:21:20 +00:00
}
2019-05-07 19:29:51 +00:00
groupsIter , err := txn . Get ( groupsTable , "bucket_key" , bucketKey )
2017-10-11 17:21:20 +00:00
if err != nil {
2018-04-05 15:49:21 +00:00
return nil , errwrap . Wrapf ( "failed to lookup groups using bucket entry key hash: {{err}}" , err )
2017-10-11 17:21:20 +00:00
}
var groups [ ] * identity . Group
for group := groupsIter . Next ( ) ; group != nil ; group = groupsIter . Next ( ) {
groups = append ( groups , group . ( * identity . Group ) )
}
return groups , nil
}
2017-11-02 20:05:48 +00:00
func ( i * IdentityStore ) MemDBGroupByAliasIDInTxn ( txn * memdb . Txn , aliasID string , clone bool ) ( * identity . Group , error ) {
if aliasID == "" {
return nil , fmt . Errorf ( "missing alias ID" )
}
if txn == nil {
return nil , fmt . Errorf ( "txn is nil" )
}
alias , err := i . MemDBAliasByIDInTxn ( txn , aliasID , false , true )
if err != nil {
return nil , err
}
if alias == nil {
return nil , nil
}
return i . MemDBGroupByIDInTxn ( txn , alias . CanonicalID , clone )
}
func ( i * IdentityStore ) MemDBGroupByAliasID ( aliasID string , clone bool ) ( * identity . Group , error ) {
if aliasID == "" {
return nil , fmt . Errorf ( "missing alias ID" )
}
txn := i . db . Txn ( false )
return i . MemDBGroupByAliasIDInTxn ( txn , aliasID , clone )
}
2019-05-01 17:47:41 +00:00
func ( i * IdentityStore ) refreshExternalGroupMembershipsByEntityID ( ctx context . Context , entityID string , groupAliases [ ] * logical . Alias ) ( [ ] * logical . Alias , error ) {
2020-04-23 18:31:22 +00:00
defer metrics . MeasureSince ( [ ] string { "identity" , "refresh_external_groups" } , time . Now ( ) )
2017-11-02 20:05:48 +00:00
if entityID == "" {
2018-08-23 01:53:04 +00:00
return nil , fmt . Errorf ( "empty entity ID" )
2017-11-02 20:05:48 +00:00
}
2020-04-23 18:31:22 +00:00
refreshFunc := func ( dryRun bool ) ( bool , [ ] * logical . Alias , error ) {
2017-11-02 20:05:48 +00:00
2020-04-23 18:31:22 +00:00
if ! dryRun {
i . groupLock . Lock ( )
defer i . groupLock . Unlock ( )
}
2017-11-02 20:05:48 +00:00
2020-04-23 18:31:22 +00:00
txn := i . db . Txn ( ! dryRun )
defer txn . Abort ( )
2018-04-17 16:01:43 +00:00
2020-04-23 18:31:22 +00:00
oldGroups , err := i . MemDBGroupsByMemberEntityIDInTxn ( txn , entityID , true , true )
2017-11-02 20:05:48 +00:00
if err != nil {
2020-04-23 18:31:22 +00:00
return false , nil , err
2017-11-02 20:05:48 +00:00
}
2020-04-23 18:31:22 +00:00
mountAccessor := ""
if len ( groupAliases ) != 0 {
mountAccessor = groupAliases [ 0 ] . MountAccessor
2017-11-02 20:05:48 +00:00
}
2020-04-23 18:31:22 +00:00
var newGroups [ ] * identity . Group
var validAliases [ ] * logical . Alias
for _ , alias := range groupAliases {
aliasByFactors , err := i . MemDBAliasByFactorsInTxn ( txn , alias . MountAccessor , alias . Name , true , true )
if err != nil {
return false , nil , err
}
if aliasByFactors == nil {
continue
}
mappingGroup , err := i . MemDBGroupByAliasIDInTxn ( txn , aliasByFactors . ID , true )
if err != nil {
return false , nil , err
}
if mappingGroup == nil {
return false , nil , fmt . Errorf ( "group unavailable for a valid alias ID %q" , aliasByFactors . ID )
}
newGroups = append ( newGroups , mappingGroup )
validAliases = append ( validAliases , alias )
2017-11-02 20:05:48 +00:00
}
2018-09-18 03:03:00 +00:00
2020-04-23 18:31:22 +00:00
diff := diffGroups ( oldGroups , newGroups )
2017-11-02 20:05:48 +00:00
2020-04-23 18:31:22 +00:00
// Add the entity ID to all the new groups
for _ , group := range diff . New {
if group . Type != groupTypeExternal {
continue
}
2017-11-02 20:05:48 +00:00
2020-04-23 18:31:22 +00:00
// We need to update a group, if we are in a dry run we should
// report back that a change needs to take place.
if dryRun {
return true , nil , nil
}
2017-11-02 20:05:48 +00:00
2020-04-23 18:31:22 +00:00
i . logger . Debug ( "adding member entity ID to external group" , "member_entity_id" , entityID , "group_id" , group . ID )
2017-11-02 20:05:48 +00:00
2020-04-23 18:31:22 +00:00
group . MemberEntityIDs = append ( group . MemberEntityIDs , entityID )
2017-11-02 20:05:48 +00:00
2020-04-23 18:31:22 +00:00
err = i . UpsertGroupInTxn ( ctx , txn , group , true )
if err != nil {
return false , nil , err
}
2017-11-02 20:05:48 +00:00
}
2020-04-23 18:31:22 +00:00
// Remove the entity ID from all the deleted groups
for _ , group := range diff . Deleted {
if group . Type != groupTypeExternal {
continue
}
2017-11-02 20:05:48 +00:00
2020-04-23 18:31:22 +00:00
// If the external group is from a different mount, don't remove the
// entity ID from it.
if mountAccessor != "" && group . Alias != nil && group . Alias . MountAccessor != mountAccessor {
continue
}
2018-04-17 16:01:43 +00:00
2020-04-23 18:31:22 +00:00
// We need to update a group, if we are in a dry run we should
// report back that a change needs to take place.
if dryRun {
return true , nil , nil
}
2017-11-02 20:05:48 +00:00
2020-04-23 18:31:22 +00:00
i . logger . Debug ( "removing member entity ID from external group" , "member_entity_id" , entityID , "group_id" , group . ID )
2017-11-02 20:05:48 +00:00
2020-04-23 18:31:22 +00:00
group . MemberEntityIDs = strutil . StrListDelete ( group . MemberEntityIDs , entityID )
err = i . UpsertGroupInTxn ( ctx , txn , group , true )
if err != nil {
return false , nil , err
}
2017-11-02 20:05:48 +00:00
}
2020-04-23 18:31:22 +00:00
txn . Commit ( )
return false , validAliases , nil
2017-11-02 20:05:48 +00:00
}
2020-04-23 18:31:22 +00:00
// dryRun
needsUpdate , validAliases , err := refreshFunc ( true )
if err != nil {
return nil , err
}
if needsUpdate || len ( groupAliases ) > 0 {
i . logger . Debug ( "refreshing external group memberships" , "entity_id" , entityID , "group_aliases" , groupAliases )
}
if ! needsUpdate {
return validAliases , nil
}
// Run the update
_ , validAliases , err = refreshFunc ( false )
if err != nil {
return nil , err
}
2017-11-02 20:05:48 +00:00
2018-08-23 01:53:04 +00:00
return validAliases , nil
2017-11-02 20:05:48 +00:00
}
// diffGroups is used to diff two sets of groups
func diffGroups ( old , new [ ] * identity . Group ) * groupDiff {
diff := & groupDiff { }
existing := make ( map [ string ] * identity . Group )
for _ , group := range old {
existing [ group . ID ] = group
}
for _ , group := range new {
// Check if the entry in new is present in the old
_ , ok := existing [ group . ID ]
// If its not present, then its a new entry
if ! ok {
diff . New = append ( diff . New , group )
continue
}
// If its present, it means that its unmodified
diff . Unmodified = append ( diff . Unmodified , group )
// By deleting the unmodified from the old set, we could determine the
// ones that are stale by looking at the remaining ones.
delete ( existing , group . ID )
}
// Any remaining entries must have been deleted
for _ , me := range existing {
diff . Deleted = append ( diff . Deleted , me )
}
return diff
}
2018-09-18 03:03:00 +00:00
func ( i * IdentityStore ) handleAliasListCommon ( ctx context . Context , groupAlias bool ) ( * logical . Response , error ) {
ns , err := namespace . FromContext ( ctx )
if err != nil {
return nil , err
}
tableName := entityAliasesTable
if groupAlias {
tableName = groupAliasesTable
}
ws := memdb . NewWatchSet ( )
txn := i . db . Txn ( false )
iter , err := txn . Get ( tableName , "namespace_id" , ns . ID )
if err != nil {
return nil , errwrap . Wrapf ( "failed to fetch iterator for aliases in memdb: {{err}}" , err )
}
ws . Add ( iter . WatchCh ( ) )
var aliasIDs [ ] string
aliasInfo := map [ string ] interface { } { }
type mountInfo struct {
MountType string
MountPath string
}
mountAccessorMap := map [ string ] mountInfo { }
for {
raw := iter . Next ( )
if raw == nil {
break
}
alias := raw . ( * identity . Alias )
aliasIDs = append ( aliasIDs , alias . ID )
aliasInfoEntry := map [ string ] interface { } {
"name" : alias . Name ,
"canonical_id" : alias . CanonicalID ,
"mount_accessor" : alias . MountAccessor ,
}
mi , ok := mountAccessorMap [ alias . MountAccessor ]
if ok {
aliasInfoEntry [ "mount_type" ] = mi . MountType
aliasInfoEntry [ "mount_path" ] = mi . MountPath
} else {
mi = mountInfo { }
if mountValidationResp := i . core . router . validateMountByAccessor ( alias . MountAccessor ) ; mountValidationResp != nil {
mi . MountType = mountValidationResp . MountType
mi . MountPath = mountValidationResp . MountPath
aliasInfoEntry [ "mount_type" ] = mi . MountType
aliasInfoEntry [ "mount_path" ] = mi . MountPath
}
mountAccessorMap [ alias . MountAccessor ] = mi
}
aliasInfo [ alias . ID ] = aliasInfoEntry
}
return logical . ListResponseWithInfo ( aliasIDs , aliasInfo ) , nil
}
2019-10-08 17:58:19 +00:00
func ( i * IdentityStore ) countEntities ( ) ( int , error ) {
txn := i . db . Txn ( false )
iter , err := txn . Get ( entitiesTable , "id" )
if err != nil {
return - 1 , err
}
count := 0
val := iter . Next ( )
for val != nil {
count ++
val = iter . Next ( )
}
return count , nil
}
2020-06-24 00:45:59 +00:00
// Sum up the number of entities belonging to each namespace (keyed by ID)
func ( i * IdentityStore ) countEntitiesByNamespace ( ctx context . Context ) ( map [ string ] int , error ) {
txn := i . db . Txn ( false )
iter , err := txn . Get ( entitiesTable , "id" )
if err != nil {
return nil , err
}
byNamespace := make ( map [ string ] int )
val := iter . Next ( )
for val != nil {
// Check if runtime exceeded.
select {
case <- ctx . Done ( ) :
return byNamespace , errors . New ( "context cancelled" )
default :
break
}
// Count in the namespace attached to the entity.
entity := val . ( * identity . Entity )
byNamespace [ entity . NamespaceID ] = byNamespace [ entity . NamespaceID ] + 1
val = iter . Next ( )
}
return byNamespace , nil
}
// Sum up the number of entities belonging to each mount point (keyed by accessor)
func ( i * IdentityStore ) countEntitiesByMountAccessor ( ctx context . Context ) ( map [ string ] int , error ) {
txn := i . db . Txn ( false )
iter , err := txn . Get ( entitiesTable , "id" )
if err != nil {
return nil , err
}
byMountAccessor := make ( map [ string ] int )
val := iter . Next ( )
for val != nil {
// Check if runtime exceeded.
select {
case <- ctx . Done ( ) :
return byMountAccessor , errors . New ( "context cancelled" )
default :
break
}
// Count each alias separately; will translate to mount point and type
// in the caller.
entity := val . ( * identity . Entity )
for _ , alias := range entity . Aliases {
byMountAccessor [ alias . MountAccessor ] = byMountAccessor [ alias . MountAccessor ] + 1
}
val = iter . Next ( )
}
return byMountAccessor , nil
}