mirror of
https://github.com/netbirdio/netbird.git
synced 2026-07-16 19:49:56 +00:00
[management] user info with role permissions
This commit is contained in:
@@ -31,7 +31,10 @@ var (
|
||||
LastLogin: &time.Time{},
|
||||
Name: "M. Essam",
|
||||
Permissions: &api.UserPermissions{
|
||||
DashboardView: ptr(api.UserPermissionsDashboardViewFull),
|
||||
AutoAllowNew: map[string]bool{
|
||||
"read": true,
|
||||
"write": true,
|
||||
},
|
||||
},
|
||||
Role: "user",
|
||||
Status: api.UserStatusActive,
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
"github.com/netbirdio/netbird/management/server/posture"
|
||||
"github.com/netbirdio/netbird/management/server/store"
|
||||
"github.com/netbirdio/netbird/management/server/types"
|
||||
"github.com/netbirdio/netbird/management/server/users"
|
||||
"github.com/netbirdio/netbird/route"
|
||||
)
|
||||
|
||||
@@ -114,5 +115,5 @@ type Manager interface {
|
||||
CreateAccountByPrivateDomain(ctx context.Context, initiatorId, domain string) (*types.Account, error)
|
||||
UpdateToPrimaryAccount(ctx context.Context, accountId string) (*types.Account, error)
|
||||
GetOwnerInfo(ctx context.Context, accountId string) (*types.UserInfo, error)
|
||||
GetCurrentUserInfo(ctx context.Context, accountID, userID string) (*types.UserInfo, error)
|
||||
GetCurrentUserInfo(ctx context.Context, accountID, userID string) (*users.UserInfoWithPermissions, error)
|
||||
}
|
||||
|
||||
@@ -191,11 +191,35 @@ components:
|
||||
UserPermissions:
|
||||
type: object
|
||||
properties:
|
||||
dashboard_view:
|
||||
description: User's permission to view the dashboard
|
||||
type: string
|
||||
enum: [ "limited", "blocked", "full" ]
|
||||
example: limited
|
||||
permissions:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: boolean
|
||||
propertyNames:
|
||||
type: string
|
||||
enum:
|
||||
- read
|
||||
- write
|
||||
propertyNames:
|
||||
type: string
|
||||
enum:
|
||||
- read
|
||||
- write
|
||||
example: {"networks": { "read": true, "write": false}, "peers": { "read": false, "write": false} }
|
||||
auto_allow_new:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: boolean
|
||||
propertyNames:
|
||||
type: string
|
||||
enum:
|
||||
- read
|
||||
- write
|
||||
required:
|
||||
- permissions
|
||||
- auto_allow_new
|
||||
UserRequest:
|
||||
type: object
|
||||
properties:
|
||||
|
||||
@@ -178,13 +178,6 @@ const (
|
||||
UserStatusInvited UserStatus = "invited"
|
||||
)
|
||||
|
||||
// Defines values for UserPermissionsDashboardView.
|
||||
const (
|
||||
UserPermissionsDashboardViewBlocked UserPermissionsDashboardView = "blocked"
|
||||
UserPermissionsDashboardViewFull UserPermissionsDashboardView = "full"
|
||||
UserPermissionsDashboardViewLimited UserPermissionsDashboardView = "limited"
|
||||
)
|
||||
|
||||
// AccessiblePeer defines model for AccessiblePeer.
|
||||
type AccessiblePeer struct {
|
||||
// CityName Commonly used English name of the city
|
||||
@@ -1710,13 +1703,10 @@ type UserCreateRequest struct {
|
||||
|
||||
// UserPermissions defines model for UserPermissions.
|
||||
type UserPermissions struct {
|
||||
// DashboardView User's permission to view the dashboard
|
||||
DashboardView *UserPermissionsDashboardView `json:"dashboard_view,omitempty"`
|
||||
AutoAllowNew map[string]bool `json:"auto_allow_new"`
|
||||
Permissions map[string]map[string]bool `json:"permissions"`
|
||||
}
|
||||
|
||||
// UserPermissionsDashboardView User's permission to view the dashboard
|
||||
type UserPermissionsDashboardView string
|
||||
|
||||
// UserRequest defines model for UserRequest.
|
||||
type UserRequest struct {
|
||||
// AutoGroups Group IDs to auto-assign to peers registered by this user
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/netbirdio/netbird/management/server/http/util"
|
||||
"github.com/netbirdio/netbird/management/server/status"
|
||||
"github.com/netbirdio/netbird/management/server/types"
|
||||
"github.com/netbirdio/netbird/management/server/users"
|
||||
|
||||
nbcontext "github.com/netbirdio/netbird/management/server/context"
|
||||
)
|
||||
@@ -280,7 +281,41 @@ func (h *handler) getCurrentUser(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
util.WriteJSONObject(r.Context(), w, toUserResponse(user, userID))
|
||||
util.WriteJSONObject(r.Context(), w, toUserWithPermissionsResponse(user, userID))
|
||||
}
|
||||
|
||||
func toUserWithPermissionsResponse(user *users.UserInfoWithPermissions, userID string) *api.User {
|
||||
response := toUserResponse(user.UserInfo, userID)
|
||||
if user.Permissions == nil {
|
||||
return response
|
||||
}
|
||||
|
||||
permissions := &api.UserPermissions{}
|
||||
if len(user.Permissions.AutoAllowNew) > 0 {
|
||||
permissions.AutoAllowNew = make(map[string]bool)
|
||||
for k, v := range user.Permissions.AutoAllowNew {
|
||||
permissions.AutoAllowNew[string(k)] = v
|
||||
}
|
||||
}
|
||||
|
||||
if len(user.Permissions.Permissions) > 0 {
|
||||
permissions.Permissions = make(map[string]map[string]bool)
|
||||
for module, operations := range user.Permissions.Permissions {
|
||||
if len(operations) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
access := make(map[string]bool)
|
||||
for k, v := range operations {
|
||||
access[string(k)] = v
|
||||
}
|
||||
permissions.Permissions[string(module)] = access
|
||||
}
|
||||
}
|
||||
|
||||
response.Permissions = permissions
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
func toUserResponse(user *types.UserInfo, currenUserID string) *api.User {
|
||||
@@ -316,8 +351,5 @@ func toUserResponse(user *types.UserInfo, currenUserID string) *api.User {
|
||||
IsBlocked: user.IsBlocked,
|
||||
LastLogin: &user.LastLogin,
|
||||
Issued: &user.Issued,
|
||||
Permissions: &api.UserPermissions{
|
||||
DashboardView: (*api.UserPermissionsDashboardView)(&user.Permissions.DashboardView),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
"github.com/netbirdio/netbird/management/server/mock_server"
|
||||
"github.com/netbirdio/netbird/management/server/status"
|
||||
"github.com/netbirdio/netbird/management/server/types"
|
||||
"github.com/netbirdio/netbird/management/server/users"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -107,7 +108,7 @@ func initUsersTestData() *handler {
|
||||
return nil, status.Errorf(status.NotFound, "user with ID %s does not exists", userID)
|
||||
}
|
||||
|
||||
info, err := update.Copy().ToUserInfo(nil, &types.Settings{RegularUsersViewBlocked: false})
|
||||
info, err := update.Copy().ToUserInfo(nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -124,7 +125,7 @@ func initUsersTestData() *handler {
|
||||
|
||||
return nil
|
||||
},
|
||||
GetCurrentUserInfoFunc: func(ctx context.Context, accountID, userID string) (*types.UserInfo, error) {
|
||||
GetCurrentUserInfoFunc: func(ctx context.Context, accountID, userID string) (*users.UserInfoWithPermissions, error) {
|
||||
switch userID {
|
||||
case "not-found":
|
||||
return nil, status.NewUserNotFoundError("not-found")
|
||||
@@ -135,47 +136,44 @@ func initUsersTestData() *handler {
|
||||
case "service-user":
|
||||
return nil, status.NewPermissionDeniedError()
|
||||
case "owner":
|
||||
return &types.UserInfo{
|
||||
ID: "owner",
|
||||
Name: "",
|
||||
Role: "owner",
|
||||
Status: "active",
|
||||
IsServiceUser: false,
|
||||
IsBlocked: false,
|
||||
NonDeletable: false,
|
||||
Issued: "api",
|
||||
Permissions: types.UserPermissions{
|
||||
DashboardView: "full",
|
||||
return &users.UserInfoWithPermissions{
|
||||
UserInfo: &types.UserInfo{
|
||||
ID: "owner",
|
||||
Name: "",
|
||||
Role: "owner",
|
||||
Status: "active",
|
||||
IsServiceUser: false,
|
||||
IsBlocked: false,
|
||||
NonDeletable: false,
|
||||
Issued: "api",
|
||||
},
|
||||
}, nil
|
||||
case "regular-user":
|
||||
return &types.UserInfo{
|
||||
ID: "regular-user",
|
||||
Name: "",
|
||||
Role: "user",
|
||||
Status: "active",
|
||||
IsServiceUser: false,
|
||||
IsBlocked: false,
|
||||
NonDeletable: false,
|
||||
Issued: "api",
|
||||
Permissions: types.UserPermissions{
|
||||
DashboardView: "limited",
|
||||
return &users.UserInfoWithPermissions{
|
||||
UserInfo: &types.UserInfo{
|
||||
ID: "regular-user",
|
||||
Name: "",
|
||||
Role: "user",
|
||||
Status: "active",
|
||||
IsServiceUser: false,
|
||||
IsBlocked: false,
|
||||
NonDeletable: false,
|
||||
Issued: "api",
|
||||
},
|
||||
}, nil
|
||||
|
||||
case "admin-user":
|
||||
return &types.UserInfo{
|
||||
ID: "admin-user",
|
||||
Name: "",
|
||||
Role: "admin",
|
||||
Status: "active",
|
||||
IsServiceUser: false,
|
||||
IsBlocked: false,
|
||||
NonDeletable: false,
|
||||
LastLogin: time.Time{},
|
||||
Issued: "api",
|
||||
Permissions: types.UserPermissions{
|
||||
DashboardView: "full",
|
||||
return &users.UserInfoWithPermissions{
|
||||
UserInfo: &types.UserInfo{
|
||||
ID: "admin-user",
|
||||
Name: "",
|
||||
Role: "admin",
|
||||
Status: "active",
|
||||
IsServiceUser: false,
|
||||
IsBlocked: false,
|
||||
NonDeletable: false,
|
||||
LastLogin: time.Time{},
|
||||
Issued: "api",
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
"github.com/netbirdio/netbird/management/server/posture"
|
||||
"github.com/netbirdio/netbird/management/server/store"
|
||||
"github.com/netbirdio/netbird/management/server/types"
|
||||
"github.com/netbirdio/netbird/management/server/users"
|
||||
"github.com/netbirdio/netbird/route"
|
||||
)
|
||||
|
||||
@@ -115,7 +116,7 @@ type MockAccountManager struct {
|
||||
CreateAccountByPrivateDomainFunc func(ctx context.Context, initiatorId, domain string) (*types.Account, error)
|
||||
UpdateToPrimaryAccountFunc func(ctx context.Context, accountId string) (*types.Account, error)
|
||||
GetOwnerInfoFunc func(ctx context.Context, accountID string) (*types.UserInfo, error)
|
||||
GetCurrentUserInfoFunc func(ctx context.Context, accountID, userID string) (*types.UserInfo, error)
|
||||
GetCurrentUserInfoFunc func(ctx context.Context, accountID, userID string) (*users.UserInfoWithPermissions, error)
|
||||
}
|
||||
|
||||
func (am *MockAccountManager) UpdateAccountPeers(ctx context.Context, accountID string) {
|
||||
@@ -873,7 +874,7 @@ func (am *MockAccountManager) GetOwnerInfo(ctx context.Context, accountId string
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GetOwnerInfo is not implemented")
|
||||
}
|
||||
|
||||
func (am *MockAccountManager) GetCurrentUserInfo(ctx context.Context, accountID, userID string) (*types.UserInfo, error) {
|
||||
func (am *MockAccountManager) GetCurrentUserInfo(ctx context.Context, accountID, userID string) (*users.UserInfoWithPermissions, error) {
|
||||
if am.GetCurrentUserInfoFunc != nil {
|
||||
return am.GetCurrentUserInfoFunc(ctx, accountID, userID)
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@ type Manager interface {
|
||||
ValidateUserPermissions(ctx context.Context, accountID, userID string, module modules.Module, operation operations.Operation) (bool, error)
|
||||
ValidateRoleModuleAccess(ctx context.Context, accountID string, role roles.RolePermissions, module modules.Module, operation operations.Operation) bool
|
||||
ValidateAccountAccess(ctx context.Context, accountID string, user *types.User, allowOwnerAndAdmin bool) error
|
||||
|
||||
GetRolePermissions(ctx context.Context, role types.UserRole) (roles.RolePermissions, error)
|
||||
}
|
||||
|
||||
type managerImpl struct {
|
||||
@@ -64,9 +66,9 @@ func (m *managerImpl) ValidateUserPermissions(
|
||||
return true, nil // this should be replaced by proper granular access role
|
||||
}
|
||||
|
||||
role, ok := roles.RolesMap[user.Role]
|
||||
if !ok {
|
||||
return false, status.NewUserRoleNotFoundError(string(user.Role))
|
||||
role, err := m.GetRolePermissions(ctx, user.Role)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return m.ValidateRoleModuleAccess(ctx, accountID, role, module, operation), nil
|
||||
@@ -96,3 +98,12 @@ func (m *managerImpl) ValidateAccountAccess(ctx context.Context, accountID strin
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *managerImpl) GetRolePermissions(ctx context.Context, role types.UserRole) (roles.RolePermissions, error) {
|
||||
permissions, ok := roles.RolesMap[role]
|
||||
if !ok {
|
||||
return roles.RolePermissions{}, status.NewUserRoleNotFoundError(string(role))
|
||||
}
|
||||
|
||||
return permissions, nil
|
||||
}
|
||||
|
||||
@@ -38,6 +38,21 @@ func (m *MockManager) EXPECT() *MockManagerMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// GetRolePermissions mocks base method.
|
||||
func (m *MockManager) GetRolePermissions(ctx context.Context, role types.UserRole) (roles.RolePermissions, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetRolePermissions", ctx, role)
|
||||
ret0, _ := ret[0].(roles.RolePermissions)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetRolePermissions indicates an expected call of GetRolePermissions.
|
||||
func (mr *MockManagerMockRecorder) GetRolePermissions(ctx, role interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRolePermissions", reflect.TypeOf((*MockManager)(nil).GetRolePermissions), ctx, role)
|
||||
}
|
||||
|
||||
// ValidateAccountAccess mocks base method.
|
||||
func (m *MockManager) ValidateAccountAccess(ctx context.Context, accountID string, user *types.User, allowOwnerAndAdmin bool) error {
|
||||
m.ctrl.T.Helper()
|
||||
|
||||
@@ -59,11 +59,6 @@ type UserInfo struct {
|
||||
LastLogin time.Time `json:"last_login"`
|
||||
Issued string `json:"issued"`
|
||||
IntegrationReference integration_reference.IntegrationReference `json:"-"`
|
||||
Permissions UserPermissions `json:"permissions"`
|
||||
}
|
||||
|
||||
type UserPermissions struct {
|
||||
DashboardView string `json:"dashboard_view"`
|
||||
}
|
||||
|
||||
// User represents a user of the system
|
||||
@@ -127,20 +122,12 @@ func (u *User) IsRegularUser() bool {
|
||||
}
|
||||
|
||||
// ToUserInfo converts a User object to a UserInfo object.
|
||||
func (u *User) ToUserInfo(userData *idp.UserData, settings *Settings) (*UserInfo, error) {
|
||||
func (u *User) ToUserInfo(userData *idp.UserData) (*UserInfo, error) {
|
||||
autoGroups := u.AutoGroups
|
||||
if autoGroups == nil {
|
||||
autoGroups = []string{}
|
||||
}
|
||||
|
||||
dashboardViewPermissions := "full"
|
||||
if !u.HasAdminPower() {
|
||||
dashboardViewPermissions = "limited"
|
||||
if settings.RegularUsersViewBlocked {
|
||||
dashboardViewPermissions = "blocked"
|
||||
}
|
||||
}
|
||||
|
||||
if userData == nil {
|
||||
return &UserInfo{
|
||||
ID: u.Id,
|
||||
@@ -153,9 +140,6 @@ func (u *User) ToUserInfo(userData *idp.UserData, settings *Settings) (*UserInfo
|
||||
IsBlocked: u.Blocked,
|
||||
LastLogin: u.GetLastLogin(),
|
||||
Issued: u.Issued,
|
||||
Permissions: UserPermissions{
|
||||
DashboardView: dashboardViewPermissions,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
if userData.ID != u.Id {
|
||||
@@ -178,9 +162,6 @@ func (u *User) ToUserInfo(userData *idp.UserData, settings *Settings) (*UserInfo
|
||||
IsBlocked: u.Blocked,
|
||||
LastLogin: u.GetLastLogin(),
|
||||
Issued: u.Issued,
|
||||
Permissions: UserPermissions{
|
||||
DashboardView: dashboardViewPermissions,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
"github.com/netbirdio/netbird/management/server/status"
|
||||
"github.com/netbirdio/netbird/management/server/store"
|
||||
"github.com/netbirdio/netbird/management/server/types"
|
||||
"github.com/netbirdio/netbird/management/server/users"
|
||||
"github.com/netbirdio/netbird/management/server/util"
|
||||
)
|
||||
|
||||
@@ -122,11 +123,6 @@ func (am *DefaultAccountManager) inviteNewUser(ctx context.Context, accountID, u
|
||||
CreatedAt: time.Now().UTC(),
|
||||
}
|
||||
|
||||
settings, err := am.Store.GetAccountSettings(ctx, store.LockingStrengthShare, accountID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = am.Store.SaveUser(ctx, store.LockingStrengthUpdate, newUser); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -138,7 +134,7 @@ func (am *DefaultAccountManager) inviteNewUser(ctx context.Context, accountID, u
|
||||
|
||||
am.StoreEvent(ctx, userID, newUser.Id, accountID, activity.UserInvited, nil)
|
||||
|
||||
return newUser.ToUserInfo(idpUser, settings)
|
||||
return newUser.ToUserInfo(idpUser)
|
||||
}
|
||||
|
||||
// createNewIdpUser validates the invite and creates a new user in the IdP
|
||||
@@ -727,19 +723,14 @@ func handleOwnerRoleTransfer(ctx context.Context, transaction store.Store, initi
|
||||
// If the AccountManager has a non-nil idpManager and the User is not a service user,
|
||||
// it will attempt to look up the UserData from the cache.
|
||||
func (am *DefaultAccountManager) getUserInfo(ctx context.Context, user *types.User, accountID string) (*types.UserInfo, error) {
|
||||
settings, err := am.Store.GetAccountSettings(ctx, store.LockingStrengthShare, accountID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !isNil(am.idpManager) && !user.IsServiceUser {
|
||||
userData, err := am.lookupUserInCache(ctx, user.Id, accountID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return user.ToUserInfo(userData, settings)
|
||||
return user.ToUserInfo(userData)
|
||||
}
|
||||
return user.ToUserInfo(nil, settings)
|
||||
return user.ToUserInfo(nil)
|
||||
}
|
||||
|
||||
// validateUserUpdate validates the update operation for a user.
|
||||
@@ -879,17 +870,12 @@ func (am *DefaultAccountManager) BuildUserInfosForAccount(ctx context.Context, a
|
||||
queriedUsers = append(queriedUsers, usersFromIntegration...)
|
||||
}
|
||||
|
||||
settings, err := am.Store.GetAccountSettings(ctx, store.LockingStrengthShare, accountID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
userInfosMap := make(map[string]*types.UserInfo)
|
||||
|
||||
// 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 accountUsers {
|
||||
info, err := accountUser.ToUserInfo(nil, settings)
|
||||
info, err := accountUser.ToUserInfo(nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -902,7 +888,7 @@ func (am *DefaultAccountManager) BuildUserInfosForAccount(ctx context.Context, a
|
||||
for _, localUser := range accountUsers {
|
||||
var info *types.UserInfo
|
||||
if queriedUser, contains := findUserInIDPUserdata(localUser.Id, queriedUsers); contains {
|
||||
info, err = localUser.ToUserInfo(queriedUser, settings)
|
||||
info, err = localUser.ToUserInfo(queriedUser)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -912,14 +898,6 @@ func (am *DefaultAccountManager) BuildUserInfosForAccount(ctx context.Context, a
|
||||
name = localUser.ServiceUserName
|
||||
}
|
||||
|
||||
dashboardViewPermissions := "full"
|
||||
if !localUser.HasAdminPower() {
|
||||
dashboardViewPermissions = "limited"
|
||||
if settings.RegularUsersViewBlocked {
|
||||
dashboardViewPermissions = "blocked"
|
||||
}
|
||||
}
|
||||
|
||||
info = &types.UserInfo{
|
||||
ID: localUser.Id,
|
||||
Email: "",
|
||||
@@ -929,7 +907,6 @@ func (am *DefaultAccountManager) BuildUserInfosForAccount(ctx context.Context, a
|
||||
Status: string(types.UserStatusActive),
|
||||
IsServiceUser: localUser.IsServiceUser,
|
||||
NonDeletable: localUser.NonDeletable,
|
||||
Permissions: types.UserPermissions{DashboardView: dashboardViewPermissions},
|
||||
}
|
||||
}
|
||||
userInfosMap[info.ID] = info
|
||||
@@ -1233,8 +1210,8 @@ func validateUserInvite(invite *types.UserInfo) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetCurrentUserInfo retrieves the account's current user info
|
||||
func (am *DefaultAccountManager) GetCurrentUserInfo(ctx context.Context, accountID, userID string) (*types.UserInfo, error) {
|
||||
// GetCurrentUserInfo retrieves the account's current user info and permissions
|
||||
func (am *DefaultAccountManager) GetCurrentUserInfo(ctx context.Context, accountID, userID string) (*users.UserInfoWithPermissions, error) {
|
||||
user, err := am.Store.GetUserByUserID(ctx, store.LockingStrengthShare, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -1252,10 +1229,27 @@ func (am *DefaultAccountManager) GetCurrentUserInfo(ctx context.Context, account
|
||||
return nil, err
|
||||
}
|
||||
|
||||
settings, err := am.Store.GetAccountSettings(ctx, store.LockingStrengthShare, accountID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
userInfo, err := am.getUserInfo(ctx, user, accountID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return userInfo, nil
|
||||
userWithPermissions := &users.UserInfoWithPermissions{
|
||||
UserInfo: userInfo,
|
||||
}
|
||||
|
||||
if user.Role == types.UserRoleUser && settings.RegularUsersViewBlocked {
|
||||
return userWithPermissions, nil
|
||||
}
|
||||
|
||||
permissions, err := am.permissionsManager.GetRolePermissions(ctx, user.Role)
|
||||
if err == nil {
|
||||
userWithPermissions.Permissions = &permissions
|
||||
}
|
||||
return userWithPermissions, nil
|
||||
}
|
||||
|
||||
@@ -13,7 +13,9 @@ import (
|
||||
nbcache "github.com/netbirdio/netbird/management/server/cache"
|
||||
nbcontext "github.com/netbirdio/netbird/management/server/context"
|
||||
"github.com/netbirdio/netbird/management/server/permissions"
|
||||
"github.com/netbirdio/netbird/management/server/permissions/roles"
|
||||
"github.com/netbirdio/netbird/management/server/status"
|
||||
"github.com/netbirdio/netbird/management/server/users"
|
||||
"github.com/netbirdio/netbird/management/server/util"
|
||||
|
||||
nbpeer "github.com/netbirdio/netbird/management/server/peer"
|
||||
@@ -1020,90 +1022,6 @@ func TestDefaultAccountManager_ListUsers(t *testing.T) {
|
||||
assert.Equal(t, 2, regular)
|
||||
}
|
||||
|
||||
func TestDefaultAccountManager_ListUsers_DashboardPermissions(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
role types.UserRole
|
||||
limitedViewSettings bool
|
||||
expectedDashboardPermissions string
|
||||
}{
|
||||
{
|
||||
name: "Regular user, no limited view settings",
|
||||
role: types.UserRoleUser,
|
||||
limitedViewSettings: false,
|
||||
expectedDashboardPermissions: "limited",
|
||||
},
|
||||
{
|
||||
name: "Admin user, no limited view settings",
|
||||
role: types.UserRoleAdmin,
|
||||
limitedViewSettings: false,
|
||||
expectedDashboardPermissions: "full",
|
||||
},
|
||||
{
|
||||
name: "Owner, no limited view settings",
|
||||
role: types.UserRoleOwner,
|
||||
limitedViewSettings: false,
|
||||
expectedDashboardPermissions: "full",
|
||||
},
|
||||
{
|
||||
name: "Regular user, limited view settings",
|
||||
role: types.UserRoleUser,
|
||||
limitedViewSettings: true,
|
||||
expectedDashboardPermissions: "blocked",
|
||||
},
|
||||
{
|
||||
name: "Admin user, limited view settings",
|
||||
role: types.UserRoleAdmin,
|
||||
limitedViewSettings: true,
|
||||
expectedDashboardPermissions: "full",
|
||||
},
|
||||
{
|
||||
name: "Owner, limited view settings",
|
||||
role: types.UserRoleOwner,
|
||||
limitedViewSettings: true,
|
||||
expectedDashboardPermissions: "full",
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
store, cleanup, err := store.NewTestStoreFromSQL(context.Background(), "", t.TempDir())
|
||||
if err != nil {
|
||||
t.Fatalf("Error when creating store: %s", err)
|
||||
}
|
||||
t.Cleanup(cleanup)
|
||||
|
||||
account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "")
|
||||
account.Users["normal_user1"] = types.NewUser("normal_user1", testCase.role, false, false, "", []string{}, types.UserIssuedAPI)
|
||||
account.Settings.RegularUsersViewBlocked = testCase.limitedViewSettings
|
||||
delete(account.Users, mockUserID)
|
||||
|
||||
err = store.SaveAccount(context.Background(), account)
|
||||
if err != nil {
|
||||
t.Fatalf("Error when saving account: %s", err)
|
||||
}
|
||||
|
||||
permissionsManager := permissions.NewManager(store)
|
||||
am := DefaultAccountManager{
|
||||
Store: store,
|
||||
eventStore: &activity.InMemoryEventStore{},
|
||||
permissionsManager: permissionsManager,
|
||||
}
|
||||
|
||||
users, err := am.ListUsers(context.Background(), mockAccountID)
|
||||
if err != nil {
|
||||
t.Fatalf("Error when checking user role: %s", err)
|
||||
}
|
||||
|
||||
assert.Equal(t, 1, len(users))
|
||||
|
||||
userInfo, _ := users[0].ToUserInfo(nil, account.Settings)
|
||||
assert.Equal(t, testCase.expectedDashboardPermissions, userInfo.Permissions.DashboardView)
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestDefaultAccountManager_ExternalCache(t *testing.T) {
|
||||
store, cleanup, err := store.NewTestStoreFromSQL(context.Background(), "", t.TempDir())
|
||||
if err != nil {
|
||||
@@ -1657,7 +1575,7 @@ func TestDefaultAccountManager_GetCurrentUserInfo(t *testing.T) {
|
||||
accountId string
|
||||
userId string
|
||||
expectedErr error
|
||||
expectedResult *types.UserInfo
|
||||
expectedResult *users.UserInfoWithPermissions
|
||||
}{
|
||||
{
|
||||
name: "not found",
|
||||
@@ -1687,81 +1605,102 @@ func TestDefaultAccountManager_GetCurrentUserInfo(t *testing.T) {
|
||||
name: "owner user",
|
||||
accountId: account1.Id,
|
||||
userId: "account1Owner",
|
||||
expectedResult: &types.UserInfo{
|
||||
ID: "account1Owner",
|
||||
Name: "",
|
||||
Role: "owner",
|
||||
AutoGroups: []string{},
|
||||
Status: "active",
|
||||
IsServiceUser: false,
|
||||
IsBlocked: false,
|
||||
NonDeletable: false,
|
||||
LastLogin: time.Time{},
|
||||
Issued: "api",
|
||||
IntegrationReference: integration_reference.IntegrationReference{},
|
||||
Permissions: types.UserPermissions{
|
||||
DashboardView: "full",
|
||||
expectedResult: &users.UserInfoWithPermissions{
|
||||
UserInfo: &types.UserInfo{
|
||||
ID: "account1Owner",
|
||||
Name: "",
|
||||
Role: "owner",
|
||||
AutoGroups: []string{},
|
||||
Status: "active",
|
||||
IsServiceUser: false,
|
||||
IsBlocked: false,
|
||||
NonDeletable: false,
|
||||
LastLogin: time.Time{},
|
||||
Issued: "api",
|
||||
IntegrationReference: integration_reference.IntegrationReference{},
|
||||
},
|
||||
Permissions: &roles.Owner,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "regular user",
|
||||
accountId: account1.Id,
|
||||
userId: "regular-user",
|
||||
expectedResult: &types.UserInfo{
|
||||
ID: "regular-user",
|
||||
Name: "",
|
||||
Role: "user",
|
||||
Status: "active",
|
||||
IsServiceUser: false,
|
||||
IsBlocked: false,
|
||||
NonDeletable: false,
|
||||
LastLogin: time.Time{},
|
||||
Issued: "api",
|
||||
IntegrationReference: integration_reference.IntegrationReference{},
|
||||
Permissions: types.UserPermissions{
|
||||
DashboardView: "limited",
|
||||
expectedResult: &users.UserInfoWithPermissions{
|
||||
UserInfo: &types.UserInfo{
|
||||
ID: "regular-user",
|
||||
Name: "",
|
||||
Role: "user",
|
||||
Status: "active",
|
||||
IsServiceUser: false,
|
||||
IsBlocked: false,
|
||||
NonDeletable: false,
|
||||
LastLogin: time.Time{},
|
||||
Issued: "api",
|
||||
IntegrationReference: integration_reference.IntegrationReference{},
|
||||
},
|
||||
Permissions: &roles.User,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "admin user",
|
||||
accountId: account1.Id,
|
||||
userId: "admin-user",
|
||||
expectedResult: &types.UserInfo{
|
||||
ID: "admin-user",
|
||||
Name: "",
|
||||
Role: "admin",
|
||||
Status: "active",
|
||||
IsServiceUser: false,
|
||||
IsBlocked: false,
|
||||
NonDeletable: false,
|
||||
LastLogin: time.Time{},
|
||||
Issued: "api",
|
||||
IntegrationReference: integration_reference.IntegrationReference{},
|
||||
Permissions: types.UserPermissions{
|
||||
DashboardView: "full",
|
||||
expectedResult: &users.UserInfoWithPermissions{
|
||||
UserInfo: &types.UserInfo{
|
||||
ID: "admin-user",
|
||||
Name: "",
|
||||
Role: "admin",
|
||||
Status: "active",
|
||||
IsServiceUser: false,
|
||||
IsBlocked: false,
|
||||
NonDeletable: false,
|
||||
LastLogin: time.Time{},
|
||||
Issued: "api",
|
||||
IntegrationReference: integration_reference.IntegrationReference{},
|
||||
},
|
||||
Permissions: &roles.Admin,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "settings blocked regular user",
|
||||
accountId: account2.Id,
|
||||
userId: "settings-blocked-user",
|
||||
expectedResult: &types.UserInfo{
|
||||
ID: "settings-blocked-user",
|
||||
Name: "",
|
||||
Role: "user",
|
||||
Status: "active",
|
||||
IsServiceUser: false,
|
||||
IsBlocked: false,
|
||||
NonDeletable: false,
|
||||
LastLogin: time.Time{},
|
||||
Issued: "api",
|
||||
IntegrationReference: integration_reference.IntegrationReference{},
|
||||
Permissions: types.UserPermissions{
|
||||
DashboardView: "blocked",
|
||||
expectedResult: &users.UserInfoWithPermissions{
|
||||
UserInfo: &types.UserInfo{
|
||||
ID: "settings-blocked-user",
|
||||
Name: "",
|
||||
Role: "user",
|
||||
Status: "active",
|
||||
IsServiceUser: false,
|
||||
IsBlocked: false,
|
||||
NonDeletable: false,
|
||||
LastLogin: time.Time{},
|
||||
Issued: "api",
|
||||
IntegrationReference: integration_reference.IntegrationReference{},
|
||||
},
|
||||
Permissions: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "settings blocked owner user",
|
||||
accountId: account2.Id,
|
||||
userId: "account2Owner",
|
||||
expectedResult: &users.UserInfoWithPermissions{
|
||||
UserInfo: &types.UserInfo{
|
||||
ID: "account2Owner",
|
||||
Name: "",
|
||||
Role: "owner",
|
||||
AutoGroups: []string{},
|
||||
Status: "active",
|
||||
IsServiceUser: false,
|
||||
IsBlocked: false,
|
||||
NonDeletable: false,
|
||||
LastLogin: time.Time{},
|
||||
Issued: "api",
|
||||
IntegrationReference: integration_reference.IntegrationReference{},
|
||||
},
|
||||
Permissions: &roles.Owner,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
13
management/server/users/user.go
Normal file
13
management/server/users/user.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package users
|
||||
|
||||
import (
|
||||
"github.com/netbirdio/netbird/management/server/permissions/roles"
|
||||
"github.com/netbirdio/netbird/management/server/types"
|
||||
)
|
||||
|
||||
// Wrapped UserInfo with Role Permissions
|
||||
type UserInfoWithPermissions struct {
|
||||
*types.UserInfo
|
||||
|
||||
Permissions *roles.RolePermissions
|
||||
}
|
||||
Reference in New Issue
Block a user