[management] users roles endpoint

This commit is contained in:
Pedro Costa
2025-04-16 12:39:58 +01:00
parent 96b22d5140
commit 42f088b80f
10 changed files with 237 additions and 30 deletions
+60
View File
@@ -2013,6 +2013,41 @@ components:
- policy_name
- icmp_type
- icmp_code
RolePermissions:
type: object
properties:
role:
type: string
example: admin
modules:
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} }
default:
type: object
additionalProperties:
type: boolean
propertyNames:
type: string
enum:
- read
- write
required:
- default
- role
responses:
not_found:
description: Resource not found
@@ -2443,6 +2478,31 @@ paths:
"$ref": "#/components/responses/forbidden"
'500':
"$ref": "#/components/responses/internal_error"
/api/users/roles:
get:
summary: Retrieves user roles and permissions
description: Get permissions for user roles
tags: [ Users ]
security:
- BearerAuth: [ ]
- TokenAuth: [ ]
responses:
'200':
description: A JSON Array of RolePermissions objects
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/RolePermissions'
'400':
"$ref": "#/components/responses/bad_request"
'401':
"$ref": "#/components/responses/requires_authentication"
'403':
"$ref": "#/components/responses/forbidden"
'500':
"$ref": "#/components/responses/internal_error"
/api/peers:
get:
summary: List all Peers
+7
View File
@@ -1400,6 +1400,13 @@ type Resource struct {
// ResourceType defines model for ResourceType.
type ResourceType string
// RolePermissions defines model for RolePermissions.
type RolePermissions struct {
Default map[string]bool `json:"default"`
Modules *map[string]map[string]bool `json:"modules,omitempty"`
Role string `json:"role"`
}
// Route defines model for Route.
type Route struct {
// AccessControlGroups Access control group identifier associated with route.
+3 -1
View File
@@ -36,6 +36,7 @@ import (
"github.com/netbirdio/netbird/management/server/networks/routers"
nbpeers "github.com/netbirdio/netbird/management/server/peers"
"github.com/netbirdio/netbird/management/server/telemetry"
musers "github.com/netbirdio/netbird/management/server/users"
)
const apiPrefix = "/api"
@@ -56,6 +57,7 @@ func NewAPIHandler(
permissionsManager permissions.Manager,
peersManager nbpeers.Manager,
settingsManager settings.Manager,
usersManager musers.Manager,
) (http.Handler, error) {
authMiddleware := middleware.NewAuthMiddleware(
@@ -80,7 +82,7 @@ func NewAPIHandler(
accounts.AddEndpoints(accountManager, settingsManager, router)
peers.AddEndpoints(accountManager, router)
users.AddEndpoints(accountManager, router)
users.AddEndpoints(accountManager, usersManager, router)
setup_keys.AddEndpoints(accountManager, router)
policies.AddEndpoints(accountManager, LocationManager, router)
policies.AddPostureCheckEndpoints(accountManager, LocationManager, router)
@@ -11,6 +11,7 @@ import (
"github.com/netbirdio/netbird/management/server/account"
"github.com/netbirdio/netbird/management/server/http/api"
"github.com/netbirdio/netbird/management/server/http/util"
"github.com/netbirdio/netbird/management/server/permissions/roles"
"github.com/netbirdio/netbird/management/server/status"
"github.com/netbirdio/netbird/management/server/types"
"github.com/netbirdio/netbird/management/server/users"
@@ -21,9 +22,10 @@ import (
// handler is a handler that returns users of the account
type handler struct {
accountManager account.Manager
usersManager users.Manager
}
func AddEndpoints(accountManager account.Manager, router *mux.Router) {
func AddEndpoints(accountManager account.Manager, usersManager users.Manager, router *mux.Router) {
userHandler := newHandler(accountManager)
router.HandleFunc("/users", userHandler.getAllUsers).Methods("GET", "OPTIONS")
router.HandleFunc("/users/current", userHandler.getCurrentUser).Methods("GET", "OPTIONS")
@@ -31,6 +33,7 @@ func AddEndpoints(accountManager account.Manager, router *mux.Router) {
router.HandleFunc("/users/{userId}", userHandler.deleteUser).Methods("DELETE", "OPTIONS")
router.HandleFunc("/users", userHandler.createUser).Methods("POST", "OPTIONS")
router.HandleFunc("/users/{userId}/invite", userHandler.inviteUser).Methods("POST", "OPTIONS")
router.HandleFunc("/users/roles", userHandler.getRoles).Methods("GET", "OPTIONS")
addUsersTokensEndpoint(accountManager, router)
}
@@ -284,6 +287,64 @@ func (h *handler) getCurrentUser(w http.ResponseWriter, r *http.Request) {
util.WriteJSONObject(r.Context(), w, toUserWithPermissionsResponse(user, userID))
}
func (h *handler) getRoles(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
util.WriteErrorResponse("wrong HTTP method", http.StatusMethodNotAllowed, w)
return
}
ctx := r.Context()
userAuth, err := nbcontext.GetUserAuthFromContext(ctx)
if err != nil {
util.WriteError(r.Context(), err, w)
return
}
accountID, userID := userAuth.AccountId, userAuth.UserId
roles, err := h.usersManager.GetRoles(ctx, accountID, userID)
if err != nil {
util.WriteError(r.Context(), err, w)
return
}
util.WriteJSONObject(r.Context(), w, toRolesResponse(roles))
}
func toRolesResponse(roles map[types.UserRole]roles.RolePermissions) []api.RolePermissions {
result := make([]api.RolePermissions, 0, len(roles))
for _, permissions := range roles {
rolePermissions := api.RolePermissions{
Role: string(permissions.Role),
}
if len(permissions.AutoAllowNew) > 0 {
rolePermissions.Default = make(map[string]bool)
for k, v := range permissions.AutoAllowNew {
rolePermissions.Default[string(k)] = v
}
}
if len(permissions.Permissions) > 0 {
modules := make(map[string]map[string]bool)
for module, operations := range permissions.Permissions {
if len(operations) == 0 {
continue
}
access := make(map[string]bool)
for k, v := range operations {
access[string(k)] = v
}
modules[string(module)] = access
}
rolePermissions.Modules = &modules
}
result = append(result, rolePermissions)
}
return result
}
func toUserWithPermissionsResponse(user *users.UserInfoWithPermissions, userID string) *api.User {
response := toUserResponse(user.UserInfo, userID)
if user.Permissions == nil {
@@ -121,9 +121,9 @@ func BuildApiBlackBoxWithDBState(t TB, sqlFile string, expectedPeerUpdate *serve
geoMock := &geolocation.Mock{}
validatorMock := server.MocIntegratedValidator{}
proxyController := integrations.NewController(store)
userManager := users.NewManager(store)
permissionsManager := permissions.NewManager(store)
settingsManager := settings.NewManager(store, userManager, integrations.NewManager(&activity.InMemoryEventStore{}), permissionsManager)
usersManager := users.NewManager(store, permissionsManager)
settingsManager := settings.NewManager(store, usersManager, integrations.NewManager(&activity.InMemoryEventStore{}), permissionsManager)
am, err := server.BuildManager(context.Background(), store, peersUpdateManager, nil, "", "", &activity.InMemoryEventStore{}, geoMock, false, validatorMock, metrics, proxyController, settingsManager, permissionsManager)
if err != nil {
t.Fatalf("Failed to create manager: %v", err)
@@ -144,7 +144,7 @@ func BuildApiBlackBoxWithDBState(t TB, sqlFile string, expectedPeerUpdate *serve
groupsManagerMock := groups.NewManagerMock()
peersManager := peers.NewManager(store, permissionsManager)
apiHandler, err := nbhttp.NewAPIHandler(context.Background(), am, networksManagerMock, resourcesManagerMock, routersManagerMock, groupsManagerMock, geoMock, authManagerMock, metrics, validatorMock, proxyController, permissionsManager, peersManager, settingsManager)
apiHandler, err := nbhttp.NewAPIHandler(context.Background(), am, networksManagerMock, resourcesManagerMock, routersManagerMock, groupsManagerMock, geoMock, authManagerMock, metrics, validatorMock, proxyController, permissionsManager, peersManager, settingsManager, usersManager)
if err != nil {
t.Fatalf("Failed to create API handler: %v", err)
}