-
Notifications
You must be signed in to change notification settings - Fork 56
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Align sync pattern between users/groups #414
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -76,7 +76,7 @@ func ImportSources(db *gorm.DB, sources []ConfigSource) error { | |
db.First(&existing, &Source{Type: SourceTypeOkta}) | ||
|
||
if existing.Id != "" { | ||
logging.L.Warn("overriding existing okta source settings, only one okta source is supported") | ||
logging.L.Warn("overriding existing okta source settings with configuration settings") | ||
} | ||
|
||
var source Source | ||
|
@@ -111,55 +111,57 @@ func ImportSources(db *gorm.DB, sources []ConfigSource) error { | |
return nil | ||
} | ||
|
||
func ApplyGroupMappings(db *gorm.DB, configGroups []ConfigGroupMapping) (groupIds []string, err error) { | ||
for _, g := range configGroups { | ||
func ImportGroupMapping(db *gorm.DB, groups []ConfigGroupMapping) error { | ||
for _, g := range groups { | ||
// get the source from the datastore that this group specifies | ||
var source Source | ||
// Assumes that only one type of each source can exist | ||
srcReadErr := db.Where(&Source{Type: g.Source}).First(&source).Error | ||
if srcReadErr != nil { | ||
if errors.Is(srcReadErr, gorm.ErrRecordNotFound) { | ||
// skip this source, it will need to be added in the config and re-applied | ||
logging.L.Debug("skipping group with source in config that does not exist: " + g.Source) | ||
logging.L.Sugar().Debugf("skipping group '%s' with source '%s' in config that does not exist", g.Name, g.Source) | ||
continue | ||
} | ||
|
||
err = srcReadErr | ||
return srcReadErr | ||
} | ||
|
||
var group Group | ||
|
||
return | ||
grpReadErr := db.Where(&Group{Name: g.Name, SourceId: source.Id}).First(&group).Error | ||
if grpReadErr != nil { | ||
if errors.Is(grpReadErr, gorm.ErrRecordNotFound) { | ||
// skip this group, if they're created these roles will be added later | ||
logging.L.Debug("skipping group in config import that has not yet been provisioned") | ||
continue | ||
} | ||
|
||
return grpReadErr | ||
} | ||
|
||
// import the roles on this group from the datastore | ||
var roles []Role | ||
|
||
roles, err = importRoles(db, g.Roles) | ||
if err != nil { | ||
return | ||
} | ||
|
||
var group Group | ||
// Group names must be unique for mapping purposes | ||
err = db.FirstOrCreate(&group, &Group{Name: g.Name, SourceId: source.Id}).Error | ||
roles, err := importRoles(db, g.Roles) | ||
if err != nil { | ||
return | ||
return err | ||
} | ||
|
||
// add the new group associations to the roles | ||
for i, role := range roles { | ||
if db.Model(&group).Where(&Role{Id: role.Id}).Association("Roles").Count() == 0 { | ||
if err = db.Model(&group).Where(&Role{Id: role.Id}).Association("Roles").Append(&roles[i]); err != nil { | ||
return | ||
return err | ||
} | ||
} | ||
} | ||
|
||
groupIds = append(groupIds, group.Id) | ||
} | ||
|
||
return groupIds, err | ||
return nil | ||
} | ||
|
||
func ApplyUserMapping(db *gorm.DB, users []ConfigUserMapping) error { | ||
func ImportUserMapping(db *gorm.DB, users []ConfigUserMapping) error { | ||
for _, u := range users { | ||
var user User | ||
|
||
|
@@ -174,29 +176,7 @@ func ApplyUserMapping(db *gorm.DB, users []ConfigUserMapping) error { | |
return usrReadErr | ||
} | ||
|
||
// add the user to groups, these declarations can be overridden by external group syncing | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This allowed for adding users declared in config to groups, this feature doesnt make sense anymore |
||
for _, gName := range u.Groups { | ||
// Assumes that only one group can exist with a given name, regardless of sources | ||
var group Group | ||
|
||
grpReadErr := db.Where(&Group{Name: gName}).First(&group).Error | ||
if grpReadErr != nil { | ||
if errors.Is(grpReadErr, gorm.ErrRecordNotFound) { | ||
logging.L.Debug("skipping unknown group \"" + gName + "\" on user") | ||
continue | ||
} | ||
|
||
return grpReadErr | ||
} | ||
|
||
if db.Model(&user).Where(&Group{Id: group.Id}).Association("Groups").Count() == 0 { | ||
if err := db.Model(&user).Where(&Group{Id: group.Id}).Association("Groups").Append(&group); err != nil { | ||
return err | ||
} | ||
} | ||
} | ||
|
||
// add roles to user | ||
// add direct user to role mappings | ||
roles, err := importRoles(db, u.Roles) | ||
if err != nil { | ||
return err | ||
|
@@ -215,29 +195,6 @@ func ApplyUserMapping(db *gorm.DB, users []ConfigUserMapping) error { | |
return nil | ||
} | ||
|
||
// ImportMappings imports the group and user role mappings and removes previously created roles if they no longer exist | ||
func ImportMappings(db *gorm.DB, groups []ConfigGroupMapping, users []ConfigUserMapping) error { | ||
// gorm blocks global delete by default: https://gorm.io/docs/delete.html#Block-Global-Delete | ||
if err := db.Where("1 = 1").Delete(&Role{}).Error; err != nil { | ||
return err | ||
} | ||
|
||
grpIdsToKeep, err := ApplyGroupMappings(db, groups) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if len(grpIdsToKeep) == 0 { | ||
logging.L.Debug("no valid groups found in configuration") | ||
} | ||
|
||
if err := db.Where("1 = 1").Not(grpIdsToKeep).Delete(&Group{}).Error; err != nil { | ||
return err | ||
} | ||
|
||
return ApplyUserMapping(db, users) | ||
} | ||
|
||
// ImportConfig tries to import all valid fields in a config file | ||
func ImportConfig(db *gorm.DB, bs []byte) error { | ||
var config Config | ||
|
@@ -248,14 +205,36 @@ func ImportConfig(db *gorm.DB, bs []byte) error { | |
initialConfig = config | ||
|
||
return db.Transaction(func(tx *gorm.DB) error { | ||
// gorm blocks global delete by default: https://gorm.io/docs/delete.html#Block-Global-Delete | ||
if err := tx.Where("1 = 1").Delete(&Role{}).Error; err != nil { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I tried the drop/create table method here instead, but it didn't re-create the relational tables. This only runs on initial config import. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we be deleting all rows here vs selectively adding/removing rows? (some of the thinking behind #241) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, I could switch to that for sure. My opinion here was that it is less complex to just clear configuration on import (which only happens on start-up) where we want the state to exactly match the config anyway. I can see that building the database back on initial sync is a bit of a pain though. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't see what protects it from running multiple times in the registry.go Run() |
||
return err | ||
} | ||
|
||
var users []User | ||
if err := tx.Find(&users).Error; err != nil { | ||
return err | ||
} | ||
if err := tx.Where("1 = 1").Delete(&users).Error; err != nil { | ||
return err | ||
} | ||
|
||
var groups []Group | ||
if err := tx.Find(&groups).Error; err != nil { | ||
return err | ||
} | ||
if err := tx.Where("1 = 1").Delete(&groups).Error; err != nil { | ||
return err | ||
} | ||
|
||
if err := ImportSources(tx, config.Sources); err != nil { | ||
return err | ||
} | ||
// Need to import of group/user mappings together because they both rely on roles | ||
if err := ImportMappings(tx, config.Groups, config.Users); err != nil { | ||
|
||
if err := ImportGroupMapping(tx, config.Groups); err != nil { | ||
return err | ||
} | ||
return nil | ||
|
||
return ImportUserMapping(tx, config.Users) | ||
}) | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Assigning groups declared in config directly to users is confusing. This was an artifact of adding groups functions before the feature of synchronizing them from the source, removing this functionality.