Merge pull request #94204 from tkashem/impersonation-with-apf

Impersonated user with a specified group should not fail flow schema match in Priority & Fairness
This commit is contained in:
Kubernetes Prow Robot 2020-09-01 18:44:00 -07:00 committed by GitHub
commit 6e663379ed
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 112 additions and 41 deletions

View File

@ -117,10 +117,37 @@ func WithImpersonation(handler http.Handler, a authorizer.Authorizer, s runtime.
} }
} }
if !groupsSpecified && username != user.Anonymous { if username != user.Anonymous {
// When impersonating a non-anonymous user, if no groups were specified // When impersonating a non-anonymous user, include the 'system:authenticated' group
// include the system:authenticated group in the impersonated user info // in the impersonated user info:
groups = append(groups, user.AllAuthenticated) // - if no groups were specified
// - if a group has been specified other than 'system:authenticated'
//
// If 'system:unauthenticated' group has been specified we should not include
// the 'system:authenticated' group.
addAuthenticated := true
for _, group := range groups {
if group == user.AllAuthenticated || group == user.AllUnauthenticated {
addAuthenticated = false
break
}
}
if addAuthenticated {
groups = append(groups, user.AllAuthenticated)
}
} else {
addUnauthenticated := true
for _, group := range groups {
if group == user.AllUnauthenticated {
addUnauthenticated = false
break
}
}
if addUnauthenticated {
groups = append(groups, user.AllUnauthenticated)
}
} }
newUser := &user.DefaultInfo{ newUser := &user.DefaultInfo{

View File

@ -163,7 +163,7 @@ func TestImpersonationFilter(t *testing.T) {
impersonationGroups: []string{"some-group"}, impersonationGroups: []string{"some-group"},
expectedUser: &user.DefaultInfo{ expectedUser: &user.DefaultInfo{
Name: "system:admin", Name: "system:admin",
Groups: []string{"some-group"}, Groups: []string{"some-group", "system:authenticated"},
Extra: map[string][]string{}, Extra: map[string][]string{},
}, },
expectedCode: http.StatusOK, expectedCode: http.StatusOK,
@ -308,7 +308,7 @@ func TestImpersonationFilter(t *testing.T) {
impersonationUser: "system:anonymous", impersonationUser: "system:anonymous",
expectedUser: &user.DefaultInfo{ expectedUser: &user.DefaultInfo{
Name: "system:anonymous", Name: "system:anonymous",
Groups: []string{}, Groups: []string{"system:unauthenticated"},
Extra: map[string][]string{}, Extra: map[string][]string{},
}, },
expectedCode: http.StatusOK, expectedCode: http.StatusOK,
@ -341,6 +341,48 @@ func TestImpersonationFilter(t *testing.T) {
}, },
expectedCode: http.StatusOK, expectedCode: http.StatusOK,
}, },
{
name: "specified-authenticated-group-prevents-double-adding-authenticated-group",
user: &user.DefaultInfo{
Name: "dev",
Groups: []string{"wheel", "group-impersonater"},
},
impersonationUser: "system:admin",
impersonationGroups: []string{"some-group", "system:authenticated"},
expectedUser: &user.DefaultInfo{
Name: "system:admin",
Groups: []string{"some-group", "system:authenticated"},
Extra: map[string][]string{},
},
expectedCode: http.StatusOK,
},
{
name: "anonymous-user-should-include-unauthenticated-group",
user: &user.DefaultInfo{
Name: "system:admin",
},
impersonationUser: "system:anonymous",
expectedUser: &user.DefaultInfo{
Name: "system:anonymous",
Groups: []string{"system:unauthenticated"},
Extra: map[string][]string{},
},
expectedCode: http.StatusOK,
},
{
name: "anonymous-user-prevents-double-adding-unauthenticated-group",
user: &user.DefaultInfo{
Name: "system:admin",
},
impersonationUser: "system:anonymous",
impersonationGroups: []string{"system:unauthenticated"},
expectedUser: &user.DefaultInfo{
Name: "system:anonymous",
Groups: []string{"system:unauthenticated"},
Extra: map[string][]string{},
},
expectedCode: http.StatusOK,
},
} }
var ctx context.Context var ctx context.Context
@ -398,42 +440,44 @@ func TestImpersonationFilter(t *testing.T) {
defer server.Close() defer server.Close()
for _, tc := range testCases { for _, tc := range testCases {
func() { t.Run(tc.name, func(t *testing.T) {
lock.Lock() func() {
defer lock.Unlock() lock.Lock()
ctx = request.WithUser(request.NewContext(), tc.user) defer lock.Unlock()
}() ctx = request.WithUser(request.NewContext(), tc.user)
}()
req, err := http.NewRequest("GET", server.URL, nil) req, err := http.NewRequest("GET", server.URL, nil)
if err != nil { if err != nil {
t.Errorf("%s: unexpected error: %v", tc.name, err) t.Errorf("%s: unexpected error: %v", tc.name, err)
continue return
} }
if len(tc.impersonationUser) > 0 { if len(tc.impersonationUser) > 0 {
req.Header.Add(authenticationapi.ImpersonateUserHeader, tc.impersonationUser) req.Header.Add(authenticationapi.ImpersonateUserHeader, tc.impersonationUser)
} }
for _, group := range tc.impersonationGroups { for _, group := range tc.impersonationGroups {
req.Header.Add(authenticationapi.ImpersonateGroupHeader, group) req.Header.Add(authenticationapi.ImpersonateGroupHeader, group)
} }
for extraKey, values := range tc.impersonationUserExtras { for extraKey, values := range tc.impersonationUserExtras {
for _, value := range values { for _, value := range values {
req.Header.Add(authenticationapi.ImpersonateUserExtraHeaderPrefix+extraKey, value) req.Header.Add(authenticationapi.ImpersonateUserExtraHeaderPrefix+extraKey, value)
}
} }
}
resp, err := http.DefaultClient.Do(req) resp, err := http.DefaultClient.Do(req)
if err != nil { if err != nil {
t.Errorf("%s: unexpected error: %v", tc.name, err) t.Errorf("%s: unexpected error: %v", tc.name, err)
continue return
} }
if resp.StatusCode != tc.expectedCode { if resp.StatusCode != tc.expectedCode {
t.Errorf("%s: expected %v, actual %v", tc.name, tc.expectedCode, resp.StatusCode) t.Errorf("%s: expected %v, actual %v", tc.name, tc.expectedCode, resp.StatusCode)
continue return
} }
if !reflect.DeepEqual(actualUser, tc.expectedUser) { if !reflect.DeepEqual(actualUser, tc.expectedUser) {
t.Errorf("%s: expected %#v, actual %#v", tc.name, tc.expectedUser, actualUser) t.Errorf("%s: expected %#v, actual %#v", tc.name, tc.expectedUser, actualUser)
continue return
} }
})
} }
} }

View File

@ -71,8 +71,8 @@ run_impersonation_tests() {
# --as-group # --as-group
kubectl create -f hack/testdata/csr.yml "${kube_flags_with_token[@]:?}" --as=user1 --as-group=group2 --as-group=group1 --as-group=,,,chameleon kubectl create -f hack/testdata/csr.yml "${kube_flags_with_token[@]:?}" --as=user1 --as-group=group2 --as-group=group1 --as-group=,,,chameleon
kube::test::get_object_assert 'csr/foo' '{{len .spec.groups}}' '3' kube::test::get_object_assert 'csr/foo' '{{len .spec.groups}}' '4'
kube::test::get_object_assert 'csr/foo' '{{range .spec.groups}}{{.}} {{end}}' 'group2 group1 ,,,chameleon ' kube::test::get_object_assert 'csr/foo' '{{range .spec.groups}}{{.}} {{end}}' 'group2 group1 ,,,chameleon system:authenticated '
kubectl delete -f hack/testdata/csr.yml "${kube_flags_with_token[@]:?}" kubectl delete -f hack/testdata/csr.yml "${kube_flags_with_token[@]:?}"
fi fi