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
9 changes: 9 additions & 0 deletions docs/coverage/aws/iam.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,15 @@ ContextualAuthorizer is an optional capability: an IAM implementation that can
| --- | --- |
| `CheckPermissionWithContext` | |

### PermissionEvaluator

PermissionEvaluator is an optional capability: an IAM implementation that

| Operation | Description |
| --- | --- |
| `EvaluatePermission` | |
| `EvaluateServiceWide` | |

### PolicyInspector

PolicyInspector is an optional capability: an IAM implementation that can
Expand Down
12 changes: 12 additions & 0 deletions docs/coverage/coverage.json
Original file line number Diff line number Diff line change
Expand Up @@ -8546,6 +8546,18 @@
}
]
},
{
"name": "PermissionEvaluator",
"doc": "PermissionEvaluator is an optional capability: an IAM implementation that",
"operations": [
{
"name": "EvaluatePermission"
},
{
"name": "EvaluateServiceWide"
}
]
},
{
"name": "PolicyInspector",
"doc": "PolicyInspector is an optional capability: an IAM implementation that can",
Expand Down
33 changes: 30 additions & 3 deletions providers/aws/iam/condition.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,31 @@ func (c ConditionContext) get(key string) (string, bool) {
// single key the listed values combine with OR (a negative operator requires
// that none match). An empty condition block is vacuously true.
func evaluateConditions(conds map[string]map[string]any, cctx ConditionContext) bool {
return evaluateConditionsWith(conds, cctx, absentKeyIAM)
}

// absentKey says how a condition key missing from the request context is
// treated.
type absentKey int

const (
// absentKeyIAM is real IAM: a plain operator fails, its ...IfExists form
// passes, and Null tests for presence.
absentKeyIAM absentKey = iota
// absentKeyMatches treats every missing key as satisfied, whatever the
// operator. Used for Deny statements when the resource is unknown.
absentKeyMatches
// absentKeyFails treats every missing key as unsatisfied, whatever the
// operator. Used for Allow statements when the resource is unknown.
absentKeyFails
)

// evaluateConditionsWith is evaluateConditions with an explicit rule for keys
// the request context does not carry.
func evaluateConditionsWith(conds map[string]map[string]any, cctx ConditionContext, absent absentKey) bool {
for rawOp, keyVals := range conds {
for key, raw := range keyVals {
if !evaluateConditionKey(rawOp, key, toStringSlice(raw), cctx) {
if !evaluateConditionKey(rawOp, key, toStringSlice(raw), cctx, absent) {
return false
}
}
Expand All @@ -56,12 +78,17 @@ func evaluateConditions(conds map[string]map[string]any, cctx ConditionContext)
// policy-supplied values. It resolves the request value from the context,
// applies the ...IfExists rule for an absent key, and dispatches to the operator
// family. The Null operator is handled first because it is defined in terms of
// key presence, not the key's value.
func evaluateConditionKey(rawOp, key string, values []string, cctx ConditionContext) bool {
// key presence, not the key's value. A non-IAM absent rule overrides all of
// that for a missing key.
func evaluateConditionKey(rawOp, key string, values []string, cctx ConditionContext, absent absentKey) bool {
base, ifExists := splitIfExists(rawOp)

ctxVal, present := cctx.get(key)

if !present && absent != absentKeyIAM {
return absent == absentKeyMatches
}

if strings.EqualFold(base, "Null") {
return evalNull(present, values)
}
Expand Down
Loading
Loading