-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add restraints to updating managed groups
Signed-off-by: Sarah Funkhouser <[email protected]>
- Loading branch information
1 parent
63decef
commit 860758d
Showing
11 changed files
with
280 additions
and
202 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,222 @@ | ||
package hooks | ||
|
||
import ( | ||
"context" | ||
|
||
"entgo.io/ent" | ||
"github.com/rs/zerolog/log" | ||
"github.com/theopenlane/entx" | ||
"github.com/theopenlane/utils/contextx" | ||
|
||
"github.com/theopenlane/core/internal/ent/generated" | ||
"github.com/theopenlane/core/internal/ent/generated/group" | ||
"github.com/theopenlane/core/internal/ent/generated/groupmembership" | ||
"github.com/theopenlane/core/pkg/enums" | ||
) | ||
|
||
// ManagedContextKey is the key name managed group updates | ||
type ManagedContextKey struct{} | ||
|
||
const ( | ||
// AdminsGroup is the group name for all organization admins and owner, these users have full read and write access in the organization | ||
AdminsGroup = "Admins" | ||
// ViewersGroup is the group name for all organization members that only have view access in the organization | ||
ViewersGroup = "Viewers" | ||
// AllMembersGroup is the group name for all members of the organization, no matter their role | ||
AllMembersGroup = "All Members" | ||
) | ||
|
||
// defaultGroups are the default groups created for an organization that are managed by the system | ||
var defaultGroups = map[string]string{ | ||
AdminsGroup: "Openlane managed group containing all organization admins with full access", | ||
ViewersGroup: "Openlane managed group containing all organization members with only view access", | ||
AllMembersGroup: "Openlane managed group containing all members of the organization", | ||
} | ||
|
||
// generateOrganizationGroups creates the default groups for an organization that are managed by Openlane | ||
// this includes the Admins, Viewers, and All Members groups where users are automatically added based on their role | ||
func generateOrganizationGroups(ctx context.Context, m *generated.OrganizationMutation, orgID string) error { | ||
// skip group creation for personal orgs | ||
if isPersonal, _ := m.PersonalOrg(); isPersonal { | ||
log.Debug().Msg("skipping group creation for personal org") | ||
|
||
return nil | ||
} | ||
|
||
builders := make([]*generated.GroupCreate, 0, len(defaultGroups)) | ||
|
||
for name, desc := range defaultGroups { | ||
groupInput := generated.CreateGroupInput{ | ||
Name: name, | ||
Description: &desc, | ||
} | ||
|
||
builders = append(builders, m.Client().Group.Create(). | ||
SetInput(groupInput). | ||
SetIsManaged(true). | ||
SetOwnerID(orgID), | ||
) | ||
} | ||
|
||
if err := m.Client().Group.CreateBulk(builders...).Exec(ctx); err != nil { | ||
log.Error().Err(err).Msg("error creating system managed groups") | ||
|
||
return err | ||
} | ||
|
||
log.Debug().Str("organization", orgID).Msg("created system managed groups") | ||
|
||
return nil | ||
} | ||
|
||
// updateManagedGroupMembers groups adds or removes the org members to the managed system groups | ||
func updateManagedGroupMembers(ctx context.Context, m *generated.OrgMembershipMutation) error { | ||
// set a context key to indicate that this is a managed group update | ||
// and allowed to skip the check for managed groups | ||
managedCtx := contextx.With(ctx, ManagedContextKey{}) | ||
|
||
op := m.Op() | ||
|
||
switch op { | ||
case ent.OpCreate: | ||
return addToManagedGroups(managedCtx, m) | ||
case ent.OpDelete, ent.OpDeleteOne: | ||
return removeFromManagedGroups(managedCtx, m) | ||
case ent.OpUpdate, ent.OpUpdateOne: | ||
if entx.CheckIsSoftDelete(managedCtx) { | ||
return removeFromManagedGroups(managedCtx, m) | ||
} | ||
|
||
return updateManagedGroups(managedCtx, m) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// updateManagedGroups updates the managed groups based on the role of the user for update requests | ||
func updateManagedGroups(ctx context.Context, m *generated.OrgMembershipMutation) error { | ||
role, ok := m.Role() | ||
if !ok { | ||
role = enums.RoleMember | ||
} | ||
|
||
oldRole, _ := m.OldRole(ctx) | ||
|
||
if oldRole != role { | ||
if err := removeFromManagedGroups(ctx, m); err != nil { | ||
return err | ||
} | ||
} | ||
|
||
return addToManagedGroups(ctx, m) | ||
} | ||
|
||
// addToManagedGroups adds the user to the system managed groups based on their role on creation or update | ||
func addToManagedGroups(ctx context.Context, m *generated.OrgMembershipMutation) error { | ||
role, ok := m.Role() | ||
if !ok { | ||
role = enums.RoleMember | ||
} | ||
|
||
userID, _ := m.UserID() | ||
|
||
switch role { | ||
case enums.RoleMember: | ||
if err := addMemberToManagedGroup(ctx, m, userID, ViewersGroup); err != nil { | ||
return err | ||
} | ||
case enums.RoleAdmin: | ||
if err := addMemberToManagedGroup(ctx, m, userID, AdminsGroup); err != nil { | ||
return err | ||
} | ||
} | ||
|
||
// add all users to the all users group | ||
if m.Op() == ent.OpCreate { | ||
return addMemberToManagedGroup(ctx, m, userID, AllMembersGroup) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// removeFromManagedGroups removes the user from the system managed groups when they are removed from the organization or their role changes | ||
func removeFromManagedGroups(ctx context.Context, m *generated.OrgMembershipMutation) error { | ||
role, ok := m.Role() | ||
if !ok { | ||
role = enums.RoleMember | ||
} | ||
|
||
userID, _ := m.UserID() | ||
|
||
switch role { | ||
case enums.RoleMember: | ||
if err := removeMemberFromManagedGroup(ctx, m, userID, ViewersGroup); err != nil { | ||
return err | ||
} | ||
case enums.RoleAdmin: | ||
if err := removeMemberFromManagedGroup(ctx, m, userID, AdminsGroup); err != nil { | ||
return err | ||
} | ||
} | ||
|
||
// remove from the all users group if they are removed from the organization | ||
if entx.CheckIsSoftDelete(ctx) { | ||
return removeMemberFromManagedGroup(ctx, m, userID, AllMembersGroup) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// addMemberToManagedGroup adds the user to the system managed groups | ||
func addMemberToManagedGroup(ctx context.Context, m *generated.OrgMembershipMutation, userID, groupName string) error { | ||
// get the group to update | ||
groupID, err := m.Client().Group.Query().Where( | ||
group.IsManaged(true), // grab the managed group | ||
group.Name(groupName), | ||
).Only(ctx) | ||
if err != nil { | ||
log.Error().Err(err).Msgf("error getting managed group: %s", groupName) | ||
return err | ||
} | ||
|
||
input := generated.CreateGroupMembershipInput{ | ||
Role: &enums.RoleMember, | ||
UserID: userID, | ||
GroupID: groupID.ID, | ||
} | ||
|
||
if err := m.Client().GroupMembership.Create().SetInput(input).Exec(ctx); err != nil { | ||
log.Error().Err(err).Msg("error adding user to managed group") | ||
return err | ||
} | ||
|
||
log.Debug().Str("user_id", userID).Str("group", groupName).Msg("user added to managed group") | ||
|
||
return nil | ||
} | ||
|
||
func removeMemberFromManagedGroup(ctx context.Context, m *generated.OrgMembershipMutation, userID, groupName string) error { | ||
// get the group to update | ||
group, err := m.Client().Group.Query().Where( | ||
group.IsManaged(true), // grab the managed group | ||
group.Name(groupName), | ||
).Only(ctx) | ||
if err != nil { | ||
log.Error().Err(err).Msgf("error getting managed group: %s", groupName) | ||
return err | ||
} | ||
|
||
if _, err := m.Client().GroupMembership.Delete().Where( | ||
groupmembership.ID(group.ID), | ||
groupmembership.UserID(userID), | ||
groupmembership.RoleEQ(enums.RoleMember), | ||
).Exec(ctx); err != nil { | ||
log.Error().Err(err).Msg("error removing user from managed group") | ||
|
||
return err | ||
} | ||
|
||
log.Debug().Str("user_id", userID).Str("group", groupName).Msg("user removed from managed group") | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.