Skip to content

A policy reports which resources and values it covers - #254

Open
SSushmitha8 wants to merge 1 commit into
mainfrom
policy-helpers
Open

A policy reports which resources and values it covers#254
SSushmitha8 wants to merge 1 commit into
mainfrom
policy-helpers

Conversation

@SSushmitha8

@SSushmitha8 SSushmitha8 commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

PR description:

needed for https://github.com/miniohq/aistor/pull/6595

What this does

Adds a new way to ask a policy document a question it couldn't answer
before: instead of just "is this one specific warehouse allowed?", it
before being shown to a user — this is just a faster way to narrow
down the list of candidates first.

  • Fixed two bugs found while building this:
    • A policy that explicitly listed what it does not apply to
      (rather than what it does) was being read backwards, which could
      incorrectly deny access that should have been allowed.
    • A specific type of condition (one that only restricts values when
      the request actually provides one) was being misread as a strict
      allow-list, which could have hidden resources a user should
      actually be able to see.
  • Some unrelated formatting-only cleanup from our code formatter is
    included in this diff — no behavior changes there.

How this was tested

  • Existing and new unit tests in the policy package all pass.

Summary by CodeRabbit

  • New Features

    • Added APIs to extract allow and deny condition values for selected resources and actions.
    • Added support for reporting S3 Tables resource patterns, including wildcard and excluded-resource scenarios.
    • Added condition-key lookup for table actions and recognition of allow-list operators.
    • Unconstrained conditions are clearly represented as allowing or denying all values.
  • Tests

    • Added comprehensive coverage for condition extraction, resource filtering, action-specific results, and unsupported operators.

@SSushmitha8 SSushmitha8 self-assigned this Aug 5, 2026
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The change adds policy APIs for extracting condition values and S3 Tables resource patterns. It adds condition-function helpers, action-key lookup, exported result types, and table-driven tests for filtering, allow/deny effects, unconstrained conditions, and resource patterns.

Changes

Policy condition inspection

Layer / File(s) Summary
Condition value extraction
policy/condition/func.go, policy/condition/name.go, policy/condition/func_test.go
Adds grouped string-value extraction by condition key and allow-list classification for StringEquals and StringLike.
Policy inspection APIs
policy/policy.go, policy/table-action.go
Adds ConditionValues, TableResourcePatterns, result structures, condition interpretation, and table action condition-key lookup.
API validation and test coverage
policy/condition_values_test.go, policy/table-action_test.go, policy/condition/stringfunc_test.go
Adds coverage for condition values, resource patterns, action and resource filtering, NotResource, unconstrained effects, and existing string-function cases.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant Policy
  participant PolicyStatements
  participant Functions
  participant ConditionValueSet
  Caller->>Policy: ConditionValues(resource, actions, key)
  Policy->>PolicyStatements: Filter by resource and action
  Policy->>Functions: Extract values for key
  Functions-->>Policy: Grouped condition values
  Policy->>ConditionValueSet: Separate allow and deny results
  ConditionValueSet-->>Caller: Per-action condition values
Loading

Possibly related PRs

  • minio/pkg#246: Both changes modify TableAction condition-key handling and mappings.

Poem

