mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-28 14:07:14 +00:00
Wire kubelet authn/authz
This commit is contained in:
parent
a602ae77b8
commit
c83f5804d2
131
cmd/kubelet/app/auth.go
Normal file
131
cmd/kubelet/app/auth.go
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2016 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package app
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
"k8s.io/kubernetes/pkg/apis/componentconfig"
|
||||||
|
"k8s.io/kubernetes/pkg/auth/authenticator"
|
||||||
|
"k8s.io/kubernetes/pkg/auth/authenticator/bearertoken"
|
||||||
|
"k8s.io/kubernetes/pkg/auth/authorizer"
|
||||||
|
"k8s.io/kubernetes/pkg/auth/group"
|
||||||
|
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
|
||||||
|
authenticationclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/authentication/unversioned"
|
||||||
|
authorizationclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/authorization/unversioned"
|
||||||
|
alwaysallowauthorizer "k8s.io/kubernetes/pkg/genericapiserver/authorizer"
|
||||||
|
"k8s.io/kubernetes/pkg/kubelet/server"
|
||||||
|
"k8s.io/kubernetes/pkg/types"
|
||||||
|
"k8s.io/kubernetes/pkg/util/cert"
|
||||||
|
"k8s.io/kubernetes/plugin/pkg/auth/authenticator/request/anonymous"
|
||||||
|
unionauth "k8s.io/kubernetes/plugin/pkg/auth/authenticator/request/union"
|
||||||
|
"k8s.io/kubernetes/plugin/pkg/auth/authenticator/request/x509"
|
||||||
|
webhooktoken "k8s.io/kubernetes/plugin/pkg/auth/authenticator/token/webhook"
|
||||||
|
webhooksar "k8s.io/kubernetes/plugin/pkg/auth/authorizer/webhook"
|
||||||
|
)
|
||||||
|
|
||||||
|
func buildAuth(nodeName types.NodeName, client internalclientset.Interface, config componentconfig.KubeletConfiguration) (server.AuthInterface, error) {
|
||||||
|
// Get clients, if provided
|
||||||
|
var (
|
||||||
|
tokenClient authenticationclient.TokenReviewInterface
|
||||||
|
sarClient authorizationclient.SubjectAccessReviewInterface
|
||||||
|
)
|
||||||
|
if client != nil && !reflect.ValueOf(client).IsNil() {
|
||||||
|
tokenClient = client.Authentication().TokenReviews()
|
||||||
|
sarClient = client.Authorization().SubjectAccessReviews()
|
||||||
|
}
|
||||||
|
|
||||||
|
authenticator, err := buildAuthn(tokenClient, config.Authentication)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
attributes := server.NewNodeAuthorizerAttributesGetter(nodeName)
|
||||||
|
|
||||||
|
authorizer, err := buildAuthz(sarClient, config.Authorization)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return server.NewKubeletAuth(authenticator, attributes, authorizer), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildAuthn(client authenticationclient.TokenReviewInterface, authn componentconfig.KubeletAuthentication) (authenticator.Request, error) {
|
||||||
|
authenticators := []authenticator.Request{}
|
||||||
|
|
||||||
|
// x509 client cert auth
|
||||||
|
if len(authn.X509.ClientCAFile) > 0 {
|
||||||
|
clientCAs, err := cert.NewPool(authn.X509.ClientCAFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to load client CA file %s: %v", authn.X509.ClientCAFile, err)
|
||||||
|
}
|
||||||
|
verifyOpts := x509.DefaultVerifyOptions()
|
||||||
|
verifyOpts.Roots = clientCAs
|
||||||
|
authenticators = append(authenticators, x509.New(verifyOpts, x509.CommonNameUserConversion))
|
||||||
|
}
|
||||||
|
|
||||||
|
// bearer token auth that uses authentication.k8s.io TokenReview to determine userinfo
|
||||||
|
if authn.Webhook.Enabled {
|
||||||
|
if client == nil {
|
||||||
|
return nil, errors.New("no client provided, cannot use webhook authentication")
|
||||||
|
}
|
||||||
|
tokenAuth, err := webhooktoken.NewFromInterface(client, authn.Webhook.CacheTTL.Duration)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
authenticators = append(authenticators, bearertoken.New(tokenAuth))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(authenticators) == 0 {
|
||||||
|
if authn.Anonymous.Enabled {
|
||||||
|
return anonymous.NewAuthenticator(), nil
|
||||||
|
}
|
||||||
|
return nil, errors.New("No authentication method configured")
|
||||||
|
}
|
||||||
|
|
||||||
|
authenticator := group.NewGroupAdder(unionauth.New(authenticators...), []string{"system:authenticated"})
|
||||||
|
if authn.Anonymous.Enabled {
|
||||||
|
authenticator = unionauth.NewFailOnError(authenticator, anonymous.NewAuthenticator())
|
||||||
|
}
|
||||||
|
return authenticator, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildAuthz(client authorizationclient.SubjectAccessReviewInterface, authz componentconfig.KubeletAuthorization) (authorizer.Authorizer, error) {
|
||||||
|
switch authz.Mode {
|
||||||
|
case componentconfig.KubeletAuthorizationModeAlwaysAllow:
|
||||||
|
return alwaysallowauthorizer.NewAlwaysAllowAuthorizer(), nil
|
||||||
|
|
||||||
|
case componentconfig.KubeletAuthorizationModeWebhook:
|
||||||
|
if client == nil {
|
||||||
|
return nil, errors.New("no client provided, cannot use webhook authorization")
|
||||||
|
}
|
||||||
|
return webhooksar.NewFromInterface(
|
||||||
|
client,
|
||||||
|
authz.Webhook.CacheAuthorizedTTL.Duration,
|
||||||
|
authz.Webhook.CacheUnauthorizedTTL.Duration,
|
||||||
|
)
|
||||||
|
|
||||||
|
case "":
|
||||||
|
return nil, fmt.Errorf("No authorization mode specified")
|
||||||
|
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("Unknown authorization mode %s", authz.Mode)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -62,6 +62,7 @@ import (
|
|||||||
kubetypes "k8s.io/kubernetes/pkg/kubelet/types"
|
kubetypes "k8s.io/kubernetes/pkg/kubelet/types"
|
||||||
"k8s.io/kubernetes/pkg/runtime"
|
"k8s.io/kubernetes/pkg/runtime"
|
||||||
"k8s.io/kubernetes/pkg/types"
|
"k8s.io/kubernetes/pkg/types"
|
||||||
|
"k8s.io/kubernetes/pkg/util/cert"
|
||||||
certutil "k8s.io/kubernetes/pkg/util/cert"
|
certutil "k8s.io/kubernetes/pkg/util/cert"
|
||||||
utilconfig "k8s.io/kubernetes/pkg/util/config"
|
utilconfig "k8s.io/kubernetes/pkg/util/config"
|
||||||
"k8s.io/kubernetes/pkg/util/configz"
|
"k8s.io/kubernetes/pkg/util/configz"
|
||||||
@ -399,6 +400,18 @@ func run(s *options.KubeletServer, kubeDeps *kubelet.KubeletDeps) (err error) {
|
|||||||
kubeDeps.EventClient = eventClient
|
kubeDeps.EventClient = eventClient
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if kubeDeps.Auth == nil {
|
||||||
|
nodeName, err := getNodeName(kubeDeps.Cloud, nodeutil.GetHostname(s.HostnameOverride))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
auth, err := buildAuth(nodeName, kubeDeps.KubeClient, s.KubeletConfiguration)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
kubeDeps.Auth = auth
|
||||||
|
}
|
||||||
|
|
||||||
if kubeDeps.CAdvisorInterface == nil {
|
if kubeDeps.CAdvisorInterface == nil {
|
||||||
kubeDeps.CAdvisorInterface, err = cadvisor.New(uint(s.CAdvisorPort), s.ContainerRuntime)
|
kubeDeps.CAdvisorInterface, err = cadvisor.New(uint(s.CAdvisorPort), s.ContainerRuntime)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -501,12 +514,22 @@ func InitializeTLS(kc *componentconfig.KubeletConfiguration) (*server.TLSOptions
|
|||||||
// Can't use TLSv1.0 because of POODLE and BEAST using CBC cipher
|
// Can't use TLSv1.0 because of POODLE and BEAST using CBC cipher
|
||||||
// Can't use TLSv1.1 because of RC4 cipher usage
|
// Can't use TLSv1.1 because of RC4 cipher usage
|
||||||
MinVersion: tls.VersionTLS12,
|
MinVersion: tls.VersionTLS12,
|
||||||
// Populate PeerCertificates in requests, but don't yet reject connections without certificates.
|
|
||||||
ClientAuth: tls.RequestClientCert,
|
|
||||||
},
|
},
|
||||||
CertFile: kc.TLSCertFile,
|
CertFile: kc.TLSCertFile,
|
||||||
KeyFile: kc.TLSPrivateKeyFile,
|
KeyFile: kc.TLSPrivateKeyFile,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(kc.Authentication.X509.ClientCAFile) > 0 {
|
||||||
|
clientCAs, err := cert.NewPool(kc.Authentication.X509.ClientCAFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to load client CA file %s: %v", kc.Authentication.X509.ClientCAFile, err)
|
||||||
|
}
|
||||||
|
// Specify allowed CAs for client certificates
|
||||||
|
tlsOptions.Config.ClientCAs = clientCAs
|
||||||
|
// Populate PeerCertificates in requests, but don't reject connections without verified certificates
|
||||||
|
tlsOptions.Config.ClientAuth = tls.RequestClientCert
|
||||||
|
}
|
||||||
|
|
||||||
return tlsOptions, nil
|
return tlsOptions, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,6 +26,7 @@ go_library(
|
|||||||
"//pkg/api/validation:go_default_library",
|
"//pkg/api/validation:go_default_library",
|
||||||
"//pkg/auth/authenticator:go_default_library",
|
"//pkg/auth/authenticator:go_default_library",
|
||||||
"//pkg/auth/authorizer:go_default_library",
|
"//pkg/auth/authorizer:go_default_library",
|
||||||
|
"//pkg/auth/user:go_default_library",
|
||||||
"//pkg/healthz:go_default_library",
|
"//pkg/healthz:go_default_library",
|
||||||
"//pkg/httplog:go_default_library",
|
"//pkg/httplog:go_default_library",
|
||||||
"//pkg/kubelet/cm:go_default_library",
|
"//pkg/kubelet/cm:go_default_library",
|
||||||
@ -53,7 +54,10 @@ go_library(
|
|||||||
|
|
||||||
go_test(
|
go_test(
|
||||||
name = "go_default_test",
|
name = "go_default_test",
|
||||||
srcs = ["server_test.go"],
|
srcs = [
|
||||||
|
"auth_test.go",
|
||||||
|
"server_test.go",
|
||||||
|
],
|
||||||
library = "go_default_library",
|
library = "go_default_library",
|
||||||
tags = ["automanaged"],
|
tags = ["automanaged"],
|
||||||
deps = [
|
deps = [
|
||||||
|
@ -17,8 +17,14 @@ limitations under the License.
|
|||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/golang/glog"
|
||||||
"k8s.io/kubernetes/pkg/auth/authenticator"
|
"k8s.io/kubernetes/pkg/auth/authenticator"
|
||||||
"k8s.io/kubernetes/pkg/auth/authorizer"
|
"k8s.io/kubernetes/pkg/auth/authorizer"
|
||||||
|
"k8s.io/kubernetes/pkg/auth/user"
|
||||||
|
"k8s.io/kubernetes/pkg/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// KubeletAuth implements AuthInterface
|
// KubeletAuth implements AuthInterface
|
||||||
@ -35,3 +41,74 @@ type KubeletAuth struct {
|
|||||||
func NewKubeletAuth(authenticator authenticator.Request, authorizerAttributeGetter authorizer.RequestAttributesGetter, authorizer authorizer.Authorizer) AuthInterface {
|
func NewKubeletAuth(authenticator authenticator.Request, authorizerAttributeGetter authorizer.RequestAttributesGetter, authorizer authorizer.Authorizer) AuthInterface {
|
||||||
return &KubeletAuth{authenticator, authorizerAttributeGetter, authorizer}
|
return &KubeletAuth{authenticator, authorizerAttributeGetter, authorizer}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewNodeAuthorizerAttributesGetter(nodeName types.NodeName) authorizer.RequestAttributesGetter {
|
||||||
|
return nodeAuthorizerAttributesGetter{nodeName: nodeName}
|
||||||
|
}
|
||||||
|
|
||||||
|
type nodeAuthorizerAttributesGetter struct {
|
||||||
|
nodeName types.NodeName
|
||||||
|
}
|
||||||
|
|
||||||
|
func isSubpath(subpath, path string) bool {
|
||||||
|
path = strings.TrimSuffix(path, "/")
|
||||||
|
return subpath == path || (strings.HasPrefix(subpath, path) && subpath[len(path)] == '/')
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRequestAttributes populates authorizer attributes for the requests to the kubelet API.
|
||||||
|
// Default attributes are: {apiVersion=v1,verb=<http verb from request>,resource=nodes,name=<node name>,subresource=proxy}
|
||||||
|
// More specific verb/resource is set for the following request patterns:
|
||||||
|
// /stats/* => verb=<api verb from request>, resource=nodes, name=<node name>, subresource=stats
|
||||||
|
// /metrics/* => verb=<api verb from request>, resource=nodes, name=<node name>, subresource=metrics
|
||||||
|
// /logs/* => verb=<api verb from request>, resource=nodes, name=<node name>, subresource=log
|
||||||
|
// /spec/* => verb=<api verb from request>, resource=nodes, name=<node name>, subresource=spec
|
||||||
|
func (n nodeAuthorizerAttributesGetter) GetRequestAttributes(u user.Info, r *http.Request) authorizer.Attributes {
|
||||||
|
|
||||||
|
apiVerb := ""
|
||||||
|
switch r.Method {
|
||||||
|
case "POST":
|
||||||
|
apiVerb = "create"
|
||||||
|
case "GET":
|
||||||
|
apiVerb = "get"
|
||||||
|
case "PUT":
|
||||||
|
apiVerb = "update"
|
||||||
|
case "PATCH":
|
||||||
|
apiVerb = "patch"
|
||||||
|
case "DELETE":
|
||||||
|
apiVerb = "delete"
|
||||||
|
}
|
||||||
|
|
||||||
|
requestPath := r.URL.Path
|
||||||
|
|
||||||
|
// Default attributes mirror the API attributes that would allow this access to the kubelet API
|
||||||
|
attrs := authorizer.AttributesRecord{
|
||||||
|
User: u,
|
||||||
|
Verb: apiVerb,
|
||||||
|
Namespace: "",
|
||||||
|
APIGroup: "",
|
||||||
|
APIVersion: "v1",
|
||||||
|
Resource: "nodes",
|
||||||
|
Subresource: "proxy",
|
||||||
|
Name: string(n.nodeName),
|
||||||
|
ResourceRequest: true,
|
||||||
|
Path: requestPath,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Override subresource for specific paths
|
||||||
|
// This allows subdividing access to the kubelet API
|
||||||
|
switch {
|
||||||
|
case isSubpath(requestPath, statsPath):
|
||||||
|
attrs.Subresource = "stats"
|
||||||
|
case isSubpath(requestPath, metricsPath):
|
||||||
|
attrs.Subresource = "metrics"
|
||||||
|
case isSubpath(requestPath, logsPath):
|
||||||
|
// "log" to match other log subresources (pods/log, etc)
|
||||||
|
attrs.Subresource = "log"
|
||||||
|
case isSubpath(requestPath, specPath):
|
||||||
|
attrs.Subresource = "spec"
|
||||||
|
}
|
||||||
|
|
||||||
|
glog.V(5).Infof("Node request attributes: attrs=%#v", attrs)
|
||||||
|
|
||||||
|
return attrs
|
||||||
|
}
|
||||||
|
53
pkg/kubelet/server/auth_test.go
Normal file
53
pkg/kubelet/server/auth_test.go
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2016 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package server
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestIsSubPath(t *testing.T) {
|
||||||
|
testcases := map[string]struct {
|
||||||
|
subpath string
|
||||||
|
path string
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
"empty": {subpath: "", path: "", expected: true},
|
||||||
|
|
||||||
|
"match 1": {subpath: "foo", path: "foo", expected: true},
|
||||||
|
"match 2": {subpath: "/foo", path: "/foo", expected: true},
|
||||||
|
"match 3": {subpath: "/foo/", path: "/foo/", expected: true},
|
||||||
|
"match 4": {subpath: "/foo/bar", path: "/foo/bar", expected: true},
|
||||||
|
|
||||||
|
"subpath of root 1": {subpath: "/foo", path: "/", expected: true},
|
||||||
|
"subpath of root 2": {subpath: "/foo/", path: "/", expected: true},
|
||||||
|
"subpath of root 3": {subpath: "/foo/bar", path: "/", expected: true},
|
||||||
|
|
||||||
|
"subpath of path 1": {subpath: "/foo", path: "/foo", expected: true},
|
||||||
|
"subpath of path 2": {subpath: "/foo/", path: "/foo", expected: true},
|
||||||
|
"subpath of path 3": {subpath: "/foo/bar", path: "/foo", expected: true},
|
||||||
|
|
||||||
|
"mismatch 1": {subpath: "/foo", path: "/bar", expected: false},
|
||||||
|
"mismatch 2": {subpath: "/foo", path: "/foobar", expected: false},
|
||||||
|
"mismatch 3": {subpath: "/foobar", path: "/foo", expected: false},
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, tc := range testcases {
|
||||||
|
result := isSubpath(tc.subpath, tc.path)
|
||||||
|
if result != tc.expected {
|
||||||
|
t.Errorf("%s: expected %v, got %v", k, tc.expected, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -605,10 +605,56 @@ func TestAuthFilters(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
methodToAPIVerb := map[string]string{"GET": "get", "POST": "create", "PUT": "update"}
|
||||||
|
pathToSubresource := func(path string) string {
|
||||||
|
switch {
|
||||||
|
// Cases for subpaths we expect specific subresources for
|
||||||
|
case isSubpath(path, statsPath):
|
||||||
|
return "stats"
|
||||||
|
case isSubpath(path, specPath):
|
||||||
|
return "spec"
|
||||||
|
case isSubpath(path, logsPath):
|
||||||
|
return "log"
|
||||||
|
case isSubpath(path, metricsPath):
|
||||||
|
return "metrics"
|
||||||
|
|
||||||
|
// Cases for subpaths we expect to map to the "proxy" subresource
|
||||||
|
case isSubpath(path, "/attach"),
|
||||||
|
isSubpath(path, "/configz"),
|
||||||
|
isSubpath(path, "/containerLogs"),
|
||||||
|
isSubpath(path, "/debug"),
|
||||||
|
isSubpath(path, "/exec"),
|
||||||
|
isSubpath(path, "/healthz"),
|
||||||
|
isSubpath(path, "/pods"),
|
||||||
|
isSubpath(path, "/portForward"),
|
||||||
|
isSubpath(path, "/run"),
|
||||||
|
isSubpath(path, "/runningpods"):
|
||||||
|
return "proxy"
|
||||||
|
|
||||||
|
default:
|
||||||
|
panic(fmt.Errorf(`unexpected kubelet API path %s.
|
||||||
|
The kubelet API has likely registered a handler for a new path.
|
||||||
|
If the new path has a use case for partitioned authorization when requested from the kubelet API,
|
||||||
|
add a specific subresource for it in auth.go#GetRequestAttributes() and in TestAuthFilters().
|
||||||
|
Otherwise, add it to the expected list of paths that map to the "proxy" subresource in TestAuthFilters().`, path))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
attributesGetter := NewNodeAuthorizerAttributesGetter(types.NodeName("test"))
|
||||||
|
|
||||||
for _, tc := range testcases {
|
for _, tc := range testcases {
|
||||||
var (
|
var (
|
||||||
expectedUser = &user.DefaultInfo{Name: "test"}
|
expectedUser = &user.DefaultInfo{Name: "test"}
|
||||||
expectedAttributes = &authorizer.AttributesRecord{User: expectedUser}
|
expectedAttributes = authorizer.AttributesRecord{
|
||||||
|
User: expectedUser,
|
||||||
|
APIGroup: "",
|
||||||
|
APIVersion: "v1",
|
||||||
|
Verb: methodToAPIVerb[tc.Method],
|
||||||
|
Resource: "nodes",
|
||||||
|
Name: "test",
|
||||||
|
Subresource: pathToSubresource(tc.Path),
|
||||||
|
ResourceRequest: true,
|
||||||
|
Path: tc.Path,
|
||||||
|
}
|
||||||
|
|
||||||
calledAuthenticate = false
|
calledAuthenticate = false
|
||||||
calledAuthorize = false
|
calledAuthorize = false
|
||||||
@ -624,12 +670,12 @@ func TestAuthFilters(t *testing.T) {
|
|||||||
if u != expectedUser {
|
if u != expectedUser {
|
||||||
t.Fatalf("%s: expected user %v, got %v", tc.Path, expectedUser, u)
|
t.Fatalf("%s: expected user %v, got %v", tc.Path, expectedUser, u)
|
||||||
}
|
}
|
||||||
return expectedAttributes
|
return attributesGetter.GetRequestAttributes(u, req)
|
||||||
}
|
}
|
||||||
fw.fakeAuth.authorizeFunc = func(a authorizer.Attributes) (authorized bool, reason string, err error) {
|
fw.fakeAuth.authorizeFunc = func(a authorizer.Attributes) (authorized bool, reason string, err error) {
|
||||||
calledAuthorize = true
|
calledAuthorize = true
|
||||||
if a != expectedAttributes {
|
if a != expectedAttributes {
|
||||||
t.Fatalf("%s: expected attributes %v, got %v", tc.Path, expectedAttributes, a)
|
t.Fatalf("%s: expected attributes\n\t%#v\ngot\n\t%#v", tc.Path, expectedAttributes, a)
|
||||||
}
|
}
|
||||||
return false, "", nil
|
return false, "", nil
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user