KEP-5284: assert pre-hashed inputs to buildKey

Signed-off-by: Monis Khan <mok@microsoft.com>
This commit is contained in:
Monis Khan
2026-03-16 16:07:29 -04:00
parent 9c7e57bb7c
commit 5bff98b884

View File

@@ -18,6 +18,7 @@ package impersonation
import (
"context"
"crypto/sha256"
"encoding/json"
"fmt"
"io"
@@ -106,6 +107,10 @@ func (c *constrainedImpersonationTest) Authorize(ctx context.Context, a authoriz
return authorizer.DecisionAllow, "", nil
}
if u.GetName() == "user-impersonater" && a.GetVerb() == "impersonate:user-info" && a.GetResource() == "uids" {
return authorizer.DecisionAllow, "", nil
}
if len(u.GetGroups()) > 0 && u.GetGroups()[0] == "group-impersonater" && a.GetVerb() == "impersonate:user-info" && a.GetResource() == "groups" {
return authorizer.DecisionAllow, "", nil
}
@@ -450,12 +455,12 @@ func (c *constrainedImpersonationTest) assertCache(r testRequest) {
}
rr.Equal(len(expectedMode.outer), outer.cache.Len())
for expectedKey, expectedUser := range expectedMode.outer {
c.checkCacheEntry(outer, expectedKey.wantedUser, expectedKey.attributes, expectedUser, verb)
c.checkCacheEntry(outer, expectedKey.wantedUser, expectedKey.attributes, expectedKey.rawKey, expectedUser, verb)
}
rr.Equal(len(expectedMode.inner), inner.cache.Len())
for expectedKey, expectedUser := range expectedMode.inner {
c.checkCacheEntry(inner, expectedKey.wantedUser, authorizer.AttributesRecord{User: expectedKey.requestor}, expectedUser, verb)
c.checkCacheEntry(inner, expectedKey.wantedUser, authorizer.AttributesRecord{User: expectedKey.requestor}, expectedKey.rawKey, expectedUser, verb)
}
}
}
@@ -465,7 +470,7 @@ func usernameHash(username string) string {
return fmt.Sprintf("%x", hash)
}
func (c *constrainedImpersonationTest) checkCacheEntry(cache *impersonationCache, wantedUser *user.DefaultInfo, attributes authorizer.Attributes, expectedUser *user.DefaultInfo, expectedVerb string) {
func (c *constrainedImpersonationTest) checkCacheEntry(cache *impersonationCache, wantedUser *user.DefaultInfo, attributes authorizer.Attributes, rawKey string, expectedUser *user.DefaultInfo, expectedVerb string) {
rr := require.New(c.t)
keyString, err := buildKey(wantedUser, attributes)
rr.NoError(err)
@@ -474,7 +479,12 @@ func (c *constrainedImpersonationTest) checkCacheEntry(cache *impersonationCache
impersonatedUser := val.(*impersonatedUserInfo)
rr.Equal(expectedVerb, impersonatedUser.constraint)
rr.Equal(expectedUser, impersonatedUser.user)
rr.True(strings.HasSuffix(keyString, "/"+attributes.GetUser().GetName()))
rr.Equal(keyHash(rawKey)+"/"+attributes.GetUser().GetName(), keyString, "hash of %q does not match", rawKey)
}
func keyHash(rawKey string) string {
hash := sha256.Sum256([]byte(rawKey))
return fmt.Sprintf("%x", hash[:])
}
func withConstrainedImpersonationAttributes(a authorizer.AttributesRecord, mode string) authorizer.AttributesRecord {
@@ -527,11 +537,12 @@ func expectDeny(attributes authorizer.AttributesRecord) authzCheck {
return expectAuthzCheck(attributes, authorizer.DecisionNoOpinion)
}
func outerCacheKey(wantedUser, requestor *user.DefaultInfo, req *request.RequestInfo) impersonationCacheKey {
func outerCacheKey(wantedUser, requestor *user.DefaultInfo, req *request.RequestInfo, rawKey string) outerKey {
attributes := withUser(requestInfoToAttributes(req), requestor)
return impersonationCacheKey{
return outerKey{
wantedUser: wantedUser,
attributes: &attributes, // use a *authorizer.AttributesRecord so that it can be used in a map key
rawKey: rawKey,
}
}
@@ -541,12 +552,19 @@ type expectedCache struct {
}
type expectedModeCache struct {
outer map[impersonationCacheKey]*user.DefaultInfo
outer map[outerKey]*user.DefaultInfo
inner map[innerKey]*user.DefaultInfo
}
type outerKey struct {
wantedUser *user.DefaultInfo
attributes *authorizer.AttributesRecord
rawKey string
}
type innerKey struct {
wantedUser, requestor *user.DefaultInfo
rawKey string
}
type testRequest struct {
@@ -619,13 +637,16 @@ func associatedNodeTestCase() []testRequest {
},
modes: map[string]expectedModeCache{
"impersonate:associated-node": {
outer: map[impersonationCacheKey]*user.DefaultInfo{
outerCacheKey(&user.DefaultInfo{Name: "system:node:*"}, saDefaultOnAnyNode, getSecretRequest): node1FullUserInfo,
outer: map[outerKey]*user.DefaultInfo{
outerCacheKey(&user.DefaultInfo{Name: "system:node:*"}, saDefaultOnAnyNode, getSecretRequest,
"\x00\x00\x00\x1d\x00\x00\x00\rsystem:node:*\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xb7\x00\x00\x00%system:serviceaccount:default:default\x00\x00\x00\x00\x00\x00\x00\x1f\x00\x00\x00\x1bassociate-node-impersonater\x00\x00\x00c\x00\x00\x001authentication.kubernetes.io/associated-node-keys\x00\x00\x00*\x00\x00\x00&authentication.kubernetes.io/node-name\x00\x00\x004\x00\x00\x00\x03get\x01\x00\x00\x00\x03bar\x00\x00\x00\asecrets\x00\x00\x00\x00\x00\x00\x00\x03foo\x00\x00\x00\x00\x00\x00\x00\x02v1\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
): node1FullUserInfo,
},
inner: map[innerKey]*user.DefaultInfo{
{
wantedUser: &user.DefaultInfo{Name: "system:node:*"},
requestor: saDefaultOnAnyNode,
rawKey: "\x00\x00\x00\x1d\x00\x00\x00\rsystem:node:*\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xb7\x00\x00\x00%system:serviceaccount:default:default\x00\x00\x00\x00\x00\x00\x00\x1f\x00\x00\x00\x1bassociate-node-impersonater\x00\x00\x00c\x00\x00\x001authentication.kubernetes.io/associated-node-keys\x00\x00\x00*\x00\x00\x00&authentication.kubernetes.io/node-name\x00\x00\x00\"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
}: node1FullUserInfo,
},
},
@@ -635,10 +656,14 @@ func associatedNodeTestCase() []testRequest {
modeIdx: cacheWithOnlyNode1Data.modeIdx,
modes: map[string]expectedModeCache{
"impersonate:associated-node": {
outer: map[impersonationCacheKey]*user.DefaultInfo{
outerCacheKey(&user.DefaultInfo{Name: "system:node:*"}, saDefaultOnAnyNode, getSecretRequest): node1FullUserInfo,
outer: map[outerKey]*user.DefaultInfo{
outerCacheKey(&user.DefaultInfo{Name: "system:node:*"}, saDefaultOnAnyNode, getSecretRequest,
"\x00\x00\x00\x1d\x00\x00\x00\rsystem:node:*\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xb7\x00\x00\x00%system:serviceaccount:default:default\x00\x00\x00\x00\x00\x00\x00\x1f\x00\x00\x00\x1bassociate-node-impersonater\x00\x00\x00c\x00\x00\x001authentication.kubernetes.io/associated-node-keys\x00\x00\x00*\x00\x00\x00&authentication.kubernetes.io/node-name\x00\x00\x004\x00\x00\x00\x03get\x01\x00\x00\x00\x03bar\x00\x00\x00\asecrets\x00\x00\x00\x00\x00\x00\x00\x03foo\x00\x00\x00\x00\x00\x00\x00\x02v1\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
): node1FullUserInfo,
// even though this request was made on node2, since the inner cache matched it returned a value for node1
outerCacheKey(&user.DefaultInfo{Name: "system:node:*"}, saDefaultOnAnyNode, getPodRequest): node1FullUserInfo,
outerCacheKey(&user.DefaultInfo{Name: "system:node:*"}, saDefaultOnAnyNode, getPodRequest,
"\x00\x00\x00\x1d\x00\x00\x00\rsystem:node:*\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xb7\x00\x00\x00%system:serviceaccount:default:default\x00\x00\x00\x00\x00\x00\x00\x1f\x00\x00\x00\x1bassociate-node-impersonater\x00\x00\x00c\x00\x00\x001authentication.kubernetes.io/associated-node-keys\x00\x00\x00*\x00\x00\x00&authentication.kubernetes.io/node-name\x00\x00\x001\x00\x00\x00\x03get\x01\x00\x00\x00\x03bar\x00\x00\x00\x04pods\x00\x00\x00\x00\x00\x00\x00\x03foo\x00\x00\x00\x00\x00\x00\x00\x02v1\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
): node1FullUserInfo,
},
inner: cacheWithOnlyNode1Data.modes["impersonate:associated-node"].inner,
},
@@ -831,11 +856,17 @@ func TestConstrainedImpersonationFilter(t *testing.T) {
},
modes: map[string]expectedModeCache{
"impersonate:user-info": {
outer: map[impersonationCacheKey]*user.DefaultInfo{
outerCacheKey(anyone, userImpersonator, getPodRequest): anyoneAuthenticated,
outer: map[outerKey]*user.DefaultInfo{
outerCacheKey(anyone, userImpersonator, getPodRequest,
"\x00\x00\x00\x16\x00\x00\x00\x06anyone\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00!\x00\x00\x00\x11user-impersonater\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x001\x00\x00\x00\x03get\x01\x00\x00\x00\x03bar\x00\x00\x00\x04pods\x00\x00\x00\x00\x00\x00\x00\x03foo\x00\x00\x00\x00\x00\x00\x00\x02v1\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
): anyoneAuthenticated,
},
inner: map[innerKey]*user.DefaultInfo{
{wantedUser: anyone, requestor: userImpersonator}: anyoneAuthenticated,
{
wantedUser: anyone,
requestor: userImpersonator,
rawKey: "\x00\x00\x00\x16\x00\x00\x00\x06anyone\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00!\x00\x00\x00\x11user-impersonater\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
}: anyoneAuthenticated,
},
},
},
@@ -861,12 +892,20 @@ func TestConstrainedImpersonationFilter(t *testing.T) {
},
modes: map[string]expectedModeCache{
"impersonate:user-info": {
outer: map[impersonationCacheKey]*user.DefaultInfo{
outerCacheKey(anyone, userImpersonator, getPodRequest): anyoneAuthenticated,
outerCacheKey(anyone, userImpersonator, getAnotherPodRequest): anyoneAuthenticated,
outer: map[outerKey]*user.DefaultInfo{
outerCacheKey(anyone, userImpersonator, getPodRequest,
"\x00\x00\x00\x16\x00\x00\x00\x06anyone\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00!\x00\x00\x00\x11user-impersonater\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x001\x00\x00\x00\x03get\x01\x00\x00\x00\x03bar\x00\x00\x00\x04pods\x00\x00\x00\x00\x00\x00\x00\x03foo\x00\x00\x00\x00\x00\x00\x00\x02v1\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
): anyoneAuthenticated,
outerCacheKey(anyone, userImpersonator, getAnotherPodRequest,
"\x00\x00\x00\x16\x00\x00\x00\x06anyone\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00!\x00\x00\x00\x11user-impersonater\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x003\x00\x00\x00\x03get\x01\x00\x00\x00\x04bar1\x00\x00\x00\x04pods\x00\x00\x00\x00\x00\x00\x00\x04foo1\x00\x00\x00\x00\x00\x00\x00\x02v1\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
): anyoneAuthenticated,
},
inner: map[innerKey]*user.DefaultInfo{
{wantedUser: anyone, requestor: userImpersonator}: anyoneAuthenticated,
{
wantedUser: anyone,
requestor: userImpersonator,
rawKey: "\x00\x00\x00\x16\x00\x00\x00\x06anyone\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00!\x00\x00\x00\x11user-impersonater\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
}: anyoneAuthenticated,
},
},
},
@@ -892,12 +931,20 @@ func TestConstrainedImpersonationFilter(t *testing.T) {
},
modes: map[string]expectedModeCache{
"impersonate:user-info": {
outer: map[impersonationCacheKey]*user.DefaultInfo{
outerCacheKey(anyone, userImpersonator, getPodRequest): anyoneAuthenticated,
outerCacheKey(anyone, userImpersonator, getAnotherPodRequest): anyoneAuthenticated,
outer: map[outerKey]*user.DefaultInfo{
outerCacheKey(anyone, userImpersonator, getPodRequest,
"\x00\x00\x00\x16\x00\x00\x00\x06anyone\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00!\x00\x00\x00\x11user-impersonater\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x001\x00\x00\x00\x03get\x01\x00\x00\x00\x03bar\x00\x00\x00\x04pods\x00\x00\x00\x00\x00\x00\x00\x03foo\x00\x00\x00\x00\x00\x00\x00\x02v1\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
): anyoneAuthenticated,
outerCacheKey(anyone, userImpersonator, getAnotherPodRequest,
"\x00\x00\x00\x16\x00\x00\x00\x06anyone\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00!\x00\x00\x00\x11user-impersonater\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x003\x00\x00\x00\x03get\x01\x00\x00\x00\x04bar1\x00\x00\x00\x04pods\x00\x00\x00\x00\x00\x00\x00\x04foo1\x00\x00\x00\x00\x00\x00\x00\x02v1\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
): anyoneAuthenticated,
},
inner: map[innerKey]*user.DefaultInfo{
{wantedUser: anyone, requestor: userImpersonator}: anyoneAuthenticated,
{
wantedUser: anyone,
requestor: userImpersonator,
rawKey: "\x00\x00\x00\x16\x00\x00\x00\x06anyone\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00!\x00\x00\x00\x11user-impersonater\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
}: anyoneAuthenticated,
},
},
},
@@ -931,13 +978,16 @@ func TestConstrainedImpersonationFilter(t *testing.T) {
},
modes: map[string]expectedModeCache{
"impersonate:serviceaccount": {
outer: map[impersonationCacheKey]*user.DefaultInfo{
outerCacheKey(saUser, saImpersonator, getPodRequest): saUserAuthenticated,
outer: map[outerKey]*user.DefaultInfo{
outerCacheKey(saUser, saImpersonator, getPodRequest,
"\x00\x00\x005\x00\x00\x00%system:serviceaccount:default:default\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1f\x00\x00\x00\x0fsa-impersonater\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x001\x00\x00\x00\x03get\x01\x00\x00\x00\x03bar\x00\x00\x00\x04pods\x00\x00\x00\x00\x00\x00\x00\x03foo\x00\x00\x00\x00\x00\x00\x00\x02v1\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
): saUserAuthenticated,
},
inner: map[innerKey]*user.DefaultInfo{
{
wantedUser: saUser,
requestor: saImpersonator,
rawKey: "\x00\x00\x005\x00\x00\x00%system:serviceaccount:default:default\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1f\x00\x00\x00\x0fsa-impersonater\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
}: saUserAuthenticated,
},
},
@@ -1018,11 +1068,17 @@ func TestConstrainedImpersonationFilter(t *testing.T) {
},
modes: map[string]expectedModeCache{
"impersonate:arbitrary-node": {
outer: map[impersonationCacheKey]*user.DefaultInfo{
outerCacheKey(nodeUser, nodeImpersonator, getPodRequest): nodeUserAuthenticated,
outer: map[outerKey]*user.DefaultInfo{
outerCacheKey(nodeUser, nodeImpersonator, getPodRequest,
"\x00\x00\x00!\x00\x00\x00\x11system:node:node1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00!\x00\x00\x00\x11node-impersonater\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x001\x00\x00\x00\x03get\x01\x00\x00\x00\x03bar\x00\x00\x00\x04pods\x00\x00\x00\x00\x00\x00\x00\x03foo\x00\x00\x00\x00\x00\x00\x00\x02v1\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
): nodeUserAuthenticated,
},
inner: map[innerKey]*user.DefaultInfo{
{wantedUser: nodeUser, requestor: nodeImpersonator}: nodeUserAuthenticated,
{
wantedUser: nodeUser,
requestor: nodeImpersonator,
rawKey: "\x00\x00\x00!\x00\x00\x00\x11system:node:node1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00!\x00\x00\x00\x11node-impersonater\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
}: nodeUserAuthenticated,
},
},
},
@@ -1106,7 +1162,7 @@ func TestConstrainedImpersonationFilter(t *testing.T) {
},
modes: map[string]expectedModeCache{
"impersonate:user-info": {
outer: map[impersonationCacheKey]*user.DefaultInfo{
outer: map[outerKey]*user.DefaultInfo{
outerCacheKey(
&user.DefaultInfo{
Name: "system:admin",
@@ -1116,7 +1172,7 @@ func TestConstrainedImpersonationFilter(t *testing.T) {
Name: "user-impersonater",
Groups: []string{"extra-setter-scopes"},
},
getPodRequest): {
getPodRequest, "\x00\x00\x00c\x00\x00\x00\fsystem:admin\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00G\x00\x00\x00\x10pandas.io/scopes\x00\x00\x00/\x00\x00\x00\ascope-a\x00\x00\x00\ascope-b\x00\x00\x00\x015\x00\x00\x00\x014\x00\x00\x00\x013\x00\x00\x00\x012\x00\x00\x00\x011\x00\x00\x008\x00\x00\x00\x11user-impersonater\x00\x00\x00\x00\x00\x00\x00\x17\x00\x00\x00\x13extra-setter-scopes\x00\x00\x00\x00\x00\x00\x001\x00\x00\x00\x03get\x01\x00\x00\x00\x03bar\x00\x00\x00\x04pods\x00\x00\x00\x00\x00\x00\x00\x03foo\x00\x00\x00\x00\x00\x00\x00\x02v1\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"): {
Name: "system:admin",
Groups: []string{"system:authenticated"},
Extra: map[string][]string{"pandas.io/scopes": {"scope-a", "scope-b", "5", "4", "3", "2", "1"}},
@@ -1132,6 +1188,7 @@ func TestConstrainedImpersonationFilter(t *testing.T) {
Name: "user-impersonater",
Groups: []string{"extra-setter-scopes"},
},
rawKey: "\x00\x00\x00c\x00\x00\x00\fsystem:admin\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00G\x00\x00\x00\x10pandas.io/scopes\x00\x00\x00/\x00\x00\x00\ascope-a\x00\x00\x00\ascope-b\x00\x00\x00\x015\x00\x00\x00\x014\x00\x00\x00\x013\x00\x00\x00\x012\x00\x00\x00\x011\x00\x00\x008\x00\x00\x00\x11user-impersonater\x00\x00\x00\x00\x00\x00\x00\x17\x00\x00\x00\x13extra-setter-scopes\x00\x00\x00\x00\x00\x00\x00\"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
}: {
Name: "system:admin",
Groups: []string{"system:authenticated"},
@@ -1273,11 +1330,13 @@ func TestConstrainedImpersonationFilter(t *testing.T) {
},
modes: map[string]expectedModeCache{
"impersonate:user-info": {
outer: map[impersonationCacheKey]*user.DefaultInfo{
outer: map[outerKey]*user.DefaultInfo{
outerCacheKey(&user.DefaultInfo{
Name: "bob",
Groups: []string{"group1", "group2", "group3", "group4"},
}, &user.DefaultInfo{Name: "many-groups-impersonater"}, getPodRequest): {
}, &user.DefaultInfo{Name: "many-groups-impersonater"}, getPodRequest,
"\x00\x00\x00;\x00\x00\x00\x03bob\x00\x00\x00\x00\x00\x00\x00(\x00\x00\x00\x06group1\x00\x00\x00\x06group2\x00\x00\x00\x06group3\x00\x00\x00\x06group4\x00\x00\x00\x00\x00\x00\x00(\x00\x00\x00\x18many-groups-impersonater\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x001\x00\x00\x00\x03get\x01\x00\x00\x00\x03bar\x00\x00\x00\x04pods\x00\x00\x00\x00\x00\x00\x00\x03foo\x00\x00\x00\x00\x00\x00\x00\x02v1\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
): {
Name: "bob",
Groups: []string{"group1", "group2", "group3", "group4", "system:authenticated"},
},
@@ -1289,6 +1348,7 @@ func TestConstrainedImpersonationFilter(t *testing.T) {
Groups: []string{"group1", "group2", "group3", "group4"},
},
requestor: &user.DefaultInfo{Name: "many-groups-impersonater"},
rawKey: "\x00\x00\x00;\x00\x00\x00\x03bob\x00\x00\x00\x00\x00\x00\x00(\x00\x00\x00\x06group1\x00\x00\x00\x06group2\x00\x00\x00\x06group3\x00\x00\x00\x06group4\x00\x00\x00\x00\x00\x00\x00(\x00\x00\x00\x18many-groups-impersonater\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
}: {
Name: "bob",
Groups: []string{"group1", "group2", "group3", "group4", "system:authenticated"},
@@ -1344,7 +1404,7 @@ func TestConstrainedImpersonationFilter(t *testing.T) {
},
modes: map[string]expectedModeCache{
"impersonate:user-info": {
outer: map[impersonationCacheKey]*user.DefaultInfo{
outer: map[outerKey]*user.DefaultInfo{
outerCacheKey(&user.DefaultInfo{
Name: "alice",
Extra: map[string][]string{
@@ -1353,7 +1413,9 @@ func TestConstrainedImpersonationFilter(t *testing.T) {
"scopes.example.com/key3": {"val3"},
"scopes.example.com/key4": {"val4"},
},
}, &user.DefaultInfo{Name: "many-extras-impersonater"}, getPodRequest): {
}, &user.DefaultInfo{Name: "many-extras-impersonater"}, getPodRequest,
"\x00\x00\x00\xb1\x00\x00\x00\x05alice\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x9c\x00\x00\x00\x17scopes.example.com/key1\x00\x00\x00\b\x00\x00\x00\x04val1\x00\x00\x00\x17scopes.example.com/key2\x00\x00\x00\b\x00\x00\x00\x04val2\x00\x00\x00\x17scopes.example.com/key3\x00\x00\x00\b\x00\x00\x00\x04val3\x00\x00\x00\x17scopes.example.com/key4\x00\x00\x00\b\x00\x00\x00\x04val4\x00\x00\x00(\x00\x00\x00\x18many-extras-impersonater\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x001\x00\x00\x00\x03get\x01\x00\x00\x00\x03bar\x00\x00\x00\x04pods\x00\x00\x00\x00\x00\x00\x00\x03foo\x00\x00\x00\x00\x00\x00\x00\x02v1\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
): {
Name: "alice",
Groups: []string{"system:authenticated"},
Extra: map[string][]string{
@@ -1376,6 +1438,7 @@ func TestConstrainedImpersonationFilter(t *testing.T) {
},
},
requestor: &user.DefaultInfo{Name: "many-extras-impersonater"},
rawKey: "\x00\x00\x00\xb1\x00\x00\x00\x05alice\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x9c\x00\x00\x00\x17scopes.example.com/key1\x00\x00\x00\b\x00\x00\x00\x04val1\x00\x00\x00\x17scopes.example.com/key2\x00\x00\x00\b\x00\x00\x00\x04val2\x00\x00\x00\x17scopes.example.com/key3\x00\x00\x00\b\x00\x00\x00\x04val3\x00\x00\x00\x17scopes.example.com/key4\x00\x00\x00\b\x00\x00\x00\x04val4\x00\x00\x00(\x00\x00\x00\x18many-extras-impersonater\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
}: {
Name: "alice",
Groups: []string{"system:authenticated"},
@@ -1444,14 +1507,16 @@ func TestConstrainedImpersonationFilter(t *testing.T) {
},
modes: map[string]expectedModeCache{
"impersonate:user-info": {
outer: map[impersonationCacheKey]*user.DefaultInfo{
outer: map[outerKey]*user.DefaultInfo{
outerCacheKey(&user.DefaultInfo{
Name: "frank",
Groups: []string{"dev", "ops", "security", "audit"},
Extra: map[string][]string{
"tags.io/env": {"prod", "staging", "dev"},
},
}, &user.DefaultInfo{Name: "mixed-impersonater"}, getPodRequest): {
}, &user.DefaultInfo{Name: "mixed-impersonater"}, getPodRequest,
"\x00\x00\x00e\x00\x00\x00\x05frank\x00\x00\x00\x00\x00\x00\x00#\x00\x00\x00\x03dev\x00\x00\x00\x03ops\x00\x00\x00\bsecurity\x00\x00\x00\x05audit\x00\x00\x00-\x00\x00\x00\vtags.io/env\x00\x00\x00\x1a\x00\x00\x00\x04prod\x00\x00\x00\astaging\x00\x00\x00\x03dev\x00\x00\x00\"\x00\x00\x00\x12mixed-impersonater\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x001\x00\x00\x00\x03get\x01\x00\x00\x00\x03bar\x00\x00\x00\x04pods\x00\x00\x00\x00\x00\x00\x00\x03foo\x00\x00\x00\x00\x00\x00\x00\x02v1\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
): {
Name: "frank",
Groups: []string{"dev", "ops", "security", "audit", "system:authenticated"},
Extra: map[string][]string{
@@ -1469,6 +1534,7 @@ func TestConstrainedImpersonationFilter(t *testing.T) {
},
},
requestor: &user.DefaultInfo{Name: "mixed-impersonater"},
rawKey: "\x00\x00\x00e\x00\x00\x00\x05frank\x00\x00\x00\x00\x00\x00\x00#\x00\x00\x00\x03dev\x00\x00\x00\x03ops\x00\x00\x00\bsecurity\x00\x00\x00\x05audit\x00\x00\x00-\x00\x00\x00\vtags.io/env\x00\x00\x00\x1a\x00\x00\x00\x04prod\x00\x00\x00\astaging\x00\x00\x00\x03dev\x00\x00\x00\"\x00\x00\x00\x12mixed-impersonater\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
}: {
Name: "frank",
Groups: []string{"dev", "ops", "security", "audit", "system:authenticated"},
@@ -1511,14 +1577,20 @@ func TestConstrainedImpersonationFilter(t *testing.T) {
},
modes: map[string]expectedModeCache{
"impersonate:user-info": {
outer: map[impersonationCacheKey]*user.DefaultInfo{
outerCacheKey(&user.DefaultInfo{Name: "bob"}, userImpersonator, getDeploymentRequest): {
outer: map[outerKey]*user.DefaultInfo{
outerCacheKey(&user.DefaultInfo{Name: "bob"}, userImpersonator, getDeploymentRequest,
"\x00\x00\x00\x13\x00\x00\x00\x03bob\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00!\x00\x00\x00\x11user-impersonater\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00<\x00\x00\x00\x03get\x01\x00\x00\x00\x03bar\x00\x00\x00\vdeployments\x00\x00\x00\x00\x00\x00\x00\x03foo\x00\x00\x00\x04apps\x00\x00\x00\x02v1\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
): {
Name: "bob",
Groups: []string{"system:authenticated"},
},
},
inner: map[innerKey]*user.DefaultInfo{
{wantedUser: &user.DefaultInfo{Name: "bob"}, requestor: userImpersonator}: {
{
wantedUser: &user.DefaultInfo{Name: "bob"},
requestor: userImpersonator,
rawKey: "\x00\x00\x00\x13\x00\x00\x00\x03bob\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00!\x00\x00\x00\x11user-impersonater\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
}: {
Name: "bob",
Groups: []string{"system:authenticated"},
},
@@ -1549,14 +1621,20 @@ func TestConstrainedImpersonationFilter(t *testing.T) {
},
modes: map[string]expectedModeCache{
"impersonate:user-info": {
outer: map[impersonationCacheKey]*user.DefaultInfo{
outerCacheKey(&user.DefaultInfo{Name: "bob"}, userImpersonator, getDeploymentRequest): {
outer: map[outerKey]*user.DefaultInfo{
outerCacheKey(&user.DefaultInfo{Name: "bob"}, userImpersonator, getDeploymentRequest,
"\x00\x00\x00\x13\x00\x00\x00\x03bob\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00!\x00\x00\x00\x11user-impersonater\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00<\x00\x00\x00\x03get\x01\x00\x00\x00\x03bar\x00\x00\x00\vdeployments\x00\x00\x00\x00\x00\x00\x00\x03foo\x00\x00\x00\x04apps\x00\x00\x00\x02v1\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
): {
Name: "bob",
Groups: []string{"system:authenticated"},
},
},
inner: map[innerKey]*user.DefaultInfo{
{wantedUser: &user.DefaultInfo{Name: "bob"}, requestor: userImpersonator}: {
{
wantedUser: &user.DefaultInfo{Name: "bob"},
requestor: userImpersonator,
rawKey: "\x00\x00\x00\x13\x00\x00\x00\x03bob\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00!\x00\x00\x00\x11user-impersonater\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
}: {
Name: "bob",
Groups: []string{"system:authenticated"},
},
@@ -1570,6 +1648,251 @@ func TestConstrainedImpersonationFilter(t *testing.T) {
},
},
},
{
name: "impersonating-user-with-uid",
requests: []testRequest{
{
request: getPodRequest,
requestor: &user.DefaultInfo{Name: "user-impersonater", UID: "requestor-uid-123"},
impersonatedUser: &user.DefaultInfo{
Name: "anyone",
UID: "wanted-uid-456",
},
expectedImpersonatedUser: &user.DefaultInfo{
Name: "anyone",
UID: "wanted-uid-456",
Groups: []string{"system:authenticated"},
},
expectedAuthzChecks: []authzCheck{
expectAllow(withImpersonateOnAttributes(getPodRequest, "user-info")),
expectAllow(withConstrainedImpersonationAttributes(authorizer.AttributesRecord{Resource: "users", Name: "anyone"}, "user-info")),
expectAllow(withConstrainedImpersonationAttributes(authorizer.AttributesRecord{Resource: "uids", Name: "wanted-uid-456"}, "user-info")),
},
expectedCache: &expectedCache{
modeIdx: map[string]string{
"user-impersonater": "impersonate:user-info",
},
modes: map[string]expectedModeCache{
"impersonate:user-info": {
outer: map[outerKey]*user.DefaultInfo{
outerCacheKey(
&user.DefaultInfo{Name: "anyone", UID: "wanted-uid-456"},
&user.DefaultInfo{Name: "user-impersonater", UID: "requestor-uid-123"},
getPodRequest,
"\x00\x00\x00$\x00\x00\x00\x06anyone\x00\x00\x00\x0ewanted-uid-456\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x002\x00\x00\x00\x11user-impersonater\x00\x00\x00\x11requestor-uid-123\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x001\x00\x00\x00\x03get\x01\x00\x00\x00\x03bar\x00\x00\x00\x04pods\x00\x00\x00\x00\x00\x00\x00\x03foo\x00\x00\x00\x00\x00\x00\x00\x02v1\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
): {
Name: "anyone",
UID: "wanted-uid-456",
Groups: []string{"system:authenticated"},
},
},
inner: map[innerKey]*user.DefaultInfo{
{
wantedUser: &user.DefaultInfo{Name: "anyone", UID: "wanted-uid-456"},
requestor: &user.DefaultInfo{Name: "user-impersonater", UID: "requestor-uid-123"},
rawKey: "\x00\x00\x00$\x00\x00\x00\x06anyone\x00\x00\x00\x0ewanted-uid-456\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x002\x00\x00\x00\x11user-impersonater\x00\x00\x00\x11requestor-uid-123\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
}: {
Name: "anyone",
UID: "wanted-uid-456",
Groups: []string{"system:authenticated"},
},
},
},
},
},
expectedCode: http.StatusOK,
expectedAttemptMode: "user-info",
expectedAttemptDecision: "allowed",
expectedAuthorizationMetrics: map[string]int{
"user-info/allowed": 3, // impersonate-on + impersonate:user-info users + uids
},
},
},
},
{
name: "impersonating-user-subresource-request",
requests: []testRequest{
{
request: &request.RequestInfo{
IsResourceRequest: true,
Verb: "get",
APIVersion: "v1",
Resource: "pods",
Subresource: "status",
Name: "foo",
Namespace: "bar",
},
requestor: userImpersonator,
impersonatedUser: anyone,
expectedImpersonatedUser: anyoneAuthenticated,
expectedAuthzChecks: []authzCheck{
expectAllow(withImpersonateOnAttributes(&request.RequestInfo{
IsResourceRequest: true,
Verb: "get",
APIVersion: "v1",
Resource: "pods",
Subresource: "status",
Name: "foo",
Namespace: "bar",
}, "user-info")),
expectAllow(withConstrainedImpersonationAttributes(authorizer.AttributesRecord{Resource: "users", Name: "anyone"}, "user-info")),
},
expectedCache: &expectedCache{
modeIdx: map[string]string{
userImpersonator.Name: "impersonate:user-info",
},
modes: map[string]expectedModeCache{
"impersonate:user-info": {
outer: map[outerKey]*user.DefaultInfo{
outerCacheKey(anyone, userImpersonator, &request.RequestInfo{
IsResourceRequest: true,
Verb: "get",
APIVersion: "v1",
Resource: "pods",
Subresource: "status",
Name: "foo",
Namespace: "bar",
},
"\x00\x00\x00\x16\x00\x00\x00\x06anyone\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00!\x00\x00\x00\x11user-impersonater\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x007\x00\x00\x00\x03get\x01\x00\x00\x00\x03bar\x00\x00\x00\x04pods\x00\x00\x00\x06status\x00\x00\x00\x03foo\x00\x00\x00\x00\x00\x00\x00\x02v1\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
): anyoneAuthenticated,
},
inner: map[innerKey]*user.DefaultInfo{
{
wantedUser: anyone,
requestor: userImpersonator,
rawKey: "\x00\x00\x00\x16\x00\x00\x00\x06anyone\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00!\x00\x00\x00\x11user-impersonater\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
}: anyoneAuthenticated,
},
},
},
},
expectedCode: http.StatusOK,
expectedAttemptMode: "user-info",
expectedAttemptDecision: "allowed",
expectedAuthorizationMetrics: map[string]int{
"user-info/allowed": 2, // impersonate-on + impersonate:user-info
},
},
},
},
{
name: "impersonating-user-non-resource-request",
requests: []testRequest{
{
request: &request.RequestInfo{
IsResourceRequest: false,
Verb: "get",
Path: "/healthz",
},
requestor: userImpersonator,
impersonatedUser: anyone,
expectedImpersonatedUser: anyoneAuthenticated,
expectedAuthzChecks: []authzCheck{
expectAllow(withImpersonateOnAttributes(&request.RequestInfo{
IsResourceRequest: false,
Verb: "get",
Path: "/healthz",
}, "user-info")),
expectAllow(withConstrainedImpersonationAttributes(authorizer.AttributesRecord{Resource: "users", Name: "anyone"}, "user-info")),
},
expectedCache: &expectedCache{
modeIdx: map[string]string{
userImpersonator.Name: "impersonate:user-info",
},
modes: map[string]expectedModeCache{
"impersonate:user-info": {
outer: map[outerKey]*user.DefaultInfo{
outerCacheKey(anyone, userImpersonator, &request.RequestInfo{
IsResourceRequest: false,
Verb: "get",
Path: "/healthz",
},
"\x00\x00\x00\x16\x00\x00\x00\x06anyone\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00!\x00\x00\x00\x11user-impersonater\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00-\x00\x00\x00\x03get\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\b/healthz\x00\x00\x00\x00\x00\x00\x00\x00",
): anyoneAuthenticated,
},
inner: map[innerKey]*user.DefaultInfo{
{
wantedUser: anyone,
requestor: userImpersonator,
rawKey: "\x00\x00\x00\x16\x00\x00\x00\x06anyone\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00!\x00\x00\x00\x11user-impersonater\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
}: anyoneAuthenticated,
},
},
},
},
expectedCode: http.StatusOK,
expectedAttemptMode: "user-info",
expectedAttemptDecision: "allowed",
expectedAuthorizationMetrics: map[string]int{
"user-info/allowed": 2, // impersonate-on + impersonate:user-info
},
},
},
},
{
name: "impersonating-user-with-field-and-label-selectors",
requests: []testRequest{
{
request: &request.RequestInfo{
IsResourceRequest: true,
Verb: "list",
APIVersion: "v1",
Resource: "pods",
Namespace: "bar",
FieldSelector: "spec.nodeName=node1",
LabelSelector: "app=web",
},
requestor: userImpersonator,
impersonatedUser: anyone,
expectedImpersonatedUser: anyoneAuthenticated,
expectedAuthzChecks: []authzCheck{
expectAllow(withImpersonateOnAttributes(&request.RequestInfo{
IsResourceRequest: true,
Verb: "list",
APIVersion: "v1",
Resource: "pods",
Namespace: "bar",
FieldSelector: "spec.nodeName=node1",
LabelSelector: "app=web",
}, "user-info")),
expectAllow(withConstrainedImpersonationAttributes(authorizer.AttributesRecord{Resource: "users", Name: "anyone"}, "user-info")),
},
expectedCache: &expectedCache{
modeIdx: map[string]string{
userImpersonator.Name: "impersonate:user-info",
},
modes: map[string]expectedModeCache{
"impersonate:user-info": {
outer: map[outerKey]*user.DefaultInfo{
outerCacheKey(anyone, userImpersonator, &request.RequestInfo{
IsResourceRequest: true,
Verb: "list",
APIVersion: "v1",
Resource: "pods",
Namespace: "bar",
FieldSelector: "spec.nodeName=node1",
LabelSelector: "app=web",
}, "\x00\x00\x00\x16\x00\x00\x00\x06anyone\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00!\x00\x00\x00\x11user-impersonater\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00/\x00\x00\x00\x04list\x01\x00\x00\x00\x03bar\x00\x00\x00\x04pods\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02v1\x01\x00\x00\x00\x00\x00\x00\x00#\x00\x00\x00\x1f\x00\x00\x00\rspec.nodeName\x00\x00\x00\x01=\x00\x00\x00\x05node1\x00\x00\x00\v\x00\x00\x00\aapp=web"): anyoneAuthenticated,
},
inner: map[innerKey]*user.DefaultInfo{
{
wantedUser: anyone,
requestor: userImpersonator,
rawKey: "\x00\x00\x00\x16\x00\x00\x00\x06anyone\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00!\x00\x00\x00\x11user-impersonater\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
}: anyoneAuthenticated,
},
},
},
},
expectedCode: http.StatusOK,
expectedAttemptMode: "user-info",
expectedAttemptDecision: "allowed",
expectedAuthorizationMetrics: map[string]int{
"user-info/allowed": 2, // impersonate-on + impersonate:user-info
},
},
},
},
}
var mux http.ServeMux