A rabbit checks each policy line,
Groups the strings in neat design.
Allow and deny now clearly show,
Table patterns map where actions go.
“Hop!” says the rabbit, “the tests all glow.”

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding policy helpers that report covered resources and values.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@policy/condition_values_test.go`:
- Around line 242-265: The table-driven test around wantAllow currently ignores
unexpected actions in got. Update the validation to compare the complete action
set, or explicitly fail when got contains an action absent from test.wantAllow,
while preserving the existing allow and deny pattern checks for expected
actions.

In `@policy/policy.go`:
- Around line 274-285: Update the loop that builds constrained values from
functions.ValuesByKey(key) so encountering any set-qualified or non-allow-list
condition immediately makes the result unconstrained, rather than skipping it
and retaining values from other functions. Preserve the existing value
aggregation for keys containing only supported allow-list conditions, and add a
test covering mixed operators such as StringEquals with StringNotEquals.
- Around line 209-254: Update the NotResource-only handling in the
policy-building flow to append ResourceARNAll whenever statement.NotResources is
non-empty, regardless of whether its entries are table resources; remove or stop
using excludesTableResource for this decision. Add a test covering a NotResource
containing only a regular S3 ARN and verify the statement produces the wildcard
table-resource candidate.

In `@policy/table-action.go`:
- Around line 482-484: Update TableActionConditionKeys to return an isolated
copy of the condition.KeySet rather than the shared map stored in
tableActionConditionKeyMap. Clone each key into a new map before returning so
callers can mutate their result without changing global state or racing with
other callers.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: e0922f9a-b8a4-46cf-b2be-b0cda82e198d

📥 Commits

Reviewing files that changed from the base of the PR and between 57f7db9 and e340894.

📒 Files selected for processing (8)
  • policy/condition/func.go
  • policy/condition/func_test.go
  • policy/condition/name.go
  • policy/condition/stringfunc_test.go
  • policy/condition_values_test.go
  • policy/policy.go
  • policy/table-action.go
  • policy/table-action_test.go

Comment on lines +242 to +265
for action, want := range test.wantAllow {
entry := got[action]
if entry == nil {
t.Fatalf("%s: no entry", action)
}
allow := entry.Allow.ToSlice()
slices.Sort(allow)
slices.Sort(want)
if !slices.Equal(allow, want) {
t.Errorf("%s allow = %v, want %v", action, allow, want)
}
}
for action, entry := range got {
deny := entry.Deny.ToSlice()
slices.Sort(deny)
want := test.wantDeny[action]
slices.Sort(want)
if len(deny) == 0 && len(want) == 0 {
continue
}
if !slices.Equal(deny, want) {
t.Errorf("%s deny = %v, want %v", action, deny, want)
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Reject unexpected action entries.

The test checks only actions in wantAllow. It passes if TableResourcePatterns incorrectly adds an Allow pattern for another action. Compare the complete action set, or fail when got contains an action absent from wantAllow.

As per coding guidelines, table-driven policy tests must cover edge cases.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@policy/condition_values_test.go` around lines 242 - 265, The table-driven
test around wantAllow currently ignores unexpected actions in got. Update the
validation to compare the complete action set, or explicitly fail when got
contains an action absent from test.wantAllow, while preserving the existing
allow and deny pattern checks for expected actions.

Source: Coding guidelines

Comment thread policy/policy.go
Comment on lines +209 to +254
// A statement naming only NotResource reaches every resource it does not
// exclude, so it names them all rather than none.
if len(statement.Resources) == 0 && excludesTableResource(statement.NotResources) {
patterns = append(patterns, ResourceARNAll.String())
}
if len(patterns) == 0 {
continue
}

for _, action := range actions {
if len(statement.Actions) > 0 && !statement.Actions.Match(action) {
continue
}
if statement.NotActions.Match(action) {
continue
}
if byAction == nil {
byAction = make(map[Action]*ResourcePatternSet, len(actions))
}
entry := byAction[action]
if entry == nil {
entry = &ResourcePatternSet{Allow: set.NewStringSet(), Deny: set.NewStringSet()}
byAction[action] = entry
}
target := entry.Allow
if statement.Effect != Allow {
target = entry.Deny
}
for _, pattern := range patterns {
target.Add(pattern)
}
}
}

return byAction
}

