mirror of
https://github.com/netbirdio/netbird.git
synced 2026-09-26 16:49:08 +02:00
[management] use permission modules (#3622)
This commit is contained in:
@@ -1,34 +1,24 @@
|
||||
package permissions
|
||||
|
||||
//go:generate go run github.com/golang/mock/mockgen -package permissions -destination=manager_mock.go -source=./manager.go -build_flags=-mod=mod
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/netbirdio/netbird/management/server/activity"
|
||||
"github.com/netbirdio/netbird/management/server/permissions/modules"
|
||||
"github.com/netbirdio/netbird/management/server/permissions/operations"
|
||||
"github.com/netbirdio/netbird/management/server/permissions/roles"
|
||||
"github.com/netbirdio/netbird/management/server/status"
|
||||
"github.com/netbirdio/netbird/management/server/store"
|
||||
"github.com/netbirdio/netbird/management/server/types"
|
||||
)
|
||||
|
||||
type Module string
|
||||
|
||||
const (
|
||||
Networks Module = "networks"
|
||||
Peers Module = "peers"
|
||||
Groups Module = "groups"
|
||||
Settings Module = "settings"
|
||||
Accounts Module = "accounts"
|
||||
)
|
||||
|
||||
type Operation string
|
||||
|
||||
const (
|
||||
Read Operation = "read"
|
||||
Write Operation = "write"
|
||||
)
|
||||
|
||||
type Manager interface {
|
||||
ValidateUserPermissions(ctx context.Context, accountID, userID string, module Module, operation Operation) (bool, error)
|
||||
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
|
||||
}
|
||||
|
||||
@@ -36,16 +26,23 @@ type managerImpl struct {
|
||||
store store.Store
|
||||
}
|
||||
|
||||
type managerMock struct {
|
||||
}
|
||||
|
||||
func NewManager(store store.Store) Manager {
|
||||
return &managerImpl{
|
||||
store: store,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *managerImpl) ValidateUserPermissions(ctx context.Context, accountID, userID string, module Module, operation Operation) (bool, error) {
|
||||
func (m *managerImpl) ValidateUserPermissions(
|
||||
ctx context.Context,
|
||||
accountID string,
|
||||
userID string,
|
||||
module modules.Module,
|
||||
operation operations.Operation,
|
||||
) (bool, error) {
|
||||
if userID == activity.SystemInitiator {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
user, err := m.store.GetUserByUserID(ctx, store.LockingStrengthShare, userID)
|
||||
if err != nil {
|
||||
return false, err
|
||||
@@ -55,49 +52,42 @@ func (m *managerImpl) ValidateUserPermissions(ctx context.Context, accountID, us
|
||||
return false, status.NewUserNotFoundError(userID)
|
||||
}
|
||||
|
||||
if user.IsBlocked() {
|
||||
return false, status.NewUserBlockedError()
|
||||
}
|
||||
|
||||
if err := m.ValidateAccountAccess(ctx, accountID, user, false); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
switch module {
|
||||
case Accounts:
|
||||
if operation == Write && user.Role != types.UserRoleOwner {
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
default:
|
||||
if operation == operations.Read && user.IsServiceUser {
|
||||
return true, nil // this should be replaced by proper granular access role
|
||||
}
|
||||
|
||||
switch user.Role {
|
||||
case types.UserRoleAdmin, types.UserRoleOwner:
|
||||
return true, nil
|
||||
case types.UserRoleUser:
|
||||
return m.validateRegularUserPermissions(ctx, accountID, module, operation)
|
||||
case types.UserRoleBillingAdmin:
|
||||
return false, nil
|
||||
default:
|
||||
return false, errors.New("invalid role")
|
||||
role, ok := roles.RolesMap[user.Role]
|
||||
if !ok {
|
||||
return false, status.NewUserRoleNotFoundError(string(user.Role))
|
||||
}
|
||||
|
||||
return m.ValidateRoleModuleAccess(ctx, accountID, role, module, operation), nil
|
||||
}
|
||||
|
||||
func (m *managerImpl) validateRegularUserPermissions(ctx context.Context, accountID string, module Module, operation Operation) (bool, error) {
|
||||
settings, err := m.store.GetAccountSettings(ctx, store.LockingStrengthShare, accountID)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to get settings: %w", err)
|
||||
}
|
||||
if settings.RegularUsersViewBlocked {
|
||||
return false, nil
|
||||
func (m *managerImpl) ValidateRoleModuleAccess(
|
||||
ctx context.Context,
|
||||
accountID string,
|
||||
role roles.RolePermissions,
|
||||
module modules.Module,
|
||||
operation operations.Operation,
|
||||
) bool {
|
||||
if permissions, ok := role.Permissions[module]; ok {
|
||||
if allowed, exists := permissions[operation]; exists {
|
||||
return allowed
|
||||
}
|
||||
log.WithContext(ctx).Tracef("operation %s not found on module %s for role %s", operation, module, role.Role)
|
||||
return false
|
||||
}
|
||||
|
||||
if operation == Write {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
if module == Peers {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
return role.AutoAllowNew[operation]
|
||||
}
|
||||
|
||||
func (m *managerImpl) ValidateAccountAccess(ctx context.Context, accountID string, user *types.User, allowOwnerAndAdmin bool) error {
|
||||
@@ -106,24 +96,3 @@ func (m *managerImpl) ValidateAccountAccess(ctx context.Context, accountID strin
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewManagerMock() Manager {
|
||||
return &managerMock{}
|
||||
}
|
||||
|
||||
func (m *managerMock) ValidateUserPermissions(ctx context.Context, accountID, userID string, module Module, operation Operation) (bool, error) {
|
||||
switch userID {
|
||||
case "a23efe53-63fb-11ec-90d6-0242ac120003", "allowedUser", "testingUser", "account_creator", "serviceUserID", "test_user":
|
||||
return true, nil
|
||||
default:
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (m *managerMock) ValidateAccountAccess(ctx context.Context, accountID string, user *types.User, allowOwnerAndAdmin bool) error {
|
||||
// @note managers explicitly checked this, so should the mock
|
||||
if user.AccountID != accountID {
|
||||
return status.NewUserNotPartOfAccountError()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
// Code generated by MockGen. DO NOT EDIT.
|
||||
// Source: ./manager.go
|
||||
|
||||
// Package permissions is a generated GoMock package.
|
||||
package permissions
|
||||
|
||||
import (
|
||||
context "context"
|
||||
reflect "reflect"
|
||||
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
modules "github.com/netbirdio/netbird/management/server/permissions/modules"
|
||||
operations "github.com/netbirdio/netbird/management/server/permissions/operations"
|
||||
roles "github.com/netbirdio/netbird/management/server/permissions/roles"
|
||||
types "github.com/netbirdio/netbird/management/server/types"
|
||||
)
|
||||
|
||||
// MockManager is a mock of Manager interface.
|
||||
type MockManager struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockManagerMockRecorder
|
||||
}
|
||||
|
||||
// MockManagerMockRecorder is the mock recorder for MockManager.
|
||||
type MockManagerMockRecorder struct {
|
||||
mock *MockManager
|
||||
}
|
||||
|
||||
// NewMockManager creates a new mock instance.
|
||||
func NewMockManager(ctrl *gomock.Controller) *MockManager {
|
||||
mock := &MockManager{ctrl: ctrl}
|
||||
mock.recorder = &MockManagerMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||
func (m *MockManager) EXPECT() *MockManagerMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// ValidateAccountAccess mocks base method.
|
||||
func (m *MockManager) ValidateAccountAccess(ctx context.Context, accountID string, user *types.User, allowOwnerAndAdmin bool) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "ValidateAccountAccess", ctx, accountID, user, allowOwnerAndAdmin)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// ValidateAccountAccess indicates an expected call of ValidateAccountAccess.
|
||||
func (mr *MockManagerMockRecorder) ValidateAccountAccess(ctx, accountID, user, allowOwnerAndAdmin interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateAccountAccess", reflect.TypeOf((*MockManager)(nil).ValidateAccountAccess), ctx, accountID, user, allowOwnerAndAdmin)
|
||||
}
|
||||
|
||||
// ValidateRoleModuleAccess mocks base method.
|
||||
func (m *MockManager) ValidateRoleModuleAccess(ctx context.Context, accountID string, role roles.RolePermissions, module modules.Module, operation operations.Operation) bool {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "ValidateRoleModuleAccess", ctx, accountID, role, module, operation)
|
||||
ret0, _ := ret[0].(bool)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// ValidateRoleModuleAccess indicates an expected call of ValidateRoleModuleAccess.
|
||||
func (mr *MockManagerMockRecorder) ValidateRoleModuleAccess(ctx, accountID, role, module, operation interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateRoleModuleAccess", reflect.TypeOf((*MockManager)(nil).ValidateRoleModuleAccess), ctx, accountID, role, module, operation)
|
||||
}
|
||||
|
||||
// ValidateUserPermissions mocks base method.
|
||||
func (m *MockManager) ValidateUserPermissions(ctx context.Context, accountID, userID string, module modules.Module, operation operations.Operation) (bool, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "ValidateUserPermissions", ctx, accountID, userID, module, operation)
|
||||
ret0, _ := ret[0].(bool)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// ValidateUserPermissions indicates an expected call of ValidateUserPermissions.
|
||||
func (mr *MockManagerMockRecorder) ValidateUserPermissions(ctx, accountID, userID, module, operation interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateUserPermissions", reflect.TypeOf((*MockManager)(nil).ValidateUserPermissions), ctx, accountID, userID, module, operation)
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package modules
|
||||
|
||||
type Module string
|
||||
|
||||
const (
|
||||
Networks Module = "networks"
|
||||
Peers Module = "peers"
|
||||
Groups Module = "groups"
|
||||
Settings Module = "settings"
|
||||
Accounts Module = "accounts"
|
||||
Dns Module = "dns"
|
||||
Nameservers Module = "nameservers"
|
||||
Events Module = "events"
|
||||
Policies Module = "policies"
|
||||
Routes Module = "routes"
|
||||
Users Module = "users"
|
||||
SetupKeys Module = "setup_keys"
|
||||
Pats Module = "pats"
|
||||
)
|
||||
@@ -0,0 +1,8 @@
|
||||
package operations
|
||||
|
||||
type Operation string
|
||||
|
||||
const (
|
||||
Read Operation = "read"
|
||||
Write Operation = "write"
|
||||
)
|
||||
@@ -0,0 +1,21 @@
|
||||
package roles
|
||||
|
||||
import (
|
||||
"github.com/netbirdio/netbird/management/server/permissions/modules"
|
||||
"github.com/netbirdio/netbird/management/server/permissions/operations"
|
||||
"github.com/netbirdio/netbird/management/server/types"
|
||||
)
|
||||
|
||||
var Admin = RolePermissions{
|
||||
Role: types.UserRoleAdmin,
|
||||
AutoAllowNew: map[operations.Operation]bool{
|
||||
operations.Read: true,
|
||||
operations.Write: true,
|
||||
},
|
||||
Permissions: Permissions{
|
||||
modules.Accounts: {
|
||||
operations.Read: true,
|
||||
operations.Write: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package roles
|
||||
|
||||
import (
|
||||
"github.com/netbirdio/netbird/management/server/permissions/operations"
|
||||
"github.com/netbirdio/netbird/management/server/types"
|
||||
)
|
||||
|
||||
var Owner = RolePermissions{
|
||||
Role: types.UserRoleOwner,
|
||||
AutoAllowNew: map[operations.Operation]bool{
|
||||
operations.Read: true,
|
||||
operations.Write: true,
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package roles
|
||||
|
||||
import (
|
||||
"github.com/netbirdio/netbird/management/server/permissions/modules"
|
||||
"github.com/netbirdio/netbird/management/server/permissions/operations"
|
||||
"github.com/netbirdio/netbird/management/server/types"
|
||||
)
|
||||
|
||||
type RolePermissions struct {
|
||||
Role types.UserRole
|
||||
Permissions Permissions
|
||||
AutoAllowNew map[operations.Operation]bool
|
||||
}
|
||||
|
||||
type Permissions map[modules.Module]map[operations.Operation]bool
|
||||
|
||||
var RolesMap = map[types.UserRole]RolePermissions{
|
||||
types.UserRoleOwner: Owner,
|
||||
types.UserRoleAdmin: Admin,
|
||||
types.UserRoleUser: User,
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package roles
|
||||
|
||||
import (
|
||||
"github.com/netbirdio/netbird/management/server/permissions/operations"
|
||||
"github.com/netbirdio/netbird/management/server/types"
|
||||
)
|
||||
|
||||
var User = RolePermissions{
|
||||
Role: types.UserRoleUser,
|
||||
AutoAllowNew: map[operations.Operation]bool{
|
||||
operations.Read: false,
|
||||
operations.Write: false,
|
||||
},
|
||||
}
|
||||
Reference in New Issue
Block a user