mirror of
https://github.com/netbirdio/netbird.git
synced 2026-10-23 22:09:09 +02:00
The usage overview and access-log listings no longer deny callers without the account-wide grant: the filter is pinned to the caller (their own user id, group filters dropped), so every authenticated user reads their own usage and requests through the same endpoints the admin dashboard uses. The dedicated /agent-network/me/usage/overview endpoint is removed in favor of that fallback. usage_viewer gains read-only access to the resources the usage view's filters and columns resolve against: users, groups, peers, and the provider list (provider and model filter options).
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/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/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"))
|
|
}
|