mirror of
https://github.com/netbirdio/netbird.git
synced 2025-05-10 11:15:02 +02:00
add owner role support (#1340)
This PR adds support to Owner roles. The owner role has a similar access level as the admin, but it has the power to delete the account. Besides that, the role has the following constraints: - The role can only be transferred. So, only a user with the owner role can transfer the owner role to a new user - It can't be assigned to users being invited - It can't be assigned to service users
This commit is contained in:
parent
b8c46e2654
commit
d7efea74b6
@ -877,7 +877,7 @@ func (am *DefaultAccountManager) UpdateAccountSettings(accountID, userID string,
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !user.IsAdmin() {
|
if !user.HasAdminPower() {
|
||||||
return nil, status.Errorf(status.PermissionDenied, "user is not allowed to update account")
|
return nil, status.Errorf(status.PermissionDenied, "user is not allowed to update account")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1019,7 +1019,7 @@ func (am *DefaultAccountManager) DeleteAccount(accountID, userID string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !user.IsAdmin() {
|
if !user.HasAdminPower() {
|
||||||
return status.Errorf(status.PermissionDenied, "user is not allowed to delete account")
|
return status.Errorf(status.PermissionDenied, "user is not allowed to delete account")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1703,7 +1703,7 @@ func newAccountWithId(accountID, userID, domain string) *Account {
|
|||||||
routes := make(map[string]*route.Route)
|
routes := make(map[string]*route.Route)
|
||||||
setupKeys := map[string]*SetupKey{}
|
setupKeys := map[string]*SetupKey{}
|
||||||
nameServersGroups := make(map[string]*nbdns.NameServerGroup)
|
nameServersGroups := make(map[string]*nbdns.NameServerGroup)
|
||||||
users[userID] = NewAdminUser(userID)
|
users[userID] = NewOwnerUser(userID)
|
||||||
dnsSettings := DNSSettings{
|
dnsSettings := DNSSettings{
|
||||||
DisabledManagementGroups: make([]string, 0),
|
DisabledManagementGroups: make([]string, 0),
|
||||||
}
|
}
|
||||||
|
@ -306,7 +306,7 @@ func TestDefaultAccountManager_GetAccountFromToken(t *testing.T) {
|
|||||||
inputInitUserParams: defaultInitAccount,
|
inputInitUserParams: defaultInitAccount,
|
||||||
testingFunc: require.NotEqual,
|
testingFunc: require.NotEqual,
|
||||||
expectedMSG: "account IDs shouldn't match",
|
expectedMSG: "account IDs shouldn't match",
|
||||||
expectedUserRole: UserRoleAdmin,
|
expectedUserRole: UserRoleOwner,
|
||||||
expectedDomainCategory: "",
|
expectedDomainCategory: "",
|
||||||
expectedDomain: publicDomain,
|
expectedDomain: publicDomain,
|
||||||
expectedPrimaryDomainStatus: false,
|
expectedPrimaryDomainStatus: false,
|
||||||
@ -328,7 +328,7 @@ func TestDefaultAccountManager_GetAccountFromToken(t *testing.T) {
|
|||||||
inputInitUserParams: initUnknown,
|
inputInitUserParams: initUnknown,
|
||||||
testingFunc: require.NotEqual,
|
testingFunc: require.NotEqual,
|
||||||
expectedMSG: "account IDs shouldn't match",
|
expectedMSG: "account IDs shouldn't match",
|
||||||
expectedUserRole: UserRoleAdmin,
|
expectedUserRole: UserRoleOwner,
|
||||||
expectedDomain: unknownDomain,
|
expectedDomain: unknownDomain,
|
||||||
expectedDomainCategory: "",
|
expectedDomainCategory: "",
|
||||||
expectedPrimaryDomainStatus: false,
|
expectedPrimaryDomainStatus: false,
|
||||||
@ -346,7 +346,7 @@ func TestDefaultAccountManager_GetAccountFromToken(t *testing.T) {
|
|||||||
inputInitUserParams: defaultInitAccount,
|
inputInitUserParams: defaultInitAccount,
|
||||||
testingFunc: require.NotEqual,
|
testingFunc: require.NotEqual,
|
||||||
expectedMSG: "account IDs shouldn't match",
|
expectedMSG: "account IDs shouldn't match",
|
||||||
expectedUserRole: UserRoleAdmin,
|
expectedUserRole: UserRoleOwner,
|
||||||
expectedDomain: privateDomain,
|
expectedDomain: privateDomain,
|
||||||
expectedDomainCategory: PrivateCategory,
|
expectedDomainCategory: PrivateCategory,
|
||||||
expectedPrimaryDomainStatus: true,
|
expectedPrimaryDomainStatus: true,
|
||||||
@ -387,7 +387,7 @@ func TestDefaultAccountManager_GetAccountFromToken(t *testing.T) {
|
|||||||
inputInitUserParams: defaultInitAccount,
|
inputInitUserParams: defaultInitAccount,
|
||||||
testingFunc: require.Equal,
|
testingFunc: require.Equal,
|
||||||
expectedMSG: "account IDs should match",
|
expectedMSG: "account IDs should match",
|
||||||
expectedUserRole: UserRoleAdmin,
|
expectedUserRole: UserRoleOwner,
|
||||||
expectedDomain: defaultInitAccount.Domain,
|
expectedDomain: defaultInitAccount.Domain,
|
||||||
expectedDomainCategory: PrivateCategory,
|
expectedDomainCategory: PrivateCategory,
|
||||||
expectedPrimaryDomainStatus: true,
|
expectedPrimaryDomainStatus: true,
|
||||||
@ -406,7 +406,7 @@ func TestDefaultAccountManager_GetAccountFromToken(t *testing.T) {
|
|||||||
inputInitUserParams: defaultInitAccount,
|
inputInitUserParams: defaultInitAccount,
|
||||||
testingFunc: require.Equal,
|
testingFunc: require.Equal,
|
||||||
expectedMSG: "account IDs should match",
|
expectedMSG: "account IDs should match",
|
||||||
expectedUserRole: UserRoleAdmin,
|
expectedUserRole: UserRoleOwner,
|
||||||
expectedDomain: defaultInitAccount.Domain,
|
expectedDomain: defaultInitAccount.Domain,
|
||||||
expectedDomainCategory: PrivateCategory,
|
expectedDomainCategory: PrivateCategory,
|
||||||
expectedPrimaryDomainStatus: true,
|
expectedPrimaryDomainStatus: true,
|
||||||
@ -424,7 +424,7 @@ func TestDefaultAccountManager_GetAccountFromToken(t *testing.T) {
|
|||||||
inputInitUserParams: defaultInitAccount,
|
inputInitUserParams: defaultInitAccount,
|
||||||
testingFunc: require.NotEqual,
|
testingFunc: require.NotEqual,
|
||||||
expectedMSG: "account IDs shouldn't match",
|
expectedMSG: "account IDs shouldn't match",
|
||||||
expectedUserRole: UserRoleAdmin,
|
expectedUserRole: UserRoleOwner,
|
||||||
expectedDomain: "",
|
expectedDomain: "",
|
||||||
expectedDomainCategory: "",
|
expectedDomainCategory: "",
|
||||||
expectedPrimaryDomainStatus: false,
|
expectedPrimaryDomainStatus: false,
|
||||||
@ -1183,7 +1183,7 @@ func TestGetUsersFromAccount(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
users := map[string]*User{"1": {Id: "1", Role: "admin"}, "2": {Id: "2", Role: "user"}, "3": {Id: "3", Role: "user"}}
|
users := map[string]*User{"1": {Id: "1", Role: UserRoleOwner}, "2": {Id: "2", Role: "user"}, "3": {Id: "3", Role: "user"}}
|
||||||
accountId := "test_account_id"
|
accountId := "test_account_id"
|
||||||
|
|
||||||
account, err := createAccount(manager, accountId, users["1"].Id, "")
|
account, err := createAccount(manager, accountId, users["1"].Id, "")
|
||||||
|
@ -120,6 +120,8 @@ const (
|
|||||||
IntegrationUpdated
|
IntegrationUpdated
|
||||||
// IntegrationDeleted indicates that the user deleted an integration
|
// IntegrationDeleted indicates that the user deleted an integration
|
||||||
IntegrationDeleted
|
IntegrationDeleted
|
||||||
|
// TransferredOwnerRole indicates that the user transferred the owner role of the account
|
||||||
|
TransferredOwnerRole
|
||||||
)
|
)
|
||||||
|
|
||||||
var activityMap = map[Activity]Code{
|
var activityMap = map[Activity]Code{
|
||||||
@ -178,6 +180,7 @@ var activityMap = map[Activity]Code{
|
|||||||
IntegrationCreated: {"Integration created", "integration.create"},
|
IntegrationCreated: {"Integration created", "integration.create"},
|
||||||
IntegrationUpdated: {"Integration updated", "integration.update"},
|
IntegrationUpdated: {"Integration updated", "integration.update"},
|
||||||
IntegrationDeleted: {"Integration deleted", "integration.delete"},
|
IntegrationDeleted: {"Integration deleted", "integration.delete"},
|
||||||
|
TransferredOwnerRole: {"Transferred owner role", "transferred.owner.role"},
|
||||||
}
|
}
|
||||||
|
|
||||||
// StringCode returns a string code of the activity
|
// StringCode returns a string code of the activity
|
||||||
|
@ -47,8 +47,8 @@ func (am *DefaultAccountManager) GetDNSSettings(accountID string, userID string)
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !user.IsAdmin() {
|
if !user.HasAdminPower() {
|
||||||
return nil, status.Errorf(status.PermissionDenied, "only admins are allowed to view DNS settings")
|
return nil, status.Errorf(status.PermissionDenied, "only users with admin power are allowed to view DNS settings")
|
||||||
}
|
}
|
||||||
dnsSettings := account.DNSSettings.Copy()
|
dnsSettings := account.DNSSettings.Copy()
|
||||||
return &dnsSettings, nil
|
return &dnsSettings, nil
|
||||||
@ -69,8 +69,8 @@ func (am *DefaultAccountManager) SaveDNSSettings(accountID string, userID string
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !user.IsAdmin() {
|
if !user.HasAdminPower() {
|
||||||
return status.Errorf(status.PermissionDenied, "only admins are allowed to update DNS settings")
|
return status.Errorf(status.PermissionDenied, "only users with admin power are allowed to update DNS settings")
|
||||||
}
|
}
|
||||||
|
|
||||||
if dnsSettingsToSave == nil {
|
if dnsSettingsToSave == nil {
|
||||||
|
@ -170,7 +170,7 @@ func (am *DefaultAccountManager) DeleteGroup(accountId, userId, groupID string)
|
|||||||
return status.Errorf(status.NotFound, "user not found")
|
return status.Errorf(status.NotFound, "user not found")
|
||||||
}
|
}
|
||||||
if executingUser.Role != UserRoleAdmin || !executingUser.IsServiceUser {
|
if executingUser.Role != UserRoleAdmin || !executingUser.IsServiceUser {
|
||||||
return status.Errorf(status.PermissionDenied, "only admins service user can delete integration group")
|
return status.Errorf(status.PermissionDenied, "only service users with admin power can delete integration group")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,7 +57,7 @@ func TestDefaultAccountManager_DeleteGroup(t *testing.T) {
|
|||||||
{
|
{
|
||||||
"integration",
|
"integration",
|
||||||
"grp-for-integration",
|
"grp-for-integration",
|
||||||
"only admins service user can delete integration group",
|
"only service users with admin power can delete integration group",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ func (h *AccountsHandler) GetAllAccounts(w http.ResponseWriter, r *http.Request)
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !user.IsAdmin() {
|
if !user.HasAdminPower() {
|
||||||
util.WriteError(status.Errorf(status.PermissionDenied, "the user has no permission to access account data"), w)
|
util.WriteError(status.Errorf(status.PermissionDenied, "the user has no permission to access account data"), w)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -53,7 +53,7 @@ func (a *AccessControl) Handler(h http.Handler) http.Handler {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !user.IsAdmin() {
|
if !user.HasAdminPower() {
|
||||||
switch r.Method {
|
switch r.Method {
|
||||||
case http.MethodDelete, http.MethodPost, http.MethodPatch, http.MethodPut:
|
case http.MethodDelete, http.MethodPost, http.MethodPatch, http.MethodPut:
|
||||||
|
|
||||||
@ -63,7 +63,7 @@ func (a *AccessControl) Handler(h http.Handler) http.Handler {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
util.WriteError(status.Errorf(status.PermissionDenied, "only admin can perform this operation"), w)
|
util.WriteError(status.Errorf(status.PermissionDenied, "only users with admin power can perform this operation"), w)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
77
management/server/idp/mock.go
Normal file
77
management/server/idp/mock.go
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
package idp
|
||||||
|
|
||||||
|
// MockIDP is a mock implementation of the IDP interface
|
||||||
|
type MockIDP struct {
|
||||||
|
UpdateUserAppMetadataFunc func(userId string, appMetadata AppMetadata) error
|
||||||
|
GetUserDataByIDFunc func(userId string, appMetadata AppMetadata) (*UserData, error)
|
||||||
|
GetAccountFunc func(accountId string) ([]*UserData, error)
|
||||||
|
GetAllAccountsFunc func() (map[string][]*UserData, error)
|
||||||
|
CreateUserFunc func(email, name, accountID, invitedByEmail string) (*UserData, error)
|
||||||
|
GetUserByEmailFunc func(email string) ([]*UserData, error)
|
||||||
|
InviteUserByIDFunc func(userID string) error
|
||||||
|
DeleteUserFunc func(userID string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateUserAppMetadata is a mock implementation of the IDP interface UpdateUserAppMetadata method
|
||||||
|
func (m *MockIDP) UpdateUserAppMetadata(userId string, appMetadata AppMetadata) error {
|
||||||
|
if m.UpdateUserAppMetadataFunc != nil {
|
||||||
|
return m.UpdateUserAppMetadataFunc(userId, appMetadata)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUserDataByID is a mock implementation of the IDP interface GetUserDataByID method
|
||||||
|
func (m *MockIDP) GetUserDataByID(userId string, appMetadata AppMetadata) (*UserData, error) {
|
||||||
|
if m.GetUserDataByIDFunc != nil {
|
||||||
|
return m.GetUserDataByIDFunc(userId, appMetadata)
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAccount is a mock implementation of the IDP interface GetAccount method
|
||||||
|
func (m *MockIDP) GetAccount(accountId string) ([]*UserData, error) {
|
||||||
|
if m.GetAccountFunc != nil {
|
||||||
|
return m.GetAccountFunc(accountId)
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAllAccounts is a mock implementation of the IDP interface GetAllAccounts method
|
||||||
|
func (m *MockIDP) GetAllAccounts() (map[string][]*UserData, error) {
|
||||||
|
if m.GetAllAccountsFunc != nil {
|
||||||
|
return m.GetAllAccountsFunc()
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateUser is a mock implementation of the IDP interface CreateUser method
|
||||||
|
func (m *MockIDP) CreateUser(email, name, accountID, invitedByEmail string) (*UserData, error) {
|
||||||
|
if m.CreateUserFunc != nil {
|
||||||
|
return m.CreateUserFunc(email, name, accountID, invitedByEmail)
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUserByEmail is a mock implementation of the IDP interface GetUserByEmail method
|
||||||
|
func (m *MockIDP) GetUserByEmail(email string) ([]*UserData, error) {
|
||||||
|
if m.GetUserByEmailFunc != nil {
|
||||||
|
return m.GetUserByEmailFunc(email)
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// InviteUserByID is a mock implementation of the IDP interface InviteUserByID method
|
||||||
|
func (m *MockIDP) InviteUserByID(userID string) error {
|
||||||
|
if m.InviteUserByIDFunc != nil {
|
||||||
|
return m.InviteUserByIDFunc(userID)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteUser is a mock implementation of the IDP interface DeleteUser method
|
||||||
|
func (m *MockIDP) DeleteUser(userID string) error {
|
||||||
|
if m.DeleteUserFunc != nil {
|
||||||
|
return m.DeleteUserFunc(userID)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
@ -214,7 +214,7 @@ func (am *DefaultAccountManager) GetPeers(accountID, userID string) ([]*Peer, er
|
|||||||
peers := make([]*Peer, 0)
|
peers := make([]*Peer, 0)
|
||||||
peersMap := make(map[string]*Peer)
|
peersMap := make(map[string]*Peer)
|
||||||
for _, peer := range account.Peers {
|
for _, peer := range account.Peers {
|
||||||
if !user.IsAdmin() && user.Id != peer.UserID {
|
if !user.HasAdminPower() && user.Id != peer.UserID {
|
||||||
// only display peers that belong to the current user if the current user is not an admin
|
// only display peers that belong to the current user if the current user is not an admin
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -862,7 +862,7 @@ func (am *DefaultAccountManager) GetPeer(accountID, peerID, userID string) (*Pee
|
|||||||
}
|
}
|
||||||
|
|
||||||
// if admin or user owns this peer, return peer
|
// if admin or user owns this peer, return peer
|
||||||
if user.IsAdmin() || peer.UserID == userID {
|
if user.HasAdminPower() || peer.UserID == userID {
|
||||||
return peer, nil
|
return peer, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -319,8 +319,8 @@ func (am *DefaultAccountManager) GetPolicy(accountID, policyID, userID string) (
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !user.IsAdmin() {
|
if !user.HasAdminPower() {
|
||||||
return nil, status.Errorf(status.PermissionDenied, "only admins are allowed to view policies")
|
return nil, status.Errorf(status.PermissionDenied, "only users with admin power are allowed to view policies")
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, policy := range account.Policies {
|
for _, policy := range account.Policies {
|
||||||
@ -402,8 +402,8 @@ func (am *DefaultAccountManager) ListPolicies(accountID, userID string) ([]*Poli
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !user.IsAdmin() {
|
if !user.HasAdminPower() {
|
||||||
return nil, status.Errorf(status.PermissionDenied, "Only Administrators can view policies")
|
return nil, status.Errorf(status.PermissionDenied, "only users with admin power can view policies")
|
||||||
}
|
}
|
||||||
|
|
||||||
return account.Policies, nil
|
return account.Policies, nil
|
||||||
|
@ -27,8 +27,8 @@ func (am *DefaultAccountManager) GetRoute(accountID, routeID, userID string) (*r
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !user.IsAdmin() {
|
if !user.HasAdminPower() {
|
||||||
return nil, status.Errorf(status.PermissionDenied, "Only administrators can view Network Routes")
|
return nil, status.Errorf(status.PermissionDenied, "only users with admin power can view Network Routes")
|
||||||
}
|
}
|
||||||
|
|
||||||
wantedRoute, found := account.Routes[routeID]
|
wantedRoute, found := account.Routes[routeID]
|
||||||
@ -296,8 +296,8 @@ func (am *DefaultAccountManager) ListRoutes(accountID, userID string) ([]*route.
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !user.IsAdmin() {
|
if !user.HasAdminPower() {
|
||||||
return nil, status.Errorf(status.PermissionDenied, "Only administrators can view Network Routes")
|
return nil, status.Errorf(status.PermissionDenied, "only users with admin power can view Network Routes")
|
||||||
}
|
}
|
||||||
|
|
||||||
routes := make([]*route.Route, 0, len(account.Routes))
|
routes := make([]*route.Route, 0, len(account.Routes))
|
||||||
|
@ -342,7 +342,7 @@ func (am *DefaultAccountManager) ListSetupKeys(accountID, userID string) ([]*Set
|
|||||||
keys := make([]*SetupKey, 0, len(account.SetupKeys))
|
keys := make([]*SetupKey, 0, len(account.SetupKeys))
|
||||||
for _, key := range account.SetupKeys {
|
for _, key := range account.SetupKeys {
|
||||||
var k *SetupKey
|
var k *SetupKey
|
||||||
if !user.IsAdmin() {
|
if !user.HasAdminPower() {
|
||||||
k = key.HiddenCopy(999)
|
k = key.HiddenCopy(999)
|
||||||
} else {
|
} else {
|
||||||
k = key.Copy()
|
k = key.Copy()
|
||||||
@ -384,7 +384,7 @@ func (am *DefaultAccountManager) GetSetupKey(accountID, userID, keyID string) (*
|
|||||||
foundKey.UpdatedAt = foundKey.CreatedAt
|
foundKey.UpdatedAt = foundKey.CreatedAt
|
||||||
}
|
}
|
||||||
|
|
||||||
if !user.IsAdmin() {
|
if !user.HasAdminPower() {
|
||||||
foundKey = foundKey.HiddenCopy(999)
|
foundKey = foundKey.HiddenCopy(999)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
UserRoleOwner UserRole = "owner"
|
||||||
UserRoleAdmin UserRole = "admin"
|
UserRoleAdmin UserRole = "admin"
|
||||||
UserRoleUser UserRole = "user"
|
UserRoleUser UserRole = "user"
|
||||||
UserRoleUnknown UserRole = "unknown"
|
UserRoleUnknown UserRole = "unknown"
|
||||||
@ -30,6 +31,8 @@ const (
|
|||||||
// StrRoleToUserRole returns UserRole for a given strRole or UserRoleUnknown if the specified role is unknown
|
// StrRoleToUserRole returns UserRole for a given strRole or UserRoleUnknown if the specified role is unknown
|
||||||
func StrRoleToUserRole(strRole string) UserRole {
|
func StrRoleToUserRole(strRole string) UserRole {
|
||||||
switch strings.ToLower(strRole) {
|
switch strings.ToLower(strRole) {
|
||||||
|
case "owner":
|
||||||
|
return UserRoleOwner
|
||||||
case "admin":
|
case "admin":
|
||||||
return UserRoleAdmin
|
return UserRoleAdmin
|
||||||
case "user":
|
case "user":
|
||||||
@ -97,9 +100,9 @@ func (u *User) LastDashboardLoginChanged(LastLogin time.Time) bool {
|
|||||||
return LastLogin.After(u.LastLogin) && !u.LastLogin.IsZero()
|
return LastLogin.After(u.LastLogin) && !u.LastLogin.IsZero()
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsAdmin returns true if the user is an admin, false otherwise
|
// HasAdminPower returns true if the user has admin or owner roles, false otherwise
|
||||||
func (u *User) IsAdmin() bool {
|
func (u *User) HasAdminPower() bool {
|
||||||
return u.Role == UserRoleAdmin
|
return u.Role == UserRoleAdmin || u.Role == UserRoleOwner
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToUserInfo converts a User object to a UserInfo object.
|
// ToUserInfo converts a User object to a UserInfo object.
|
||||||
@ -193,6 +196,11 @@ func NewAdminUser(id string) *User {
|
|||||||
return NewUser(id, UserRoleAdmin, false, false, "", []string{}, UserIssuedAPI)
|
return NewUser(id, UserRoleAdmin, false, false, "", []string{}, UserIssuedAPI)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewOwnerUser creates a new user with role UserRoleOwner
|
||||||
|
func NewOwnerUser(id string) *User {
|
||||||
|
return NewUser(id, UserRoleOwner, false, false, "", []string{}, UserIssuedAPI)
|
||||||
|
}
|
||||||
|
|
||||||
// createServiceUser creates a new service user under the given account.
|
// createServiceUser creates a new service user under the given account.
|
||||||
func (am *DefaultAccountManager) createServiceUser(accountID string, initiatorUserID string, role UserRole, serviceUserName string, nonDeletable bool, autoGroups []string) (*UserInfo, error) {
|
func (am *DefaultAccountManager) createServiceUser(accountID string, initiatorUserID string, role UserRole, serviceUserName string, nonDeletable bool, autoGroups []string) (*UserInfo, error) {
|
||||||
unlock := am.Store.AcquireAccountLock(accountID)
|
unlock := am.Store.AcquireAccountLock(accountID)
|
||||||
@ -207,8 +215,12 @@ func (am *DefaultAccountManager) createServiceUser(accountID string, initiatorUs
|
|||||||
if executingUser == nil {
|
if executingUser == nil {
|
||||||
return nil, status.Errorf(status.NotFound, "user not found")
|
return nil, status.Errorf(status.NotFound, "user not found")
|
||||||
}
|
}
|
||||||
if executingUser.Role != UserRoleAdmin {
|
if !executingUser.HasAdminPower() {
|
||||||
return nil, status.Errorf(status.PermissionDenied, "only admins can create service users")
|
return nil, status.Errorf(status.PermissionDenied, "only users with admin power can create service users")
|
||||||
|
}
|
||||||
|
|
||||||
|
if role == UserRoleOwner {
|
||||||
|
return nil, status.Errorf(status.InvalidArgument, "can't create a service user with owner role")
|
||||||
}
|
}
|
||||||
|
|
||||||
newUserID := uuid.New().String()
|
newUserID := uuid.New().String()
|
||||||
@ -258,11 +270,15 @@ func (am *DefaultAccountManager) inviteNewUser(accountID, userID string, invite
|
|||||||
return nil, fmt.Errorf("provided user update is nil")
|
return nil, fmt.Errorf("provided user update is nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
invitedRole := StrRoleToUserRole(invite.Role)
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case invite.Name == "":
|
case invite.Name == "":
|
||||||
return nil, status.Errorf(status.InvalidArgument, "name can't be empty")
|
return nil, status.Errorf(status.InvalidArgument, "name can't be empty")
|
||||||
case invite.Email == "":
|
case invite.Email == "":
|
||||||
return nil, status.Errorf(status.InvalidArgument, "email can't be empty")
|
return nil, status.Errorf(status.InvalidArgument, "email can't be empty")
|
||||||
|
case invitedRole == UserRoleOwner:
|
||||||
|
return nil, status.Errorf(status.InvalidArgument, "can't invite a user with owner role")
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -311,10 +327,9 @@ func (am *DefaultAccountManager) inviteNewUser(accountID, userID string, invite
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
role := StrRoleToUserRole(invite.Role)
|
|
||||||
newUser := &User{
|
newUser := &User{
|
||||||
Id: idpUser.ID,
|
Id: idpUser.ID,
|
||||||
Role: role,
|
Role: invitedRole,
|
||||||
AutoGroups: invite.AutoGroups,
|
AutoGroups: invite.AutoGroups,
|
||||||
Issued: invite.Issued,
|
Issued: invite.Issued,
|
||||||
IntegrationReference: invite.IntegrationReference,
|
IntegrationReference: invite.IntegrationReference,
|
||||||
@ -416,8 +431,8 @@ func (am *DefaultAccountManager) DeleteUser(accountID, initiatorUserID string, t
|
|||||||
if executingUser == nil {
|
if executingUser == nil {
|
||||||
return status.Errorf(status.NotFound, "user not found")
|
return status.Errorf(status.NotFound, "user not found")
|
||||||
}
|
}
|
||||||
if executingUser.Role != UserRoleAdmin {
|
if !executingUser.HasAdminPower() {
|
||||||
return status.Errorf(status.PermissionDenied, "only admins can delete users")
|
return status.Errorf(status.PermissionDenied, "only users with admin power can delete users")
|
||||||
}
|
}
|
||||||
|
|
||||||
targetUser := account.Users[targetUserID]
|
targetUser := account.Users[targetUserID]
|
||||||
@ -425,9 +440,13 @@ func (am *DefaultAccountManager) DeleteUser(accountID, initiatorUserID string, t
|
|||||||
return status.Errorf(status.NotFound, "target user not found")
|
return status.Errorf(status.NotFound, "target user not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if targetUser.Role == UserRoleOwner {
|
||||||
|
return status.Errorf(status.PermissionDenied, "unable to delete a user with owner role")
|
||||||
|
}
|
||||||
|
|
||||||
// disable deleting integration user if the initiator is not admin service user
|
// disable deleting integration user if the initiator is not admin service user
|
||||||
if targetUser.Issued == UserIssuedIntegration && !executingUser.IsServiceUser {
|
if targetUser.Issued == UserIssuedIntegration && !executingUser.IsServiceUser {
|
||||||
return status.Errorf(status.PermissionDenied, "only admin service user can delete this user")
|
return status.Errorf(status.PermissionDenied, "only integration service user can delete this user")
|
||||||
}
|
}
|
||||||
|
|
||||||
// handle service user first and exit, no need to fetch extra data from IDP, etc
|
// handle service user first and exit, no need to fetch extra data from IDP, etc
|
||||||
@ -566,7 +585,7 @@ func (am *DefaultAccountManager) CreatePAT(accountID string, initiatorUserID str
|
|||||||
return nil, status.Errorf(status.NotFound, "user not found")
|
return nil, status.Errorf(status.NotFound, "user not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
if !(initiatorUserID == targetUserID || (executingUser.IsAdmin() && targetUser.IsServiceUser)) {
|
if !(initiatorUserID == targetUserID || (executingUser.HasAdminPower() && targetUser.IsServiceUser)) {
|
||||||
return nil, status.Errorf(status.PermissionDenied, "no permission to create PAT for this user")
|
return nil, status.Errorf(status.PermissionDenied, "no permission to create PAT for this user")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -608,7 +627,7 @@ func (am *DefaultAccountManager) DeletePAT(accountID string, initiatorUserID str
|
|||||||
return status.Errorf(status.NotFound, "user not found")
|
return status.Errorf(status.NotFound, "user not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
if !(initiatorUserID == targetUserID || (executingUser.IsAdmin() && targetUser.IsServiceUser)) {
|
if !(initiatorUserID == targetUserID || (executingUser.HasAdminPower() && targetUser.IsServiceUser)) {
|
||||||
return status.Errorf(status.PermissionDenied, "no permission to delete PAT for this user")
|
return status.Errorf(status.PermissionDenied, "no permission to delete PAT for this user")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -658,7 +677,7 @@ func (am *DefaultAccountManager) GetPAT(accountID string, initiatorUserID string
|
|||||||
return nil, status.Errorf(status.NotFound, "user not found")
|
return nil, status.Errorf(status.NotFound, "user not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
if !(initiatorUserID == targetUserID || (executingUser.IsAdmin() && targetUser.IsServiceUser)) {
|
if !(initiatorUserID == targetUserID || (executingUser.HasAdminPower() && targetUser.IsServiceUser)) {
|
||||||
return nil, status.Errorf(status.PermissionDenied, "no permission to get PAT for this userser")
|
return nil, status.Errorf(status.PermissionDenied, "no permission to get PAT for this userser")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -690,7 +709,7 @@ func (am *DefaultAccountManager) GetAllPATs(accountID string, initiatorUserID st
|
|||||||
return nil, status.Errorf(status.NotFound, "user not found")
|
return nil, status.Errorf(status.NotFound, "user not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
if !(initiatorUserID == targetUserID || (executingUser.IsAdmin() && targetUser.IsServiceUser)) {
|
if !(initiatorUserID == targetUserID || (executingUser.HasAdminPower() && targetUser.IsServiceUser)) {
|
||||||
return nil, status.Errorf(status.PermissionDenied, "no permission to get PAT for this user")
|
return nil, status.Errorf(status.PermissionDenied, "no permission to get PAT for this user")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -727,8 +746,8 @@ func (am *DefaultAccountManager) SaveOrAddUser(accountID, initiatorUserID string
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !initiatorUser.IsAdmin() || initiatorUser.IsBlocked() {
|
if !initiatorUser.HasAdminPower() || initiatorUser.IsBlocked() {
|
||||||
return nil, status.Errorf(status.PermissionDenied, "only admins are authorized to perform user update operations")
|
return nil, status.Errorf(status.PermissionDenied, "only users with admin power are authorized to perform user update operations")
|
||||||
}
|
}
|
||||||
|
|
||||||
oldUser := account.Users[update.Id]
|
oldUser := account.Users[update.Id]
|
||||||
@ -740,14 +759,38 @@ func (am *DefaultAccountManager) SaveOrAddUser(accountID, initiatorUserID string
|
|||||||
oldUser = update
|
oldUser = update
|
||||||
}
|
}
|
||||||
|
|
||||||
if initiatorUser.IsAdmin() && initiatorUserID == update.Id && oldUser.Blocked != update.Blocked {
|
if initiatorUser.HasAdminPower() && initiatorUserID == update.Id && oldUser.Blocked != update.Blocked {
|
||||||
return nil, status.Errorf(status.PermissionDenied, "admins can't block or unblock themselves")
|
return nil, status.Errorf(status.PermissionDenied, "admins can't block or unblock themselves")
|
||||||
}
|
}
|
||||||
|
|
||||||
if initiatorUser.IsAdmin() && initiatorUserID == update.Id && update.Role != UserRoleAdmin {
|
if initiatorUser.HasAdminPower() && initiatorUserID == update.Id && update.Role != initiatorUser.Role {
|
||||||
return nil, status.Errorf(status.PermissionDenied, "admins can't change their role")
|
return nil, status.Errorf(status.PermissionDenied, "admins can't change their role")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if initiatorUser.Role == UserRoleAdmin && oldUser.Role == UserRoleOwner && update.Role != oldUser.Role {
|
||||||
|
return nil, status.Errorf(status.PermissionDenied, "only owners can remove owner role from their user")
|
||||||
|
}
|
||||||
|
|
||||||
|
if initiatorUser.Role == UserRoleAdmin && oldUser.Role == UserRoleOwner && update.IsBlocked() && !oldUser.IsBlocked() {
|
||||||
|
return nil, status.Errorf(status.PermissionDenied, "unable to block owner user")
|
||||||
|
}
|
||||||
|
|
||||||
|
if initiatorUser.Role == UserRoleAdmin && update.Role == UserRoleOwner && update.Role != oldUser.Role {
|
||||||
|
return nil, status.Errorf(status.PermissionDenied, "only owners can add owner role to other users")
|
||||||
|
}
|
||||||
|
|
||||||
|
if oldUser.IsServiceUser && update.Role == UserRoleOwner {
|
||||||
|
return nil, status.Errorf(status.PermissionDenied, "can't update a service user with owner role")
|
||||||
|
}
|
||||||
|
|
||||||
|
transferedOwnerRole := false
|
||||||
|
if initiatorUser.Role == UserRoleOwner && initiatorUserID != update.Id && update.Role == UserRoleOwner {
|
||||||
|
newInitiatorUser := initiatorUser.Copy()
|
||||||
|
newInitiatorUser.Role = UserRoleAdmin
|
||||||
|
account.Users[initiatorUserID] = newInitiatorUser
|
||||||
|
transferedOwnerRole = true
|
||||||
|
}
|
||||||
|
|
||||||
// only auto groups, revoked status, and integration reference can be updated for now
|
// only auto groups, revoked status, and integration reference can be updated for now
|
||||||
newUser := oldUser.Copy()
|
newUser := oldUser.Copy()
|
||||||
newUser.Role = update.Role
|
newUser.Role = update.Role
|
||||||
@ -806,9 +849,12 @@ func (am *DefaultAccountManager) SaveOrAddUser(accountID, initiatorUserID string
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// store activity logs
|
switch {
|
||||||
if oldUser.Role != newUser.Role {
|
case transferedOwnerRole:
|
||||||
|
am.StoreEvent(initiatorUserID, oldUser.Id, accountID, activity.TransferredOwnerRole, nil)
|
||||||
|
case oldUser.Role != newUser.Role:
|
||||||
am.StoreEvent(initiatorUserID, oldUser.Id, accountID, activity.UserRoleUpdated, map[string]any{"role": newUser.Role})
|
am.StoreEvent(initiatorUserID, oldUser.Id, accountID, activity.UserRoleUpdated, map[string]any{"role": newUser.Role})
|
||||||
|
default:
|
||||||
}
|
}
|
||||||
|
|
||||||
if update.AutoGroups != nil {
|
if update.AutoGroups != nil {
|
||||||
@ -882,7 +928,7 @@ func (am *DefaultAccountManager) GetOrCreateAccountByUser(userID, domain string)
|
|||||||
|
|
||||||
userObj := account.Users[userID]
|
userObj := account.Users[userID]
|
||||||
|
|
||||||
if account.Domain != lowerDomain && userObj.Role == UserRoleAdmin {
|
if account.Domain != lowerDomain && userObj.Role == UserRoleOwner {
|
||||||
account.Domain = lowerDomain
|
account.Domain = lowerDomain
|
||||||
err = am.Store.SaveAccount(account)
|
err = am.Store.SaveAccount(account)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -940,7 +986,7 @@ func (am *DefaultAccountManager) GetUsersFromAccount(accountID, userID string) (
|
|||||||
// in case of self-hosted, or IDP doesn't return anything, we will return the locally stored userInfo
|
// in case of self-hosted, or IDP doesn't return anything, we will return the locally stored userInfo
|
||||||
if len(queriedUsers) == 0 {
|
if len(queriedUsers) == 0 {
|
||||||
for _, accountUser := range account.Users {
|
for _, accountUser := range account.Users {
|
||||||
if !user.IsAdmin() && user.Id != accountUser.Id {
|
if !user.HasAdminPower() && user.Id != accountUser.Id {
|
||||||
// if user is not an admin then show only current user and do not show other users
|
// if user is not an admin then show only current user and do not show other users
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -954,7 +1000,7 @@ func (am *DefaultAccountManager) GetUsersFromAccount(accountID, userID string) (
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, localUser := range account.Users {
|
for _, localUser := range account.Users {
|
||||||
if !user.IsAdmin() && user.Id != localUser.Id {
|
if !user.HasAdminPower() && user.Id != localUser.Id {
|
||||||
// if user is not an admin then show only current user and do not show other users
|
// if user is not an admin then show only current user and do not show other users
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -348,6 +348,11 @@ func TestUser_CreateServiceUser(t *testing.T) {
|
|||||||
assert.Zero(t, user.Email)
|
assert.Zero(t, user.Email)
|
||||||
assert.True(t, user.IsServiceUser)
|
assert.True(t, user.IsServiceUser)
|
||||||
assert.Equal(t, "active", user.Status)
|
assert.Equal(t, "active", user.Status)
|
||||||
|
|
||||||
|
_, err = am.createServiceUser(mockAccountID, mockUserID, UserRoleOwner, mockServiceUserName, false, nil)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("should return error when creating service user with owner role")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUser_CreateUser_ServiceUser(t *testing.T) {
|
func TestUser_CreateUser_ServiceUser(t *testing.T) {
|
||||||
@ -412,6 +417,75 @@ func TestUser_CreateUser_RegularUser(t *testing.T) {
|
|||||||
assert.Errorf(t, err, "Not configured IDP will throw error but right path used")
|
assert.Errorf(t, err, "Not configured IDP will throw error but right path used")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestUser_InviteNewUser(t *testing.T) {
|
||||||
|
store := newStore(t)
|
||||||
|
account := newAccountWithId(mockAccountID, mockUserID, "")
|
||||||
|
|
||||||
|
err := store.SaveAccount(account)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error when saving account: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
am := DefaultAccountManager{
|
||||||
|
Store: store,
|
||||||
|
eventStore: &activity.InMemoryEventStore{},
|
||||||
|
cacheLoading: map[string]chan struct{}{},
|
||||||
|
}
|
||||||
|
|
||||||
|
goCacheClient := gocache.New(CacheExpirationMax, 30*time.Minute)
|
||||||
|
goCacheStore := cacheStore.NewGoCache(goCacheClient)
|
||||||
|
am.cacheManager = cache.NewLoadable[[]*idp.UserData](am.loadAccount, cache.New[[]*idp.UserData](goCacheStore))
|
||||||
|
|
||||||
|
mockData := []*idp.UserData{
|
||||||
|
{
|
||||||
|
Email: "user@test.com",
|
||||||
|
Name: "user",
|
||||||
|
ID: mockUserID,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
idpMock := idp.MockIDP{
|
||||||
|
CreateUserFunc: func(email, name, accountID, invitedByEmail string) (*idp.UserData, error) {
|
||||||
|
newData := &idp.UserData{
|
||||||
|
Email: email,
|
||||||
|
Name: name,
|
||||||
|
ID: "id",
|
||||||
|
}
|
||||||
|
|
||||||
|
mockData = append(mockData, newData)
|
||||||
|
|
||||||
|
return newData, nil
|
||||||
|
},
|
||||||
|
GetAccountFunc: func(accountId string) ([]*idp.UserData, error) {
|
||||||
|
return mockData, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
am.idpManager = &idpMock
|
||||||
|
|
||||||
|
// test if new invite with regular role works
|
||||||
|
_, err = am.inviteNewUser(mockAccountID, mockUserID, &UserInfo{
|
||||||
|
Name: mockServiceUserName,
|
||||||
|
Role: mockRole,
|
||||||
|
Email: "test@teste.com",
|
||||||
|
IsServiceUser: false,
|
||||||
|
AutoGroups: []string{"group1", "group2"},
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.NoErrorf(t, err, "Invite user should not throw error")
|
||||||
|
|
||||||
|
// test if new invite with owner role fails
|
||||||
|
_, err = am.inviteNewUser(mockAccountID, mockUserID, &UserInfo{
|
||||||
|
Name: mockServiceUserName,
|
||||||
|
Role: string(UserRoleOwner),
|
||||||
|
Email: "test2@teste.com",
|
||||||
|
IsServiceUser: false,
|
||||||
|
AutoGroups: []string{"group1", "group2"},
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.Errorf(t, err, "Invite user with owner role should throw error")
|
||||||
|
}
|
||||||
|
|
||||||
func TestUser_DeleteUser_ServiceUser(t *testing.T) {
|
func TestUser_DeleteUser_ServiceUser(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
@ -514,6 +588,14 @@ func TestUser_DeleteUser_regularUser(t *testing.T) {
|
|||||||
Issued: UserIssuedIntegration,
|
Issued: UserIssuedIntegration,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
targetId = "user5"
|
||||||
|
account.Users[targetId] = &User{
|
||||||
|
Id: targetId,
|
||||||
|
IsServiceUser: false,
|
||||||
|
Issued: UserIssuedAPI,
|
||||||
|
Role: UserRoleOwner,
|
||||||
|
}
|
||||||
|
|
||||||
err := store.SaveAccount(account)
|
err := store.SaveAccount(account)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Error when saving account: %s", err)
|
t.Fatalf("Error when saving account: %s", err)
|
||||||
@ -546,6 +628,12 @@ func TestUser_DeleteUser_regularUser(t *testing.T) {
|
|||||||
assertErrFunc: assert.Error,
|
assertErrFunc: assert.Error,
|
||||||
assertErrMessage: "only admin service user can delete this user",
|
assertErrMessage: "only admin service user can delete this user",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "Delete user with owner role should return permission denied ",
|
||||||
|
userID: "user5",
|
||||||
|
assertErrFunc: assert.Error,
|
||||||
|
assertErrMessage: "unable to delete a user with owner role",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, testCase := range testCases {
|
for _, testCase := range testCases {
|
||||||
@ -581,7 +669,7 @@ func TestDefaultAccountManager_GetUser(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
assert.Equal(t, mockUserID, user.Id)
|
assert.Equal(t, mockUserID, user.Id)
|
||||||
assert.True(t, user.IsAdmin())
|
assert.True(t, user.HasAdminPower())
|
||||||
assert.False(t, user.IsBlocked())
|
assert.False(t, user.IsBlocked())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -609,7 +697,7 @@ func TestDefaultAccountManager_ListUsers(t *testing.T) {
|
|||||||
admins := 0
|
admins := 0
|
||||||
regular := 0
|
regular := 0
|
||||||
for _, user := range users {
|
for _, user := range users {
|
||||||
if user.IsAdmin() {
|
if user.HasAdminPower() {
|
||||||
admins++
|
admins++
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -677,10 +765,10 @@ func TestDefaultAccountManager_ExternalCache(t *testing.T) {
|
|||||||
func TestUser_IsAdmin(t *testing.T) {
|
func TestUser_IsAdmin(t *testing.T) {
|
||||||
|
|
||||||
user := NewAdminUser(mockUserID)
|
user := NewAdminUser(mockUserID)
|
||||||
assert.True(t, user.IsAdmin())
|
assert.True(t, user.HasAdminPower())
|
||||||
|
|
||||||
user = NewRegularUser(mockUserID)
|
user = NewRegularUser(mockUserID)
|
||||||
assert.False(t, user.IsAdmin())
|
assert.False(t, user.HasAdminPower())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUser_GetUsersFromAccount_ForAdmin(t *testing.T) {
|
func TestUser_GetUsersFromAccount_ForAdmin(t *testing.T) {
|
||||||
@ -746,26 +834,39 @@ func TestDefaultAccountManager_SaveUser(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
regularUserID := "regularUser"
|
regularUserID := "regularUser"
|
||||||
|
serviceUserID := "serviceUser"
|
||||||
|
adminUserID := "adminUser"
|
||||||
|
ownerUserID := "ownerUser"
|
||||||
|
|
||||||
tt := []struct {
|
tt := []struct {
|
||||||
name string
|
name string
|
||||||
adminInitiator bool
|
initiatorID string
|
||||||
update *User
|
update *User
|
||||||
expectedErr bool
|
expectedErr bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "Should_Fail_To_Update_Admin_Role",
|
name: "Should_Fail_To_Update_Admin_Role",
|
||||||
expectedErr: true,
|
expectedErr: true,
|
||||||
adminInitiator: true,
|
initiatorID: adminUserID,
|
||||||
update: &User{
|
update: &User{
|
||||||
Id: userID,
|
Id: adminUserID,
|
||||||
Role: UserRoleUser,
|
Role: UserRoleUser,
|
||||||
Blocked: false,
|
Blocked: false,
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
name: "Should_Fail_When_Admin_Blocks_Themselves",
|
name: "Should_Fail_When_Admin_Blocks_Themselves",
|
||||||
expectedErr: true,
|
expectedErr: true,
|
||||||
adminInitiator: true,
|
initiatorID: adminUserID,
|
||||||
|
update: &User{
|
||||||
|
Id: adminUserID,
|
||||||
|
Role: UserRoleAdmin,
|
||||||
|
Blocked: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Should_Fail_To_Update_Non_Existing_User",
|
||||||
|
expectedErr: true,
|
||||||
|
initiatorID: adminUserID,
|
||||||
update: &User{
|
update: &User{
|
||||||
Id: userID,
|
Id: userID,
|
||||||
Role: UserRoleAdmin,
|
Role: UserRoleAdmin,
|
||||||
@ -773,66 +874,125 @@ func TestDefaultAccountManager_SaveUser(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Should_Fail_To_Update_Non_Existing_User",
|
name: "Should_Fail_To_Update_When_Initiator_Is_Not_An_Admin",
|
||||||
expectedErr: true,
|
expectedErr: true,
|
||||||
adminInitiator: true,
|
initiatorID: regularUserID,
|
||||||
update: &User{
|
update: &User{
|
||||||
Id: userID,
|
Id: adminUserID,
|
||||||
Role: UserRoleAdmin,
|
Role: UserRoleAdmin,
|
||||||
Blocked: true,
|
Blocked: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Should_Fail_To_Update_When_Initiator_Is_Not_An_Admin",
|
name: "Should_Update_User",
|
||||||
expectedErr: true,
|
expectedErr: false,
|
||||||
adminInitiator: false,
|
initiatorID: adminUserID,
|
||||||
update: &User{
|
|
||||||
Id: userID,
|
|
||||||
Role: UserRoleAdmin,
|
|
||||||
Blocked: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Should_Update_User",
|
|
||||||
expectedErr: false,
|
|
||||||
adminInitiator: true,
|
|
||||||
update: &User{
|
update: &User{
|
||||||
Id: regularUserID,
|
Id: regularUserID,
|
||||||
Role: UserRoleAdmin,
|
Role: UserRoleAdmin,
|
||||||
Blocked: true,
|
Blocked: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "Should_Transfer_Owner_Role_To_User",
|
||||||
|
expectedErr: false,
|
||||||
|
initiatorID: ownerUserID,
|
||||||
|
update: &User{
|
||||||
|
Id: adminUserID,
|
||||||
|
Role: UserRoleAdmin,
|
||||||
|
Blocked: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Should_Fail_To_Transfer_Owner_Role_To_Service_User",
|
||||||
|
expectedErr: true,
|
||||||
|
initiatorID: ownerUserID,
|
||||||
|
update: &User{
|
||||||
|
Id: serviceUserID,
|
||||||
|
Role: UserRoleOwner,
|
||||||
|
Blocked: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Should_Fail_To_Update_Owner_User_Role_By_Admin",
|
||||||
|
expectedErr: true,
|
||||||
|
initiatorID: adminUserID,
|
||||||
|
update: &User{
|
||||||
|
Id: ownerUserID,
|
||||||
|
Role: UserRoleAdmin,
|
||||||
|
Blocked: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Should_Fail_To_Update_Owner_User_Role_By_User",
|
||||||
|
expectedErr: true,
|
||||||
|
initiatorID: regularUserID,
|
||||||
|
update: &User{
|
||||||
|
Id: ownerUserID,
|
||||||
|
Role: UserRoleAdmin,
|
||||||
|
Blocked: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Should_Fail_To_Update_Owner_User_Role_By_Service_User",
|
||||||
|
expectedErr: true,
|
||||||
|
initiatorID: serviceUserID,
|
||||||
|
update: &User{
|
||||||
|
Id: ownerUserID,
|
||||||
|
Role: UserRoleAdmin,
|
||||||
|
Blocked: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Should_Fail_To_Update_Owner_Role_By_Admin",
|
||||||
|
expectedErr: true,
|
||||||
|
initiatorID: adminUserID,
|
||||||
|
update: &User{
|
||||||
|
Id: regularUserID,
|
||||||
|
Role: UserRoleOwner,
|
||||||
|
Blocked: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Should_Fail_To_Block_Owner_Role_By_Admin",
|
||||||
|
expectedErr: true,
|
||||||
|
initiatorID: adminUserID,
|
||||||
|
update: &User{
|
||||||
|
Id: ownerUserID,
|
||||||
|
Role: UserRoleOwner,
|
||||||
|
Blocked: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range tt {
|
for _, tc := range tt {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
|
||||||
// create an account and an admin user
|
// create an account and an admin user
|
||||||
account, err := manager.GetOrCreateAccountByUser(userID, "netbird.io")
|
account, err := manager.GetOrCreateAccountByUser(ownerUserID, "netbird.io")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// create a regular user
|
// create other users
|
||||||
account.Users[regularUserID] = NewRegularUser(regularUserID)
|
account.Users[regularUserID] = NewRegularUser(regularUserID)
|
||||||
err = manager.Store.SaveAccount(account)
|
account.Users[adminUserID] = NewAdminUser(adminUserID)
|
||||||
if err != nil {
|
account.Users[serviceUserID] = &User{IsServiceUser: true, Id: serviceUserID, Role: UserRoleAdmin, ServiceUserName: "service"}
|
||||||
t.Fatal(err)
|
err = manager.Store.SaveAccount(account)
|
||||||
}
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
initiatorID := userID
|
updated, err := manager.SaveUser(account.Id, tc.initiatorID, tc.update)
|
||||||
if !tc.adminInitiator {
|
if tc.expectedErr {
|
||||||
initiatorID = regularUserID
|
require.Errorf(t, err, "expecting SaveUser to throw an error")
|
||||||
}
|
} else {
|
||||||
|
require.NoError(t, err, "expecting SaveUser not to throw an error")
|
||||||
|
assert.NotNil(t, updated)
|
||||||
|
|
||||||
updated, err := manager.SaveUser(account.Id, initiatorID, tc.update)
|
assert.Equal(t, string(tc.update.Role), updated.Role)
|
||||||
if tc.expectedErr {
|
assert.Equal(t, tc.update.IsBlocked(), updated.IsBlocked)
|
||||||
require.Errorf(t, err, "expecting SaveUser to throw an error")
|
}
|
||||||
} else {
|
})
|
||||||
require.NoError(t, err, "expecting SaveUser not to throw an error")
|
|
||||||
assert.NotNil(t, updated)
|
|
||||||
|
|
||||||
assert.Equal(t, string(tc.update.Role), updated.Role)
|
|
||||||
assert.Equal(t, tc.update.IsBlocked(), updated.IsBlocked)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user