mirror of
https://github.com/openziti/ziti.git
synced 2026-10-07 13:20:55 +00:00
607 lines
22 KiB
Go
607 lines
22 KiB
Go
/*
|
|
Copyright NetFoundry, Inc.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
https://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package persistence
|
|
|
|
import (
|
|
"github.com/michaelquigley/pfxlog"
|
|
"github.com/openziti/edge/eid"
|
|
"github.com/openziti/fabric/controller/db"
|
|
"github.com/openziti/foundation/storage/ast"
|
|
"github.com/openziti/foundation/storage/boltz"
|
|
"github.com/openziti/foundation/util/errorz"
|
|
"github.com/openziti/sdk-golang/ziti"
|
|
"github.com/pkg/errors"
|
|
"go.etcd.io/bbolt"
|
|
)
|
|
|
|
const (
|
|
FieldIdentityType = "type"
|
|
FieldIdentityIsDefaultAdmin = "isDefaultAdmin"
|
|
FieldIdentityIsAdmin = "isAdmin"
|
|
FieldIdentityEnrollments = "enrollments"
|
|
FieldIdentityAuthenticators = "authenticators"
|
|
FieldIdentityServiceConfigs = "serviceConfigs"
|
|
|
|
FieldIdentityEnvInfoArch = "envInfoArch"
|
|
FieldIdentityEnvInfoOs = "envInfoOs"
|
|
FieldIdentityEnvInfoOsRelease = "envInfoRelease"
|
|
FieldIdentityEnvInfoOsVersion = "envInfoVersion"
|
|
FieldIdentitySdkInfoBranch = "sdkInfoBranch"
|
|
FieldIdentitySdkInfoRevision = "sdkInfoRevision"
|
|
FieldIdentitySdkInfoType = "sdkInfoType"
|
|
FieldIdentitySdkInfoVersion = "sdkInfoVersion"
|
|
FieldIdentitySdkInfoAppId = "sdkInfoAppId"
|
|
FieldIdentitySdkInfoAppVersion = "sdkInfoAppVersion"
|
|
|
|
FieldIdentityBindServices = "bindServices"
|
|
FieldIdentityDialServices = "dialServices"
|
|
FieldIdentityDefaultHostingPrecedence = "hostingPrecedence"
|
|
FieldIdentityDefaultHostingCost = "hostingCost"
|
|
FieldIdentityServiceHostingPrecedences = "serviceHostingPrecedences"
|
|
FieldIdentityServiceHostingCosts = "serviceHostingCosts"
|
|
FieldIdentityAppData = "appData"
|
|
)
|
|
|
|
func newIdentity(name string, identityTypeId string, roleAttributes ...string) *Identity {
|
|
return &Identity{
|
|
BaseExtEntity: boltz.BaseExtEntity{Id: eid.New()},
|
|
Name: name,
|
|
IdentityTypeId: identityTypeId,
|
|
RoleAttributes: roleAttributes,
|
|
}
|
|
}
|
|
|
|
type EnvInfo struct {
|
|
Arch string
|
|
Os string
|
|
OsRelease string
|
|
OsVersion string
|
|
}
|
|
|
|
type SdkInfo struct {
|
|
Branch string
|
|
Revision string
|
|
Type string
|
|
Version string
|
|
AppId string
|
|
AppVersion string
|
|
}
|
|
|
|
type Identity struct {
|
|
boltz.BaseExtEntity
|
|
Name string
|
|
IdentityTypeId string
|
|
IsDefaultAdmin bool
|
|
IsAdmin bool
|
|
Enrollments []string
|
|
Authenticators []string
|
|
RoleAttributes []string
|
|
SdkInfo *SdkInfo
|
|
EnvInfo *EnvInfo
|
|
DefaultHostingPrecedence ziti.Precedence
|
|
DefaultHostingCost uint16
|
|
ServiceHostingPrecedences map[string]ziti.Precedence
|
|
ServiceHostingCosts map[string]uint16
|
|
AppData map[string]interface{}
|
|
}
|
|
|
|
type ServiceConfig struct {
|
|
ServiceId string
|
|
ConfigId string
|
|
}
|
|
|
|
var identityFieldMappings = map[string]string{FieldIdentityType: "identityTypeId"}
|
|
|
|
func (entity *Identity) LoadValues(_ boltz.CrudStore, bucket *boltz.TypedBucket) {
|
|
entity.LoadBaseValues(bucket)
|
|
entity.Name = bucket.GetStringOrError(FieldName)
|
|
entity.IdentityTypeId = bucket.GetStringWithDefault(FieldIdentityType, "")
|
|
entity.IsDefaultAdmin = bucket.GetBoolWithDefault(FieldIdentityIsDefaultAdmin, false)
|
|
entity.IsAdmin = bucket.GetBoolWithDefault(FieldIdentityIsAdmin, false)
|
|
entity.Authenticators = bucket.GetStringList(FieldIdentityAuthenticators)
|
|
entity.Enrollments = bucket.GetStringList(FieldIdentityEnrollments)
|
|
entity.RoleAttributes = bucket.GetStringList(FieldRoleAttributes)
|
|
entity.DefaultHostingPrecedence = ziti.Precedence(bucket.GetInt32WithDefault(FieldIdentityDefaultHostingPrecedence, 0))
|
|
entity.DefaultHostingCost = uint16(bucket.GetInt32WithDefault(FieldIdentityDefaultHostingCost, 0))
|
|
entity.AppData = bucket.GetMap(FieldIdentityAppData)
|
|
|
|
entity.SdkInfo = &SdkInfo{
|
|
Branch: bucket.GetStringWithDefault(FieldIdentitySdkInfoBranch, ""),
|
|
Revision: bucket.GetStringWithDefault(FieldIdentitySdkInfoRevision, ""),
|
|
Type: bucket.GetStringWithDefault(FieldIdentitySdkInfoType, ""),
|
|
Version: bucket.GetStringWithDefault(FieldIdentitySdkInfoVersion, ""),
|
|
AppId: bucket.GetStringWithDefault(FieldIdentitySdkInfoAppId, ""),
|
|
AppVersion: bucket.GetStringWithDefault(FieldIdentitySdkInfoAppVersion, ""),
|
|
}
|
|
|
|
entity.EnvInfo = &EnvInfo{
|
|
Arch: bucket.GetStringWithDefault(FieldIdentityEnvInfoArch, ""),
|
|
Os: bucket.GetStringWithDefault(FieldIdentityEnvInfoOs, ""),
|
|
OsRelease: bucket.GetStringWithDefault(FieldIdentityEnvInfoOsRelease, ""),
|
|
OsVersion: bucket.GetStringWithDefault(FieldIdentityEnvInfoOsVersion, ""),
|
|
}
|
|
|
|
entity.ServiceHostingPrecedences = map[string]ziti.Precedence{}
|
|
for k, v := range bucket.GetMap(FieldIdentityServiceHostingPrecedences) {
|
|
entity.ServiceHostingPrecedences[k] = ziti.Precedence(v.(int32))
|
|
}
|
|
|
|
entity.ServiceHostingCosts = map[string]uint16{}
|
|
for k, v := range bucket.GetMap(FieldIdentityServiceHostingCosts) {
|
|
entity.ServiceHostingCosts[k] = uint16(v.(int32))
|
|
}
|
|
}
|
|
|
|
func (entity *Identity) SetValues(ctx *boltz.PersistContext) {
|
|
ctx.WithFieldOverrides(identityFieldMappings)
|
|
|
|
entity.SetBaseValues(ctx)
|
|
|
|
store := ctx.Store.(*identityStoreImpl)
|
|
ctx.SetString(FieldName, entity.Name)
|
|
ctx.SetBool(FieldIdentityIsDefaultAdmin, entity.IsDefaultAdmin)
|
|
ctx.SetBool(FieldIdentityIsAdmin, entity.IsAdmin)
|
|
if oldValue, changed := ctx.GetAndSetString(FieldIdentityType, entity.IdentityTypeId); changed {
|
|
if oldValue != nil && *oldValue == RouterIdentityType {
|
|
ctx.Bucket.SetError(errors.New("cannot change type of router identity"))
|
|
}
|
|
}
|
|
ctx.SetStringList(FieldRoleAttributes, entity.RoleAttributes)
|
|
ctx.SetInt32(FieldIdentityDefaultHostingPrecedence, int32(entity.DefaultHostingPrecedence))
|
|
ctx.SetInt32(FieldIdentityDefaultHostingCost, int32(entity.DefaultHostingCost))
|
|
ctx.Bucket.PutMap(FieldIdentityAppData, entity.AppData, ctx.FieldChecker, false)
|
|
|
|
if entity.EnvInfo != nil {
|
|
ctx.SetString(FieldIdentityEnvInfoArch, entity.EnvInfo.Arch)
|
|
ctx.SetString(FieldIdentityEnvInfoOs, entity.EnvInfo.Os)
|
|
ctx.SetString(FieldIdentityEnvInfoOsRelease, entity.EnvInfo.OsRelease)
|
|
ctx.SetString(FieldIdentityEnvInfoOsVersion, entity.EnvInfo.OsVersion)
|
|
}
|
|
|
|
if entity.SdkInfo != nil {
|
|
ctx.SetString(FieldIdentitySdkInfoBranch, entity.SdkInfo.Branch)
|
|
ctx.SetString(FieldIdentitySdkInfoRevision, entity.SdkInfo.Revision)
|
|
ctx.SetString(FieldIdentitySdkInfoType, entity.SdkInfo.Type)
|
|
ctx.SetString(FieldIdentitySdkInfoVersion, entity.SdkInfo.Version)
|
|
ctx.SetString(FieldIdentitySdkInfoAppId, entity.SdkInfo.AppId)
|
|
ctx.SetString(FieldIdentitySdkInfoAppVersion, entity.SdkInfo.AppVersion)
|
|
}
|
|
|
|
serviceStore := ctx.Store.(*identityStoreImpl).stores.Service
|
|
|
|
if ctx.ProceedWithSet(FieldIdentityServiceHostingPrecedences) {
|
|
mapBucket, err := ctx.Bucket.EmptyBucket(FieldIdentityServiceHostingPrecedences)
|
|
if !ctx.Bucket.SetError(err) {
|
|
for k, v := range entity.ServiceHostingPrecedences {
|
|
if !serviceStore.IsEntityPresent(ctx.Tx(), k) {
|
|
ctx.Bucket.SetError(boltz.NewNotFoundError(serviceStore.GetEntityType(), "id", k))
|
|
return
|
|
}
|
|
mapBucket.SetInt32(k, int32(v), nil)
|
|
}
|
|
ctx.Bucket.SetError(mapBucket.Err)
|
|
}
|
|
}
|
|
|
|
if ctx.ProceedWithSet(FieldIdentityServiceHostingCosts) {
|
|
mapBucket, err := ctx.Bucket.EmptyBucket(FieldIdentityServiceHostingCosts)
|
|
if !ctx.Bucket.SetError(err) {
|
|
for k, v := range entity.ServiceHostingCosts {
|
|
if !serviceStore.IsEntityPresent(ctx.Tx(), k) {
|
|
ctx.Bucket.SetError(boltz.NewNotFoundError(serviceStore.GetEntityType(), "id", k))
|
|
return
|
|
}
|
|
|
|
mapBucket.SetInt32(k, int32(v), nil)
|
|
}
|
|
ctx.Bucket.SetError(mapBucket.Err)
|
|
}
|
|
}
|
|
|
|
// index change won't fire if we don't have any roles on create, but we need to evaluate if we match any #all roles
|
|
if ctx.IsCreate && len(entity.RoleAttributes) == 0 {
|
|
store.rolesChanged(ctx.MutateContext, []byte(entity.Id), nil, nil, ctx.Bucket)
|
|
}
|
|
}
|
|
|
|
func (entity *Identity) GetEntityType() string {
|
|
return EntityTypeIdentities
|
|
}
|
|
|
|
func (entity *Identity) GetName() string {
|
|
return entity.Name
|
|
}
|
|
|
|
type IdentityStore interface {
|
|
NameIndexedStore
|
|
LoadOneById(tx *bbolt.Tx, id string) (*Identity, error)
|
|
LoadOneByName(tx *bbolt.Tx, id string) (*Identity, error)
|
|
|
|
GetRoleAttributesIndex() boltz.SetReadIndex
|
|
GetRoleAttributesCursorProvider(values []string, semantic string) (ast.SetCursorProvider, error)
|
|
|
|
AssignServiceConfigs(tx *bbolt.Tx, identityId string, serviceConfigs ...ServiceConfig) error
|
|
RemoveServiceConfigs(tx *bbolt.Tx, identityId string, serviceConfigs ...ServiceConfig) error
|
|
GetServiceConfigs(tx *bbolt.Tx, identityId string) ([]ServiceConfig, error)
|
|
LoadServiceConfigsByServiceAndType(tx *bbolt.Tx, identityId string, configTypes map[string]struct{}) map[string]map[string]map[string]interface{}
|
|
}
|
|
|
|
func newIdentityStore(stores *stores) *identityStoreImpl {
|
|
store := &identityStoreImpl{
|
|
baseStore: newBaseStore(stores, EntityTypeIdentities),
|
|
}
|
|
store.InitImpl(store)
|
|
return store
|
|
}
|
|
|
|
type identityStoreImpl struct {
|
|
*baseStore
|
|
|
|
indexName boltz.ReadIndex
|
|
indexRoleAttributes boltz.SetReadIndex
|
|
|
|
symbolRoleAttributes boltz.EntitySetSymbol
|
|
symbolAuthenticators boltz.EntitySetSymbol
|
|
symbolIdentityTypeId boltz.EntitySymbol
|
|
symbolEnrollments boltz.EntitySetSymbol
|
|
|
|
symbolEdgeRouterPolicies boltz.EntitySetSymbol
|
|
symbolServicePolicies boltz.EntitySetSymbol
|
|
symbolEdgeRouters boltz.EntitySetSymbol
|
|
symbolBindServices boltz.EntitySetSymbol
|
|
symbolDialServices boltz.EntitySetSymbol
|
|
|
|
edgeRoutersCollection boltz.RefCountedLinkCollection
|
|
bindServicesCollection boltz.RefCountedLinkCollection
|
|
dialServicesCollection boltz.RefCountedLinkCollection
|
|
}
|
|
|
|
func (store *identityStoreImpl) NewStoreEntity() boltz.Entity {
|
|
return &Identity{}
|
|
}
|
|
|
|
func (store *identityStoreImpl) GetRoleAttributesIndex() boltz.SetReadIndex {
|
|
return store.indexRoleAttributes
|
|
}
|
|
|
|
func (store *identityStoreImpl) initializeLocal() {
|
|
store.AddExtEntitySymbols()
|
|
|
|
store.symbolRoleAttributes = store.AddSetSymbol(FieldRoleAttributes, ast.NodeTypeString)
|
|
store.indexRoleAttributes = store.AddSetIndex(store.symbolRoleAttributes)
|
|
|
|
store.indexName = store.addUniqueNameField()
|
|
store.symbolEdgeRouters = store.AddFkSetSymbol(db.EntityTypeRouters, store.stores.edgeRouter)
|
|
store.symbolBindServices = store.AddFkSetSymbol(FieldIdentityBindServices, store.stores.edgeService)
|
|
store.symbolDialServices = store.AddFkSetSymbol(FieldIdentityDialServices, store.stores.edgeService)
|
|
store.symbolEdgeRouterPolicies = store.AddFkSetSymbol(EntityTypeEdgeRouterPolicies, store.stores.edgeRouterPolicy)
|
|
store.symbolServicePolicies = store.AddFkSetSymbol(EntityTypeServicePolicies, store.stores.servicePolicy)
|
|
store.symbolEnrollments = store.AddFkSetSymbol(FieldIdentityEnrollments, store.stores.enrollment)
|
|
store.symbolAuthenticators = store.AddFkSetSymbol(FieldIdentityAuthenticators, store.stores.authenticator)
|
|
|
|
store.symbolIdentityTypeId = store.AddFkSymbol(FieldIdentityType, store.stores.identityType)
|
|
|
|
store.AddSymbol(FieldIdentityIsAdmin, ast.NodeTypeBool)
|
|
store.AddSymbol(FieldIdentityIsDefaultAdmin, ast.NodeTypeBool)
|
|
|
|
store.indexRoleAttributes.AddListener(store.rolesChanged)
|
|
}
|
|
|
|
func (store *identityStoreImpl) initializeLinked() {
|
|
store.AddLinkCollection(store.symbolEdgeRouterPolicies, store.stores.edgeRouterPolicy.symbolIdentities)
|
|
store.AddLinkCollection(store.symbolServicePolicies, store.stores.servicePolicy.symbolIdentities)
|
|
|
|
store.edgeRoutersCollection = store.AddRefCountedLinkCollection(store.symbolEdgeRouters, store.stores.edgeRouter.symbolIdentities)
|
|
store.bindServicesCollection = store.AddRefCountedLinkCollection(store.symbolBindServices, store.stores.edgeService.symbolBindIdentities)
|
|
store.dialServicesCollection = store.AddRefCountedLinkCollection(store.symbolDialServices, store.stores.edgeService.symbolDialIdentities)
|
|
}
|
|
|
|
func (store *identityStoreImpl) rolesChanged(mutateCtx boltz.MutateContext, rowId []byte, _ []boltz.FieldTypeAndValue, new []boltz.FieldTypeAndValue, holder errorz.ErrorHolder) {
|
|
ctx := &roleAttributeChangeContext{
|
|
tx: mutateCtx.Tx(),
|
|
rolesSymbol: store.stores.edgeRouterPolicy.symbolIdentityRoles,
|
|
linkCollection: store.stores.edgeRouterPolicy.identityCollection,
|
|
relatedLinkCollection: store.stores.edgeRouterPolicy.edgeRouterCollection,
|
|
denormLinkCollection: store.edgeRoutersCollection,
|
|
ErrorHolder: holder,
|
|
}
|
|
UpdateRelatedRoles(ctx, rowId, new, store.stores.edgeRouterPolicy.symbolSemantic)
|
|
|
|
ctx = &roleAttributeChangeContext{
|
|
tx: mutateCtx.Tx(),
|
|
rolesSymbol: store.stores.servicePolicy.symbolIdentityRoles,
|
|
linkCollection: store.stores.servicePolicy.identityCollection,
|
|
relatedLinkCollection: store.stores.servicePolicy.serviceCollection,
|
|
ErrorHolder: holder,
|
|
}
|
|
store.updateServicePolicyRelatedRoles(ctx, rowId, new)
|
|
}
|
|
|
|
func (store *identityStoreImpl) GetNameIndex() boltz.ReadIndex {
|
|
return store.indexName
|
|
}
|
|
|
|
func (store *identityStoreImpl) LoadOneById(tx *bbolt.Tx, id string) (*Identity, error) {
|
|
entity := &Identity{}
|
|
if err := store.baseLoadOneById(tx, id, entity); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return entity, nil
|
|
}
|
|
|
|
func (store *identityStoreImpl) LoadOneByName(tx *bbolt.Tx, name string) (*Identity, error) {
|
|
id := store.indexName.Read(tx, []byte(name))
|
|
if id != nil {
|
|
return store.LoadOneById(tx, string(id))
|
|
}
|
|
return nil, nil
|
|
}
|
|
|
|
func (store *identityStoreImpl) DeleteById(ctx boltz.MutateContext, id string) error {
|
|
for _, enrollmentId := range store.GetRelatedEntitiesIdList(ctx.Tx(), id, FieldIdentityEnrollments) {
|
|
if err := store.stores.enrollment.DeleteById(ctx, enrollmentId); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
for _, authenticatorId := range store.GetRelatedEntitiesIdList(ctx.Tx(), id, FieldIdentityAuthenticators) {
|
|
if err := store.stores.authenticator.DeleteById(ctx, authenticatorId); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if entity, _ := store.LoadOneById(ctx.Tx(), id); entity != nil {
|
|
if entity.IdentityTypeId == RouterIdentityType {
|
|
if !ctx.IsSystemContext() {
|
|
if router, _ := store.stores.Router.LoadOneByName(ctx.Tx(), entity.Name); router != nil {
|
|
err := errors.Errorf("cannot delete router identity %v until associated router is deleted", entity.Name)
|
|
return errorz.NewEntityCanNotBeDeletedFrom(err)
|
|
}
|
|
}
|
|
}
|
|
// Remove entity from IdentityRoles in edge router policies
|
|
if err := store.deleteEntityReferences(ctx.Tx(), entity, store.stores.edgeRouterPolicy.symbolIdentityRoles); err != nil {
|
|
return err
|
|
}
|
|
// Remove entity from IdentityRoles in service policies
|
|
if err := store.deleteEntityReferences(ctx.Tx(), entity, store.stores.servicePolicy.symbolIdentityRoles); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if err := store.removeServiceConfigs(ctx.Tx(), id, func(_, _, _ string) bool { return true }); err != nil {
|
|
return err
|
|
}
|
|
|
|
return store.baseStore.DeleteById(ctx, id)
|
|
}
|
|
|
|
func (store *identityStoreImpl) AssignServiceConfigs(tx *bbolt.Tx, identityId string, serviceConfigs ...ServiceConfig) error {
|
|
entityBucket := store.GetEntityBucket(tx, []byte(identityId))
|
|
if entityBucket == nil {
|
|
return boltz.NewNotFoundError(store.GetSingularEntityType(), "id", identityId)
|
|
}
|
|
configsBucket := entityBucket.GetOrCreateBucket(FieldIdentityServiceConfigs)
|
|
if configsBucket.HasError() {
|
|
return configsBucket.GetError()
|
|
}
|
|
configTypes := map[string]struct{}{}
|
|
for _, serviceConfig := range serviceConfigs {
|
|
config, err := store.stores.config.LoadOneById(tx, serviceConfig.ConfigId)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if _, ok := configTypes[config.Type]; ok {
|
|
return errors.Errorf("multiple service configs provided for identity %v of config type %v", identityId, config.Type)
|
|
}
|
|
configTypes[config.Type] = struct{}{}
|
|
|
|
serviceBucket := configsBucket.GetOrCreateBucket(serviceConfig.ServiceId)
|
|
if serviceBucket.HasError() {
|
|
return serviceBucket.GetError()
|
|
}
|
|
|
|
// un-index old value
|
|
if currentConfigId := serviceBucket.GetString(config.Type); currentConfigId != nil {
|
|
if err := store.stores.config.identityServicesLinks.RemoveCompoundLink(tx, *currentConfigId, ss(identityId, serviceConfig.ServiceId)); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
// set new value
|
|
if serviceBucket.SetString(config.Type, config.Id, nil).HasError() {
|
|
return serviceBucket.GetError()
|
|
}
|
|
|
|
// index new value
|
|
if err := store.stores.config.identityServicesLinks.AddCompoundLink(tx, config.Id, ss(identityId, serviceConfig.ServiceId)); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
var serviceEvents []*ServiceEvent
|
|
|
|
for _, serviceConfig := range serviceConfigs {
|
|
serviceEvents = append(serviceEvents, &ServiceEvent{
|
|
Type: ServiceUpdated,
|
|
IdentityId: identityId,
|
|
ServiceId: serviceConfig.ServiceId,
|
|
})
|
|
}
|
|
|
|
tx.OnCommit(func() {
|
|
ServiceEvents.dispatchEventsAsync(serviceEvents)
|
|
})
|
|
|
|
return nil
|
|
}
|
|
|
|
func (store *identityStoreImpl) RemoveServiceConfigs(tx *bbolt.Tx, identityId string, serviceConfigs ...ServiceConfig) error {
|
|
// could make this more efficient with maps, but only necessary if we have large number of overrides, which seems unlikely
|
|
// optimize later, if necessary
|
|
result := store.removeServiceConfigs(tx, identityId, func(serviceId, _, configId string) bool {
|
|
if len(serviceConfigs) == 0 {
|
|
return true
|
|
}
|
|
for _, config := range serviceConfigs {
|
|
if config.ServiceId == serviceId && config.ConfigId == configId {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
})
|
|
|
|
var serviceEvents []*ServiceEvent
|
|
|
|
for _, serviceConfig := range serviceConfigs {
|
|
serviceEvents = append(serviceEvents, &ServiceEvent{
|
|
Type: ServiceUpdated,
|
|
IdentityId: identityId,
|
|
ServiceId: serviceConfig.ServiceId,
|
|
})
|
|
}
|
|
|
|
tx.OnCommit(func() {
|
|
ServiceEvents.dispatchEventsAsync(serviceEvents)
|
|
})
|
|
|
|
return result
|
|
}
|
|
|
|
func (store *identityStoreImpl) removeServiceConfigs(tx *bbolt.Tx, identityId string, removeFilter func(serviceId, configTypeId, configId string) bool) error {
|
|
entityBucket := store.GetEntityBucket(tx, []byte(identityId))
|
|
if entityBucket == nil {
|
|
return boltz.NewNotFoundError(store.GetSingularEntityType(), "id", identityId)
|
|
}
|
|
configsBucket := entityBucket.GetBucket(FieldIdentityServiceConfigs)
|
|
if configsBucket == nil {
|
|
// no service configs found, nothing to do, bail out
|
|
return nil
|
|
}
|
|
|
|
servicesCursor := configsBucket.Cursor()
|
|
for sKey, _ := servicesCursor.First(); sKey != nil; sKey, _ = servicesCursor.Next() {
|
|
serviceId := string(sKey)
|
|
serviceBucket := configsBucket.GetBucket(serviceId)
|
|
if serviceBucket == nil {
|
|
// doesn't exist, nothing to do, continue
|
|
continue
|
|
}
|
|
cursor := serviceBucket.Cursor()
|
|
for k, v := cursor.First(); k != nil; k, v = cursor.Next() {
|
|
configTypeId := string(k)
|
|
configId := *boltz.FieldToString(boltz.GetTypeAndValue(v))
|
|
if removeFilter(serviceId, configTypeId, configId) {
|
|
if err := cursor.Delete(); err != nil {
|
|
return err
|
|
}
|
|
if err := store.stores.config.identityServicesLinks.RemoveCompoundLink(tx, configId, ss(identityId, serviceId)); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (store *identityStoreImpl) GetServiceConfigs(tx *bbolt.Tx, identityId string) ([]ServiceConfig, error) {
|
|
entityBucket := store.GetEntityBucket(tx, []byte(identityId))
|
|
if entityBucket == nil {
|
|
return nil, boltz.NewNotFoundError(store.GetSingularEntityType(), "id", identityId)
|
|
}
|
|
var result []ServiceConfig
|
|
configsBucket := entityBucket.GetBucket(FieldIdentityServiceConfigs)
|
|
if configsBucket == nil {
|
|
// no service configs found, nothing to do, bail out
|
|
return result, nil
|
|
}
|
|
|
|
servicesCursor := configsBucket.Cursor()
|
|
for sKey, _ := servicesCursor.First(); sKey != nil; sKey, _ = servicesCursor.Next() {
|
|
serviceId := string(sKey)
|
|
serviceBucket := configsBucket.GetBucket(serviceId)
|
|
if serviceBucket == nil {
|
|
// doesn't exist, nothing to do, continue
|
|
continue
|
|
}
|
|
cursor := serviceBucket.Cursor()
|
|
for k, v := cursor.First(); k != nil; k, v = cursor.Next() {
|
|
configId := *boltz.FieldToString(boltz.GetTypeAndValue(v))
|
|
result = append(result, ServiceConfig{ServiceId: serviceId, ConfigId: configId})
|
|
}
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
func (store *identityStoreImpl) LoadServiceConfigsByServiceAndType(tx *bbolt.Tx, identityId string, configTypes map[string]struct{}) map[string]map[string]map[string]interface{} {
|
|
if len(configTypes) == 0 {
|
|
return nil
|
|
}
|
|
result := map[string]map[string]map[string]interface{}{}
|
|
|
|
entityBucket := store.GetEntityBucket(tx, []byte(identityId))
|
|
if entityBucket == nil {
|
|
return result
|
|
}
|
|
configsBucket := entityBucket.GetBucket(FieldIdentityServiceConfigs)
|
|
if configsBucket == nil {
|
|
return result
|
|
}
|
|
|
|
servicesCursor := configsBucket.Cursor()
|
|
for sKey, _ := servicesCursor.First(); sKey != nil; sKey, _ = servicesCursor.Next() {
|
|
serviceId := string(sKey)
|
|
serviceBucket := configsBucket.GetBucket(serviceId)
|
|
if serviceBucket == nil {
|
|
// doesn't exist, nothing to do, continue
|
|
continue
|
|
}
|
|
|
|
_, wantAll := configTypes["all"]
|
|
cursor := serviceBucket.Cursor()
|
|
for k, v := cursor.First(); k != nil; k, v = cursor.Next() {
|
|
configTypeId := string(k)
|
|
configId := *boltz.FieldToString(boltz.GetTypeAndValue(v))
|
|
|
|
wantsType := wantAll
|
|
if !wantsType {
|
|
_, wantsType = configTypes[configTypeId]
|
|
}
|
|
if wantsType {
|
|
if config, _ := store.stores.config.LoadOneById(tx, configId); config != nil {
|
|
serviceMap, ok := result[serviceId]
|
|
if !ok {
|
|
serviceMap = map[string]map[string]interface{}{}
|
|
result[serviceId] = serviceMap
|
|
}
|
|
serviceMap[configTypeId] = config.Data
|
|
} else {
|
|
pfxlog.Logger().Errorf("config id %v was referenced by identity %v, but no longer exists", configId, identityId)
|
|
_ = cursor.Delete()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
func (store *identityStoreImpl) GetRoleAttributesCursorProvider(values []string, semantic string) (ast.SetCursorProvider, error) {
|
|
return store.getRoleAttributesCursorProvider(store.indexRoleAttributes, values, semantic)
|
|
}
|