From e9761c0bfe83d51dbf24001579b70b0a84a0b391 Mon Sep 17 00:00:00 2001 From: Taran Pelkey Date: Wed, 21 Oct 2026 13:23:55 -0500 Subject: [PATCH] Add files actions to consoleadmin and new policy --- policy/constants.go | 26 +++++++++++++++++++++++++ policy/constants_test.go | 39 +++++++++++++++++++++++++++++++++++++ policy/files-action_test.go | 11 ++++++----- 3 files changed, 71 insertions(+), 5 deletions(-) diff --git a/policy/constants.go b/policy/constants.go index d838c47..d714833 100644 --- a/policy/constants.go +++ b/policy/constants.go @@ -162,6 +162,25 @@ var DefaultPolicies = []struct { }, }, + // FilesAdmin - provides admin access to the AIStor Files export API. Like + // memoryAdmin it carries its own action family only, so an operator who + // also manages the exported buckets combines it with an S3 policy. + { + Name: "filesAdmin", + Definition: Policy{ + Version: DefaultVersion, + Statements: []Statement{ + { + SID: ID(""), + Effect: Allow, + Actions: NewActionSet(Action(AllFilesActions)), + Resources: NewResourceSet(), + Conditions: condition.NewFunctions(), + }, + }, + }, + }, + // IAMAdmin - provides IAM management access (users, groups, policies, // service accounts) but no infrastructure, diagnostics, or S3 data access. { @@ -559,6 +578,13 @@ var DefaultPolicies = []struct { Resources: NewResourceSet(NewMemoryResource("*")), Conditions: condition.NewFunctions(), }, + { + SID: ID(""), + Effect: Allow, + Actions: NewActionSet(Action(AllFilesActions)), + Resources: NewResourceSet(), + Conditions: condition.NewFunctions(), + }, }, }, }, diff --git a/policy/constants_test.go b/policy/constants_test.go index 87ea4ad..65b2804 100644 --- a/policy/constants_test.go +++ b/policy/constants_test.go @@ -138,6 +138,29 @@ func TestDefaultPolicyMemoryAdmin(t *testing.T) { } } +func TestDefaultPolicyFilesAdmin(t *testing.T) { + p, ok := findDefaultPolicy("filesAdmin") + if !ok { + t.Fatal("filesAdmin default policy not found") + } + if err := p.Validate(); err != nil { + t.Fatalf("filesAdmin policy invalid: %v", err) + } + + for action := range SupportedFilesActions { + if !p.IsAllowed(Args{AccountName: "operator", Action: Action(action)}) { + t.Errorf("filesAdmin should allow %v", action) + } + } + + // It carries its own action family only, like memoryAdmin. + for _, action := range []Action{GetObjectAction, Action(ServerInfoAdminAction)} { + if p.IsAllowed(Args{AccountName: "operator", Action: action, BucketName: "exports"}) { + t.Errorf("filesAdmin should NOT allow %v", action) + } + } +} + func TestDefaultPolicyConsoleAdminAllowsMemory(t *testing.T) { args := Args{ AccountName: "admin", @@ -164,3 +187,19 @@ func TestDefaultPolicyConsoleAdminAllowsMemory(t *testing.T) { t.Error("readwrite should NOT allow memory:ListCortexes (sanity check)") } } + +func TestDefaultPolicyConsoleAdminAllowsFiles(t *testing.T) { + p, ok := findDefaultPolicy("consoleAdmin") + if !ok { + t.Fatal("consoleAdmin default policy not found") + } + // Files actions take no Resource, so the statement must still validate. + if err := p.Validate(); err != nil { + t.Fatalf("consoleAdmin policy invalid: %v", err) + } + for _, action := range []FilesAction{FilesGetExportStatusAction, FilesGetExportStatsAction} { + if !p.IsAllowed(Args{AccountName: "admin", Action: Action(action)}) { + t.Errorf("consoleAdmin should allow %v", action) + } + } +} diff --git a/policy/files-action_test.go b/policy/files-action_test.go index 3dc4312..3f81326 100644 --- a/policy/files-action_test.go +++ b/policy/files-action_test.go @@ -244,16 +244,17 @@ func TestFilesPolicyIsAllowed(t *testing.T) { }) } -func TestFilesActionNotGrantedByDefaultPolicies(t *testing.T) { - // No canned policy grants the Files namespace yet, consoleAdmin included: - // it is granted on its own, as the issue that introduced it asks. +func TestFilesActionGrantedOnlyByFilesAndConsoleAdmin(t *testing.T) { + // consoleAdmin drives the whole console, Files exports included, and + // filesAdmin grants the namespace on its own; no other canned policy may. for _, dp := range DefaultPolicies { + want := dp.Name == "consoleAdmin" || dp.Name == "filesAdmin" for action := range SupportedFilesActions { if action == AllFilesActions { continue } - if dp.Definition.IsAllowed(filesArgs(action)) { - t.Errorf("default policy %q allows %v", dp.Name, action) + if got := dp.Definition.IsAllowed(filesArgs(action)); got != want { + t.Errorf("default policy %q allows %v = %v, want %v", dp.Name, action, got, want) } } }