Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions policy/actionset.go
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,19 @@ func (actionSet ActionSet) ToVectorsSlice() []VectorsAction {
return actions
}

// ToMemorySlice - returns slice of Memory actions from the action set.
func (actionSet ActionSet) ToMemorySlice() []MemoryAction {
if len(actionSet) == 0 {
return nil
}
actions := make([]MemoryAction, 0, len(actionSet))
for action := range actionSet {
actions = append(actions, MemoryAction(action))
}

return actions
}

// UnmarshalJSON - decodes JSON data to ActionSet.
func (actionSet *ActionSet) UnmarshalJSON(data []byte) error {
var sset set.StringSet
Expand Down Expand Up @@ -293,6 +306,16 @@ func (actionSet ActionSet) ValidateVectors() error {
return nil
}

// ValidateMemory checks if all actions are valid Memory actions
func (actionSet ActionSet) ValidateMemory() error {
for _, action := range actionSet.ToMemorySlice() {
if !action.IsValid() {
return Errorf("unsupported memory action '%v'", action)
}
}
return nil
}

// Validate checks if all actions are valid
func (actionSet ActionSet) Validate() error {
for _, action := range actionSet.ToSlice() {
Expand Down
95 changes: 95 additions & 0 deletions policy/memory-action.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// Copyright (c) 2015-2025 MinIO, Inc.
//
// This file is part of MinIO Object Storage stack
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

package policy

import (
"github.com/minio/pkg/v3/policy/condition"
)

// MemoryAction - AIStor Memory API policy action. The Memory API is a MinIO
// AIStor extension serving agent memory on a Cortex (Memory Bucket).
type MemoryAction string

const (
// MemoryCreateCortexAction - create a Memory cortex (Memory Bucket).
MemoryCreateCortexAction MemoryAction = "memory:CreateCortex"

// MemoryDeleteCortexAction - delete a Memory cortex.
MemoryDeleteCortexAction = "memory:DeleteCortex"

// MemoryGetCortexAction - read a Memory cortex's metadata.
MemoryGetCortexAction = "memory:GetCortex"

// MemoryListCortexesAction - list Memory cortexes.
MemoryListCortexesAction = "memory:ListCortexes"

// MemoryPutSecretAction - write a secret in a cortex.
MemoryPutSecretAction = "memory:PutSecret"

// MemoryGetSecretAction - read a secret's decrypted value from a cortex.
MemoryGetSecretAction = "memory:GetSecret"

// MemoryDeleteSecretAction - delete a secret from a cortex.
MemoryDeleteSecretAction = "memory:DeleteSecret"

// MemoryListSecretsAction - list the secrets in a cortex.
MemoryListSecretsAction = "memory:ListSecrets"

// MemorySearchAction - search (corpus-grep) the objects in a cortex.
MemorySearchAction = "memory:Search"

// AllMemoryActions - all AIStor Memory API actions.
AllMemoryActions = "memory:*"
)

// SupportedMemoryActions - list of all supported AIStor Memory API actions.
var SupportedMemoryActions = map[MemoryAction]struct{}{
MemoryCreateCortexAction: {},
MemoryDeleteCortexAction: {},
MemoryGetCortexAction: {},
MemoryListCortexesAction: {},
MemoryPutSecretAction: {},
MemoryGetSecretAction: {},
MemoryDeleteSecretAction: {},
MemoryListSecretsAction: {},
MemorySearchAction: {},
AllMemoryActions: {},
}

// IsValid - checks if action is valid or not.
func (action MemoryAction) IsValid() bool {
_, ok := SupportedMemoryActions[action]
return ok
}

func createMemoryActionConditionKeyMap() map[Action]condition.KeySet {
commonKeys := []condition.Key{}
for _, keyName := range condition.CommonKeys {
commonKeys = append(commonKeys, keyName.ToKey())
}

memoryActionConditionKeyMap := map[Action]condition.KeySet{}
for act := range SupportedMemoryActions {
memoryActionConditionKeyMap[Action(act)] = condition.NewKeySet(commonKeys...)
}

return memoryActionConditionKeyMap
}

// MemoryActionConditionKeyMap - holds mapping of Memory actions to condition keys.
var MemoryActionConditionKeyMap = createMemoryActionConditionKeyMap()
56 changes: 56 additions & 0 deletions policy/memory-action_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// Copyright (c) 2015-2026 MinIO, Inc.
//
// This file is part of MinIO Object Storage stack
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

package policy

import (
"testing"
)

func TestMemoryActionIsValid(t *testing.T) {
testCases := []struct {
action MemoryAction
expectedResult bool
}{
{MemoryCreateCortexAction, true},
{MemoryDeleteCortexAction, true},
{MemoryGetCortexAction, true},
{MemoryListCortexesAction, true},
{MemoryPutSecretAction, true},
{MemoryGetSecretAction, true},
{MemoryDeleteSecretAction, true},
{MemoryListSecretsAction, true},
{MemorySearchAction, true},
{AllMemoryActions, true},
{MemoryAction("memory:FooBar"), false},
{MemoryAction("s3tables:CreateTable"), false},
}

for i, testCase := range testCases {
if result := testCase.action.IsValid(); result != testCase.expectedResult {
t.Fatalf("case %v: action %v: expected: %v, got: %v", i+1, testCase.action, testCase.expectedResult, result)
}
}
}

func TestMemoryActionConditionKeys(t *testing.T) {
for action := range SupportedMemoryActions {
if _, ok := MemoryActionConditionKeyMap[Action(action)]; !ok {
t.Fatalf("action %v: no condition key set registered", action)
}
}
}
16 changes: 16 additions & 0 deletions policy/resourceset.go
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,22 @@ func (resourceSet ResourceSet) ValidateVectors() error {
return nil
}

// ValidateMemory - validates ResourceSet for the AIStor Memory API.
// A Memory cortex is an S3 bucket, so resources use S3 ARN format
// (e.g., arn:aws:s3:::my-cortex or arn:aws:s3:::my-cortex/*).
func (resourceSet ResourceSet) ValidateMemory() error {
for resource := range resourceSet {
if !resource.isS3() {
return Errorf("resource '%v' type is not S3", resource)
}
if err := resource.Validate(); err != nil {
return err
}
}

return nil
}

// ValidateBucket - validates ResourceSet is for given bucket or not.
func (resourceSet ResourceSet) ValidateBucket(bucketName string) error {
for resource := range resourceSet {
Expand Down
55 changes: 53 additions & 2 deletions policy/statement.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ func (statement Statement) validateActionTypes() error {
if len(actions) == 0 {
actions = statement.NotActions
}
var hasS3, hasAdmin, hasSTS, hasKMS, hasTable, hasVectors bool
var hasS3, hasAdmin, hasSTS, hasKMS, hasTable, hasVectors, hasMemory bool
for action := range actions {
switch {
case AdminAction(action).IsValid():
Expand All @@ -159,12 +159,14 @@ func (statement Statement) validateActionTypes() error {
hasTable = true
case VectorsAction(action).IsValid():
hasVectors = true
case MemoryAction(action).IsValid():
hasMemory = true
default:
hasS3 = true
}
}
count := 0
for _, b := range []bool{hasS3, hasAdmin, hasSTS, hasKMS, hasTable, hasVectors} {
for _, b := range []bool{hasS3, hasAdmin, hasSTS, hasKMS, hasTable, hasVectors, hasMemory} {
if b {
count++
}
Expand Down Expand Up @@ -231,6 +233,15 @@ func (statement Statement) isVectors() bool {
return false
}

func (statement Statement) isMemory() bool {
for action := range statement.Actions {
if MemoryAction(action).IsValid() {
return true
}
}
return false
}

// isValid - checks whether statement is valid or not.
func (statement Statement) isValid() error {
if !statement.Effect.IsValid() {
Expand Down Expand Up @@ -360,6 +371,46 @@ func (statement Statement) isValid() error {
return nil
}

if statement.isMemory() {
if err := statement.Actions.ValidateMemory(); err != nil {
return err
}
for action := range statement.Actions {
keys := statement.Conditions.Keys()
keyDiff := keys.Difference(MemoryActionConditionKeyMap[action])
if !keyDiff.IsEmpty() {
return Errorf("unsupported condition keys '%v' used for action '%v'", keyDiff, action)
}
}

if len(statement.Resources) == 0 && len(statement.NotResources) == 0 {
return Errorf("Resource must not be empty")
}

if len(statement.Resources) > 0 && len(statement.NotResources) > 0 {
return Errorf("Resource and NotResource cannot be specified in the same statement")
}

if err := statement.Resources.ValidateMemory(); err != nil {
return err
}

if err := statement.NotResources.ValidateMemory(); err != nil {
return err
}

for action := range statement.Actions {
if len(statement.Resources) > 0 && !statement.Resources.ObjectResourceExists() && !statement.Resources.BucketResourceExists() {
return Errorf("unsupported Resource found %v for action %v", statement.Resources, action)
}
if len(statement.NotResources) > 0 && !statement.NotResources.ObjectResourceExists() && !statement.NotResources.BucketResourceExists() {
return Errorf("unsupported NotResource found %v for action %v", statement.NotResources, action)
}
}

return nil
}

if !statement.SID.IsValid() {
return Errorf("invalid SID %v", statement.SID)
}
Expand Down
Loading