未验证 提交 aadb5e91 编写于 作者: H hongming

update

Signed-off-by: Nhongming <talonwan@yunify.com>
上级 1f26e621
...@@ -37,13 +37,13 @@ func (s *jwtTokenIssuer) Verify(tokenString string) (User, error) { ...@@ -37,13 +37,13 @@ func (s *jwtTokenIssuer) Verify(tokenString string) (User, error) {
return nil, err return nil, err
} }
return &iam.User{Username: clm.Username, Email: clm.UID}, nil return &iam.User{Username: clm.Username, UID: clm.UID}, nil
} }
func (s *jwtTokenIssuer) IssueTo(user User) (string, error) { func (s *jwtTokenIssuer) IssueTo(user User) (string, error) {
clm := &claims{ clm := &claims{
Username: user.Name(), Username: user.GetName(),
UID: user.UID(), UID: user.GetUID(),
StandardClaims: jwt.StandardClaims{ StandardClaims: jwt.StandardClaims{
IssuedAt: time.Now().Unix(), IssuedAt: time.Now().Unix(),
Issuer: s.name, Issuer: s.name,
......
...@@ -12,22 +12,22 @@ func TestJwtTokenIssuer(t *testing.T) { ...@@ -12,22 +12,22 @@ func TestJwtTokenIssuer(t *testing.T) {
testCases := []struct { testCases := []struct {
description string description string
name string name string
email string uid string
}{ }{
{ {
name: "admin", name: "admin",
email: "admin@kubesphere.io", uid: "b8be6edd-2c92-4535-9b2a-df6326474458",
}, },
{ {
name: "bar", name: "bar",
email: "bar@kubesphere.io", uid: "b8be6edd-2c92-4535-9b2a-df6326474452",
}, },
} }
for _, testCase := range testCases { for _, testCase := range testCases {
user := &iam.User{ user := &iam.User{
Username: testCase.name, Username: testCase.name,
Email: testCase.email, UID: testCase.uid,
} }
t.Run(testCase.description, func(t *testing.T) { t.Run(testCase.description, func(t *testing.T) {
......
...@@ -2,7 +2,8 @@ package token ...@@ -2,7 +2,8 @@ package token
type User interface { type User interface {
// Name // Name
Name() string GetName() string
UID() string // UID
GetUID() string
} }
...@@ -7,6 +7,7 @@ import ( ...@@ -7,6 +7,7 @@ import (
type User struct { type User struct {
Username string `json:"username"` Username string `json:"username"`
UID string `json:"uid"`
Email string `json:"email"` Email string `json:"email"`
Lang string `json:"lang,omitempty"` Lang string `json:"lang,omitempty"`
Description string `json:"description"` Description string `json:"description"`
...@@ -18,6 +19,7 @@ type User struct { ...@@ -18,6 +19,7 @@ type User struct {
func NewUser() *User { func NewUser() *User {
return &User{ return &User{
Username: "", Username: "",
UID: "",
Email: "", Email: "",
Lang: "", Lang: "",
Description: "", Description: "",
...@@ -27,12 +29,12 @@ func NewUser() *User { ...@@ -27,12 +29,12 @@ func NewUser() *User {
} }
} }
func (u *User) Name() string { func (u *User) GetName() string {
return u.Username return u.Username
} }
func (u *User) UID() string { func (u *User) GetUID() string {
return u.Email return ""
} }
func (u *User) Validate() error { func (u *User) Validate() error {
......
...@@ -175,23 +175,19 @@ func (s *APIServer) buildHandlerChain() { ...@@ -175,23 +175,19 @@ func (s *APIServer) buildHandlerChain() {
GrouplessAPIPrefixes: sets.NewString("api", "kapi"), GrouplessAPIPrefixes: sets.NewString("api", "kapi"),
} }
failed := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
w.WriteHeader(http.StatusUnauthorized)
})
handler := s.Server.Handler handler := s.Server.Handler
handler = filters.WithRequestInfo(handler, requestInfoResolver) handler = filters.WithKubeAPIServer(handler, s.KubernetesClient.Config(), &errorResponder{})
authn := unionauth.New(&authenticationrequest.AnonymousAuthenticator{}, bearertoken.New(jwttoken.NewTokenAuthenticator(s.CacheClient, s.AuthenticateOptions.JwtSecret))) handler = filters.WithMultipleClusterDispatcher(handler, dispatch.DefaultClusterDispatch)
handler = filters.WithAuthentication(handler, authn, failed)
excludedPaths := []string{"/oauth/authorize", "/oauth/token"} excludedPaths := []string{"/oauth/authorize", "/oauth/token"}
pathAuthorizer, _ := path.NewAuthorizer(excludedPaths) pathAuthorizer, _ := path.NewAuthorizer(excludedPaths)
authorizer := unionauthorizer.New(pathAuthorizer, authorizerfactory.NewOPAAuthorizer(am.NewFakeAMOperator(cache.NewSimpleCache()))) authorizer := unionauthorizer.New(pathAuthorizer, authorizerfactory.NewOPAAuthorizer(am.NewFakeAMOperator(cache.NewSimpleCache())))
handler = filters.WithAuthorization(handler, authorizer) handler = filters.WithAuthorization(handler, authorizer)
handler = filters.WithMultipleClusterDispatcher(handler, dispatch.DefaultClusterDispatch)
handler = filters.WithKubeAPIServer(handler, s.KubernetesClient.Config(), &errorResponder{})
authn := unionauth.New(&authenticationrequest.AnonymousAuthenticator{}, bearertoken.New(jwttoken.NewTokenAuthenticator(s.CacheClient, s.AuthenticateOptions.JwtSecret)))
handler = filters.WithAuthentication(handler, authn)
handler = filters.WithRequestInfo(handler, requestInfoResolver)
s.Server.Handler = handler s.Server.Handler = handler
} }
......
...@@ -35,17 +35,18 @@ func (t *tokenAuthenticator) AuthenticateToken(ctx context.Context, token string ...@@ -35,17 +35,18 @@ func (t *tokenAuthenticator) AuthenticateToken(ctx context.Context, token string
return nil, false, err return nil, false, err
} }
_, err = t.cacheClient.Get(tokenKeyForUsername(providedUser.Name(), token)) // TODO implement token cache
if err != nil { //_, err = t.cacheClient.Get(tokenKeyForUsername(providedUser.Name(), token))
return nil, false, errTokenExpired //if err != nil {
} // return nil, false, errTokenExpired
//}
// Should we need to refresh token? // Should we need to refresh token?
return &authenticator.Response{ return &authenticator.Response{
User: &user.DefaultInfo{ User: &user.DefaultInfo{
Name: providedUser.Name(), Name: providedUser.GetName(),
UID: providedUser.UID(), UID: providedUser.GetUID(),
Groups: []string{user.AllAuthenticated}, Groups: []string{user.AllAuthenticated},
}, },
}, true, nil }, true, nil
......
package filters package filters
import ( import (
"errors"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/runtime/serializer"
"k8s.io/apiserver/pkg/authentication/authenticator" "k8s.io/apiserver/pkg/authentication/authenticator"
"k8s.io/apiserver/pkg/endpoints/request" "k8s.io/apiserver/pkg/endpoints/handlers/responsewriters"
"k8s.io/klog" "k8s.io/klog"
"kubesphere.io/kubesphere/pkg/apiserver/request"
"net/http" "net/http"
) )
// WithAuthentication installs authentication handler to handler chain. // WithAuthentication installs authentication handler to handler chain.
func WithAuthentication(handler http.Handler, auth authenticator.Request, failed http.Handler) http.Handler { func WithAuthentication(handler http.Handler, auth authenticator.Request) http.Handler {
if auth == nil { if auth == nil {
klog.Warningf("Authentication is disabled") klog.Warningf("Authentication is disabled")
return handler return handler
} }
s := serializer.NewCodecFactory(runtime.NewScheme()).WithoutConversion()
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
//authenticationStart := time.Now()
resp, ok, err := auth.AuthenticateRequest(req) resp, ok, err := auth.AuthenticateRequest(req)
if err != nil || !ok { if err != nil || !ok {
if err != nil { if err != nil {
klog.Errorf("Unable to authenticate the request due to error: %v", err) klog.Errorf("Unable to authenticate the request due to error: %v", err)
} }
failed.ServeHTTP(w, req)
ctx := req.Context()
requestInfo, found := request.RequestInfoFrom(ctx)
if !found {
responsewriters.InternalError(w, req, errors.New("no RequestInfo found in the context"))
return
}
gv := schema.GroupVersion{Group: requestInfo.APIGroup, Version: requestInfo.APIVersion}
responsewriters.ErrorNegotiated(apierrors.NewUnauthorized("Unauthorized"), s, gv, w, req)
return return
} }
......
...@@ -3,8 +3,9 @@ package filters ...@@ -3,8 +3,9 @@ package filters
import ( import (
"context" "context"
"errors" "errors"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/serializer"
"k8s.io/apiserver/pkg/endpoints/handlers/responsewriters" "k8s.io/apiserver/pkg/endpoints/handlers/responsewriters"
k8srequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/klog" "k8s.io/klog"
"kubesphere.io/kubesphere/pkg/apiserver/authorization/authorizer" "kubesphere.io/kubesphere/pkg/apiserver/authorization/authorizer"
"kubesphere.io/kubesphere/pkg/apiserver/request" "kubesphere.io/kubesphere/pkg/apiserver/request"
...@@ -18,6 +19,8 @@ func WithAuthorization(handler http.Handler, a authorizer.Authorizer) http.Handl ...@@ -18,6 +19,8 @@ func WithAuthorization(handler http.Handler, a authorizer.Authorizer) http.Handl
return handler return handler
} }
serializer := serializer.NewCodecFactory(runtime.NewScheme()).WithoutConversion()
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
ctx := req.Context() ctx := req.Context()
...@@ -38,14 +41,14 @@ func WithAuthorization(handler http.Handler, a authorizer.Authorizer) http.Handl ...@@ -38,14 +41,14 @@ func WithAuthorization(handler http.Handler, a authorizer.Authorizer) http.Handl
} }
klog.V(4).Infof("Forbidden: %#v, Reason: %q", req.RequestURI, reason) klog.V(4).Infof("Forbidden: %#v, Reason: %q", req.RequestURI, reason)
w.WriteHeader(http.StatusForbidden) responsewriters.Forbidden(ctx, attributes, w, req, reason, serializer)
}) })
} }
func GetAuthorizerAttributes(ctx context.Context) (authorizer.Attributes, error) { func GetAuthorizerAttributes(ctx context.Context) (authorizer.Attributes, error) {
attribs := authorizer.AttributesRecord{} attribs := authorizer.AttributesRecord{}
user, ok := k8srequest.UserFrom(ctx) user, ok := request.UserFrom(ctx)
if ok { if ok {
attribs.User = user attribs.User = user
} }
......
...@@ -7,6 +7,7 @@ import ( ...@@ -7,6 +7,7 @@ import (
"kubesphere.io/kubesphere/pkg/apiserver/dispatch" "kubesphere.io/kubesphere/pkg/apiserver/dispatch"
"kubesphere.io/kubesphere/pkg/apiserver/request" "kubesphere.io/kubesphere/pkg/apiserver/request"
"net/http" "net/http"
"strings"
) )
// Multiple cluster dispatcher forward request to desired cluster based on request cluster name // Multiple cluster dispatcher forward request to desired cluster based on request cluster name
...@@ -23,9 +24,11 @@ func WithMultipleClusterDispatcher(handler http.Handler, dispatch dispatch.Dispa ...@@ -23,9 +24,11 @@ func WithMultipleClusterDispatcher(handler http.Handler, dispatch dispatch.Dispa
return return
} }
if info.Cluster == "" { if info.Cluster == "host-cluster" || info.Cluster == "" {
handler.ServeHTTP(w, req) handler.ServeHTTP(w, req)
} else { } else {
// remove cluster path
req.URL.Path = strings.Replace(req.URL.Path, fmt.Sprintf("/clusters/%s", info.Cluster), "", 1)
dispatch.Dispatch(w, req) dispatch.Dispatch(w, req)
} }
}) })
......
package filters package filters
import ( import (
"fmt"
"k8s.io/apiserver/pkg/endpoints/handlers/responsewriters" "k8s.io/apiserver/pkg/endpoints/handlers/responsewriters"
"k8s.io/client-go/rest" "k8s.io/client-go/rest"
"k8s.io/klog" "k8s.io/klog"
...@@ -8,6 +9,7 @@ import ( ...@@ -8,6 +9,7 @@ import (
"kubesphere.io/kubesphere/pkg/server/errors" "kubesphere.io/kubesphere/pkg/server/errors"
"net/http" "net/http"
"net/url" "net/url"
"strings"
"k8s.io/apimachinery/pkg/util/proxy" "k8s.io/apimachinery/pkg/util/proxy"
) )
...@@ -33,6 +35,8 @@ func WithKubeAPIServer(handler http.Handler, config *rest.Config, failed proxy.E ...@@ -33,6 +35,8 @@ func WithKubeAPIServer(handler http.Handler, config *rest.Config, failed proxy.E
s := *req.URL s := *req.URL
s.Host = kubernetes.Host s.Host = kubernetes.Host
s.Scheme = kubernetes.Scheme s.Scheme = kubernetes.Scheme
// remove cluster path
s.Path = strings.Replace(s.Path, fmt.Sprintf("/clusters/%s", info.Cluster), "", 1)
httpProxy := proxy.NewUpgradeAwareHandler(&s, defaultTransport, true, false, failed) httpProxy := proxy.NewUpgradeAwareHandler(&s, defaultTransport, true, false, failed)
httpProxy.ServeHTTP(w, req) httpProxy.ServeHTTP(w, req)
......
/*
Copyright 2014 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 request
import (
"context"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apiserver/pkg/apis/audit"
"k8s.io/apiserver/pkg/authentication/user"
)
// The key type is unexported to prevent collisions
type key int
const (
// namespaceKey is the context key for the request namespace.
namespaceKey key = iota
// userKey is the context key for the request user.
userKey
// auditKey is the context key for the audit event.
auditKey
// audiencesKey is the context key for request audiences.
audiencesKey
)
// NewContext instantiates a base context object for request flows.
func NewContext() context.Context {
return context.TODO()
}
// NewDefaultContext instantiates a base context object for request flows in the default namespace
func NewDefaultContext() context.Context {
return WithNamespace(NewContext(), metav1.NamespaceDefault)
}
// WithValue returns a copy of parent in which the value associated with key is val.
func WithValue(parent context.Context, key interface{}, val interface{}) context.Context {
return context.WithValue(parent, key, val)
}
// WithNamespace returns a copy of parent in which the namespace value is set
func WithNamespace(parent context.Context, namespace string) context.Context {
return WithValue(parent, namespaceKey, namespace)
}
// NamespaceFrom returns the value of the namespace key on the ctx
func NamespaceFrom(ctx context.Context) (string, bool) {
namespace, ok := ctx.Value(namespaceKey).(string)
return namespace, ok
}
// NamespaceValue returns the value of the namespace key on the ctx, or the empty string if none
func NamespaceValue(ctx context.Context) string {
namespace, _ := NamespaceFrom(ctx)
return namespace
}
// WithUser returns a copy of parent in which the user value is set
func WithUser(parent context.Context, user user.Info) context.Context {
return WithValue(parent, userKey, user)
}
// UserFrom returns the value of the user key on the ctx
func UserFrom(ctx context.Context) (user.Info, bool) {
user, ok := ctx.Value(userKey).(user.Info)
return user, ok
}
// WithAuditEvent returns set audit event struct.
func WithAuditEvent(parent context.Context, ev *audit.Event) context.Context {
return WithValue(parent, auditKey, ev)
}
// AuditEventFrom returns the audit event struct on the ctx
func AuditEventFrom(ctx context.Context) *audit.Event {
ev, _ := ctx.Value(auditKey).(*audit.Event)
return ev
}
/*
Copyright 2014 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 request
import (
"testing"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apiserver/pkg/authentication/user"
)
// TestNamespaceContext validates that a namespace can be get/set on a context object
func TestNamespaceContext(t *testing.T) {
ctx := NewDefaultContext()
result, ok := NamespaceFrom(ctx)
if !ok {
t.Fatalf("Error getting namespace")
}
if metav1.NamespaceDefault != result {
t.Fatalf("Expected: %s, Actual: %s", metav1.NamespaceDefault, result)
}
ctx = NewContext()
result, ok = NamespaceFrom(ctx)
if ok {
t.Fatalf("Should not be ok because there is no namespace on the context")
}
}
//TestUserContext validates that a userinfo can be get/set on a context object
func TestUserContext(t *testing.T) {
ctx := NewContext()
_, ok := UserFrom(ctx)
if ok {
t.Fatalf("Should not be ok because there is no user.Info on the context")
}
ctx = WithUser(
ctx,
&user.DefaultInfo{
Name: "bob",
UID: "123",
Groups: []string{"group1"},
Extra: map[string][]string{"foo": {"bar"}},
},
)
result, ok := UserFrom(ctx)
if !ok {
t.Fatalf("Error getting user info")
}
expectedName := "bob"
if result.GetName() != expectedName {
t.Fatalf("Get user name error, Expected: %s, Actual: %s", expectedName, result.GetName())
}
expectedUID := "123"
if result.GetUID() != expectedUID {
t.Fatalf("Get UID error, Expected: %s, Actual: %s", expectedUID, result.GetName())
}
expectedGroup := "group1"
actualGroup := result.GetGroups()
if len(actualGroup) != 1 {
t.Fatalf("Get user group number error, Expected: 1, Actual: %d", len(actualGroup))
} else if actualGroup[0] != expectedGroup {
t.Fatalf("Get user group error, Expected: %s, Actual: %s", expectedGroup, actualGroup[0])
}
expectedExtraKey := "foo"
expectedExtraValue := "bar"
actualExtra := result.GetExtra()
if len(actualExtra[expectedExtraKey]) != 1 {
t.Fatalf("Get user extra map number error, Expected: 1, Actual: %d", len(actualExtra[expectedExtraKey]))
} else if actualExtra[expectedExtraKey][0] != expectedExtraValue {
t.Fatalf("Get user extra map value error, Expected: %s, Actual: %s", expectedExtraValue, actualExtra[expectedExtraKey])
}
}
...@@ -187,7 +187,7 @@ func (im *imOperator) VerifyToken(tokenString string) (*iam.User, error) { ...@@ -187,7 +187,7 @@ func (im *imOperator) VerifyToken(tokenString string) (*iam.User, error) {
return nil, err return nil, err
} }
user, err := im.ldapClient.Get(providedUser.Name()) user, err := im.ldapClient.Get(providedUser.GetName())
if err != nil { if err != nil {
return nil, err return nil, err
} }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册