// excludesTableResource reports whether a NotResource set excludes any S3 Tables
// resource, meaning the statement reaches the remaining ones.
func excludesTableResource(notResources ResourceSet) bool {
for resource := range notResources {
if resource.isTable() {
return true
}
}
return false

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Include table resources for every NotResource-only statement.

If NotResource contains only a regular S3 ARN, it excludes no S3 Tables ARN. The statement still applies to every S3 Tables resource. The current excludesTableResource check drops that statement and hides valid candidates.

Use the presence of NotResource, not its resource type, to report "*". Add a non-table NotResource test case.

Proposed fix
-	if len(statement.Resources) == 0 && excludesTableResource(statement.NotResources) {
+	if len(statement.Resources) == 0 && len(statement.NotResources) > 0 {
 		patterns = append(patterns, ResourceARNAll.String())
 	}
@@
-// excludesTableResource reports whether a NotResource set excludes any S3 Tables
-// resource, meaning the statement reaches the remaining ones.
-func excludesTableResource(notResources ResourceSet) bool {
-	for resource := range notResources {
-		if resource.isTable() {
-			return true
-		}
-	}
-	return false
-}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@policy/policy.go` around lines 209 - 254, Update the NotResource-only
handling in the policy-building flow to append ResourceARNAll whenever
statement.NotResources is non-empty, regardless of whether its entries are table
resources; remove or stop using excludesTableResource for this decision. Add a
test covering a NotResource containing only a regular S3 ARN and verify the
statement produces the wildcard table-resource candidate.

Comment thread policy/policy.go
Comment on lines +274 to +285
for name, vs := range functions.ValuesByKey(key) {
// A set qualifier such as "ForAllValues:" holds when the request carries no
// value for the key, so the listed values are not the reachable set.
if strings.ContainsRune(name, ':') {
continue
}
if !condition.IsAllowList(name) {
continue
}
constrained = true
values = append(values, vs...)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Mark mixed condition forms as unconstrained.

The loop ignores an unsupported or set-qualified function and still returns values from another function on the same key. For example, StringEquals plus StringNotEquals returns a partial value set with AllowAll unset. This conflicts with the API contract that an unsupported form must not narrow the result.

Return constrained == false when any condition name for the key is unsupported or qualified. Add a mixed-operator test.

Proposed fix
 for name, vs := range functions.ValuesByKey(key) {
-	if strings.ContainsRune(name, ':') {
-		continue
-	}
-	if !condition.IsAllowList(name) {
-		continue
+	if strings.ContainsRune(name, ':') || !condition.IsAllowList(name) {
+		return nil, false
 	}
 	constrained = true
 	values = append(values, vs...)
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
for name, vs := range functions.ValuesByKey(key) {
// A set qualifier such as "ForAllValues:" holds when the request carries no
// value for the key, so the listed values are not the reachable set.
if strings.ContainsRune(name, ':') {
continue
}
if !condition.IsAllowList(name) {
continue
}
constrained = true
values = append(values, vs...)
}
for name, vs := range functions.ValuesByKey(key) {
// A set qualifier such as "ForAllValues:" holds when the request carries no
// value for the key, so the listed values are not the reachable set.
if strings.ContainsRune(name, ':') || !condition.IsAllowList(name) {
return nil, false
}
constrained = true
values = append(values, vs...)
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@policy/policy.go` around lines 274 - 285, Update the loop that builds
constrained values from functions.ValuesByKey(key) so encountering any
set-qualified or non-allow-list condition immediately makes the result
unconstrained, rather than skipping it and retaining values from other
functions. Preserve the existing value aggregation for keys containing only
supported allow-list conditions, and add a test covering mixed operators such as
StringEquals with StringNotEquals.

Comment thread policy/table-action.go
Comment on lines +482 to +484
func TableActionConditionKeys(action TableAction) condition.KeySet {
return tableActionConditionKeyMap[Action(action)]
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -C 4 'type KeySet|func \(.*KeySet.*\) (Add|Delete|Remove|Clone)' policy/condition
rg -n -C 3 'TableActionConditionKeys|tableActionConditionKeyMap' policy

Repository: minio/pkg

Length of output: 12839


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '--- policy/condition/key.go relevant definitions ---\n'
sed -n '1,220p' policy/condition/key.go | cat -n

printf '\n--- table-action.go relevant helper/map construction ---\n'
sed -n '360,485p' policy/table-action.go | cat -n

printf '\n--- statement.go relevant call site ---\n'
sed -n '280,305p' policy/statement.go | cat -n

Repository: minio/pkg

Length of output: 238


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- policy/condition/key.go relevant definitions ---'
sed -n '1,220p' policy/condition/key.go | cat -n

printf '%s\n' ''
printf '%s\n' '--- table-action.go relevant helper/map construction ---'
sed -n '360,485p' policy/table-action.go | cat -n

printf '%s\n' ''
printf '%s\n' '--- statement.go relevant call site ---'
sed -n '280,305p' policy/statement.go | cat -n

Repository: minio/pkg

Length of output: 16207


Return an isolated key set from TableActionConditionKeys.

condition.KeySet is map[Key]struct{} and KeySet.Add mutates the map. This returns the map stored in tableActionConditionKeyMap, so callers can change shared global state and concurrent callers can race. Return an unexported wrapper, clone the map before returning, or otherwise avoid exposing writable state.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@policy/table-action.go` around lines 482 - 484, Update
TableActionConditionKeys to return an isolated copy of the condition.KeySet
rather than the shared map stored in tableActionConditionKeyMap. Clone each key
into a new map before returning so callers can mutate their result without
changing global state or racing with other callers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant