mirror of
https://github.com/mulgadc/spinifex.git
synced 2026-10-15 00:52:38 +00:00
feat: supply the aws:PrincipalType condition key at the AWS gateway door (#1052)
Bump bluebottle for the new key. requestConditionKeys now maps this door's internal principal-type spelling to the canonical AWS value through the bluebottle constants, and omits the key for an unrecognized type rather than supplying an empty string.
This commit is contained in:
@@ -22,7 +22,7 @@ require (
|
||||
github.com/klauspost/cpuid/v2 v2.4.0
|
||||
github.com/miekg/dns v1.1.73
|
||||
github.com/minio/highwayhash v1.0.4
|
||||
github.com/mulgadc/bluebottle v1.20.0
|
||||
github.com/mulgadc/bluebottle v1.20.1-0.20260916040751-5a5ca6f0fb33
|
||||
github.com/mulgadc/northstar v1.20.0
|
||||
github.com/mulgadc/predastore v1.20.0
|
||||
github.com/mulgadc/viperblock v1.20.0
|
||||
|
||||
@@ -1329,6 +1329,8 @@ github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc
|
||||
github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk=
|
||||
github.com/mulgadc/bluebottle v1.20.0 h1:Udi+aOA14CgDLfhanw4Rsa5HqkVt+/9e6mxoQWTRCNs=
|
||||
github.com/mulgadc/bluebottle v1.20.0/go.mod h1:4rjLL3B0xZBPmxacaXPkw6sPOZZgyuCDBFmPSOC81yE=
|
||||
github.com/mulgadc/bluebottle v1.20.1-0.20260916040751-5a5ca6f0fb33 h1:Rw3mnRF2YrhVZgHpCsOi6La8cOu/V3xnhB000iWcdr8=
|
||||
github.com/mulgadc/bluebottle v1.20.1-0.20260916040751-5a5ca6f0fb33/go.mod h1:4rjLL3B0xZBPmxacaXPkw6sPOZZgyuCDBFmPSOC81yE=
|
||||
github.com/mulgadc/northstar v1.20.0 h1:nkWTuJv1aagrJUQiZHruo7xzu9RBtLHEzrQGABElwls=
|
||||
github.com/mulgadc/northstar v1.20.0/go.mod h1:3GB49VUE9rA2idp88se5jAMv3ql1Oq9kIcT29WAZwDU=
|
||||
github.com/mulgadc/predastore v1.20.0 h1:BBk02ZWWbCGqy0PkYEXh5Yk5lbOUuDEcMS++voxSc6E=
|
||||
|
||||
@@ -25,6 +25,7 @@ var gatewayDoorKeys = []string{
|
||||
iampolicy.KeyUserID,
|
||||
iampolicy.KeyPrincipalAccount,
|
||||
iampolicy.KeySourceIP,
|
||||
iampolicy.KeyPrincipalType,
|
||||
}
|
||||
|
||||
// Every condition key the evaluator names, so the write-path gate below covers
|
||||
@@ -36,6 +37,7 @@ var allConditionKeys = []string{
|
||||
iampolicy.KeyUsername,
|
||||
iampolicy.KeyPrincipalAccount,
|
||||
iampolicy.KeyUserID,
|
||||
iampolicy.KeyPrincipalType,
|
||||
// Deliberately outside the allowlist: there is no MFA in the stack, so the
|
||||
// key could never be true. It is here to prove the validator says no.
|
||||
"aws:MultiFactorAuthPresent",
|
||||
|
||||
@@ -40,9 +40,37 @@ func TestRequestConditionKeys_PopulatesAvailableKeys(t *testing.T) {
|
||||
iampolicy.KeyUsername: "alice",
|
||||
iampolicy.KeyUserID: "AIDAALICE",
|
||||
iampolicy.KeyPrincipalAccount: "000000000001",
|
||||
iampolicy.KeyPrincipalType: iampolicy.PrincipalTypeUser,
|
||||
}, keys)
|
||||
}
|
||||
|
||||
// Every principal type this door recognizes maps to the canonical AWS spelling,
|
||||
// and an unrecognized one omits the key rather than supplying an empty string.
|
||||
func TestRequestConditionKeys_PrincipalType(t *testing.T) {
|
||||
r := httptest.NewRequest(http.MethodPost, "/", nil)
|
||||
|
||||
tests := []struct {
|
||||
principalType string
|
||||
want string
|
||||
omitted bool
|
||||
}{
|
||||
{principalType: principalTypeUser, want: iampolicy.PrincipalTypeUser},
|
||||
{principalType: principalTypeAssumedRole, want: iampolicy.PrincipalTypeAssumedRole},
|
||||
{principalType: principalTypeRoot, want: iampolicy.PrincipalTypeAccount},
|
||||
{principalType: "", omitted: true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
keys := requestConditionKeys(r, principalContext{
|
||||
identity: "alice", accountID: "000000000001", principalType: tt.principalType,
|
||||
})
|
||||
if tt.omitted {
|
||||
assert.NotContains(t, keys, iampolicy.KeyPrincipalType, "principalType %q", tt.principalType)
|
||||
continue
|
||||
}
|
||||
assert.Equal(t, tt.want, keys[iampolicy.KeyPrincipalType], "principalType %q", tt.principalType)
|
||||
}
|
||||
}
|
||||
|
||||
// A role session's aws:userid is the ID STS minted for it. Both halves come from
|
||||
// the resolved role, so unlike aws:username it can carry a decision.
|
||||
func TestRequestConditionKeys_UserIDForAssumedRole(t *testing.T) {
|
||||
|
||||
@@ -651,9 +651,32 @@ func requestConditionKeys(r *http.Request, principal principalContext) iampolicy
|
||||
if ip := utils.ClientIP(r.RemoteAddr); ip != "" {
|
||||
keys[iampolicy.KeySourceIP] = ip
|
||||
}
|
||||
// Resolved from the credential record, never from anything the caller
|
||||
// supplies, so unlike aws:username it is safe for a role session too.
|
||||
if v, ok := principalTypeCondition(principal.principalType); ok {
|
||||
keys[iampolicy.KeyPrincipalType] = v
|
||||
}
|
||||
return keys
|
||||
}
|
||||
|
||||
// principalTypeCondition maps this gateway's internal principal-type spelling
|
||||
// to the canonical aws:PrincipalType value AWS documents, so a policy written
|
||||
// against the documented value applies here and at predastore identically. An
|
||||
// empty or unrecognized principal type reports false so the door omits the
|
||||
// key rather than supplying an empty string.
|
||||
func principalTypeCondition(principalType string) (string, bool) {
|
||||
switch principalType {
|
||||
case principalTypeUser:
|
||||
return iampolicy.PrincipalTypeUser, true
|
||||
case principalTypeAssumedRole:
|
||||
return iampolicy.PrincipalTypeAssumedRole, true
|
||||
case principalTypeRoot:
|
||||
return iampolicy.PrincipalTypeAccount, true
|
||||
default:
|
||||
return "", false
|
||||
}
|
||||
}
|
||||
|
||||
// principalUserID resolves aws:userid: an IAM user's unique ID, the role ID and
|
||||
// session name STS minted for a role session, or the account ID for root. Both
|
||||
// halves of a session's ID come from the resolved role, so unlike aws:username
|
||||
|
||||
Reference in New Issue
Block a user