[management] permission manager validate account access (#3444)

This commit is contained in:
Pedro Maia Costa
2025-03-30 16:08:22 +01:00
committed by GitHub
parent 21464ac770
commit cbec7bda80
39 changed files with 814 additions and 279 deletions

View File

@@ -30,8 +30,8 @@ func (am *DefaultAccountManager) createServiceUser(ctx context.Context, accountI
return nil, err
}
if initiatorUser.AccountID != accountID {
return nil, status.NewUserNotPartOfAccountError()
if err := am.permissionsManager.ValidateAccountAccess(ctx, accountID, initiatorUser, false); err != nil {
return nil, err
}
if !initiatorUser.HasAdminPower() {
@@ -93,8 +93,8 @@ func (am *DefaultAccountManager) inviteNewUser(ctx context.Context, accountID, u
return nil, err
}
if initiatorUser.AccountID != accountID {
return nil, status.NewUserNotPartOfAccountError()
if err := am.permissionsManager.ValidateAccountAccess(ctx, accountID, initiatorUser, false); err != nil {
return nil, err
}
inviterID := userID
@@ -142,12 +142,21 @@ func (am *DefaultAccountManager) inviteNewUser(ctx context.Context, accountID, u
// createNewIdpUser validates the invite and creates a new user in the IdP
func (am *DefaultAccountManager) createNewIdpUser(ctx context.Context, accountID string, inviterID string, invite *types.UserInfo) (*idp.UserData, error) {
inviter, err := am.GetUserByID(ctx, inviterID)
if err != nil {
return nil, fmt.Errorf("failed to get inviter user: %w", err)
}
// inviterUser is the one who is inviting the new user
inviterUser, err := am.lookupUserInCache(ctx, inviterID, accountID)
inviterUser, err := am.lookupUserInCache(ctx, inviterID, inviter.AccountID)
if err != nil {
return nil, status.Errorf(status.NotFound, "inviter user with ID %s doesn't exist in IdP", inviterID)
}
if inviterUser == nil {
return nil, status.Errorf(status.NotFound, "inviter user with ID %s is empty", inviterID)
}
// check if the user is already registered with this email => reject
user, err := am.lookupUserInCacheByEmail(ctx, invite.Email, accountID)
if err != nil {
@@ -188,7 +197,7 @@ func (am *DefaultAccountManager) GetUserFromUserAuth(ctx context.Context, userAu
err = am.Store.SaveUserLastLogin(ctx, userAuth.AccountId, userAuth.UserId, userAuth.LastLogin)
if err != nil {
log.WithContext(ctx).Errorf("failed saving user last login: %v", err)
log.WithContext(ctx).Debugf("failed to update user last login: %v", err)
}
if newLogin {
@@ -228,8 +237,8 @@ func (am *DefaultAccountManager) DeleteUser(ctx context.Context, accountID, init
return err
}
if initiatorUser.AccountID != accountID {
return status.NewUserNotPartOfAccountError()
if err := am.permissionsManager.ValidateAccountAccess(ctx, accountID, initiatorUser, false); err != nil {
return err
}
if !initiatorUser.HasAdminPower() {
@@ -290,8 +299,8 @@ func (am *DefaultAccountManager) InviteUser(ctx context.Context, accountID strin
return err
}
if initiatorUser.AccountID != accountID {
return status.NewUserNotPartOfAccountError()
if err := am.permissionsManager.ValidateAccountAccess(ctx, accountID, initiatorUser, false); err != nil {
return err
}
// check if the user is already registered with this ID
@@ -338,8 +347,8 @@ func (am *DefaultAccountManager) CreatePAT(ctx context.Context, accountID string
return nil, err
}
if initiatorUser.AccountID != accountID {
return nil, status.NewUserNotPartOfAccountError()
if err := am.permissionsManager.ValidateAccountAccess(ctx, accountID, initiatorUser, false); err != nil {
return nil, err
}
targetUser, err := am.Store.GetUserByUserID(ctx, store.LockingStrengthShare, targetUserID)
@@ -376,8 +385,8 @@ func (am *DefaultAccountManager) DeletePAT(ctx context.Context, accountID string
return err
}
if initiatorUser.AccountID != accountID {
return status.NewUserNotPartOfAccountError()
if err := am.permissionsManager.ValidateAccountAccess(ctx, accountID, initiatorUser, false); err != nil {
return err
}
if initiatorUserID != targetUserID && initiatorUser.IsRegularUser() {
@@ -411,8 +420,8 @@ func (am *DefaultAccountManager) GetPAT(ctx context.Context, accountID string, i
return nil, err
}
if initiatorUser.AccountID != accountID {
return nil, status.NewUserNotPartOfAccountError()
if err := am.permissionsManager.ValidateAccountAccess(ctx, accountID, initiatorUser, false); err != nil {
return nil, err
}
if initiatorUserID != targetUserID && initiatorUser.IsRegularUser() {
@@ -429,8 +438,8 @@ func (am *DefaultAccountManager) GetAllPATs(ctx context.Context, accountID strin
return nil, err
}
if initiatorUser.AccountID != accountID {
return nil, status.NewUserNotPartOfAccountError()
if err := am.permissionsManager.ValidateAccountAccess(ctx, accountID, initiatorUser, false); err != nil {
return nil, err
}
if initiatorUserID != targetUserID && initiatorUser.IsRegularUser() {
@@ -476,8 +485,8 @@ func (am *DefaultAccountManager) SaveOrAddUsers(ctx context.Context, accountID,
return nil, err
}
if initiatorUser.AccountID != accountID {
return nil, status.NewUserNotPartOfAccountError()
if err := am.permissionsManager.ValidateAccountAccess(ctx, accountID, initiatorUser, false); err != nil {
return nil, err
}
if !initiatorUser.HasAdminPower() || initiatorUser.IsBlocked() {
@@ -511,7 +520,7 @@ func (am *DefaultAccountManager) SaveOrAddUsers(ctx context.Context, accountID,
}
userHadPeers, updatedUser, userPeersToExpire, userEvents, err := am.processUserUpdate(
ctx, transaction, groupsMap, initiatorUser, update, addIfNotExists, settings,
ctx, transaction, groupsMap, accountID, initiatorUser, update, addIfNotExists, settings,
)
if err != nil {
return fmt.Errorf("failed to process user update: %w", err)
@@ -597,13 +606,13 @@ func (am *DefaultAccountManager) prepareUserUpdateEvents(ctx context.Context, ac
}
func (am *DefaultAccountManager) processUserUpdate(ctx context.Context, transaction store.Store, groupsMap map[string]*types.Group,
initiatorUser, update *types.User, addIfNotExists bool, settings *types.Settings) (bool, *types.User, []*nbpeer.Peer, []func(), error) {
accountID string, initiatorUser, update *types.User, addIfNotExists bool, settings *types.Settings) (bool, *types.User, []*nbpeer.Peer, []func(), error) {
if update == nil {
return false, nil, nil, nil, status.Errorf(status.InvalidArgument, "provided user update is nil")
}
oldUser, err := getUserOrCreateIfNotExists(ctx, transaction, update, addIfNotExists)
oldUser, err := getUserOrCreateIfNotExists(ctx, transaction, accountID, update, addIfNotExists)
if err != nil {
return false, nil, nil, nil, err
}
@@ -614,7 +623,6 @@ func (am *DefaultAccountManager) processUserUpdate(ctx context.Context, transact
// only auto groups, revoked status, and integration reference can be updated for now
updatedUser := oldUser.Copy()
updatedUser.AccountID = initiatorUser.AccountID
updatedUser.Role = update.Role
updatedUser.Blocked = update.Blocked
updatedUser.AutoGroups = update.AutoGroups
@@ -657,17 +665,23 @@ func (am *DefaultAccountManager) processUserUpdate(ctx context.Context, transact
}
// getUserOrCreateIfNotExists retrieves the existing user or creates a new one if it doesn't exist.
func getUserOrCreateIfNotExists(ctx context.Context, transaction store.Store, update *types.User, addIfNotExists bool) (*types.User, error) {
func getUserOrCreateIfNotExists(ctx context.Context, transaction store.Store, accountID string, update *types.User, addIfNotExists bool) (*types.User, error) {
existingUser, err := transaction.GetUserByUserID(ctx, store.LockingStrengthShare, update.Id)
if err != nil {
if sErr, ok := status.FromError(err); ok && sErr.Type() == status.NotFound {
if !addIfNotExists {
return nil, status.Errorf(status.NotFound, "user to update doesn't exist: %s", update.Id)
}
update.AccountID = accountID
return update, nil // use all fields from update if addIfNotExists is true
}
return nil, err
}
if existingUser.AccountID != accountID {
return nil, status.Errorf(status.InvalidArgument, "user account ID mismatch")
}
return existingUser, nil
}
@@ -705,6 +719,7 @@ func (am *DefaultAccountManager) getUserInfo(ctx context.Context, user *types.Us
// validateUserUpdate validates the update operation for a user.
func validateUserUpdate(groupsMap map[string]*types.Group, initiatorUser, oldUser, update *types.User) error {
// @todo double check these
if initiatorUser.HasAdminPower() && initiatorUser.Id == update.Id && oldUser.Blocked != update.Blocked {
return status.Errorf(status.PermissionDenied, "admins can't block or unblock themselves")
}
@@ -790,8 +805,8 @@ func (am *DefaultAccountManager) GetUsersFromAccount(ctx context.Context, accoun
return nil, err
}
if initiatorUser.AccountID != accountID {
return nil, status.NewUserNotPartOfAccountError()
if err := am.permissionsManager.ValidateAccountAccess(ctx, accountID, initiatorUser, false); err != nil {
return nil, err
}
return am.BuildUserInfosForAccount(ctx, accountID, initiatorUserID, accountUsers)
@@ -967,6 +982,10 @@ func (am *DefaultAccountManager) DeleteRegularUsers(ctx context.Context, account
return err
}
if err := am.permissionsManager.ValidateAccountAccess(ctx, accountID, initiatorUser, false); err != nil {
return err
}
if !initiatorUser.HasAdminPower() {
return status.NewAdminPermissionError()
}
@@ -1081,6 +1100,25 @@ func (am *DefaultAccountManager) deleteRegularUser(ctx context.Context, accountI
return updateAccountPeers, nil
}
// GetOwnerInfo retrieves the owner information for a given account ID.
func (am *DefaultAccountManager) GetOwnerInfo(ctx context.Context, accountID string) (*types.UserInfo, error) {
owner, err := am.Store.GetAccountOwner(ctx, store.LockingStrengthShare, accountID)
if err != nil {
return nil, err
}
if owner == nil {
return nil, status.Errorf(status.NotFound, "owner not found")
}
userInfo, err := am.getUserInfo(ctx, owner, accountID)
if err != nil {
return nil, err
}
return userInfo, nil
}
// updateUserPeersInGroups updates the user's peers in the specified groups by adding or removing them.
func updateUserPeersInGroups(accountGroups map[string]*types.Group, peers []*nbpeer.Peer, groupsToAdd, groupsToRemove []string) (groupsToUpdate []*types.Group, err error) {
if len(groupsToAdd) == 0 && len(groupsToRemove) == 0 {