mirror of
https://github.com/netbirdio/netbird.git
synced 2025-08-07 06:27:20 +02:00
Merging full feature branch into main. Adding full support for service users including backend objects, persistence, verification and api endpoints.
682 lines
20 KiB
Go
682 lines
20 KiB
Go
package server
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/google/uuid"
|
|
log "github.com/sirupsen/logrus"
|
|
|
|
"github.com/netbirdio/netbird/management/server/activity"
|
|
"github.com/netbirdio/netbird/management/server/idp"
|
|
"github.com/netbirdio/netbird/management/server/status"
|
|
)
|
|
|
|
const (
|
|
UserRoleAdmin UserRole = "admin"
|
|
UserRoleUser UserRole = "user"
|
|
UserRoleUnknown UserRole = "unknown"
|
|
|
|
UserStatusActive UserStatus = "active"
|
|
UserStatusDisabled UserStatus = "disabled"
|
|
UserStatusInvited UserStatus = "invited"
|
|
)
|
|
|
|
// StrRoleToUserRole returns UserRole for a given strRole or UserRoleUnknown if the specified role is unknown
|
|
func StrRoleToUserRole(strRole string) UserRole {
|
|
switch strings.ToLower(strRole) {
|
|
case "admin":
|
|
return UserRoleAdmin
|
|
case "user":
|
|
return UserRoleUser
|
|
default:
|
|
return UserRoleUnknown
|
|
}
|
|
}
|
|
|
|
// UserStatus is the status of a User
|
|
type UserStatus string
|
|
|
|
// UserRole is the role of a User
|
|
type UserRole string
|
|
|
|
// User represents a user of the system
|
|
type User struct {
|
|
Id string
|
|
Role UserRole
|
|
IsServiceUser bool
|
|
// ServiceUserName is only set if IsServiceUser is true
|
|
ServiceUserName string
|
|
// AutoGroups is a list of Group IDs to auto-assign to peers registered by this user
|
|
AutoGroups []string
|
|
PATs map[string]*PersonalAccessToken
|
|
}
|
|
|
|
// IsAdmin returns true if user is an admin, false otherwise
|
|
func (u *User) IsAdmin() bool {
|
|
return u.Role == UserRoleAdmin
|
|
}
|
|
|
|
// toUserInfo converts a User object to a UserInfo object.
|
|
func (u *User) toUserInfo(userData *idp.UserData) (*UserInfo, error) {
|
|
autoGroups := u.AutoGroups
|
|
if autoGroups == nil {
|
|
autoGroups = []string{}
|
|
}
|
|
|
|
if userData == nil {
|
|
return &UserInfo{
|
|
ID: u.Id,
|
|
Email: "",
|
|
Name: u.ServiceUserName,
|
|
Role: string(u.Role),
|
|
AutoGroups: u.AutoGroups,
|
|
Status: string(UserStatusActive),
|
|
IsServiceUser: u.IsServiceUser,
|
|
}, nil
|
|
}
|
|
if userData.ID != u.Id {
|
|
return nil, fmt.Errorf("wrong UserData provided for user %s", u.Id)
|
|
}
|
|
|
|
userStatus := UserStatusActive
|
|
if userData.AppMetadata.WTPendingInvite != nil && *userData.AppMetadata.WTPendingInvite {
|
|
userStatus = UserStatusInvited
|
|
}
|
|
|
|
return &UserInfo{
|
|
ID: u.Id,
|
|
Email: userData.Email,
|
|
Name: userData.Name,
|
|
Role: string(u.Role),
|
|
AutoGroups: autoGroups,
|
|
Status: string(userStatus),
|
|
IsServiceUser: u.IsServiceUser,
|
|
}, nil
|
|
}
|
|
|
|
// Copy the user
|
|
func (u *User) Copy() *User {
|
|
autoGroups := make([]string, len(u.AutoGroups))
|
|
copy(autoGroups, u.AutoGroups)
|
|
pats := make(map[string]*PersonalAccessToken, len(u.PATs))
|
|
for k, v := range u.PATs {
|
|
patCopy := new(PersonalAccessToken)
|
|
*patCopy = *v
|
|
pats[k] = patCopy
|
|
}
|
|
return &User{
|
|
Id: u.Id,
|
|
Role: u.Role,
|
|
AutoGroups: autoGroups,
|
|
IsServiceUser: u.IsServiceUser,
|
|
ServiceUserName: u.ServiceUserName,
|
|
PATs: pats,
|
|
}
|
|
}
|
|
|
|
// NewUser creates a new user
|
|
func NewUser(id string, role UserRole, isServiceUser bool, serviceUserName string, autoGroups []string) *User {
|
|
return &User{
|
|
Id: id,
|
|
Role: role,
|
|
IsServiceUser: isServiceUser,
|
|
ServiceUserName: serviceUserName,
|
|
AutoGroups: autoGroups,
|
|
}
|
|
}
|
|
|
|
// NewRegularUser creates a new user with role UserRoleUser
|
|
func NewRegularUser(id string) *User {
|
|
return NewUser(id, UserRoleUser, false, "", []string{})
|
|
}
|
|
|
|
// NewAdminUser creates a new user with role UserRoleAdmin
|
|
func NewAdminUser(id string) *User {
|
|
return NewUser(id, UserRoleAdmin, false, "", []string{})
|
|
}
|
|
|
|
// createServiceUser creates a new service user under the given account.
|
|
func (am *DefaultAccountManager) createServiceUser(accountID string, executingUserID string, role UserRole, serviceUserName string, autoGroups []string) (*UserInfo, error) {
|
|
unlock := am.Store.AcquireAccountLock(accountID)
|
|
defer unlock()
|
|
|
|
account, err := am.Store.GetAccount(accountID)
|
|
if err != nil {
|
|
return nil, status.Errorf(status.NotFound, "account %s doesn't exist", accountID)
|
|
}
|
|
|
|
executingUser := account.Users[executingUserID]
|
|
if executingUser == nil {
|
|
return nil, status.Errorf(status.NotFound, "user not found")
|
|
}
|
|
if executingUser.Role != UserRoleAdmin {
|
|
return nil, status.Errorf(status.PermissionDenied, "only admins can create service users")
|
|
}
|
|
|
|
newUserID := uuid.New().String()
|
|
newUser := NewUser(newUserID, role, true, serviceUserName, autoGroups)
|
|
log.Debugf("New User: %v", newUser)
|
|
account.Users[newUserID] = newUser
|
|
|
|
err = am.Store.SaveAccount(account)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
meta := map[string]any{"name": newUser.ServiceUserName}
|
|
am.storeEvent(executingUserID, newUser.Id, accountID, activity.ServiceUserCreated, meta)
|
|
|
|
return &UserInfo{
|
|
ID: newUser.Id,
|
|
Email: "",
|
|
Name: newUser.ServiceUserName,
|
|
Role: string(newUser.Role),
|
|
AutoGroups: newUser.AutoGroups,
|
|
Status: string(UserStatusActive),
|
|
IsServiceUser: true,
|
|
}, nil
|
|
}
|
|
|
|
// CreateUser creates a new user under the given account. Effectively this is a user invite.
|
|
func (am *DefaultAccountManager) CreateUser(accountID, userID string, user *UserInfo) (*UserInfo, error) {
|
|
if user.IsServiceUser {
|
|
return am.createServiceUser(accountID, userID, StrRoleToUserRole(user.Role), user.Name, user.AutoGroups)
|
|
}
|
|
return am.inviteNewUser(accountID, userID, user)
|
|
}
|
|
|
|
// inviteNewUser Invites a USer to a given account and creates reference in datastore
|
|
func (am *DefaultAccountManager) inviteNewUser(accountID, userID string, invite *UserInfo) (*UserInfo, error) {
|
|
unlock := am.Store.AcquireAccountLock(accountID)
|
|
defer unlock()
|
|
|
|
if am.idpManager == nil {
|
|
return nil, status.Errorf(status.PreconditionFailed, "IdP manager must be enabled to send user invites")
|
|
}
|
|
|
|
if invite == nil {
|
|
return nil, fmt.Errorf("provided user update is nil")
|
|
}
|
|
|
|
account, err := am.Store.GetAccount(accountID)
|
|
if err != nil {
|
|
return nil, status.Errorf(status.NotFound, "account %s doesn't exist", accountID)
|
|
}
|
|
|
|
// check if the user is already registered with this email => reject
|
|
user, err := am.lookupUserInCacheByEmail(invite.Email, accountID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if user != nil {
|
|
return nil, status.Errorf(status.UserAlreadyExists, "user has an existing account")
|
|
}
|
|
|
|
users, err := am.idpManager.GetUserByEmail(invite.Email)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if len(users) > 0 {
|
|
return nil, status.Errorf(status.UserAlreadyExists, "user has an existing account")
|
|
}
|
|
|
|
idpUser, err := am.idpManager.CreateUser(invite.Email, invite.Name, accountID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
role := StrRoleToUserRole(invite.Role)
|
|
newUser := &User{
|
|
Id: idpUser.ID,
|
|
Role: role,
|
|
AutoGroups: invite.AutoGroups,
|
|
}
|
|
account.Users[idpUser.ID] = newUser
|
|
|
|
err = am.Store.SaveAccount(account)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
_, err = am.refreshCache(account.Id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
am.storeEvent(userID, newUser.Id, accountID, activity.UserInvited, nil)
|
|
|
|
return newUser.toUserInfo(idpUser)
|
|
|
|
}
|
|
|
|
// DeleteUser deletes a user from the given account.
|
|
func (am *DefaultAccountManager) DeleteUser(accountID, executingUserID string, targetUserID string) error {
|
|
unlock := am.Store.AcquireAccountLock(accountID)
|
|
defer unlock()
|
|
|
|
account, err := am.Store.GetAccount(accountID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
targetUser := account.Users[targetUserID]
|
|
if targetUser == nil {
|
|
return status.Errorf(status.NotFound, "user not found")
|
|
}
|
|
|
|
executingUser := account.Users[executingUserID]
|
|
if executingUser == nil {
|
|
return status.Errorf(status.NotFound, "user not found")
|
|
}
|
|
if executingUser.Role != UserRoleAdmin {
|
|
return status.Errorf(status.PermissionDenied, "only admins can delete service users")
|
|
}
|
|
|
|
if !targetUser.IsServiceUser {
|
|
return status.Errorf(status.PermissionDenied, "regular users can not be deleted")
|
|
}
|
|
|
|
meta := map[string]any{"name": targetUser.ServiceUserName}
|
|
am.storeEvent(executingUserID, targetUserID, accountID, activity.ServiceUserDeleted, meta)
|
|
|
|
delete(account.Users, targetUserID)
|
|
|
|
err = am.Store.SaveAccount(account)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// CreatePAT creates a new PAT for the given user
|
|
func (am *DefaultAccountManager) CreatePAT(accountID string, executingUserID string, targetUserID string, tokenName string, expiresIn int) (*PersonalAccessTokenGenerated, error) {
|
|
unlock := am.Store.AcquireAccountLock(accountID)
|
|
defer unlock()
|
|
|
|
if tokenName == "" {
|
|
return nil, status.Errorf(status.InvalidArgument, "token name can't be empty")
|
|
}
|
|
|
|
if expiresIn < 1 || expiresIn > 365 {
|
|
return nil, status.Errorf(status.InvalidArgument, "expiration has to be between 1 and 365")
|
|
}
|
|
|
|
account, err := am.Store.GetAccount(accountID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
targetUser := account.Users[targetUserID]
|
|
if targetUser == nil {
|
|
return nil, status.Errorf(status.NotFound, "targetUser not found")
|
|
}
|
|
|
|
executingUser := account.Users[executingUserID]
|
|
if targetUser == nil {
|
|
return nil, status.Errorf(status.NotFound, "user not found")
|
|
}
|
|
|
|
if !(executingUserID == targetUserID || (executingUser.IsAdmin() && targetUser.IsServiceUser)) {
|
|
return nil, status.Errorf(status.PermissionDenied, "no permission to create PAT for this user")
|
|
}
|
|
|
|
pat, err := CreateNewPAT(tokenName, expiresIn, executingUser.Id)
|
|
if err != nil {
|
|
return nil, status.Errorf(status.Internal, "failed to create PAT: %v", err)
|
|
}
|
|
|
|
targetUser.PATs[pat.ID] = &pat.PersonalAccessToken
|
|
|
|
err = am.Store.SaveAccount(account)
|
|
if err != nil {
|
|
return nil, status.Errorf(status.Internal, "failed to save account: %v", err)
|
|
}
|
|
|
|
meta := map[string]any{"name": pat.Name, "is_service_user": targetUser.IsServiceUser, "user_name": targetUser.ServiceUserName}
|
|
am.storeEvent(executingUserID, targetUserID, accountID, activity.PersonalAccessTokenCreated, meta)
|
|
|
|
return pat, nil
|
|
}
|
|
|
|
// DeletePAT deletes a specific PAT from a user
|
|
func (am *DefaultAccountManager) DeletePAT(accountID string, executingUserID string, targetUserID string, tokenID string) error {
|
|
unlock := am.Store.AcquireAccountLock(accountID)
|
|
defer unlock()
|
|
|
|
account, err := am.Store.GetAccount(accountID)
|
|
if err != nil {
|
|
return status.Errorf(status.NotFound, "account not found: %s", err)
|
|
}
|
|
|
|
targetUser := account.Users[targetUserID]
|
|
if targetUser == nil {
|
|
return status.Errorf(status.NotFound, "user not found")
|
|
}
|
|
|
|
executingUser := account.Users[executingUserID]
|
|
if targetUser == nil {
|
|
return status.Errorf(status.NotFound, "user not found")
|
|
}
|
|
|
|
if !(executingUserID == targetUserID || (executingUser.IsAdmin() && targetUser.IsServiceUser)) {
|
|
return status.Errorf(status.PermissionDenied, "no permission to delete PAT for this user")
|
|
}
|
|
|
|
pat := targetUser.PATs[tokenID]
|
|
if pat == nil {
|
|
return status.Errorf(status.NotFound, "PAT not found")
|
|
}
|
|
|
|
err = am.Store.DeleteTokenID2UserIDIndex(pat.ID)
|
|
if err != nil {
|
|
return status.Errorf(status.Internal, "Failed to delete token id index: %s", err)
|
|
}
|
|
err = am.Store.DeleteHashedPAT2TokenIDIndex(pat.HashedToken)
|
|
if err != nil {
|
|
return status.Errorf(status.Internal, "Failed to delete hashed token index: %s", err)
|
|
}
|
|
|
|
meta := map[string]any{"name": pat.Name, "is_service_user": targetUser.IsServiceUser, "user_name": targetUser.ServiceUserName}
|
|
am.storeEvent(executingUserID, targetUserID, accountID, activity.PersonalAccessTokenDeleted, meta)
|
|
|
|
delete(targetUser.PATs, tokenID)
|
|
|
|
err = am.Store.SaveAccount(account)
|
|
if err != nil {
|
|
return status.Errorf(status.Internal, "Failed to save account: %s", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GetPAT returns a specific PAT from a user
|
|
func (am *DefaultAccountManager) GetPAT(accountID string, executingUserID string, targetUserID string, tokenID string) (*PersonalAccessToken, error) {
|
|
unlock := am.Store.AcquireAccountLock(accountID)
|
|
defer unlock()
|
|
|
|
account, err := am.Store.GetAccount(accountID)
|
|
if err != nil {
|
|
return nil, status.Errorf(status.NotFound, "account not found: %s", err)
|
|
}
|
|
|
|
targetUser := account.Users[targetUserID]
|
|
if targetUser == nil {
|
|
return nil, status.Errorf(status.NotFound, "user not found")
|
|
}
|
|
|
|
executingUser := account.Users[executingUserID]
|
|
if targetUser == nil {
|
|
return nil, status.Errorf(status.NotFound, "user not found")
|
|
}
|
|
|
|
if !(executingUserID == targetUserID || (executingUser.IsAdmin() && targetUser.IsServiceUser)) {
|
|
return nil, status.Errorf(status.PermissionDenied, "no permission to get PAT for this userser")
|
|
}
|
|
|
|
pat := targetUser.PATs[tokenID]
|
|
if pat == nil {
|
|
return nil, status.Errorf(status.NotFound, "PAT not found")
|
|
}
|
|
|
|
return pat, nil
|
|
}
|
|
|
|
// GetAllPATs returns all PATs for a user
|
|
func (am *DefaultAccountManager) GetAllPATs(accountID string, executingUserID string, targetUserID string) ([]*PersonalAccessToken, error) {
|
|
unlock := am.Store.AcquireAccountLock(accountID)
|
|
defer unlock()
|
|
|
|
account, err := am.Store.GetAccount(accountID)
|
|
if err != nil {
|
|
return nil, status.Errorf(status.NotFound, "account not found: %s", err)
|
|
}
|
|
|
|
targetUser := account.Users[targetUserID]
|
|
if targetUser == nil {
|
|
return nil, status.Errorf(status.NotFound, "user not found")
|
|
}
|
|
|
|
executingUser := account.Users[executingUserID]
|
|
if targetUser == nil {
|
|
return nil, status.Errorf(status.NotFound, "user not found")
|
|
}
|
|
|
|
if !(executingUserID == targetUserID || (executingUser.IsAdmin() && targetUser.IsServiceUser)) {
|
|
return nil, status.Errorf(status.PermissionDenied, "no permission to get PAT for this user")
|
|
}
|
|
|
|
var pats []*PersonalAccessToken
|
|
for _, pat := range targetUser.PATs {
|
|
pats = append(pats, pat)
|
|
}
|
|
|
|
return pats, nil
|
|
}
|
|
|
|
// SaveUser saves updates a given user. If the user doesn't exit it will throw status.NotFound error.
|
|
// Only User.AutoGroups field is allowed to be updated for now.
|
|
func (am *DefaultAccountManager) SaveUser(accountID, userID string, update *User) (*UserInfo, error) {
|
|
unlock := am.Store.AcquireAccountLock(accountID)
|
|
defer unlock()
|
|
|
|
if update == nil {
|
|
return nil, status.Errorf(status.InvalidArgument, "provided user update is nil")
|
|
}
|
|
|
|
account, err := am.Store.GetAccount(accountID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for _, newGroupID := range update.AutoGroups {
|
|
if _, ok := account.Groups[newGroupID]; !ok {
|
|
return nil, status.Errorf(status.InvalidArgument, "provided group ID %s in the user %s update doesn't exist",
|
|
newGroupID, update.Id)
|
|
}
|
|
}
|
|
|
|
oldUser := account.Users[update.Id]
|
|
if oldUser == nil {
|
|
return nil, status.Errorf(status.NotFound, "update not found")
|
|
}
|
|
|
|
// only auto groups, revoked status, and name can be updated for now
|
|
newUser := oldUser.Copy()
|
|
newUser.AutoGroups = update.AutoGroups
|
|
newUser.Role = update.Role
|
|
|
|
account.Users[newUser.Id] = newUser
|
|
|
|
if err = am.Store.SaveAccount(account); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
defer func() {
|
|
if oldUser.Role != newUser.Role {
|
|
am.storeEvent(userID, oldUser.Id, accountID, activity.UserRoleUpdated, map[string]any{"role": newUser.Role})
|
|
}
|
|
|
|
removedGroups := difference(oldUser.AutoGroups, update.AutoGroups)
|
|
addedGroups := difference(newUser.AutoGroups, oldUser.AutoGroups)
|
|
for _, g := range removedGroups {
|
|
group := account.GetGroup(g)
|
|
if group != nil {
|
|
am.storeEvent(userID, oldUser.Id, accountID, activity.GroupRemovedFromUser,
|
|
map[string]any{"group": group.Name, "group_id": group.ID, "is_service_user": newUser.IsServiceUser, "user_name": newUser.ServiceUserName})
|
|
} else {
|
|
log.Errorf("group %s not found while saving user activity event of account %s", g, account.Id)
|
|
}
|
|
|
|
}
|
|
|
|
for _, g := range addedGroups {
|
|
group := account.GetGroup(g)
|
|
if group != nil {
|
|
am.storeEvent(userID, oldUser.Id, accountID, activity.GroupAddedToUser,
|
|
map[string]any{"group": group.Name, "group_id": group.ID, "is_service_user": newUser.IsServiceUser, "user_name": newUser.ServiceUserName})
|
|
} else {
|
|
log.Errorf("group %s not found while saving user activity event of account %s", g, account.Id)
|
|
}
|
|
}
|
|
}()
|
|
|
|
if !isNil(am.idpManager) && !newUser.IsServiceUser {
|
|
userData, err := am.lookupUserInCache(newUser.Id, account)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if userData == nil {
|
|
return nil, status.Errorf(status.NotFound, "user %s not found in the IdP", newUser.Id)
|
|
}
|
|
return newUser.toUserInfo(userData)
|
|
}
|
|
return newUser.toUserInfo(nil)
|
|
}
|
|
|
|
// GetOrCreateAccountByUser returns an existing account for a given user id or creates a new one if doesn't exist
|
|
func (am *DefaultAccountManager) GetOrCreateAccountByUser(userID, domain string) (*Account, error) {
|
|
unlock := am.Store.AcquireGlobalLock()
|
|
defer unlock()
|
|
|
|
lowerDomain := strings.ToLower(domain)
|
|
|
|
account, err := am.Store.GetAccountByUser(userID)
|
|
if err != nil {
|
|
if s, ok := status.FromError(err); ok && s.Type() == status.NotFound {
|
|
account, err = am.newAccount(userID, lowerDomain)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
err = am.Store.SaveAccount(account)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
} else {
|
|
// other error
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
userObj := account.Users[userID]
|
|
|
|
if account.Domain != lowerDomain && userObj.Role == UserRoleAdmin {
|
|
account.Domain = lowerDomain
|
|
err = am.Store.SaveAccount(account)
|
|
if err != nil {
|
|
return nil, status.Errorf(status.Internal, "failed updating account with domain")
|
|
}
|
|
}
|
|
|
|
return account, nil
|
|
}
|
|
|
|
// GetAccountByUserID returns an existing account for a given user id
|
|
func (am *DefaultAccountManager) GetAccountByUserID(userID string) (*Account, error) {
|
|
return am.Store.GetAccountByUser(userID)
|
|
}
|
|
|
|
// IsUserAdmin looks up a user by his ID and returns true if he is an admin
|
|
func (am *DefaultAccountManager) IsUserAdmin(userID string) (bool, error) {
|
|
account, err := am.GetAccountByUserID(userID)
|
|
if err != nil {
|
|
return false, fmt.Errorf("get account: %v", err)
|
|
}
|
|
|
|
user, ok := account.Users[userID]
|
|
if !ok {
|
|
return false, status.Errorf(status.NotFound, "user not found")
|
|
}
|
|
|
|
return user.Role == UserRoleAdmin, nil
|
|
}
|
|
|
|
// GetUsersFromAccount performs a batched request for users from IDP by account ID apply filter on what data to return
|
|
// based on provided user role.
|
|
func (am *DefaultAccountManager) GetUsersFromAccount(accountID, userID string) ([]*UserInfo, error) {
|
|
account, err := am.Store.GetAccount(accountID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
user, err := account.FindUser(userID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
queriedUsers := make([]*idp.UserData, 0)
|
|
if !isNil(am.idpManager) {
|
|
users := make(map[string]struct{}, len(account.Users))
|
|
for _, user := range account.Users {
|
|
if !user.IsServiceUser {
|
|
users[user.Id] = struct{}{}
|
|
}
|
|
}
|
|
queriedUsers, err = am.lookupCache(users, accountID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
userInfos := make([]*UserInfo, 0)
|
|
|
|
// in case of self-hosted, or IDP doesn't return anything, we will return the locally stored userInfo
|
|
if len(queriedUsers) == 0 {
|
|
for _, accountUser := range account.Users {
|
|
if !user.IsAdmin() && user.Id != accountUser.Id {
|
|
// if user is not an admin then show only current user and do not show other users
|
|
continue
|
|
}
|
|
info, err := accountUser.toUserInfo(nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
userInfos = append(userInfos, info)
|
|
}
|
|
return userInfos, nil
|
|
}
|
|
|
|
for _, localUser := range account.Users {
|
|
if !user.IsAdmin() && user.Id != localUser.Id {
|
|
// if user is not an admin then show only current user and do not show other users
|
|
continue
|
|
}
|
|
|
|
var info *UserInfo
|
|
if queriedUser, contains := findUserInIDPUserdata(localUser.Id, queriedUsers); contains {
|
|
info, err = localUser.toUserInfo(queriedUser)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
} else {
|
|
name := ""
|
|
if localUser.IsServiceUser {
|
|
name = localUser.ServiceUserName
|
|
}
|
|
info = &UserInfo{
|
|
ID: localUser.Id,
|
|
Email: "",
|
|
Name: name,
|
|
Role: string(localUser.Role),
|
|
AutoGroups: localUser.AutoGroups,
|
|
Status: string(UserStatusActive),
|
|
IsServiceUser: localUser.IsServiceUser,
|
|
}
|
|
}
|
|
userInfos = append(userInfos, info)
|
|
}
|
|
|
|
return userInfos, nil
|
|
}
|
|
|
|
func findUserInIDPUserdata(userID string, userData []*idp.UserData) (*idp.UserData, bool) {
|
|
for _, user := range userData {
|
|
if user.ID == userID {
|
|
return user, true
|
|
}
|
|
}
|
|
return nil, false
|
|
}
|