mirror of
https://github.com/netbirdio/netbird.git
synced 2026-09-15 11:19:08 +02:00
141 lines
5.3 KiB
Go
141 lines
5.3 KiB
Go
package permissions
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/netbirdio/netbird/management/internals/modules/permissions/modules"
|
|
"github.com/netbirdio/netbird/management/internals/modules/permissions/operations"
|
|
"github.com/netbirdio/netbird/management/internals/modules/permissions/roles"
|
|
"github.com/netbirdio/netbird/management/server/types"
|
|
)
|
|
|
|
var allOps = []operations.Operation{operations.Read, operations.Create, operations.Update, operations.Delete}
|
|
|
|
// TestAgentNetworkAdminRole pins the delegated-admin contract: full control
|
|
// over the whole agent_network area (parent grant cascades to every
|
|
// submodule), read-only on the account objects needed to build policies,
|
|
// and nothing else in the account.
|
|
func TestAgentNetworkAdminRole(t *testing.T) {
|
|
manager := NewManager(nil)
|
|
ctx := context.Background()
|
|
|
|
role, ok := roles.RolesMap[types.UserRoleAgentNetworkAdmin]
|
|
require.True(t, ok, "agent_network_admin must exist in RolesMap")
|
|
|
|
agentNetworkModules := []modules.Module{
|
|
modules.AgentNetwork,
|
|
modules.AgentNetworkProviders,
|
|
modules.AgentNetworkPolicies,
|
|
modules.AgentNetworkGuardrails,
|
|
modules.AgentNetworkBudgets,
|
|
modules.AgentNetworkUsage,
|
|
modules.AgentNetworkLogs,
|
|
modules.AgentNetworkSettings,
|
|
}
|
|
for _, m := range agentNetworkModules {
|
|
for _, op := range allOps {
|
|
assert.True(t, manager.ValidateRoleModuleAccess(ctx, "account", role, m, op),
|
|
"agent_network_admin must have %s on %s", op, m)
|
|
}
|
|
}
|
|
|
|
// Settings read rides along because GET /api/accounts (which the
|
|
// dashboard needs to boot) validates it, like network_admin.
|
|
for _, m := range []modules.Module{modules.Users, modules.Groups, modules.Peers, modules.Accounts, modules.Settings} {
|
|
assert.True(t, manager.ValidateRoleModuleAccess(ctx, "account", role, m, operations.Read),
|
|
"agent_network_admin must read %s to build policies and load the dashboard", m)
|
|
for _, op := range []operations.Operation{operations.Create, operations.Update, operations.Delete} {
|
|
assert.False(t, manager.ValidateRoleModuleAccess(ctx, "account", role, m, op),
|
|
"agent_network_admin must not have %s on %s", op, m)
|
|
}
|
|
}
|
|
|
|
for _, m := range []modules.Module{modules.Networks, modules.Dns, modules.SetupKeys, modules.Routes} {
|
|
for _, op := range allOps {
|
|
assert.False(t, manager.ValidateRoleModuleAccess(ctx, "account", role, m, op),
|
|
"agent_network_admin must not have %s on %s", op, m)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestUsageViewerRole pins the least-privilege cost role: read on the
|
|
// aggregated usage overview plus read-only on the resources its filters
|
|
// and display columns resolve against (users, groups, peers, the provider
|
|
// list) — no policies, no request-level logs (which can contain captured
|
|
// prompts), nothing else in the account.
|
|
func TestUsageViewerRole(t *testing.T) {
|
|
manager := NewManager(nil)
|
|
ctx := context.Background()
|
|
|
|
role, ok := roles.RolesMap[types.UserRoleUsageViewer]
|
|
require.True(t, ok, "usage_viewer must exist in RolesMap")
|
|
|
|
readOnly := []modules.Module{
|
|
modules.AgentNetworkUsage,
|
|
modules.AgentNetworkProviders,
|
|
modules.Users,
|
|
modules.Groups,
|
|
modules.Peers,
|
|
}
|
|
for _, m := range readOnly {
|
|
assert.True(t, manager.ValidateRoleModuleAccess(ctx, "account", role, m, operations.Read),
|
|
"usage_viewer must read %s for the usage view and its filters", m)
|
|
for _, op := range []operations.Operation{operations.Create, operations.Update, operations.Delete} {
|
|
assert.False(t, manager.ValidateRoleModuleAccess(ctx, "account", role, m, op),
|
|
"usage_viewer must not have %s on %s", op, m)
|
|
}
|
|
}
|
|
|
|
denied := []modules.Module{
|
|
modules.AgentNetwork,
|
|
modules.AgentNetworkPolicies,
|
|
modules.AgentNetworkGuardrails,
|
|
modules.AgentNetworkBudgets,
|
|
modules.AgentNetworkLogs,
|
|
modules.AgentNetworkSettings,
|
|
modules.Networks,
|
|
modules.SetupKeys,
|
|
}
|
|
for _, m := range denied {
|
|
for _, op := range allOps {
|
|
assert.False(t, manager.ValidateRoleModuleAccess(ctx, "account", role, m, op),
|
|
"usage_viewer must not have %s on %s", op, m)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestBillingAdminRoleResolves pins that billing_admin has a proper entry
|
|
// in the permission map. Its plan/seat/invoice permissions are enforced
|
|
// outside this map; management-side it carries the regular User baseline
|
|
// instead of failing role resolution.
|
|
func TestBillingAdminRoleResolves(t *testing.T) {
|
|
manager := NewManager(nil)
|
|
ctx := context.Background()
|
|
|
|
role, ok := roles.RolesMap[types.UserRoleBillingAdmin]
|
|
require.True(t, ok, "billing_admin must exist in RolesMap")
|
|
|
|
permissions, err := manager.GetPermissionsByRole(ctx, types.UserRoleBillingAdmin)
|
|
require.NoError(t, err, "billing_admin role must resolve")
|
|
require.NotEmpty(t, permissions)
|
|
|
|
for _, m := range []modules.Module{modules.AgentNetwork, modules.Networks, modules.Users, modules.Peers} {
|
|
for _, op := range allOps {
|
|
assert.False(t, manager.ValidateRoleModuleAccess(ctx, "account", role, m, op),
|
|
"billing_admin must not have %s on %s", op, m)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestNewRolesParse pins the API role strings, which are permanent once
|
|
// released.
|
|
func TestNewRolesParse(t *testing.T) {
|
|
assert.Equal(t, types.UserRoleAgentNetworkAdmin, types.StrRoleToUserRole("agent_network_admin"))
|
|
assert.Equal(t, types.UserRoleUsageViewer, types.StrRoleToUserRole("usage_viewer"))
|
|
assert.Equal(t, types.UserRoleBillingAdmin, types.StrRoleToUserRole("billing_admin"))
|
|
}
|