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

update

Signed-off-by: Nhongming <talonwan@yunify.com>
上级 97693570
...@@ -39,16 +39,12 @@ define ALL_HELP_INFO ...@@ -39,16 +39,12 @@ define ALL_HELP_INFO
# debugging tools like delve. # debugging tools like delve.
endef endef
.PHONY: all .PHONY: all
all: test hypersphere ks-apiserver ks-apigateway controller-manager all: test hypersphere ks-apiserver controller-manager
# Build ks-apiserver binary # Build ks-apiserver binary
ks-apiserver: fmt vet ks-apiserver: fmt vet
hack/gobuild.sh cmd/ks-apiserver hack/gobuild.sh cmd/ks-apiserver
# Build ks-apigateway binary
ks-apigateway: fmt vet
hack/gobuild.sh cmd/ks-apigateway
# Build controller-manager binary # Build controller-manager binary
controller-manager: fmt vet controller-manager: fmt vet
hack/gobuild.sh cmd/controller-manager hack/gobuild.sh cmd/controller-manager
......
# Copyright 2018 The KubeSphere Authors. All rights reserved.
# Use of this source code is governed by a Apache license
# that can be found in the LICENSE file.
# Copyright 2018 The KubeSphere Authors. All rights reserved.
# Use of this source code is governed by a Apache license
# that can be found in the LICENSE file.
FROM golang:1.12 as ks-apigateway-builder
COPY / /go/src/kubesphere.io/kubesphere
WORKDIR /go/src/kubesphere.io/kubesphere
RUN CGO_ENABLED=0 GO111MODULE=on GOOS=linux GOARCH=amd64 GOFLAGS=-mod=vendor go build -i -ldflags '-w -s' -o ks-apigateway cmd/ks-apigateway/apiserver.go && \
go run tools/cmd/doc-gen/main.go --output=install/swagger-ui/api.json
FROM alpine:3.9
RUN apk add --update ca-certificates && update-ca-certificates
COPY --from=ks-apigateway-builder /go/src/kubesphere.io/kubesphere/ks-apigateway /usr/local/bin/
COPY --from=ks-apigateway-builder /go/src/kubesphere.io/kubesphere/install/swagger-ui /var/static/swagger-ui
CMD ["sh"]
# Copyright 2018 The KubeSphere Authors. All rights reserved.
# Use of this source code is governed by a Apache license
# that can be found in the LICENSE file.
# Copyright 2018 The KubeSphere Authors. All rights reserved.
# Use of this source code is governed by a Apache license
# that can be found in the LICENSE file.
FROM golang:1.12 as ks-iam-builder
COPY / /go/src/kubesphere.io/kubesphere
WORKDIR /go/src/kubesphere.io/kubesphere
RUN CGO_ENABLED=0 GO111MODULE=on GOOS=linux GOARCH=amd64 GOFLAGS=-mod=vendor go build -i -ldflags '-w -s' -o ks-iam cmd/ks-iam/apiserver.go
FROM alpine:3.9
RUN apk add --update ca-certificates && update-ca-certificates
COPY --from=ks-iam-builder /go/src/kubesphere.io/kubesphere/ks-iam /usr/local/bin/
CMD ["sh"]
...@@ -46,6 +46,8 @@ func NewAPIServerCommand() *cobra.Command { ...@@ -46,6 +46,8 @@ func NewAPIServerCommand() *cobra.Command {
S3Options: conf.S3Options, S3Options: conf.S3Options,
OpenPitrixOptions: conf.OpenPitrixOptions, OpenPitrixOptions: conf.OpenPitrixOptions,
LoggingOptions: conf.LoggingOptions, LoggingOptions: conf.LoggingOptions,
LdapOptions: conf.LdapOptions,
CacheOptions: conf.RedisOptions,
AuthenticateOptions: conf.AuthenticateOptions, AuthenticateOptions: conf.AuthenticateOptions,
} }
} }
......
...@@ -187,7 +187,7 @@ func (s *APIServer) buildHandlerChain() { ...@@ -187,7 +187,7 @@ func (s *APIServer) buildHandlerChain() {
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.NewAMOperator(s.KubernetesClient.Kubernetes(), s.InformerFactory.KubernetesSharedInformerFactory()))) 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.WithMultipleClusterDispatcher(handler, dispatch.DefaultClusterDispatch)
handler = filters.WithKubeAPIServer(handler, s.KubernetesClient.Config(), &errorResponder{}) handler = filters.WithKubeAPIServer(handler, s.KubernetesClient.Config(), &errorResponder{})
......
...@@ -48,9 +48,6 @@ type Attributes interface { ...@@ -48,9 +48,6 @@ type Attributes interface {
// The namespace of the object, if a request is for a REST object. // The namespace of the object, if a request is for a REST object.
GetNamespace() string GetNamespace() string
// The devops project of the object, if a request is for a REST object.
GetDevopsProject() string
// The kind of object, if a request is for a REST object. // The kind of object, if a request is for a REST object.
GetResource() string GetResource() string
...@@ -106,7 +103,6 @@ type AttributesRecord struct { ...@@ -106,7 +103,6 @@ type AttributesRecord struct {
Cluster string Cluster string
Workspace string Workspace string
Namespace string Namespace string
DevopsProject string
APIGroup string APIGroup string
APIVersion string APIVersion string
Resource string Resource string
...@@ -141,10 +137,6 @@ func (a AttributesRecord) GetNamespace() string { ...@@ -141,10 +137,6 @@ func (a AttributesRecord) GetNamespace() string {
return a.Namespace return a.Namespace
} }
func (a AttributesRecord) GetDevopsProject() string {
return a.DevopsProject
}
func (a AttributesRecord) GetResource() string { func (a AttributesRecord) GetResource() string {
return a.Resource return a.Resource
} }
......
...@@ -22,79 +22,68 @@ import ( ...@@ -22,79 +22,68 @@ import (
"context" "context"
"github.com/open-policy-agent/opa/rego" "github.com/open-policy-agent/opa/rego"
"kubesphere.io/kubesphere/pkg/apiserver/authorization/authorizer" "kubesphere.io/kubesphere/pkg/apiserver/authorization/authorizer"
am2 "kubesphere.io/kubesphere/pkg/models/iam/am" "kubesphere.io/kubesphere/pkg/models/iam/am"
) )
type opaAuthorizer struct { type opaAuthorizer struct {
am am2.AccessManagementInterface am am.AccessManagementInterface
} }
func (o *opaAuthorizer) Authorize(a authorizer.Attributes) (authorized authorizer.Decision, reason string, err error) { func (o *opaAuthorizer) Authorize(attr authorizer.Attributes) (authorized authorizer.Decision, reason string, err error) {
platformRole, err := o.am.GetPlatformRole(a.GetUser().GetName()) platformRole, err := o.am.GetPlatformRole(attr.GetUser().GetName())
if err != nil { if err != nil {
return authorizer.DecisionDeny, "", err return authorizer.DecisionDeny, "", err
} }
// check platform role policy rules // check platform role policy rules
if a, r, e := o.roleAuthorize(platformRole, a); a == authorizer.DecisionAllow { if a, r, e := makeDecision(platformRole, attr); a == authorizer.DecisionAllow {
return a, r, e return a, r, e
} }
// it's not in cluster resource, permission denied // it's not in cluster resource, permission denied
// TODO declare implicit cluster info in request Info // TODO declare implicit cluster info in request Info
if a.GetCluster() == "" { if attr.GetCluster() == "" {
return authorizer.DecisionDeny, "permission undefined", nil return authorizer.DecisionDeny, "permission undefined", nil
} }
clusterRole, err := o.am.GetClusterRole(a.GetCluster(), a.GetUser().GetName()) clusterRole, err := o.am.GetClusterRole(attr.GetCluster(), attr.GetUser().GetName())
if err != nil { if err != nil {
return authorizer.DecisionDeny, "", err return authorizer.DecisionDeny, "", err
} }
// check cluster role policy rules // check cluster role policy rules
if a, r, e := o.roleAuthorize(clusterRole, a); a == authorizer.DecisionAllow { if a, r, e := makeDecision(clusterRole, attr); a == authorizer.DecisionAllow {
return a, r, e return a, r, e
} }
// it's not in cluster resource, permission denied // it's not in cluster resource, permission denied
if a.GetWorkspace() == "" && a.GetNamespace() == "" && a.GetDevopsProject() == "" { if attr.GetWorkspace() == "" && attr.GetNamespace() == "" {
return authorizer.DecisionDeny, "permission undefined", nil return authorizer.DecisionDeny, "permission undefined", nil
} }
workspaceRole, err := o.am.GetWorkspaceRole(a.GetWorkspace(), a.GetUser().GetName()) workspaceRole, err := o.am.GetWorkspaceRole(attr.GetWorkspace(), attr.GetUser().GetName())
if err != nil { if err != nil {
return authorizer.DecisionDeny, "", err return authorizer.DecisionDeny, "", err
} }
// check workspace role policy rules // check workspace role policy rules
if a, r, e := o.roleAuthorize(workspaceRole, a); a == authorizer.DecisionAllow { if a, r, e := makeDecision(workspaceRole, attr); a == authorizer.DecisionAllow {
return a, r, e return a, r, e
} }
// it's not in workspace resource, permission denied // it's not in workspace resource, permission denied
if a.GetNamespace() == "" && a.GetDevopsProject() == "" { if attr.GetNamespace() == "" {
return authorizer.DecisionDeny, "permission undefined", nil return authorizer.DecisionDeny, "permission undefined", nil
} }
if a.GetNamespace() != "" { if attr.GetNamespace() != "" {
namespaceRole, err := o.am.GetNamespaceRole(a.GetNamespace(), a.GetUser().GetName()) namespaceRole, err := o.am.GetNamespaceRole(attr.GetNamespace(), attr.GetUser().GetName())
if err != nil { if err != nil {
return authorizer.DecisionDeny, "", err return authorizer.DecisionDeny, "", err
} }
// check namespace role policy rules // check namespace role policy rules
if a, r, e := o.roleAuthorize(namespaceRole, a); a == authorizer.DecisionAllow { if a, r, e := makeDecision(namespaceRole, attr); a == authorizer.DecisionAllow {
return a, r, e
}
}
if a.GetDevopsProject() != "" {
devOpsRole, err := o.am.GetDevOpsRole(a.GetNamespace(), a.GetUser().GetName())
if err != nil {
return authorizer.DecisionDeny, "", err
}
// check devops role policy rules
if a, r, e := o.roleAuthorize(devOpsRole, a); a == authorizer.DecisionAllow {
return a, r, e return a, r, e
} }
} }
...@@ -102,14 +91,18 @@ func (o *opaAuthorizer) Authorize(a authorizer.Attributes) (authorized authorize ...@@ -102,14 +91,18 @@ func (o *opaAuthorizer) Authorize(a authorizer.Attributes) (authorized authorize
return authorizer.DecisionDeny, "", nil return authorizer.DecisionDeny, "", nil
} }
func (o *opaAuthorizer) roleAuthorize(role am2.Role, a authorizer.Attributes) (authorized authorizer.Decision, reason string, err error) { // Make decision base on role
func makeDecision(role am.Role, a authorizer.Attributes) (authorized authorizer.Decision, reason string, err error) {
// Call the rego.New function to create an object that can be prepared or evaluated
// After constructing a new rego.Rego object you can call PrepareForEval() to obtain an executable query
query, err := rego.New(rego.Query("data.authz.allow"), rego.Module("authz.rego", role.GetRego())).PrepareForEval(context.Background()) query, err := rego.New(rego.Query("data.authz.allow"), rego.Module("authz.rego", role.GetRego())).PrepareForEval(context.Background())
if err != nil { if err != nil {
return authorizer.DecisionDeny, "", err return authorizer.DecisionDeny, "", err
} }
// The policy decision is contained in the results returned by the Eval() call. You can inspect the decision and handle it accordingly.
results, err := query.Eval(context.Background(), rego.EvalInput(a)) results, err := query.Eval(context.Background(), rego.EvalInput(a))
if err != nil { if err != nil {
...@@ -123,6 +116,6 @@ func (o *opaAuthorizer) roleAuthorize(role am2.Role, a authorizer.Attributes) (a ...@@ -123,6 +116,6 @@ func (o *opaAuthorizer) roleAuthorize(role am2.Role, a authorizer.Attributes) (a
return authorizer.DecisionDeny, "permission undefined", nil return authorizer.DecisionDeny, "permission undefined", nil
} }
func NewOPAAuthorizer(am am2.AccessManagementInterface) *opaAuthorizer { func NewOPAAuthorizer(am am.AccessManagementInterface) *opaAuthorizer {
return &opaAuthorizer{am: am} return &opaAuthorizer{am: am}
} }
...@@ -19,66 +19,79 @@ ...@@ -19,66 +19,79 @@
package authorizerfactory package authorizerfactory
import ( import (
"context"
"github.com/open-policy-agent/opa/rego"
"k8s.io/apiserver/pkg/authentication/user" "k8s.io/apiserver/pkg/authentication/user"
"kubesphere.io/kubesphere/pkg/apiserver/authorization/authorizer" "kubesphere.io/kubesphere/pkg/apiserver/authorization/authorizer"
"kubesphere.io/kubesphere/pkg/models/iam/am"
"kubesphere.io/kubesphere/pkg/simple/client/cache"
"testing" "testing"
) )
func TestPlatformRole(t *testing.T) { func TestPlatformRole(t *testing.T) {
module := `package platform.authz opa := NewOPAAuthorizer(am.NewFakeAMOperator(cache.NewSimpleCache()))
default allow = false
allow { tests := []struct {
input.User.name == "admin" name string
} request authorizer.AttributesRecord
expectedDecision authorizer.Decision
allow { }{
is_admin {
} name: "list nodes",
request: authorizer.AttributesRecord{
is_admin { User: &user.DefaultInfo{
input.User.Groups[_] == "admin" Name: "admin",
} UID: "0",
` Groups: []string{"admin"},
query, err := rego.New(rego.Query("data.authz.allow"), rego.Module("authz.rego", module)).PrepareForEval(context.Background()) Extra: nil,
if err != nil { },
t.Fatal(err) Verb: "list",
} Cluster: "",
Workspace: "",
input := authorizer.AttributesRecord{ Namespace: "",
User: &user.DefaultInfo{ APIGroup: "",
Name: "admin", APIVersion: "v1",
UID: "0", Resource: "nodes",
Groups: []string{"admin"}, Subresource: "",
Extra: nil, Name: "",
KubernetesRequest: true,
ResourceRequest: true,
Path: "/api/v1/nodes",
},
expectedDecision: authorizer.DecisionAllow,
},
{
name: "list nodes",
request: authorizer.AttributesRecord{
User: &user.DefaultInfo{
Name: user.Anonymous,
UID: "0",
Groups: []string{"admin"},
Extra: nil,
},
Verb: "list",
Cluster: "",
Workspace: "",
Namespace: "",
APIGroup: "",
APIVersion: "v1",
Resource: "nodes",
Subresource: "",
Name: "",
KubernetesRequest: true,
ResourceRequest: true,
Path: "/api/v1/nodes",
},
expectedDecision: authorizer.DecisionDeny,
}, },
Verb: "list",
Cluster: "",
Workspace: "",
Namespace: "",
DevopsProject: "",
APIGroup: "",
APIVersion: "v1",
Resource: "nodes",
Subresource: "",
Name: "",
KubernetesRequest: true,
ResourceRequest: true,
Path: "/api/v1/nodes",
}
results, err := query.Eval(context.Background(), rego.EvalInput(input))
if err != nil {
t.Log(err)
} }
if len(results) > 0 && results[0].Expressions[0].Value == true { for _, test := range tests {
t.Log("allowed") decision, _, err := opa.Authorize(test.request)
} else { if err != nil {
t.Log("deny") t.Error(err)
}
if decision != test.expectedDecision {
t.Errorf("%s: expected decision %v, actual %+v", test.name, test.expectedDecision, decision)
}
} }
} }
...@@ -64,7 +64,7 @@ func newTestConfig() *Config { ...@@ -64,7 +64,7 @@ func newTestConfig() *Config {
GroupSearchBase: "ou=Groups,dc=example,dc=org", GroupSearchBase: "ou=Groups,dc=example,dc=org",
}, },
RedisOptions: &cache.Options{ RedisOptions: &cache.Options{
Host: "localhost:6379", Host: "localhost",
Port: 6379, Port: 6379,
Password: "P@88w0rd", Password: "P@88w0rd",
DB: 0, DB: 0,
......
...@@ -68,7 +68,6 @@ func GetAuthorizerAttributes(ctx context.Context) (authorizer.Attributes, error) ...@@ -68,7 +68,6 @@ func GetAuthorizerAttributes(ctx context.Context) (authorizer.Attributes, error)
attribs.Resource = requestInfo.Resource attribs.Resource = requestInfo.Resource
attribs.Subresource = requestInfo.Subresource attribs.Subresource = requestInfo.Subresource
attribs.Namespace = requestInfo.Namespace attribs.Namespace = requestInfo.Namespace
attribs.DevopsProject = requestInfo.DevopsProject
attribs.Name = requestInfo.Name attribs.Name = requestInfo.Name
return &attribs, nil return &attribs, nil
......
...@@ -3,7 +3,11 @@ package request ...@@ -3,7 +3,11 @@ package request
import ( import (
"context" "context"
"fmt" "fmt"
"k8s.io/apimachinery/pkg/api/validation/path"
metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/sets"
"k8s.io/klog"
"net/http" "net/http"
"strings" "strings"
...@@ -19,6 +23,13 @@ type RequestInfoResolver interface { ...@@ -19,6 +23,13 @@ type RequestInfoResolver interface {
// master's Mux. // master's Mux.
var specialVerbs = sets.NewString("proxy", "watch") var specialVerbs = sets.NewString("proxy", "watch")
// specialVerbsNoSubresources contains root verbs which do not allow subresources
var specialVerbsNoSubresources = sets.NewString("proxy")
// namespaceSubresources contains subresources of namespace
// this list allows the parser to distinguish between a namespace subresource, and a namespaced resource
var namespaceSubresources = sets.NewString("status", "finalize")
var kubernetesAPIPrefixes = sets.NewString("api", "apis") var kubernetesAPIPrefixes = sets.NewString("api", "apis")
// RequestInfo holds information parsed from the http.Request, // RequestInfo holds information parsed from the http.Request,
...@@ -34,9 +45,6 @@ type RequestInfo struct { ...@@ -34,9 +45,6 @@ type RequestInfo struct {
// Cluster of requested resource, this is empty in single-cluster environment // Cluster of requested resource, this is empty in single-cluster environment
Cluster string Cluster string
// Devops project of requested resource, this may be empty
DevopsProject string
} }
type RequestInfoFactory struct { type RequestInfoFactory struct {
...@@ -101,16 +109,9 @@ func (r *RequestInfoFactory) NewRequestInfo(req *http.Request) (*RequestInfo, er ...@@ -101,16 +109,9 @@ func (r *RequestInfoFactory) NewRequestInfo(req *http.Request) (*RequestInfo, er
currentParts = currentParts[1:] currentParts = currentParts[1:]
if !r.GrouplessAPIPrefixes.Has(requestInfo.APIPrefix) { if !r.GrouplessAPIPrefixes.Has(requestInfo.APIPrefix) {
if len(currentParts) < 2 { // one part (APIPrefix) has already been consumed, so this is actually "do we have four parts?"
return &requestInfo, nil
}
if currentParts[0] == "clusters" {
requestInfo.Cluster = currentParts[1]
currentParts = currentParts[2:]
}
if len(currentParts) < 3 { if len(currentParts) < 3 {
// return a non-resource request
return &requestInfo, nil return &requestInfo, nil
} }
...@@ -122,6 +123,18 @@ func (r *RequestInfoFactory) NewRequestInfo(req *http.Request) (*RequestInfo, er ...@@ -122,6 +123,18 @@ func (r *RequestInfoFactory) NewRequestInfo(req *http.Request) (*RequestInfo, er
requestInfo.APIVersion = currentParts[0] requestInfo.APIVersion = currentParts[0]
currentParts = currentParts[1:] currentParts = currentParts[1:]
if currentParts[0] == "clusters" {
requestInfo.Cluster = currentParts[1]
currentParts = currentParts[2:]
} else if len(currentParts) > 0 {
requestInfo.Cluster = "host-cluster"
}
if currentParts[0] == "workspaces" {
requestInfo.Workspace = currentParts[1]
currentParts = currentParts[2:]
}
if specialVerbs.Has(currentParts[0]) { if specialVerbs.Has(currentParts[0]) {
if len(currentParts) < 2 { if len(currentParts) < 2 {
return &requestInfo, fmt.Errorf("unable to determine kind and namespace from url: %v", req.URL) return &requestInfo, fmt.Errorf("unable to determine kind and namespace from url: %v", req.URL)
...@@ -146,6 +159,73 @@ func (r *RequestInfoFactory) NewRequestInfo(req *http.Request) (*RequestInfo, er ...@@ -146,6 +159,73 @@ func (r *RequestInfoFactory) NewRequestInfo(req *http.Request) (*RequestInfo, er
} }
} }
// URL forms: /namespaces/{namespace}/{kind}/*, where parts are adjusted to be relative to kind
if currentParts[0] == "namespaces" {
if len(currentParts) > 1 {
requestInfo.Namespace = currentParts[1]
// if there is another step after the namespace name and it is not a known namespace subresource
// move currentParts to include it as a resource in its own right
if len(currentParts) > 2 && !namespaceSubresources.Has(currentParts[2]) {
currentParts = currentParts[2:]
}
}
} else {
requestInfo.Namespace = metav1.NamespaceNone
}
// parsing successful, so we now know the proper value for .Parts
requestInfo.Parts = currentParts
// parts look like: resource/resourceName/subresource/other/stuff/we/don't/interpret
switch {
case len(requestInfo.Parts) >= 3 && !specialVerbsNoSubresources.Has(requestInfo.Verb):
requestInfo.Subresource = requestInfo.Parts[2]
fallthrough
case len(requestInfo.Parts) >= 2:
requestInfo.Name = requestInfo.Parts[1]
fallthrough
case len(requestInfo.Parts) >= 1:
requestInfo.Resource = requestInfo.Parts[0]
}
// if there's no name on the request and we thought it was a get before, then the actual verb is a list or a watch
if len(requestInfo.Name) == 0 && requestInfo.Verb == "get" {
opts := metainternalversion.ListOptions{}
if err := metainternalversion.ParameterCodec.DecodeParameters(req.URL.Query(), metav1.SchemeGroupVersion, &opts); err != nil {
// An error in parsing request will result in default to "list" and not setting "name" field.
klog.Errorf("Couldn't parse request %#v: %v", req.URL.Query(), err)
// Reset opts to not rely on partial results from parsing.
// However, if watch is set, let's report it.
opts = metainternalversion.ListOptions{}
if values := req.URL.Query()["watch"]; len(values) > 0 {
switch strings.ToLower(values[0]) {
case "false", "0":
default:
opts.Watch = true
}
}
}
if opts.Watch {
requestInfo.Verb = "watch"
} else {
requestInfo.Verb = "list"
}
if opts.FieldSelector != nil {
if name, ok := opts.FieldSelector.RequiresExactMatch("metadata.name"); ok {
if len(path.IsValidPathSegmentName(name)) == 0 {
requestInfo.Name = name
}
}
}
}
// if there's no name on the request and we thought it was a delete before, then the actual verb is deletecollection
if len(requestInfo.Name) == 0 && requestInfo.Verb == "delete" {
requestInfo.Verb = "deletecollection"
}
return &requestInfo, nil return &requestInfo, nil
} }
......
...@@ -43,6 +43,8 @@ func TestRequestInfoFactory_NewRequestInfo(t *testing.T) { ...@@ -43,6 +43,8 @@ func TestRequestInfoFactory_NewRequestInfo(t *testing.T) {
expectedResource string expectedResource string
expectedIsResourceRequest bool expectedIsResourceRequest bool
expectedCluster string expectedCluster string
expectedWorkspace string
exceptedNamespace string
}{ }{
{ {
name: "login", name: "login",
...@@ -54,15 +56,88 @@ func TestRequestInfoFactory_NewRequestInfo(t *testing.T) { ...@@ -54,15 +56,88 @@ func TestRequestInfoFactory_NewRequestInfo(t *testing.T) {
expectedIsResourceRequest: false, expectedIsResourceRequest: false,
expectedCluster: "", expectedCluster: "",
}, },
{
name: "list cluster roles",
url: "/apis/rbac.authorization.k8s.io/v1/clusters/cluster1/clusterroles",
method: http.MethodGet,
expectedErr: nil,
expectedVerb: "list",
expectedResource: "clusterroles",
expectedIsResourceRequest: true,
expectedCluster: "cluster1",
},
{
name: "list cluster nodes",
url: "/api/v1/clusters/cluster1/nodes",
method: http.MethodGet,
expectedErr: nil,
expectedVerb: "list",
expectedResource: "nodes",
expectedIsResourceRequest: true,
expectedCluster: "cluster1",
},
{
name: "list cluster nodes",
url: "/api/v1/clusters/cluster1/nodes",
method: http.MethodGet,
expectedErr: nil,
expectedVerb: "list",
expectedResource: "nodes",
expectedIsResourceRequest: true,
expectedCluster: "cluster1",
},
{
name: "list cluster nodes",
url: "/api/v1/nodes",
method: http.MethodGet,
expectedErr: nil,
expectedVerb: "list",
expectedResource: "nodes",
expectedIsResourceRequest: true,
expectedCluster: "host-cluster",
},
{
name: "list roles",
url: "/apis/rbac.authorization.k8s.io/v1/clusters/cluster1/namespaces/namespace1/roles",
method: http.MethodGet,
expectedErr: nil,
expectedVerb: "list",
expectedResource: "roles",
expectedIsResourceRequest: true,
exceptedNamespace: "namespace1",
expectedCluster: "cluster1",
},
{
name: "list roles",
url: "/apis/rbac.authorization.k8s.io/v1/namespaces/namespace1/roles",
method: http.MethodGet,
expectedErr: nil,
expectedVerb: "list",
expectedResource: "roles",
expectedIsResourceRequest: true,
expectedCluster: "host-cluster",
},
{ {
name: "list namespaces", name: "list namespaces",
url: "/kapis/resources.kubesphere.io/v1alpha2/namespaces", url: "/kapis/resources.kubesphere.io/v1alpha3/workspaces/workspace1/namespaces",
method: http.MethodGet, method: http.MethodGet,
expectedErr: nil, expectedErr: nil,
expectedVerb: "list", expectedVerb: "list",
expectedResource: "namespaces", expectedResource: "namespaces",
expectedIsResourceRequest: true, expectedIsResourceRequest: true,
expectedCluster: "", expectedWorkspace: "workspace1",
expectedCluster: "host-cluster",
},
{
name: "list namespaces",
url: "/kapis/resources.kubesphere.io/v1alpha3/clusters/cluster1/workspaces/workspace1/namespaces",
method: http.MethodGet,
expectedErr: nil,
expectedVerb: "list",
expectedResource: "namespaces",
expectedIsResourceRequest: true,
expectedWorkspace: "workspace1",
expectedCluster: "cluster1",
}, },
} }
...@@ -80,15 +155,24 @@ func TestRequestInfoFactory_NewRequestInfo(t *testing.T) { ...@@ -80,15 +155,24 @@ func TestRequestInfoFactory_NewRequestInfo(t *testing.T) {
t.Errorf("%s: expected error %v, actual %v", test.name, test.expectedErr, err) t.Errorf("%s: expected error %v, actual %v", test.name, test.expectedErr, err)
} }
} else { } else {
if test.expectedVerb != requestInfo.Verb { if test.expectedVerb != "" && test.expectedVerb != requestInfo.Verb {
t.Errorf("%s: expected verb %v, actual %+v", test.name, test.expectedVerb, requestInfo.Verb) t.Errorf("%s: expected verb %v, actual %+v", test.name, test.expectedVerb, requestInfo.Verb)
} }
if test.expectedResource != requestInfo.Resource { if test.expectedResource != "" && test.expectedResource != requestInfo.Resource {
t.Errorf("%s: expected resource %v, actual %+v", test.name, test.expectedResource, requestInfo.Resource) t.Errorf("%s: expected resource %v, actual %+v", test.name, test.expectedResource, requestInfo.Resource)
} }
if test.expectedIsResourceRequest != requestInfo.IsResourceRequest { if test.expectedIsResourceRequest != requestInfo.IsResourceRequest {
t.Errorf("%s: expected is resource request %v, actual %+v", test.name, test.expectedIsResourceRequest, requestInfo.IsResourceRequest) t.Errorf("%s: expected is resource request %v, actual %+v", test.name, test.expectedIsResourceRequest, requestInfo.IsResourceRequest)
} }
if test.expectedCluster != "" && test.expectedCluster != requestInfo.Cluster {
t.Errorf("%s: expected cluster %v, actual %+v", test.name, test.expectedCluster, requestInfo.Cluster)
}
if test.expectedWorkspace != "" && test.expectedWorkspace != requestInfo.Workspace {
t.Errorf("%s: expected workspace %v, actual %+v", test.name, test.expectedWorkspace, requestInfo.Workspace)
}
if test.exceptedNamespace != "" && test.exceptedNamespace != requestInfo.Namespace {
t.Errorf("%s: expected namespace %v, actual %+v", test.name, test.exceptedNamespace, requestInfo.Namespace)
}
} }
} }
} }
...@@ -8,7 +8,7 @@ import ( ...@@ -8,7 +8,7 @@ import (
"kubesphere.io/kubesphere/pkg/api" "kubesphere.io/kubesphere/pkg/api"
"kubesphere.io/kubesphere/pkg/constants" "kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/informers" "kubesphere.io/kubesphere/pkg/informers"
am2 "kubesphere.io/kubesphere/pkg/models/iam/am" "kubesphere.io/kubesphere/pkg/models/iam/am"
"kubesphere.io/kubesphere/pkg/models/monitoring" "kubesphere.io/kubesphere/pkg/models/monitoring"
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha2" "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2"
"kubesphere.io/kubesphere/pkg/models/tenant" "kubesphere.io/kubesphere/pkg/models/tenant"
...@@ -21,14 +21,14 @@ import ( ...@@ -21,14 +21,14 @@ import (
type tenantHandler struct { type tenantHandler struct {
tenant tenant.Interface tenant tenant.Interface
am am2.AccessManagementInterface am am.AccessManagementInterface
} }
func newTenantHandler(k8sClient k8s.Client, factory informers.InformerFactory, db *mysql.Database) *tenantHandler { func newTenantHandler(k8sClient k8s.Client, factory informers.InformerFactory, db *mysql.Database) *tenantHandler {
return &tenantHandler{ return &tenantHandler{
tenant: tenant.New(k8sClient.Kubernetes(), factory.KubernetesSharedInformerFactory(), factory.KubeSphereSharedInformerFactory(), db), tenant: tenant.New(k8sClient.Kubernetes(), factory.KubernetesSharedInformerFactory(), factory.KubeSphereSharedInformerFactory(), db),
am: am2.NewAMOperator(k8sClient.Kubernetes(), factory.KubernetesSharedInformerFactory()), am: am.NewAMOperator(k8sClient.Kubernetes(), factory.KubernetesSharedInformerFactory()),
} }
} }
......
...@@ -18,7 +18,6 @@ ...@@ -18,7 +18,6 @@
package am package am
import ( import (
rbacv1 "k8s.io/api/rbac/v1"
"k8s.io/client-go/informers" "k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes"
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha2" "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2"
...@@ -38,7 +37,6 @@ type AccessManagementInterface interface { ...@@ -38,7 +37,6 @@ type AccessManagementInterface interface {
GetClusterRole(cluster, username string) (Role, error) GetClusterRole(cluster, username string) (Role, error)
GetWorkspaceRole(workspace, username string) (Role, error) GetWorkspaceRole(workspace, username string) (Role, error)
GetNamespaceRole(namespace, username string) (Role, error) GetNamespaceRole(namespace, username string) (Role, error)
GetDevOpsRole(project, username string) (Role, error)
} }
type Role interface { type Role interface {
...@@ -52,26 +50,6 @@ type amOperator struct { ...@@ -52,26 +50,6 @@ type amOperator struct {
kubeClient kubernetes.Interface kubeClient kubernetes.Interface
} }
func (am *amOperator) ListClusterRoleBindings(clusterRole string) ([]*rbacv1.ClusterRoleBinding, error) {
panic("implement me")
}
func (am *amOperator) GetRoles(namespace, username string) ([]*rbacv1.Role, error) {
panic("implement me")
}
func (am *amOperator) GetClusterPolicyRules(username string) ([]rbacv1.PolicyRule, error) {
panic("implement me")
}
func (am *amOperator) GetPolicyRules(namespace, username string) ([]rbacv1.PolicyRule, error) {
panic("implement me")
}
func (am *amOperator) GetWorkspaceRole(workspace, username string) (Role, error) {
panic("implement me")
}
func NewAMOperator(kubeClient kubernetes.Interface, informers informers.SharedInformerFactory) AccessManagementInterface { func NewAMOperator(kubeClient kubernetes.Interface, informers informers.SharedInformerFactory) AccessManagementInterface {
resourceGetter := resource.ResourceGetter{} resourceGetter := resource.ResourceGetter{}
resourceGetter.Add(v1alpha2.Role, role.NewRoleSearcher(informers)) resourceGetter.Add(v1alpha2.Role, role.NewRoleSearcher(informers))
...@@ -91,6 +69,10 @@ func (am *amOperator) GetClusterRole(cluster, username string) (Role, error) { ...@@ -91,6 +69,10 @@ func (am *amOperator) GetClusterRole(cluster, username string) (Role, error) {
panic("implement me") panic("implement me")
} }
func (am *amOperator) GetWorkspaceRole(workspace, username string) (Role, error) {
panic("implement me")
}
func (am *amOperator) GetNamespaceRole(namespace, username string) (Role, error) { func (am *amOperator) GetNamespaceRole(namespace, username string) (Role, error) {
panic("implement me") panic("implement me")
} }
......
/*
*
* Copyright 2020 The KubeSphere 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 am
import (
"k8s.io/apiserver/pkg/authentication/user"
"kubesphere.io/kubesphere/pkg/simple/client/cache"
)
type fakeRole struct {
Name string
Rego string
}
type fakeOperator struct {
cache cache.Interface
}
func newFakeRole(username string) Role {
if username == user.Anonymous {
return &fakeRole{
Name: "anonymous",
Rego: "package authz\ndefault allow = false",
}
}
return &fakeRole{
Name: "admin",
Rego: "package authz\ndefault allow = true",
}
}
func (f fakeOperator) GetPlatformRole(username string) (Role, error) {
return newFakeRole(username), nil
}
func (f fakeOperator) GetClusterRole(cluster, username string) (Role, error) {
return newFakeRole(username), nil
}
func (f fakeOperator) GetWorkspaceRole(workspace, username string) (Role, error) {
return newFakeRole(username), nil
}
func (f fakeOperator) GetNamespaceRole(namespace, username string) (Role, error) {
return newFakeRole(username), nil
}
func (f fakeRole) GetName() string {
return f.Name
}
func (f fakeRole) GetRego() string {
return f.Rego
}
func NewFakeAMOperator(cache cache.Interface) AccessManagementInterface {
return &fakeOperator{cache: cache}
}
...@@ -20,9 +20,9 @@ package tenant ...@@ -20,9 +20,9 @@ package tenant
import ( import (
"k8s.io/api/core/v1" "k8s.io/api/core/v1"
k8sinformers "k8s.io/client-go/informers" k8sinformers "k8s.io/client-go/informers"
kubernetes "k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes"
"kubesphere.io/kubesphere/pkg/constants" "kubesphere.io/kubesphere/pkg/constants"
am2 "kubesphere.io/kubesphere/pkg/models/iam/am" "kubesphere.io/kubesphere/pkg/models/iam/am"
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha2" "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2"
"kubesphere.io/kubesphere/pkg/server/params" "kubesphere.io/kubesphere/pkg/server/params"
"kubesphere.io/kubesphere/pkg/utils/sliceutil" "kubesphere.io/kubesphere/pkg/utils/sliceutil"
...@@ -37,7 +37,7 @@ type NamespaceInterface interface { ...@@ -37,7 +37,7 @@ type NamespaceInterface interface {
type namespaceSearcher struct { type namespaceSearcher struct {
k8s kubernetes.Interface k8s kubernetes.Interface
informers k8sinformers.SharedInformerFactory informers k8sinformers.SharedInformerFactory
am am2.AccessManagementInterface am am.AccessManagementInterface
} }
func (s *namespaceSearcher) CreateNamespace(workspace string, namespace *v1.Namespace, username string) (*v1.Namespace, error) { func (s *namespaceSearcher) CreateNamespace(workspace string, namespace *v1.Namespace, username string) (*v1.Namespace, error) {
...@@ -53,7 +53,7 @@ func (s *namespaceSearcher) CreateNamespace(workspace string, namespace *v1.Name ...@@ -53,7 +53,7 @@ func (s *namespaceSearcher) CreateNamespace(workspace string, namespace *v1.Name
return s.k8s.CoreV1().Namespaces().Create(namespace) return s.k8s.CoreV1().Namespaces().Create(namespace)
} }
func newNamespaceOperator(k8s kubernetes.Interface, informers k8sinformers.SharedInformerFactory, am am2.AccessManagementInterface) NamespaceInterface { func newNamespaceOperator(k8s kubernetes.Interface, informers k8sinformers.SharedInformerFactory, am am.AccessManagementInterface) NamespaceInterface {
return &namespaceSearcher{k8s: k8s, informers: informers, am: am} return &namespaceSearcher{k8s: k8s, informers: informers, am: am}
} }
......
...@@ -24,7 +24,7 @@ import ( ...@@ -24,7 +24,7 @@ import (
"kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1" "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1"
ksinformers "kubesphere.io/kubesphere/pkg/client/informers/externalversions" ksinformers "kubesphere.io/kubesphere/pkg/client/informers/externalversions"
"kubesphere.io/kubesphere/pkg/models" "kubesphere.io/kubesphere/pkg/models"
am2 "kubesphere.io/kubesphere/pkg/models/iam/am" "kubesphere.io/kubesphere/pkg/models/iam/am"
"kubesphere.io/kubesphere/pkg/server/params" "kubesphere.io/kubesphere/pkg/server/params"
"kubesphere.io/kubesphere/pkg/simple/client/mysql" "kubesphere.io/kubesphere/pkg/simple/client/mysql"
) )
...@@ -43,7 +43,7 @@ type Interface interface { ...@@ -43,7 +43,7 @@ type Interface interface {
type tenantOperator struct { type tenantOperator struct {
workspaces WorkspaceInterface workspaces WorkspaceInterface
namespaces NamespaceInterface namespaces NamespaceInterface
am am2.AccessManagementInterface am am.AccessManagementInterface
devops DevOpsProjectOperator devops DevOpsProjectOperator
} }
...@@ -68,7 +68,7 @@ func (t *tenantOperator) DeleteNamespace(workspace, namespace string) error { ...@@ -68,7 +68,7 @@ func (t *tenantOperator) DeleteNamespace(workspace, namespace string) error {
} }
func New(client kubernetes.Interface, informers k8sinformers.SharedInformerFactory, ksinformers ksinformers.SharedInformerFactory, db *mysql.Database) Interface { func New(client kubernetes.Interface, informers k8sinformers.SharedInformerFactory, ksinformers ksinformers.SharedInformerFactory, db *mysql.Database) Interface {
amOperator := am2.NewAMOperator(client, informers) amOperator := am.NewAMOperator(client, informers)
return &tenantOperator{ return &tenantOperator{
workspaces: newWorkspaceOperator(client, informers, ksinformers, amOperator, db), workspaces: newWorkspaceOperator(client, informers, ksinformers, amOperator, db),
namespaces: newNamespaceOperator(client, informers, amOperator), namespaces: newNamespaceOperator(client, informers, amOperator),
......
...@@ -27,7 +27,7 @@ import ( ...@@ -27,7 +27,7 @@ import (
"kubesphere.io/kubesphere/pkg/constants" "kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/db" "kubesphere.io/kubesphere/pkg/db"
"kubesphere.io/kubesphere/pkg/models/devops" "kubesphere.io/kubesphere/pkg/models/devops"
am2 "kubesphere.io/kubesphere/pkg/models/iam/am" "kubesphere.io/kubesphere/pkg/models/iam/am"
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha2" "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2"
"kubesphere.io/kubesphere/pkg/server/params" "kubesphere.io/kubesphere/pkg/server/params"
"kubesphere.io/kubesphere/pkg/simple/client/mysql" "kubesphere.io/kubesphere/pkg/simple/client/mysql"
...@@ -64,14 +64,14 @@ type workspaceOperator struct { ...@@ -64,14 +64,14 @@ type workspaceOperator struct {
client kubernetes.Interface client kubernetes.Interface
informers informers.SharedInformerFactory informers informers.SharedInformerFactory
ksInformers externalversions.SharedInformerFactory ksInformers externalversions.SharedInformerFactory
am am2.AccessManagementInterface am am.AccessManagementInterface
// TODO: use db interface instead of mysql client // TODO: use db interface instead of mysql client
// we can refactor this after rewrite devops using crd // we can refactor this after rewrite devops using crd
db *mysql.Database db *mysql.Database
} }
func newWorkspaceOperator(client kubernetes.Interface, informers informers.SharedInformerFactory, ksinformers externalversions.SharedInformerFactory, am am2.AccessManagementInterface, db *mysql.Database) WorkspaceInterface { func newWorkspaceOperator(client kubernetes.Interface, informers informers.SharedInformerFactory, ksinformers externalversions.SharedInformerFactory, am am.AccessManagementInterface, db *mysql.Database) WorkspaceInterface {
return &workspaceOperator{ return &workspaceOperator{
client: client, client: client,
informers: informers, informers: informers,
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册