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

code refactor (#1786)

* implement LDAP mock client
Signed-off-by: Nhongming <talonwan@yunify.com>

* update
Signed-off-by: Nhongming <talonwan@yunify.com>

* update
Signed-off-by: Nhongming <talonwan@yunify.com>

* resolve conflict
Signed-off-by: Nhongming <talonwan@yunify.com>
上级 96aee0e6
...@@ -29,7 +29,7 @@ func NewKubeSphereControllerManagerOptions() *KubeSphereControllerManagerOptions ...@@ -29,7 +29,7 @@ func NewKubeSphereControllerManagerOptions() *KubeSphereControllerManagerOptions
KubernetesOptions: k8s.NewKubernetesOptions(), KubernetesOptions: k8s.NewKubernetesOptions(),
DevopsOptions: jenkins.NewDevopsOptions(), DevopsOptions: jenkins.NewDevopsOptions(),
S3Options: s3.NewS3Options(), S3Options: s3.NewS3Options(),
OpenPitrixOptions: openpitrix.NewOpenPitrixOptions(), OpenPitrixOptions: openpitrix.NewOptions(),
LeaderElection: &leaderelection.LeaderElectionConfig{ LeaderElection: &leaderelection.LeaderElectionConfig{
LeaseDuration: 30 * time.Second, LeaseDuration: 30 * time.Second,
RenewDeadline: 15 * time.Second, RenewDeadline: 15 * time.Second,
......
...@@ -42,7 +42,7 @@ func NewServerRunOptions() *ServerRunOptions { ...@@ -42,7 +42,7 @@ func NewServerRunOptions() *ServerRunOptions {
MySQLOptions: mysql.NewMySQLOptions(), MySQLOptions: mysql.NewMySQLOptions(),
MonitoringOptions: prometheus.NewPrometheusOptions(), MonitoringOptions: prometheus.NewPrometheusOptions(),
S3Options: s3.NewS3Options(), S3Options: s3.NewS3Options(),
OpenPitrixOptions: openpitrix.NewOpenPitrixOptions(), OpenPitrixOptions: openpitrix.NewOptions(),
LoggingOptions: esclient.NewElasticSearchOptions(), LoggingOptions: esclient.NewElasticSearchOptions(),
} }
......
...@@ -176,7 +176,7 @@ func createDeps(s *options.ServerRunOptions, stopCh <-chan struct{}) *apiserver. ...@@ -176,7 +176,7 @@ func createDeps(s *options.ServerRunOptions, stopCh <-chan struct{}) *apiserver.
} }
if s.OpenPitrixOptions != nil && !s.OpenPitrixOptions.IsEmpty() { if s.OpenPitrixOptions != nil && !s.OpenPitrixOptions.IsEmpty() {
deps.OpenPitrix, err = openpitrix.NewOpenPitrixClient(s.OpenPitrixOptions) deps.OpenPitrix, err = openpitrix.NewClient(s.OpenPitrixOptions)
if err != nil { if err != nil {
klog.Fatalf("error happened when initializing openpitrix client, %v", err) klog.Fatalf("error happened when initializing openpitrix client, %v", err)
} }
......
...@@ -49,7 +49,7 @@ func NewServerRunOptions() *ServerRunOptions { ...@@ -49,7 +49,7 @@ func NewServerRunOptions() *ServerRunOptions {
s := &ServerRunOptions{ s := &ServerRunOptions{
GenericServerRunOptions: genericoptions.NewServerRunOptions(), GenericServerRunOptions: genericoptions.NewServerRunOptions(),
KubernetesOptions: k8s.NewKubernetesOptions(), KubernetesOptions: k8s.NewKubernetesOptions(),
LdapOptions: ldap.NewLdapOptions(), LdapOptions: ldap.NewOptions(),
MySQLOptions: mysql.NewMySQLOptions(), MySQLOptions: mysql.NewMySQLOptions(),
RedisOptions: cache.NewRedisOptions(), RedisOptions: cache.NewRedisOptions(),
} }
......
...@@ -70,6 +70,7 @@ require ( ...@@ -70,6 +70,7 @@ require (
github.com/opencontainers/go-digest v1.0.0-rc1 github.com/opencontainers/go-digest v1.0.0-rc1
github.com/opencontainers/image-spec v1.0.1 // indirect github.com/opencontainers/image-spec v1.0.1 // indirect
github.com/openshift/api v3.9.0+incompatible // indirect github.com/openshift/api v3.9.0+incompatible // indirect
github.com/pkg/errors v0.8.1
github.com/projectcalico/libcalico-go v1.7.2-0.20191104213956-8f81e1e344ce github.com/projectcalico/libcalico-go v1.7.2-0.20191104213956-8f81e1e344ce
github.com/prometheus/common v0.4.0 github.com/prometheus/common v0.4.0
github.com/sony/sonyflake v0.0.0-20181109022403-6d5bd6181009 github.com/sony/sonyflake v0.0.0-20181109022403-6d5bd6181009
......
...@@ -47,12 +47,16 @@ type LoginRequest struct { ...@@ -47,12 +47,16 @@ type LoginRequest struct {
Password string `json:"password" description:"password"` Password string `json:"password" description:"password"`
} }
type UserCreateRequest struct { type UserDetail struct {
*iam.User *iam.User
ClusterRole string `json:"cluster_role"` ClusterRole string `json:"cluster_role"`
} }
func (request *UserCreateRequest) Validate() error { type CreateUserRequest struct {
*UserDetail
}
func (request *CreateUserRequest) Validate() error {
if request.Username == "" { if request.Username == "" {
return fmt.Errorf("username must not be empty") return fmt.Errorf("username must not be empty")
} }
...@@ -68,3 +72,39 @@ func (request *UserCreateRequest) Validate() error { ...@@ -68,3 +72,39 @@ func (request *UserCreateRequest) Validate() error {
return nil return nil
} }
type ModifyUserRequest struct {
*UserDetail
CurrentPassword string `json:"current_password,omitempty" description:"this is necessary if you need to change your password"`
}
func (request *TokenReview) Validate() error {
if request.Spec == nil || request.Spec.Token == "" {
return fmt.Errorf("token must not be null")
}
return nil
}
func (request ModifyUserRequest) Validate() error {
// Parses a single RFC 5322 address, e.g. "Barry Gibbs <bg@example.com>"
if _, err := mail.ParseAddress(request.Email); err != nil {
return fmt.Errorf("invalid email: %s", request.Email)
}
if request.Password != "" {
if len(request.Password) < minPasswordLength {
return fmt.Errorf("password must be at least %d characters long", minPasswordLength)
}
if len(request.CurrentPassword) < minPasswordLength {
return fmt.Errorf("password must be at least %d characters long", minPasswordLength)
}
}
return nil
}
type ListUserResponse struct {
Items []*UserDetail `json:"items"`
TotalCount int `json:"total_count"`
}
/*
Copyright 2019 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 iam
/*
Copyright 2019 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 iam
import (
"fmt"
"github.com/emicklei/go-restful"
iamv1alpha2 "kubesphere.io/kubesphere/pkg/api/iam/v1alpha2"
"kubesphere.io/kubesphere/pkg/models"
"kubesphere.io/kubesphere/pkg/models/iam"
"kubesphere.io/kubesphere/pkg/server/errors"
"kubesphere.io/kubesphere/pkg/utils/iputil"
"net/http"
)
type OAuthRequest struct {
GrantType string `json:"grant_type"`
Username string `json:"username,omitempty" description:"username"`
Password string `json:"password,omitempty" description:"password"`
RefreshToken string `json:"refresh_token,omitempty"`
}
func OAuth(req *restful.Request, resp *restful.Response) {
authRequest := &OAuthRequest{}
err := req.ReadEntity(authRequest)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
return
}
var result *models.AuthGrantResponse
switch authRequest.GrantType {
case "refresh_token":
result, err = iam.RefreshToken(authRequest.RefreshToken)
case "password":
ip := iputil.RemoteIp(req.Request)
result, err = iam.PasswordCredentialGrant(authRequest.Username, authRequest.Password, ip)
default:
resp.Header().Set("WWW-Authenticate", "grant_type is not supported")
resp.WriteHeaderAndEntity(http.StatusUnauthorized, errors.Wrap(fmt.Errorf("grant_type is not supported")))
return
}
if err != nil {
resp.Header().Set("WWW-Authenticate", err.Error())
resp.WriteHeaderAndEntity(http.StatusUnauthorized, errors.Wrap(err))
return
}
resp.WriteEntity(result)
}
/*
Copyright 2019 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 iam
import (
"fmt"
"net/http"
"regexp"
"strings"
"github.com/emicklei/go-restful"
"github.com/go-ldap/ldap"
"kubesphere.io/kubesphere/pkg/models"
"kubesphere.io/kubesphere/pkg/models/iam"
"kubesphere.io/kubesphere/pkg/server/errors"
)
func CreateGroup(req *restful.Request, resp *restful.Response) {
var group models.Group
err := req.ReadEntity(&group)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
return
}
if !regexp.MustCompile("[a-z0-9]([-a-z0-9]*[a-z0-9])?").MatchString(group.Name) {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.New(fmt.Sprintf("incalid group name %s", group)))
return
}
created, err := iam.CreateGroup(&group)
if err != nil {
if ldap.IsErrorWithCode(err, ldap.LDAPResultEntryAlreadyExists) {
resp.WriteHeaderAndEntity(http.StatusConflict, errors.Wrap(err))
} else {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
}
return
}
resp.WriteAsJson(created)
}
func DeleteGroup(req *restful.Request, resp *restful.Response) {
path := req.PathParameter("group")
if path == "" {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(fmt.Errorf("group path must not be null")))
return
}
err := iam.DeleteGroup(path)
if err != nil {
if ldap.IsErrorWithCode(err, ldap.LDAPResultNoSuchObject) {
resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err))
return
}
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(errors.None)
}
func UpdateGroup(req *restful.Request, resp *restful.Response) {
groupPathInPath := req.PathParameter("group")
var group models.Group
req.ReadEntity(&group)
if groupPathInPath != group.Path {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(fmt.Errorf("the path of group (%s) does not match the path on the URL (%s)", group.Path, groupPathInPath)))
return
}
edited, err := iam.UpdateGroup(&group)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(edited)
}
func DescribeGroup(req *restful.Request, resp *restful.Response) {
path := req.PathParameter("group")
group, err := iam.DescribeGroup(path)
if err != nil {
if ldap.IsErrorWithCode(err, ldap.LDAPResultNoSuchObject) {
resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err))
} else {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
}
return
}
resp.WriteAsJson(group)
}
func ListGroupUsers(req *restful.Request, resp *restful.Response) {
path := req.PathParameter("group")
group, err := iam.DescribeGroup(path)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
users := make([]*iam.User, 0)
modify := false
for i := 0; i < len(group.Members); i++ {
name := group.Members[i]
user, err := iam.GetUserInfo(name)
if err != nil {
if ldap.IsErrorWithCode(err, ldap.LDAPResultNoSuchObject) {
group.Members = append(group.Members[:i], group.Members[i+1:]...)
i--
modify = true
continue
} else {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
}
users = append(users, user)
}
if modify {
go iam.UpdateGroup(group)
}
resp.WriteAsJson(users)
}
func ListGroups(req *restful.Request, resp *restful.Response) {
array := req.QueryParameter("path")
if array == "" {
groups, err := iam.ChildList("")
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(groups)
} else {
paths := strings.Split(array, ",")
groups := make([]*models.Group, 0)
for _, v := range paths {
path := strings.TrimSpace(v)
group, err := iam.DescribeGroup(path)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
groups = append(groups, group)
}
resp.WriteAsJson(groups)
}
}
/*
Copyright 2019 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 iam
import (
"fmt"
"k8s.io/klog"
"kubesphere.io/kubesphere/pkg/server/params"
"net/http"
"net/mail"
"strings"
"github.com/emicklei/go-restful"
"github.com/go-ldap/ldap"
rbacv1 "k8s.io/api/rbac/v1"
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/models/iam"
"kubesphere.io/kubesphere/pkg/server/errors"
)
func DeleteUser(req *restful.Request, resp *restful.Response) {
username := req.PathParameter("user")
operator := req.HeaderParameter(constants.UserNameHeader)
if operator == username {
err := fmt.Errorf("cannot delete yourself")
klog.Info(err)
resp.WriteHeaderAndEntity(http.StatusForbidden, errors.Wrap(err))
return
}
err := iam.DeleteUser(username)
if err != nil {
klog.Info(err)
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(errors.None)
}
func UpdateUser(req *restful.Request, resp *restful.Response) {
usernameInPath := req.PathParameter("user")
usernameInHeader := req.HeaderParameter(constants.UserNameHeader)
var user iam.User
err := req.ReadEntity(&user)
if err != nil {
klog.Info(err)
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
return
}
if usernameInPath != user.Username {
err = fmt.Errorf("the name of user (%s) does not match the name on the URL (%s)", user.Username, usernameInPath)
klog.Info(err)
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
return
}
if _, err = mail.ParseAddress(user.Email); err != nil {
err = fmt.Errorf("invalid email: %s", user.Email)
klog.Info(err)
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
return
}
if user.Password != "" && len(user.Password) < 6 {
err = fmt.Errorf("invalid password")
klog.Info(err)
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
return
}
// change password by self
if usernameInHeader == user.Username && user.Password != "" {
isUserManager, err := isUserManager(usernameInHeader)
if err != nil {
klog.Error(err)
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
if !isUserManager {
_, err = iam.Login(usernameInHeader, user.CurrentPassword, "")
}
if err != nil {
err = fmt.Errorf("incorrect current password")
klog.Info(err)
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
return
}
}
if usernameInHeader == user.Username {
// change cluster role by self is not permitted
user.ClusterRole = ""
}
result, err := iam.UpdateUser(&user)
if err != nil {
if ldap.IsErrorWithCode(err, ldap.LDAPResultEntryAlreadyExists) {
klog.Info(err)
resp.WriteHeaderAndEntity(http.StatusConflict, errors.Wrap(err))
return
}
klog.Error(err)
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(result)
}
func isUserManager(username string) (bool, error) {
rules, err := iam.GetUserClusterRules(username)
if err != nil {
return false, err
}
if iam.RulesMatchesRequired(rules, rbacv1.PolicyRule{Verbs: []string{"update"}, Resources: []string{"users"}, APIGroups: []string{"iam.kubesphere.io"}}) {
return true, nil
}
return false, nil
}
func UserLoginLogs(req *restful.Request, resp *restful.Response) {
username := req.PathParameter("user")
logs, err := iam.LoginLog(username)
if err != nil {
klog.Error(err)
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
result := make([]map[string]string, 0)
for _, v := range logs {
item := strings.Split(v, ",")
time := item[0]
var ip string
if len(item) > 1 {
ip = item[1]
}
result = append(result, map[string]string{"login_time": time, "login_ip": ip})
}
resp.WriteAsJson(result)
}
func DescribeUser(req *restful.Request, resp *restful.Response) {
username := req.PathParameter("user")
user, err := iam.DescribeUser(username)
if err != nil {
if ldap.IsErrorWithCode(err, ldap.LDAPResultNoSuchObject) {
klog.Info(err)
resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err))
} else {
klog.Error(err)
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
}
return
}
clusterRole, err := iam.GetUserClusterRole(username)
if err != nil {
klog.Error(err)
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
user.ClusterRole = clusterRole.Name
clusterRules, err := iam.GetUserClusterSimpleRules(username)
if err != nil {
klog.Error(err)
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
result := struct {
*iam.User
ClusterRules []iam.SimpleRule `json:"cluster_rules"`
}{
User: user,
ClusterRules: clusterRules,
}
resp.WriteAsJson(result)
}
func Precheck(req *restful.Request, resp *restful.Response) {
check := req.QueryParameter("check")
exist, err := iam.UserCreateCheck(check)
if err != nil {
klog.Error(err)
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(map[string]bool{"exist": exist})
}
func ListUsers(req *restful.Request, resp *restful.Response) {
if check := req.QueryParameter("check"); check != "" {
Precheck(req, resp)
return
}
limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam))
conditions, err := params.ParseConditions(req.QueryParameter(params.ConditionsParam))
orderBy := req.QueryParameter(params.OrderByParam)
reverse := params.ParseReverse(req)
if err != nil {
klog.Info(err)
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
return
}
users, err := iam.ListUsers(conditions, orderBy, reverse, limit, offset)
if err != nil {
klog.Error(err)
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(users)
}
/*
Copyright 2019 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 iam
/*
Copyright 2019 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 iam
import (
"github.com/emicklei/go-restful"
k8serr "k8s.io/apimachinery/pkg/api/errors"
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/models/iam"
"kubesphere.io/kubesphere/pkg/models/workspaces"
"kubesphere.io/kubesphere/pkg/server/errors"
"kubesphere.io/kubesphere/pkg/server/params"
"net/http"
)
func ListWorkspaceRoles(req *restful.Request, resp *restful.Response) {
workspace := req.PathParameter("workspace")
conditions, err := params.ParseConditions(req.QueryParameter(params.ConditionsParam))
orderBy := req.QueryParameter(params.OrderByParam)
limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam))
reverse := params.ParseReverse(req)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
return
}
result, err := iam.ListWorkspaceRoles(workspace, conditions, orderBy, reverse, limit, offset)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(result.Items)
}
func ListWorkspaceRoleRules(req *restful.Request, resp *restful.Response) {
workspace := req.PathParameter("workspace")
role := req.PathParameter("role")
rules := iam.GetWorkspaceRoleSimpleRules(workspace, role)
resp.WriteAsJson(rules)
}
func DescribeWorkspaceRole(req *restful.Request, resp *restful.Response) {
workspace := req.PathParameter("workspace")
roleName := req.PathParameter("role")
role, err := iam.GetWorkspaceRole(workspace, roleName)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(role)
}
func DescribeWorkspaceUser(req *restful.Request, resp *restful.Response) {
workspace := req.PathParameter("workspace")
username := req.PathParameter("member")
workspaceRole, err := iam.GetUserWorkspaceRole(workspace, username)
if err != nil {
if k8serr.IsNotFound(err) {
resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err))
} else {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
}
return
}
user, err := iam.GetUserInfo(username)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
user.WorkspaceRole = workspaceRole.Annotations[constants.DisplayNameAnnotationKey]
resp.WriteAsJson(user)
}
func ListDevopsRoleRules(req *restful.Request, resp *restful.Response) {
role := req.PathParameter("role")
rules := iam.GetDevopsRoleSimpleRules(role)
resp.WriteAsJson(rules)
}
func InviteUser(req *restful.Request, resp *restful.Response) {
workspace := req.PathParameter("workspace")
var user iam.User
err := req.ReadEntity(&user)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
return
}
err = workspaces.InviteUser(workspace, &user)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(errors.None)
}
func RemoveUser(req *restful.Request, resp *restful.Response) {
workspace := req.PathParameter("workspace")
username := req.PathParameter("member")
err := workspaces.RemoveUser(workspace, username)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(errors.None)
}
func ListWorkspaceUsers(req *restful.Request, resp *restful.Response) {
workspace := req.PathParameter("workspace")
conditions, err := params.ParseConditions(req.QueryParameter(params.ConditionsParam))
orderBy := req.QueryParameter(params.OrderByParam)
limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam))
reverse := params.ParseReverse(req)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
return
}
result, err := iam.ListWorkspaceUsers(workspace, conditions, orderBy, reverse, limit, offset)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(result)
}
...@@ -2,21 +2,26 @@ package v1alpha2 ...@@ -2,21 +2,26 @@ package v1alpha2
import ( import (
"errors" "errors"
"fmt"
"github.com/dgrijalva/jwt-go" "github.com/dgrijalva/jwt-go"
"github.com/emicklei/go-restful" "github.com/emicklei/go-restful"
"github.com/go-ldap/ldap" "github.com/go-ldap/ldap"
rbacv1 "k8s.io/api/rbac/v1" rbacv1 "k8s.io/api/rbac/v1"
k8serr "k8s.io/apimachinery/pkg/api/errors" "k8s.io/klog"
"kubesphere.io/kubesphere/pkg/api" "kubesphere.io/kubesphere/pkg/api"
iamv1alpha2 "kubesphere.io/kubesphere/pkg/api/iam/v1alpha2" iamv1alpha2 "kubesphere.io/kubesphere/pkg/api/iam/v1alpha2"
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/informers"
"kubesphere.io/kubesphere/pkg/models/iam" "kubesphere.io/kubesphere/pkg/models/iam"
"kubesphere.io/kubesphere/pkg/models/iam/policy" "kubesphere.io/kubesphere/pkg/models/iam/policy"
kserr "kubesphere.io/kubesphere/pkg/server/errors" "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2"
apierr "kubesphere.io/kubesphere/pkg/server/errors"
"kubesphere.io/kubesphere/pkg/server/params" "kubesphere.io/kubesphere/pkg/server/params"
"kubesphere.io/kubesphere/pkg/simple/client/k8s"
ldappool "kubesphere.io/kubesphere/pkg/simple/client/ldap"
"kubesphere.io/kubesphere/pkg/utils/iputil" "kubesphere.io/kubesphere/pkg/utils/iputil"
"kubesphere.io/kubesphere/pkg/utils/jwtutil" "kubesphere.io/kubesphere/pkg/utils/jwtutil"
"net/http" "net/http"
"sort"
) )
type iamHandler struct { type iamHandler struct {
...@@ -24,29 +29,34 @@ type iamHandler struct { ...@@ -24,29 +29,34 @@ type iamHandler struct {
imOperator iam.IdentityManagementInterface imOperator iam.IdentityManagementInterface
} }
func newIAMHandler() *iamHandler { func newIAMHandler(k8sClient k8s.Client, ldapClient ldappool.Client, options iam.Config) *iamHandler {
return &iamHandler{} factory := informers.NewInformerFactories(k8sClient.Kubernetes(), k8sClient.KubeSphere(), k8sClient.S2i(), k8sClient.Application())
return &iamHandler{
amOperator: iam.NewAMOperator(factory.KubernetesSharedInformerFactory()),
imOperator: iam.NewIMOperator(ldapClient, options),
}
} }
// k8s token review // Implement webhook authentication interface
// https://kubernetes.io/docs/reference/access-authn-authz/authentication/#webhook-token-authentication
func (h *iamHandler) TokenReviewHandler(req *restful.Request, resp *restful.Response) { func (h *iamHandler) TokenReviewHandler(req *restful.Request, resp *restful.Response) {
var tokenReview iamv1alpha2.TokenReview var tokenReview iamv1alpha2.TokenReview
err := req.ReadEntity(&tokenReview) err := req.ReadEntity(&tokenReview)
if err != nil { if err != nil {
klog.V(4).Infoln(err)
api.HandleBadRequest(resp, err) api.HandleBadRequest(resp, err)
return return
} }
if tokenReview.Spec == nil { if err = tokenReview.Validate(); err != nil {
api.HandleBadRequest(resp, errors.New("token must not be null")) klog.V(4).Infoln(err)
api.HandleBadRequest(resp, err)
return return
} }
uToken := tokenReview.Spec.Token token, err := jwtutil.ValidateToken(tokenReview.Spec.Token)
token, err := jwtutil.ValidateToken(uToken)
if err != nil { if err != nil {
failed := iamv1alpha2.TokenReview{APIVersion: tokenReview.APIVersion, failed := iamv1alpha2.TokenReview{APIVersion: tokenReview.APIVersion,
...@@ -59,18 +69,24 @@ func (h *iamHandler) TokenReviewHandler(req *restful.Request, resp *restful.Resp ...@@ -59,18 +69,24 @@ func (h *iamHandler) TokenReviewHandler(req *restful.Request, resp *restful.Resp
return return
} }
claims := token.Claims.(jwt.MapClaims) claims, ok := token.Claims.(jwt.MapClaims)
if !ok {
api.HandleBadRequest(resp, errors.New("invalid token"))
return
}
username, ok := claims["username"].(string) username, ok := claims["username"].(string)
if !ok { if !ok {
api.HandleBadRequest(resp, errors.New("username not found")) api.HandleBadRequest(resp, errors.New("invalid token"))
return return
} }
user, err := h.imOperator.DescribeUser(username) user, err := h.imOperator.DescribeUser(username)
if err != nil { if err != nil {
klog.Errorln(err)
api.HandleInternalError(resp, err) api.HandleInternalError(resp, err)
return return
} }
...@@ -92,7 +108,9 @@ func (h *iamHandler) Login(req *restful.Request, resp *restful.Response) { ...@@ -92,7 +108,9 @@ func (h *iamHandler) Login(req *restful.Request, resp *restful.Response) {
err := req.ReadEntity(&loginRequest) err := req.ReadEntity(&loginRequest)
if err != nil || loginRequest.Username == "" || loginRequest.Password == "" { if err != nil || loginRequest.Username == "" || loginRequest.Password == "" {
resp.WriteHeaderAndEntity(http.StatusUnauthorized, errors.New("incorrect username or password")) err = errors.New("incorrect username or password")
klog.V(4).Infoln(err)
resp.WriteHeaderAndEntity(http.StatusUnauthorized, err)
return return
} }
...@@ -101,10 +119,12 @@ func (h *iamHandler) Login(req *restful.Request, resp *restful.Response) { ...@@ -101,10 +119,12 @@ func (h *iamHandler) Login(req *restful.Request, resp *restful.Response) {
token, err := h.imOperator.Login(loginRequest.Username, loginRequest.Password, ip) token, err := h.imOperator.Login(loginRequest.Username, loginRequest.Password, ip)
if err != nil { if err != nil {
if serviceError, ok := err.(restful.ServiceError); ok { if err == iam.AuthRateLimitExceeded {
resp.WriteHeaderAndEntity(serviceError.Code, errors.New(serviceError.Message)) klog.V(4).Infoln(err)
resp.WriteHeaderAndEntity(http.StatusTooManyRequests, err)
return return
} }
klog.V(4).Infoln(err)
resp.WriteHeaderAndEntity(http.StatusUnauthorized, err) resp.WriteHeaderAndEntity(http.StatusUnauthorized, err)
return return
} }
...@@ -113,14 +133,16 @@ func (h *iamHandler) Login(req *restful.Request, resp *restful.Response) { ...@@ -113,14 +133,16 @@ func (h *iamHandler) Login(req *restful.Request, resp *restful.Response) {
} }
func (h *iamHandler) CreateUser(req *restful.Request, resp *restful.Response) { func (h *iamHandler) CreateUser(req *restful.Request, resp *restful.Response) {
var createRequest iamv1alpha2.UserCreateRequest var createRequest iamv1alpha2.CreateUserRequest
err := req.ReadEntity(&createRequest) err := req.ReadEntity(&createRequest)
if err != nil { if err != nil {
klog.V(4).Infoln(err)
api.HandleBadRequest(resp, err) api.HandleBadRequest(resp, err)
return return
} }
if err := createRequest.Validate(); err != nil { if err := createRequest.Validate(); err != nil {
klog.V(4).Infoln(err)
api.HandleBadRequest(resp, err) api.HandleBadRequest(resp, err)
return return
} }
...@@ -128,200 +150,383 @@ func (h *iamHandler) CreateUser(req *restful.Request, resp *restful.Response) { ...@@ -128,200 +150,383 @@ func (h *iamHandler) CreateUser(req *restful.Request, resp *restful.Response) {
created, err := h.imOperator.CreateUser(createRequest.User) created, err := h.imOperator.CreateUser(createRequest.User)
if err != nil { if err != nil {
if ldap.IsErrorWithCode(err, ldap.LDAPResultEntryAlreadyExists) { if err == iam.UserAlreadyExists {
resp.WriteHeaderAndEntity(http.StatusConflict, kserr.Wrap(err)) klog.V(4).Infoln(err)
resp.WriteHeaderAndEntity(http.StatusConflict, err)
return return
} }
klog.Errorln(err)
api.HandleInternalError(resp, err) api.HandleInternalError(resp, err)
return return
} }
err := h.amOperator.CreateClusterRoleBinding(created.Username, createRequest.ClusterRole) err = h.amOperator.CreateClusterRoleBinding(created.Username, createRequest.ClusterRole)
if err != nil { if err != nil {
klog.Errorln(err)
api.HandleInternalError(resp, err) api.HandleInternalError(resp, err)
return return
} }
resp.WriteEntity(created) resp.WriteEntity(created)
} }
func (h *iamHandler) ListRoleUsers(req *restful.Request, resp *restful.Response) { func (h *iamHandler) DeleteUser(req *restful.Request, resp *restful.Response) {
role := req.PathParameter("role") username := req.PathParameter("user")
namespace := req.PathParameter("namespace") operator := req.HeaderParameter(constants.UserNameHeader)
roleBindings, err := h.amOperator.ListRoleBindings(namespace, role) if operator == username {
err := errors.New("cannot delete yourself")
klog.V(4).Infoln(err)
api.HandleForbidden(resp, err)
return
}
err := h.amOperator.UnBindAllRoles(username)
if err != nil { if err != nil {
klog.Errorln(err)
api.HandleInternalError(resp, err) api.HandleInternalError(resp, err)
return return
} }
result := make([]*iam.User, 0)
for _, roleBinding := range roleBindings { err = h.imOperator.DeleteUser(username)
for _, subject := range roleBinding.Subjects {
if subject.Kind == rbacv1.UserKind { // TODO release user resources
user, err := h.imOperator.GetUserInfo(subject.Name) if err != nil {
// skip if user not exist klog.Errorln(err)
if ldap.IsErrorWithCode(err, ldap.LDAPResultNoSuchObject) { api.HandleInternalError(resp, err)
continue return
} }
if err != nil {
api.HandleInternalError(resp, err) resp.WriteEntity(apierr.None)
return }
}
result = append(result, user) func (h *iamHandler) ModifyUser(request *restful.Request, response *restful.Response) {
}
username := request.PathParameter("user")
operator := request.HeaderParameter(constants.UserNameHeader)
var modifyUserRequest iamv1alpha2.ModifyUserRequest
err := request.ReadEntity(&modifyUserRequest)
if err != nil {
klog.V(4).Infoln(err)
api.HandleBadRequest(response, err)
return
}
if username != modifyUserRequest.Username {
err = fmt.Errorf("the name of user (%s) does not match the name on the URL (%s)", modifyUserRequest.Username, username)
klog.V(4).Infoln(err)
api.HandleBadRequest(response, err)
return
}
if err = modifyUserRequest.Validate(); err != nil {
klog.V(4).Infoln(err)
api.HandleBadRequest(response, err)
return
}
// change password by self
if operator == modifyUserRequest.Username && modifyUserRequest.Password != "" {
}
result, err := h.imOperator.ModifyUser(modifyUserRequest.User)
if err != nil {
klog.Errorln(err)
api.HandleInternalError(response, err)
return
}
// TODO modify cluster role
response.WriteEntity(result)
}
func (h *iamHandler) DescribeUser(req *restful.Request, resp *restful.Response) {
username := req.PathParameter("user")
user, err := h.imOperator.DescribeUser(username)
if err != nil {
if err == iam.UserNotExists {
klog.V(4).Infoln(err)
api.HandleNotFound(resp, err)
return
} }
klog.Errorln(err)
api.HandleInternalError(resp, err)
return
}
// TODO append more user info
clusterRole, err := h.amOperator.GetClusterRole(username)
if err != nil {
klog.Errorln(err)
api.HandleInternalError(resp, err)
return
}
result := iamv1alpha2.UserDetail{
User: user,
ClusterRole: clusterRole.Name,
} }
resp.WriteEntity(result) resp.WriteEntity(result)
} }
func (h *iamHandler) ListClusterRoles(req *restful.Request, resp *restful.Response) { func (h *iamHandler) ListUsers(req *restful.Request, resp *restful.Response) {
conditions, err := params.ParseConditions(req.QueryParameter(params.ConditionsParam))
orderBy := req.QueryParameter(params.OrderByParam) limit, offset := params.ParsePaging(req)
limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam)) orderBy := params.GetStringValueWithDefault(req, params.OrderByParam, v1alpha2.CreateTime)
reverse := params.ParseReverse(req) reverse := params.GetBoolValueWithDefault(req, params.ReverseParam, true)
conditions, err := params.ParseConditions(req)
if err != nil { if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) klog.V(4).Infoln(err)
api.HandleBadRequest(resp, err)
return return
} }
result, err := iam.ListClusterRoles(conditions, orderBy, reverse, limit, offset) result, err := h.imOperator.ListUsers(conditions, orderBy, reverse, limit, offset)
if err != nil { if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) klog.Errorln(err)
api.HandleInternalError(resp, err)
return return
} }
resp.WriteAsJson(result) resp.WriteEntity(result)
}
func (h *iamHandler) ListUserRoles(req *restful.Request, resp *restful.Response) {
username := req.PathParameter("user")
roles, err := h.imOperator.GetUserRoles(username)
if err != nil {
klog.Errorln(err)
api.HandleInternalError(resp, err)
return
}
resp.WriteEntity(roles)
} }
func (h *iamHandler) ListRoles(req *restful.Request, resp *restful.Response) { func (h *iamHandler) ListRoles(req *restful.Request, resp *restful.Response) {
namespace := req.PathParameter("namespace") namespace := req.PathParameter("namespace")
conditions, err := params.ParseConditions(req.QueryParameter(params.ConditionsParam)) limit, offset := params.ParsePaging(req)
orderBy := req.QueryParameter(params.OrderByParam) orderBy := params.GetStringValueWithDefault(req, params.OrderByParam, v1alpha2.CreateTime)
limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam)) reverse := params.GetBoolValueWithDefault(req, params.ReverseParam, true)
reverse := params.ParseReverse(req) conditions, err := params.ParseConditions(req)
if err != nil { if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) klog.V(4).Infoln(err)
api.HandleBadRequest(resp, err)
return return
} }
result, err := iam.ListRoles(namespace, conditions, orderBy, reverse, limit, offset) result, err := h.amOperator.ListRoles(namespace, conditions, orderBy, reverse, limit, offset)
if err != nil { if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) klog.Errorln(err)
api.HandleInternalError(resp, err)
return return
} }
resp.WriteAsJson(result) resp.WriteAsJson(result)
} }
func (h *iamHandler) ListClusterRoles(req *restful.Request, resp *restful.Response) {
limit, offset := params.ParsePaging(req)
orderBy := params.GetStringValueWithDefault(req, params.OrderByParam, v1alpha2.CreateTime)
reverse := params.GetBoolValueWithDefault(req, params.ReverseParam, true)
conditions, err := params.ParseConditions(req)
// List users by namespace if err != nil {
func (h *iamHandler) ListNamespaceUsers(req *restful.Request, resp *restful.Response) { klog.V(4).Infoln(err)
api.HandleBadRequest(resp, err)
return
}
result, err := h.amOperator.ListClusterRoles(conditions, orderBy, reverse, limit, offset)
if err != nil {
klog.Errorln(err)
api.HandleInternalError(resp, err)
return
}
resp.WriteEntity(result)
}
func (h *iamHandler) ListRoleUsers(req *restful.Request, resp *restful.Response) {
role := req.PathParameter("role")
namespace := req.PathParameter("namespace") namespace := req.PathParameter("namespace")
users, err := iam.NamespaceUsers(namespace) roleBindings, err := h.amOperator.ListRoleBindings(namespace, role)
if err != nil { if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) klog.Errorln(err)
api.HandleInternalError(resp, err)
return return
} }
result := make([]*iam.User, 0)
for _, roleBinding := range roleBindings {
for _, subject := range roleBinding.Subjects {
if subject.Kind == rbacv1.UserKind {
user, err := h.imOperator.DescribeUser(subject.Name)
// skip if user not exist
if ldap.IsErrorWithCode(err, ldap.LDAPResultNoSuchObject) {
continue
}
if err != nil {
klog.Errorln(err)
api.HandleInternalError(resp, err)
return
}
result = append(result, user)
}
}
}
// sort by time by default resp.WriteEntity(result)
sort.Slice(users, func(i, j int) bool {
return users[i].RoleBindTime.After(*users[j].RoleBindTime)
})
resp.WriteAsJson(users)
} }
func (h *iamHandler) ListUserRoles(req *restful.Request, resp *restful.Response) { // List users by namespace
func (h *iamHandler) ListNamespaceUsers(req *restful.Request, resp *restful.Response) {
username := req.PathParameter("user") namespace := req.PathParameter("namespace")
roles, err := iam.GetUserRoles("", username) roleBindings, err := h.amOperator.ListRoleBindings(namespace, "")
if err != nil { if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) klog.Errorln(err)
api.HandleInternalError(resp, err)
return return
} }
_, clusterRoles, err := iam.GetUserClusterRoles(username) result := make([]*iam.User, 0)
for _, roleBinding := range roleBindings {
for _, subject := range roleBinding.Subjects {
if subject.Kind == rbacv1.UserKind {
user, err := h.imOperator.DescribeUser(subject.Name)
// skip if user not exist
if ldap.IsErrorWithCode(err, ldap.LDAPResultNoSuchObject) {
continue
}
if err != nil {
klog.Errorln(err)
api.HandleInternalError(resp, err)
return
}
result = append(result, user)
}
}
}
resp.WriteEntity(result)
}
func (h *iamHandler) ListClusterRoleUsers(req *restful.Request, resp *restful.Response) {
clusterRole := req.PathParameter("clusterrole")
clusterRoleBindings, err := h.amOperator.ListClusterRoleBindings(clusterRole)
if err != nil { if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) klog.Errorln(err)
api.HandleInternalError(resp, err)
return return
} }
roleList := RoleList{} result := make([]*iam.User, 0)
roleList.Roles = roles for _, roleBinding := range clusterRoleBindings {
roleList.ClusterRoles = clusterRoles for _, subject := range roleBinding.Subjects {
if subject.Kind == rbacv1.UserKind {
user, err := h.imOperator.DescribeUser(subject.Name)
// skip if user not exist
if ldap.IsErrorWithCode(err, ldap.LDAPResultNoSuchObject) {
continue
}
if err != nil {
klog.Errorln(err)
api.HandleInternalError(resp, err)
return
}
result = append(result, user)
}
}
}
resp.WriteAsJson(roleList) resp.WriteEntity(result)
} }
func (h *iamHandler) RulesMapping(req *restful.Request, resp *restful.Response) { func (h *iamHandler) RulesMapping(req *restful.Request, resp *restful.Response) {
rules := policy.RoleRuleMapping rules := policy.RoleRuleMapping
resp.WriteAsJson(rules) resp.WriteEntity(rules)
} }
func (h *iamHandler) ClusterRulesMapping(req *restful.Request, resp *restful.Response) { func (h *iamHandler) ClusterRulesMapping(req *restful.Request, resp *restful.Response) {
rules := policy.ClusterRoleRuleMapping rules := policy.ClusterRoleRuleMapping
resp.WriteAsJson(rules) resp.WriteEntity(rules)
} }
func (h *iamHandler) ListClusterRoleRules(req *restful.Request, resp *restful.Response) { func (h *iamHandler) ListClusterRoleRules(req *restful.Request, resp *restful.Response) {
clusterRoleName := req.PathParameter("clusterrole") clusterRole := req.PathParameter("clusterrole")
rules, err := iam.GetClusterRoleSimpleRules(clusterRoleName) rules, err := h.amOperator.GetClusterRoleSimpleRules(clusterRole)
if err != nil { if err != nil {
resp.WriteError(http.StatusInternalServerError, err) klog.Errorln(err)
api.HandleInternalError(resp, err)
return return
} }
resp.WriteAsJson(rules) resp.WriteEntity(rules)
} }
func (h *iamHandler) ListClusterRoleUsers(req *restful.Request, resp *restful.Response) { func (h *iamHandler) ListRoleRules(req *restful.Request, resp *restful.Response) {
clusterRoleName := req.PathParameter("clusterrole") namespace := req.PathParameter("namespace")
conditions, err := params.ParseConditions(req.QueryParameter(params.ConditionsParam)) role := req.PathParameter("role")
orderBy := req.QueryParameter(params.OrderByParam)
limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam)) rules, err := h.amOperator.GetRoleSimpleRules(namespace, role)
reverse := params.ParseReverse(req)
if err != nil { if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) klog.Errorln(err)
api.HandleInternalError(resp, err)
return return
} }
result, err := iam.ListClusterRoleUsers(clusterRoleName, conditions, orderBy, reverse, limit, offset) resp.WriteEntity(rules)
}
if err != nil { func (h *iamHandler) ListWorkspaceRoles(request *restful.Request, response *restful.Response) {
if k8serr.IsNotFound(err) { panic("implement me")
resp.WriteError(http.StatusNotFound, err) }
} else {
resp.WriteError(http.StatusInternalServerError, err)
}
return
}
resp.WriteAsJson(result) func (h *iamHandler) DescribeWorkspaceRole(request *restful.Request, response *restful.Response) {
panic("implement me")
} }
func (h *iamHandler) ListRoleRules(req *restful.Request, resp *restful.Response) { func (h *iamHandler) ListWorkspaceRoleRules(request *restful.Request, response *restful.Response) {
namespaceName := req.PathParameter("namespace") panic("implement me")
roleName := req.PathParameter("role") }
func (h *iamHandler) ListWorkspaceUsers(request *restful.Request, response *restful.Response) {
panic("implement me")
}
rules, err := iam.GetRoleSimpleRules(namespaceName, roleName) func (h *iamHandler) InviteUser(request *restful.Request, response *restful.Response) {
panic("implement me")
}
if err != nil { func (h *iamHandler) RemoveUser(request *restful.Request, response *restful.Response) {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) panic("implement me")
return }
}
resp.WriteAsJson(rules) func (h *iamHandler) DescribeWorkspaceUser(request *restful.Request, response *restful.Response) {
panic("implement me")
} }
...@@ -24,104 +24,25 @@ import ( ...@@ -24,104 +24,25 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"kubesphere.io/kubesphere/pkg/api" "kubesphere.io/kubesphere/pkg/api"
iamv1alpha2 "kubesphere.io/kubesphere/pkg/api/iam/v1alpha2" iamv1alpha2 "kubesphere.io/kubesphere/pkg/api/iam/v1alpha2"
"kubesphere.io/kubesphere/pkg/apiserver/iam"
"kubesphere.io/kubesphere/pkg/apiserver/runtime" "kubesphere.io/kubesphere/pkg/apiserver/runtime"
"kubesphere.io/kubesphere/pkg/constants" "kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/models" "kubesphere.io/kubesphere/pkg/models"
iam2 "kubesphere.io/kubesphere/pkg/models/iam" "kubesphere.io/kubesphere/pkg/models/iam"
"kubesphere.io/kubesphere/pkg/models/iam/policy" "kubesphere.io/kubesphere/pkg/models/iam/policy"
"kubesphere.io/kubesphere/pkg/server/errors" "kubesphere.io/kubesphere/pkg/server/errors"
"kubesphere.io/kubesphere/pkg/simple/client/k8s"
ldappool "kubesphere.io/kubesphere/pkg/simple/client/ldap"
"net/http" "net/http"
"time"
) )
const GroupName = "iam.kubesphere.io" const GroupName = "iam.kubesphere.io"
var GroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha2"} var GroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha2"}
var ( func AddToContainer(c *restful.Container, k8sClient k8s.Client, ldapClient ldappool.Client, options iam.Config) error {
WebServiceBuilder = runtime.NewContainerBuilder(addWebService)
AddToContainer = WebServiceBuilder.AddToContainer
)
type UserUpdateRequest struct {
Username string `json:"username" description:"username"`
Email string `json:"email" description:"email address"`
Lang string `json:"lang" description:"user's language setting, default is zh-CN"`
Description string `json:"description" description:"user's description"`
Password string `json:"password,omitempty" description:"this is necessary if you need to change your password"`
CurrentPassword string `json:"current_password,omitempty" description:"this is necessary if you need to change your password"`
ClusterRole string `json:"cluster_role" description:"user's cluster role"`
}
type CreateUserRequest struct {
Username string `json:"username" description:"username"`
Email string `json:"email" description:"email address"`
Lang string `json:"lang,omitempty" description:"user's language setting, default is zh-CN"`
Description string `json:"description" description:"user's description"`
Password string `json:"password" description:"password'"`
ClusterRole string `json:"cluster_role" description:"user's cluster role"`
}
type UserList struct {
Items []struct {
Username string `json:"username" description:"username"`
Email string `json:"email" description:"email address"`
Lang string `json:"lang,omitempty" description:"user's language setting, default is zh-CN"`
Description string `json:"description" description:"user's description"`
ClusterRole string `json:"cluster_role" description:"user's cluster role"`
CreateTime time.Time `json:"create_time" description:"user creation time"`
LastLoginTime time.Time `json:"last_login_time" description:"last login time"`
} `json:"items" description:"paging data"`
TotalCount int `json:"total_count" description:"total count"`
}
type NamespacedUser struct {
Username string `json:"username" description:"username"`
Email string `json:"email" description:"email address"`
Lang string `json:"lang,omitempty" description:"user's language setting, default is zh-CN"`
Description string `json:"description" description:"user's description"`
Role string `json:"role" description:"user's role in the specified namespace"`
RoleBinding string `json:"role_binding" description:"user's role binding name in the specified namespace"`
RoleBindTime string `json:"role_bind_time" description:"user's role binding time"`
CreateTime time.Time `json:"create_time" description:"user creation time"`
LastLoginTime time.Time `json:"last_login_time" description:"last login time"`
}
type ClusterRoleList struct {
Items []rbacv1.ClusterRole `json:"items" description:"paging data"`
TotalCount int `json:"total_count" description:"total count"`
}
type LoginLog struct {
LoginTime string `json:"login_time" description:"last login time"`
LoginIP string `json:"login_ip" description:"last login ip"`
}
type RoleList struct {
Items []rbacv1.Role `json:"items" description:"paging data"`
TotalCount int `json:"total_count" description:"total count"`
}
type InviteUserRequest struct {
Username string `json:"username" description:"username"`
WorkspaceRole string `json:"workspace_role" description:"user's workspace role'"`
}
type DescribeWorkspaceUserResponse struct {
Username string `json:"username" description:"username"`
Email string `json:"email" description:"email address"`
Lang string `json:"lang" description:"user's language setting, default is zh-CN"`
Description string `json:"description" description:"user's description"`
ClusterRole string `json:"cluster_role" description:"user's cluster role"`
WorkspaceRole string `json:"workspace_role" description:"user's workspace role"`
CreateTime time.Time `json:"create_time" description:"user creation time"`
LastLoginTime time.Time `json:"last_login_time" description:"last login time"`
}
func addWebService(c *restful.Container) error {
ws := runtime.NewWebService(GroupVersion) ws := runtime.NewWebService(GroupVersion)
handler := newIAMHandler() handler := newIAMHandler(k8sClient, ldapClient, options)
ws.Route(ws.POST("/authenticate"). ws.Route(ws.POST("/authenticate").
To(handler.TokenReviewHandler). To(handler.TokenReviewHandler).
...@@ -138,152 +59,132 @@ func addWebService(c *restful.Container) error { ...@@ -138,152 +59,132 @@ func addWebService(c *restful.Container) error {
ws.Route(ws.POST("/users"). ws.Route(ws.POST("/users").
To(handler.CreateUser). To(handler.CreateUser).
Doc("Create a user account."). Doc("Create a user account.").
Reads(CreateUserRequest{}). Reads(iamv1alpha2.CreateUserRequest{}).
Returns(http.StatusOK, api.StatusOK, errors.Error{}). Returns(http.StatusOK, api.StatusOK, iamv1alpha2.UserDetail{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.IdentityManagementTag})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.IdentityManagementTag}))
ws.Route(ws.GET("/users/{user}").
To(iam.DescribeUser).
Doc("Describe the specified user.").
Param(ws.PathParameter("user", "username")).
Returns(http.StatusOK, api.StatusOK, iam2.User{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.IdentityManagementTag}))
ws.Route(ws.DELETE("/users/{user}"). ws.Route(ws.DELETE("/users/{user}").
To(iam.DeleteUser). To(handler.DeleteUser).
Doc("Delete the specified user."). Doc("Delete the specified user.").
Param(ws.PathParameter("user", "username")). Param(ws.PathParameter("user", "username")).
Returns(http.StatusOK, api.StatusOK, errors.Error{}). Returns(http.StatusOK, api.StatusOK, errors.Error{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.IdentityManagementTag})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.IdentityManagementTag}))
ws.Route(ws.PUT("/users/{user}"). ws.Route(ws.PUT("/users/{user}").
To(iam.UpdateUser). To(handler.ModifyUser).
Doc("Update information about the specified user."). Doc("Update information about the specified user.").
Param(ws.PathParameter("user", "username")). Param(ws.PathParameter("user", "username")).
Reads(UserUpdateRequest{}). Reads(iamv1alpha2.ModifyUserRequest{}).
Returns(http.StatusOK, api.StatusOK, errors.Error{}). Returns(http.StatusOK, api.StatusOK, errors.Error{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.IdentityManagementTag})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.IdentityManagementTag}))
ws.Route(ws.GET("/users/{user}/logs"). ws.Route(ws.GET("/users/{user}").
To(iam.UserLoginLogs). To(handler.DescribeUser).
Doc("Retrieve the \"login logs\" for the specified user."). Doc("Describe the specified user.").
Param(ws.PathParameter("user", "username")). Param(ws.PathParameter("user", "username")).
Returns(http.StatusOK, api.StatusOK, LoginLog{}). Returns(http.StatusOK, api.StatusOK, iamv1alpha2.UserDetail{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.IdentityManagementTag})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.IdentityManagementTag}))
ws.Route(ws.GET("/users"). ws.Route(ws.GET("/users").
To(iam.ListUsers). To(handler.ListUsers).
Doc("List all users."). Doc("List all users.").
Returns(http.StatusOK, api.StatusOK, UserList{}). Returns(http.StatusOK, api.StatusOK, iamv1alpha2.ListUserResponse{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.IdentityManagementTag})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.IdentityManagementTag}))
ws.Route(ws.GET("/users/{user}/roles"). ws.Route(ws.GET("/users/{user}/roles").
To(iam.ListUserRoles). To(handler.ListUserRoles).
Doc("Retrieve all the roles that are assigned to the specified user."). Doc("Retrieve all the roles that are assigned to the specified user.").
Param(ws.PathParameter("user", "username")). Param(ws.PathParameter("user", "username")).
Returns(http.StatusOK, api.StatusOK, iam.RoleList{}). Returns(http.StatusOK, api.StatusOK, []*rbacv1.Role{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag}))
ws.Route(ws.GET("/namespaces/{namespace}/roles"). ws.Route(ws.GET("/namespaces/{namespace}/roles").
To(iam.ListRoles). To(handler.ListRoles).
Doc("Retrieve the roles that are assigned to the user in the specified namespace."). Doc("Retrieve the roles that are assigned to the user in the specified namespace.").
Param(ws.PathParameter("namespace", "kubernetes namespace")). Param(ws.PathParameter("namespace", "kubernetes namespace")).
Returns(http.StatusOK, api.StatusOK, RoleList{}). Returns(http.StatusOK, api.StatusOK, models.PageableResponse{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag}))
ws.Route(ws.GET("/clusterroles"). ws.Route(ws.GET("/clusterroles").
To(iam.ListClusterRoles). To(handler.ListClusterRoles).
Doc("List all cluster roles."). Doc("List all cluster roles.").
Returns(http.StatusOK, api.StatusOK, ClusterRoleList{}). Returns(http.StatusOK, api.StatusOK, models.PageableResponse{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag}))
ws.Route(ws.GET("/namespaces/{namespace}/roles/{role}/users"). ws.Route(ws.GET("/namespaces/{namespace}/roles/{role}/users").
To(handler.ListRoleUsers). To(handler.ListRoleUsers).
Doc("Retrieve the users that are bound to the role in the specified namespace."). Doc("Retrieve the users that are bound to the role in the specified namespace.").
Param(ws.PathParameter("namespace", "kubernetes namespace")). Param(ws.PathParameter("namespace", "kubernetes namespace")).
Param(ws.PathParameter("role", "role name")). Param(ws.PathParameter("role", "role name")).
Returns(http.StatusOK, api.StatusOK, []NamespacedUser{}). Returns(http.StatusOK, api.StatusOK, []iamv1alpha2.ListUserResponse{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag}))
ws.Route(ws.GET("/namespaces/{namespace}/users"). ws.Route(ws.GET("/namespaces/{namespace}/users").
To(iam.ListNamespaceUsers). To(handler.ListNamespaceUsers).
Doc("List all users in the specified namespace."). Doc("List all users in the specified namespace.").
Param(ws.PathParameter("namespace", "kubernetes namespace")). Param(ws.PathParameter("namespace", "kubernetes namespace")).
Returns(http.StatusOK, api.StatusOK, []NamespacedUser{}). Returns(http.StatusOK, api.StatusOK, []iamv1alpha2.ListUserResponse{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag}))
ws.Route(ws.GET("/clusterroles/{clusterrole}/users"). ws.Route(ws.GET("/clusterroles/{clusterrole}/users").
To(iam.ListClusterRoleUsers). To(handler.ListClusterRoleUsers).
Doc("List all users that are bound to the specified cluster role."). Doc("List all users that are bound to the specified cluster role.").
Param(ws.PathParameter("clusterrole", "cluster role name")). Param(ws.PathParameter("clusterrole", "cluster role name")).
Returns(http.StatusOK, api.StatusOK, UserList{}). Returns(http.StatusOK, api.StatusOK, []iamv1alpha2.ListUserResponse{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag}))
ws.Route(ws.GET("/clusterroles/{clusterrole}/rules"). ws.Route(ws.GET("/clusterroles/{clusterrole}/rules").
To(iam.ListClusterRoleRules). To(handler.ListClusterRoleRules).
Doc("List all policy rules of the specified cluster role."). Doc("List all policy rules of the specified cluster role.").
Param(ws.PathParameter("clusterrole", "cluster role name")). Param(ws.PathParameter("clusterrole", "cluster role name")).
Returns(http.StatusOK, api.StatusOK, []iam2.SimpleRule{}). Returns(http.StatusOK, api.StatusOK, []iam.SimpleRule{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag}))
ws.Route(ws.GET("/namespaces/{namespace}/roles/{role}/rules"). ws.Route(ws.GET("/namespaces/{namespace}/roles/{role}/rules").
To(iam.ListRoleRules). To(handler.ListRoleRules).
Doc("List all policy rules of the specified role in the given namespace."). Doc("List all policy rules of the specified role in the given namespace.").
Param(ws.PathParameter("namespace", "kubernetes namespace")). Param(ws.PathParameter("namespace", "kubernetes namespace")).
Param(ws.PathParameter("role", "role name")). Param(ws.PathParameter("role", "role name")).
Returns(http.StatusOK, api.StatusOK, []iam2.SimpleRule{}). Returns(http.StatusOK, api.StatusOK, []iam.SimpleRule{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag}))
ws.Route(ws.GET("/devops/{devops}/roles/{role}/rules").
To(iam.ListDevopsRoleRules).
Doc("List all policy rules of the specified role in the given devops project.").
Param(ws.PathParameter("devops", "devops project ID")).
Param(ws.PathParameter("role", "devops role name")).
Returns(http.StatusOK, api.StatusOK, []iam2.SimpleRule{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag}))
ws.Route(ws.GET("/rulesmapping/clusterroles"). ws.Route(ws.GET("/rulesmapping/clusterroles").
To(iam.ClusterRulesMapping). To(handler.ClusterRulesMapping).
Doc("Get the mapping relationships between cluster roles and policy rules."). Doc("Get the mapping relationships between cluster roles and policy rules.").
Returns(http.StatusOK, api.StatusOK, policy.ClusterRoleRuleMapping). Returns(http.StatusOK, api.StatusOK, policy.ClusterRoleRuleMapping).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag}))
ws.Route(ws.GET("/rulesmapping/roles"). ws.Route(ws.GET("/rulesmapping/roles").
To(iam.RulesMapping). To(handler.RulesMapping).
Doc("Get the mapping relationships between namespaced roles and policy rules."). Doc("Get the mapping relationships between namespaced roles and policy rules.").
Returns(http.StatusOK, api.StatusOK, policy.RoleRuleMapping). Returns(http.StatusOK, api.StatusOK, policy.RoleRuleMapping).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag}))
ws.Route(ws.GET("/workspaces/{workspace}/roles"). ws.Route(ws.GET("/workspaces/{workspace}/roles").
To(iam.ListWorkspaceRoles). To(handler.ListWorkspaceRoles).
Doc("List all workspace roles."). Doc("List all workspace roles.").
Param(ws.PathParameter("workspace", "workspace name")). Param(ws.PathParameter("workspace", "workspace name")).
Returns(http.StatusOK, api.StatusOK, ClusterRoleList{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag}))
ws.Route(ws.GET("/workspaces/{workspace}/roles/{role}"). ws.Route(ws.GET("/workspaces/{workspace}/roles/{role}").
To(iam.DescribeWorkspaceRole). To(handler.DescribeWorkspaceRole).
Doc("Describe the workspace role."). Doc("Describe the workspace role.").
Param(ws.PathParameter("workspace", "workspace name")). Param(ws.PathParameter("workspace", "workspace name")).
Param(ws.PathParameter("role", "workspace role name")). Param(ws.PathParameter("role", "workspace role name")).
Returns(http.StatusOK, api.StatusOK, rbacv1.ClusterRole{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag}))
ws.Route(ws.GET("/workspaces/{workspace}/roles/{role}/rules"). ws.Route(ws.GET("/workspaces/{workspace}/roles/{role}/rules").
To(iam.ListWorkspaceRoleRules). To(handler.ListWorkspaceRoleRules).
Doc("List all policy rules of the specified workspace role."). Doc("List all policy rules of the specified workspace role.").
Param(ws.PathParameter("workspace", "workspace name")). Param(ws.PathParameter("workspace", "workspace name")).
Param(ws.PathParameter("role", "workspace role name")). Param(ws.PathParameter("role", "workspace role name")).
Returns(http.StatusOK, api.StatusOK, []iam2.SimpleRule{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag}))
ws.Route(ws.GET("/workspaces/{workspace}/members"). ws.Route(ws.GET("/workspaces/{workspace}/members").
To(iam.ListWorkspaceUsers). To(handler.ListWorkspaceUsers).
Doc("List all members in the specified workspace."). Doc("List all members in the specified workspace.").
Param(ws.PathParameter("workspace", "workspace name")). Param(ws.PathParameter("workspace", "workspace name")).
Returns(http.StatusOK, api.StatusOK, UserList{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag}))
ws.Route(ws.POST("/workspaces/{workspace}/members"). ws.Route(ws.POST("/workspaces/{workspace}/members").
To(iam.InviteUser). To(handler.InviteUser).
Doc("Invite a member to the specified workspace."). Doc("Invite a member to the specified workspace.").
Param(ws.PathParameter("workspace", "workspace name")). Param(ws.PathParameter("workspace", "workspace name")).
Reads(InviteUserRequest{}).
Returns(http.StatusOK, api.StatusOK, errors.Error{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag}))
ws.Route(ws.DELETE("/workspaces/{workspace}/members/{member}"). ws.Route(ws.DELETE("/workspaces/{workspace}/members/{member}").
To(iam.RemoveUser). To(handler.RemoveUser).
Doc("Remove the specified member from the workspace."). Doc("Remove the specified member from the workspace.").
Param(ws.PathParameter("workspace", "workspace name")). Param(ws.PathParameter("workspace", "workspace name")).
Param(ws.PathParameter("member", "username")). Param(ws.PathParameter("member", "username")).
Returns(http.StatusOK, api.StatusOK, errors.Error{}). Returns(http.StatusOK, api.StatusOK, errors.Error{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag}))
ws.Route(ws.GET("/workspaces/{workspace}/members/{member}"). ws.Route(ws.GET("/workspaces/{workspace}/members/{member}").
To(iam.DescribeWorkspaceUser). To(handler.DescribeWorkspaceUser).
Doc("Describe the specified user in the given workspace."). Doc("Describe the specified user in the given workspace.").
Param(ws.PathParameter("workspace", "workspace name")). Param(ws.PathParameter("workspace", "workspace name")).
Param(ws.PathParameter("member", "username")). Param(ws.PathParameter("member", "username")).
Returns(http.StatusOK, api.StatusOK, DescribeWorkspaceUserResponse{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag}))
c.Add(ws) c.Add(ws)
return nil return nil
......
...@@ -13,22 +13,25 @@ import ( ...@@ -13,22 +13,25 @@ import (
servicemeshv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/servicemesh/metrics/v1alpha2" servicemeshv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/servicemesh/metrics/v1alpha2"
tenantv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/tenant/v1alpha2" tenantv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/tenant/v1alpha2"
terminalv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/terminal/v1alpha2" terminalv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/terminal/v1alpha2"
"kubesphere.io/kubesphere/pkg/models/iam"
"kubesphere.io/kubesphere/pkg/simple/client/k8s" "kubesphere.io/kubesphere/pkg/simple/client/k8s"
ldappool "kubesphere.io/kubesphere/pkg/simple/client/ldap"
"kubesphere.io/kubesphere/pkg/simple/client/mysql"
op "kubesphere.io/kubesphere/pkg/simple/client/openpitrix"
) )
func InstallAPIs(container *restful.Container, client k8s.Client) { func InstallAPIs(container *restful.Container, client k8s.Client, op op.Client, db *mysql.Database) {
urlruntime.Must(servicemeshv1alpha2.AddToContainer(container)) urlruntime.Must(servicemeshv1alpha2.AddToContainer(container))
urlruntime.Must(devopsv1alpha2.AddToContainer(container)) urlruntime.Must(devopsv1alpha2.AddToContainer(container))
urlruntime.Must(loggingv1alpha2.AddToContainer(container)) urlruntime.Must(loggingv1alpha2.AddToContainer(container))
urlruntime.Must(monitoringv1alpha2.AddToContainer(container)) urlruntime.Must(monitoringv1alpha2.AddToContainer(container))
urlruntime.Must(openpitrixv1.AddToContainer(container)) urlruntime.Must(openpitrixv1.AddToContainer(container, client, op))
urlruntime.Must(operationsv1alpha2.AddToContainer(container, client)) urlruntime.Must(operationsv1alpha2.AddToContainer(container, client))
urlruntime.Must(resourcesv1alpha2.AddToContainer(container, client)) urlruntime.Must(resourcesv1alpha2.AddToContainer(container, client))
urlruntime.Must(tenantv1alpha2.AddToContainer(container)) urlruntime.Must(tenantv1alpha2.AddToContainer(container, client, db))
urlruntime.Must(terminalv1alpha2.AddToContainer(container)) urlruntime.Must(terminalv1alpha2.AddToContainer(container, client))
urlruntime.Must(tenantv1alpha2.AddToContainer(container))
} }
func InstallAuthorizationAPIs(container *restful.Container) { func InstallAuthorizationAPIs(container *restful.Container, k8sClient k8s.Client, ldapClient ldappool.Client, imOptions iam.Config) {
urlruntime.Must(iamv1alpha2.AddToContainer(container)) urlruntime.Must(iamv1alpha2.AddToContainer(container, k8sClient, ldapClient, imOptions))
} }
此差异已折叠。
/*
Copyright 2019 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 v1alpha2
import (
"github.com/emicklei/go-restful"
"k8s.io/klog"
"kubesphere.io/kubesphere/pkg/api"
devopsv1alpha2 "kubesphere.io/kubesphere/pkg/api/devops/v1alpha2"
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/server/errors"
"kubesphere.io/kubesphere/pkg/server/params"
"net/http"
)
func (h *tenantHandler) DeleteDevOpsProjectHandler(req *restful.Request, resp *restful.Response) {
projectId := req.PathParameter("devops")
workspaceName := req.PathParameter("workspace")
username := req.HeaderParameter(constants.UserNameHeader)
_, err := h.tenant.GetWorkspace(workspaceName)
if err != nil {
klog.Errorf("%+v", err)
errors.ParseSvcErr(restful.NewError(http.StatusBadRequest, err.Error()), resp)
return
}
err = h.tenant.DeleteDevOpsProject(projectId, username)
if err != nil {
klog.Errorf("%+v", err)
errors.ParseSvcErr(err, resp)
return
}
resp.WriteAsJson(errors.None)
}
func (h *tenantHandler) CreateDevOpsProjectHandler(req *restful.Request, resp *restful.Response) {
workspaceName := req.PathParameter("workspace")
username := req.HeaderParameter(constants.UserNameHeader)
var devops devopsv1alpha2.DevOpsProject
err := req.ReadEntity(&devops)
if err != nil {
klog.Infof("%+v", err)
errors.ParseSvcErr(restful.NewError(http.StatusBadRequest, err.Error()), resp)
return
}
klog.Infoln("create workspace", username, workspaceName, devops)
project, err := h.tenant.CreateDevOpsProject(username, workspaceName, &devops)
if err != nil {
klog.Errorf("%+v", err)
errors.ParseSvcErr(err, resp)
return
}
resp.WriteAsJson(project)
}
func (h *tenantHandler) GetDevOpsProjectsCountHandler(req *restful.Request, resp *restful.Response) {
username := req.HeaderParameter(constants.UserNameHeader)
result, err := h.tenant.GetDevOpsProjectsCount(username)
if err != nil {
klog.Errorf("%+v", err)
errors.ParseSvcErr(err, resp)
return
}
resp.WriteAsJson(struct {
Count uint32 `json:"count"`
}{Count: result})
}
func (h *tenantHandler) ListDevOpsProjectsHandler(req *restful.Request, resp *restful.Response) {
workspace := req.PathParameter("workspace")
username := req.PathParameter("member")
if username == "" {
username = req.HeaderParameter(constants.UserNameHeader)
}
orderBy := req.QueryParameter(params.OrderByParam)
reverse := params.GetBoolValueWithDefault(req, params.ReverseParam, false)
limit, offset := params.ParsePaging(req)
conditions, err := params.ParseConditions(req)
if err != nil {
klog.Errorf("%+v", err)
errors.ParseSvcErr(restful.NewError(http.StatusBadRequest, err.Error()), resp)
return
}
result, err := h.tenant.ListDevOpsProjects(workspace, username, conditions, orderBy, reverse, limit, offset)
if err != nil {
klog.Errorf("%+v", err)
errors.ParseSvcErr(err, resp)
return
}
resp.WriteAsJson(result)
}
func (h *tenantHandler) ListDevOpsRules(req *restful.Request, resp *restful.Response) {
devops := req.PathParameter("devops")
username := req.HeaderParameter(constants.UserNameHeader)
rules, err := h.tenant.GetUserDevOpsSimpleRules(username, devops)
if err != nil {
klog.Errorf("%+v", err)
errors.ParseSvcErr(err, resp)
return
}
resp.WriteAsJson(rules)
}
func (h *tenantHandler) ListDevopsRules(req *restful.Request, resp *restful.Response) {
devops := req.PathParameter("devops")
username := req.HeaderParameter(constants.UserNameHeader)
rules, err := h.tenant.GetUserDevOpsSimpleRules(username, devops)
if err != nil {
api.HandleInternalError(resp, err)
return
}
resp.WriteAsJson(rules)
}
...@@ -11,12 +11,15 @@ import ( ...@@ -11,12 +11,15 @@ import (
loggingv1alpha2 "kubesphere.io/kubesphere/pkg/api/logging/v1alpha2" loggingv1alpha2 "kubesphere.io/kubesphere/pkg/api/logging/v1alpha2"
"kubesphere.io/kubesphere/pkg/apiserver/logging" "kubesphere.io/kubesphere/pkg/apiserver/logging"
"kubesphere.io/kubesphere/pkg/constants" "kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/informers"
"kubesphere.io/kubesphere/pkg/models/iam" "kubesphere.io/kubesphere/pkg/models/iam"
"kubesphere.io/kubesphere/pkg/models/metrics" "kubesphere.io/kubesphere/pkg/models/metrics"
"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"
"kubesphere.io/kubesphere/pkg/server/errors" apierr "kubesphere.io/kubesphere/pkg/server/errors"
"kubesphere.io/kubesphere/pkg/server/params" "kubesphere.io/kubesphere/pkg/server/params"
"kubesphere.io/kubesphere/pkg/simple/client/k8s"
"kubesphere.io/kubesphere/pkg/simple/client/mysql"
"kubesphere.io/kubesphere/pkg/utils/sliceutil" "kubesphere.io/kubesphere/pkg/utils/sliceutil"
"net/http" "net/http"
"strings" "strings"
...@@ -24,24 +27,31 @@ import ( ...@@ -24,24 +27,31 @@ import (
type tenantHandler struct { type tenantHandler struct {
tenant tenant.Interface tenant tenant.Interface
am iam.AccessManagementInterface
} }
func newTenantHandler() *tenantHandler { func newTenantHandler(k8sClient k8s.Client, db *mysql.Database) *tenantHandler {
return &tenantHandler{} factory := informers.NewInformerFactories(k8sClient.Kubernetes(), k8sClient.KubeSphere(), k8sClient.S2i(), k8sClient.Application())
return &tenantHandler{
tenant: tenant.New(k8sClient.Kubernetes(), factory.KubernetesSharedInformerFactory(), factory.KubeSphereSharedInformerFactory(), db),
am: iam.NewAMOperator(factory.KubernetesSharedInformerFactory()),
}
} }
func (h *tenantHandler) ListWorkspaceRules(req *restful.Request, resp *restful.Response) { func (h *tenantHandler) ListWorkspaceRules(req *restful.Request, resp *restful.Response) {
workspace := req.PathParameter("workspace") workspace := req.PathParameter("workspace")
username := req.HeaderParameter(constants.UserNameHeader) username := req.HeaderParameter(constants.UserNameHeader)
rules, err := iam.GetUserWorkspaceSimpleRules(workspace, username) rules, err := h.tenant.GetWorkspaceSimpleRules(workspace, username)
if err != nil { if err != nil {
klog.Errorln(err)
api.HandleInternalError(resp, err) api.HandleInternalError(resp, err)
return return
} }
resp.WriteAsJson(rules) resp.WriteEntity(rules)
} }
func (h *tenantHandler) ListWorkspaces(req *restful.Request, resp *restful.Response) { func (h *tenantHandler) ListWorkspaces(req *restful.Request, resp *restful.Response) {
...@@ -52,6 +62,7 @@ func (h *tenantHandler) ListWorkspaces(req *restful.Request, resp *restful.Respo ...@@ -52,6 +62,7 @@ func (h *tenantHandler) ListWorkspaces(req *restful.Request, resp *restful.Respo
conditions, err := params.ParseConditions(req) conditions, err := params.ParseConditions(req)
if err != nil { if err != nil {
klog.Errorln(err)
api.HandleBadRequest(resp, err) api.HandleBadRequest(resp, err)
return return
} }
...@@ -180,9 +191,49 @@ func (h *tenantHandler) DeleteNamespace(req *restful.Request, resp *restful.Resp ...@@ -180,9 +191,49 @@ func (h *tenantHandler) DeleteNamespace(req *restful.Request, resp *restful.Resp
return return
} }
resp.WriteAsJson(errors.None) resp.WriteAsJson(apierr.None)
}
func (h *tenantHandler) ListDevopsProjects(req *restful.Request, resp *restful.Response) {
workspace := req.PathParameter("workspace")
username := req.PathParameter("member")
if username == "" {
username = req.HeaderParameter(constants.UserNameHeader)
}
orderBy := params.GetStringValueWithDefault(req, params.OrderByParam, v1alpha2.CreateTime)
limit, offset := params.ParsePaging(req)
reverse := params.GetBoolValueWithDefault(req, params.ReverseParam, true)
conditions, err := params.ParseConditions(req)
if err != nil {
api.HandleBadRequest(resp, err)
return
}
conditions.Match["workspace"] = workspace
result, err := h.tenant.ListDevopsProjects(username, conditions, orderBy, reverse, limit, offset)
if err != nil {
api.HandleInternalError(resp, err)
return
}
resp.WriteEntity(result)
} }
func (h *tenantHandler) GetDevOpsProjectsCount(req *restful.Request, resp *restful.Response) {
username := req.HeaderParameter(constants.UserNameHeader)
result, err := h.tenant.ListDevopsProjects(username, nil, "", false, 1, 0)
if err != nil {
api.HandleInternalError(resp, err)
return
}
resp.WriteEntity(struct {
Count int `json:"count"`
}{Count: result.TotalCount})
}
func (h *tenantHandler) DeleteDevopsProject(req *restful.Request, resp *restful.Response) { func (h *tenantHandler) DeleteDevopsProject(req *restful.Request, resp *restful.Response) {
projectId := req.PathParameter("devops") projectId := req.PathParameter("devops")
workspace := req.PathParameter("workspace") workspace := req.PathParameter("workspace")
...@@ -195,21 +246,40 @@ func (h *tenantHandler) DeleteDevopsProject(req *restful.Request, resp *restful. ...@@ -195,21 +246,40 @@ func (h *tenantHandler) DeleteDevopsProject(req *restful.Request, resp *restful.
return return
} }
err = h.tenant.DeleteDevOpsProject(projectId, username) err = h.tenant.DeleteDevOpsProject(username, projectId)
if err != nil { if err != nil {
api.HandleInternalError(resp, err) api.HandleInternalError(resp, err)
return return
} }
resp.WriteAsJson(errors.None) resp.WriteEntity(apierr.None)
}
func (h *tenantHandler) CreateDevopsProject(req *restful.Request, resp *restful.Response) {
} }
func (h *tenantHandler) ListNamespaceRules(req *restful.Request, resp *restful.Response) { func (h *tenantHandler) ListNamespaceRules(req *restful.Request, resp *restful.Response) {
namespace := req.PathParameter("namespace") namespace := req.PathParameter("namespace")
username := req.HeaderParameter(constants.UserNameHeader) username := req.HeaderParameter(constants.UserNameHeader)
rules, err := iam.GetUserNamespaceSimpleRules(namespace, username) rules, err := h.tenant.GetNamespaceSimpleRules(namespace, username)
if err != nil {
api.HandleInternalError(resp, err)
return
}
resp.WriteAsJson(rules)
}
func (h *tenantHandler) ListDevopsRules(req *restful.Request, resp *restful.Response) {
devops := req.PathParameter("devops")
username := req.HeaderParameter(constants.UserNameHeader)
rules, err := h.tenant.GetUserDevopsSimpleRules(username, devops)
if err != nil { if err != nil {
api.HandleInternalError(resp, err) api.HandleInternalError(resp, err)
...@@ -247,19 +317,20 @@ func (h *tenantHandler) regenerateLoggingRequest(req *restful.Request) (*restful ...@@ -247,19 +317,20 @@ func (h *tenantHandler) regenerateLoggingRequest(req *restful.Request) (*restful
newUrl := net.FormatURL("http", "127.0.0.1", 80, "/kapis/logging.kubesphere.io/v1alpha2/cluster") newUrl := net.FormatURL("http", "127.0.0.1", 80, "/kapis/logging.kubesphere.io/v1alpha2/cluster")
values := req.Request.URL.Query() values := req.Request.URL.Query()
clusterRules, err := iam.GetUserClusterRules(username) clusterRoleRules, err := h.am.GetClusterPolicyRules(username)
if err != nil { if err != nil {
klog.Errorln(err) klog.Errorln(err)
return nil, err return nil, err
} }
hasClusterLogAccess := iam.RulesMatchesRequired(clusterRules, rbacv1.PolicyRule{Verbs: []string{"get"}, Resources: []string{"*"}, APIGroups: []string{"logging.kubesphere.io"}}) hasClusterLogAccess := iam.RulesMatchesRequired(clusterRoleRules, rbacv1.PolicyRule{Verbs: []string{"get"}, Resources: []string{"*"}, APIGroups: []string{"logging.kubesphere.io"}})
// if the user is not a cluster admin // if the user is not a cluster admin
if !hasClusterLogAccess { if !hasClusterLogAccess {
queryNamespaces := strings.Split(req.QueryParameter("namespaces"), ",") queryNamespaces := strings.Split(req.QueryParameter("namespaces"), ",")
// then the user can only view logs of namespaces he belongs to // then the user can only view logs of namespaces he belongs to
namespaces := make([]string, 0) namespaces := make([]string, 0)
roles, err := iam.GetUserRoles("", username) roles, err := h.am.GetRoles("", username)
if err != nil { if err != nil {
klog.Errorln(err) klog.Errorln(err)
return nil, err return nil, err
......
...@@ -32,6 +32,8 @@ import ( ...@@ -32,6 +32,8 @@ import (
"kubesphere.io/kubesphere/pkg/models/iam" "kubesphere.io/kubesphere/pkg/models/iam"
"kubesphere.io/kubesphere/pkg/server/errors" "kubesphere.io/kubesphere/pkg/server/errors"
"kubesphere.io/kubesphere/pkg/server/params" "kubesphere.io/kubesphere/pkg/server/params"
"kubesphere.io/kubesphere/pkg/simple/client/k8s"
"kubesphere.io/kubesphere/pkg/simple/client/mysql"
"net/http" "net/http"
) )
...@@ -42,9 +44,9 @@ const ( ...@@ -42,9 +44,9 @@ const (
var GroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha2"} var GroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha2"}
func AddToContainer(c *restful.Container) error { func AddToContainer(c *restful.Container, k8sClient k8s.Client, db *mysql.Database) error {
ws := runtime.NewWebService(GroupVersion) ws := runtime.NewWebService(GroupVersion)
handler := newTenantHandler() handler := newTenantHandler(k8sClient, db)
ws.Route(ws.GET("/workspaces"). ws.Route(ws.GET("/workspaces").
To(handler.ListWorkspaces). To(handler.ListWorkspaces).
...@@ -103,7 +105,7 @@ func AddToContainer(c *restful.Container) error { ...@@ -103,7 +105,7 @@ func AddToContainer(c *restful.Container) error {
Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag}))
ws.Route(ws.GET("/workspaces/{workspace}/devops"). ws.Route(ws.GET("/workspaces/{workspace}/devops").
To(handler.ListDevOpsProjectsHandler). To(handler.ListDevopsProjects).
Param(ws.PathParameter("workspace", "workspace name")). Param(ws.PathParameter("workspace", "workspace name")).
Param(ws.QueryParameter(params.PagingParam, "page"). Param(ws.QueryParameter(params.PagingParam, "page").
Required(false). Required(false).
...@@ -115,7 +117,7 @@ func AddToContainer(c *restful.Container) error { ...@@ -115,7 +117,7 @@ func AddToContainer(c *restful.Container) error {
Doc("List devops projects for the current user"). Doc("List devops projects for the current user").
Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag}))
ws.Route(ws.GET("/workspaces/{workspace}/members/{member}/devops"). ws.Route(ws.GET("/workspaces/{workspace}/members/{member}/devops").
To(handler.ListDevOpsProjectsHandler). To(handler.ListDevopsProjects).
Param(ws.PathParameter("workspace", "workspace name")). Param(ws.PathParameter("workspace", "workspace name")).
Param(ws.PathParameter("member", "workspace member's username")). Param(ws.PathParameter("member", "workspace member's username")).
Param(ws.QueryParameter(params.PagingParam, "page"). Param(ws.QueryParameter(params.PagingParam, "page").
...@@ -129,14 +131,14 @@ func AddToContainer(c *restful.Container) error { ...@@ -129,14 +131,14 @@ func AddToContainer(c *restful.Container) error {
Doc("List the devops projects for the workspace member"). Doc("List the devops projects for the workspace member").
Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag}))
ws.Route(ws.GET("/devopscount"). ws.Route(ws.GET("/devopscount").
To(handler.GetDevOpsProjectsCountHandler). To(handler.GetDevOpsProjectsCount).
Returns(http.StatusOK, api.StatusOK, struct { Returns(http.StatusOK, api.StatusOK, struct {
Count uint32 `json:"count"` Count uint32 `json:"count"`
}{}). }{}).
Doc("Get the devops projects count for the member"). Doc("Get the devops projects count for the member").
Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag}))
ws.Route(ws.POST("/workspaces/{workspace}/devops"). ws.Route(ws.POST("/workspaces/{workspace}/devops").
To(handler.CreateDevOpsProjectHandler). To(handler.CreateDevopsProject).
Param(ws.PathParameter("workspace", "workspace name")). Param(ws.PathParameter("workspace", "workspace name")).
Doc("Create a devops project in the specified workspace"). Doc("Create a devops project in the specified workspace").
Reads(devopsv1alpha2.DevOpsProject{}). Reads(devopsv1alpha2.DevOpsProject{}).
......
...@@ -21,11 +21,10 @@ import ( ...@@ -21,11 +21,10 @@ import (
"github.com/emicklei/go-restful" "github.com/emicklei/go-restful"
"github.com/emicklei/go-restful-openapi" "github.com/emicklei/go-restful-openapi"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"kubesphere.io/kubesphere/pkg/apiserver/runtime" "kubesphere.io/kubesphere/pkg/apiserver/runtime"
"kubesphere.io/kubesphere/pkg/constants" "kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/models" "kubesphere.io/kubesphere/pkg/models"
"kubesphere.io/kubesphere/pkg/simple/client/k8s"
) )
const ( const (
...@@ -34,11 +33,11 @@ const ( ...@@ -34,11 +33,11 @@ const (
var GroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha2"} var GroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha2"}
func AddToContainer(c *restful.Container, client kubernetes.Interface, config *rest.Config) error { func AddToContainer(c *restful.Container, k8sClient k8s.Client) error {
webservice := runtime.NewWebService(GroupVersion) webservice := runtime.NewWebService(GroupVersion)
handler := newTerminalHandler(client, config) handler := newTerminalHandler(k8sClient.Kubernetes(), k8sClient.Config())
webservice.Route(webservice.GET("/namespaces/{namespace}/pods/{pod}"). webservice.Route(webservice.GET("/namespaces/{namespace}/pods/{pod}").
To(handler.handleTerminalSession). To(handler.handleTerminalSession).
......
...@@ -18,8 +18,6 @@ ...@@ -18,8 +18,6 @@
package iam package iam
import ( import (
"fmt"
"github.com/go-ldap/ldap"
rbacv1 "k8s.io/api/rbac/v1" rbacv1 "k8s.io/api/rbac/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors" apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
...@@ -38,9 +36,6 @@ import ( ...@@ -38,9 +36,6 @@ import (
"kubesphere.io/kubesphere/pkg/server/params" "kubesphere.io/kubesphere/pkg/server/params"
"kubesphere.io/kubesphere/pkg/simple/client" "kubesphere.io/kubesphere/pkg/simple/client"
"kubesphere.io/kubesphere/pkg/utils/k8sutil" "kubesphere.io/kubesphere/pkg/utils/k8sutil"
"kubesphere.io/kubesphere/pkg/utils/sliceutil"
"sort"
"strings"
) )
const ( const (
...@@ -50,9 +45,21 @@ const ( ...@@ -50,9 +45,21 @@ const (
) )
type AccessManagementInterface interface { type AccessManagementInterface interface {
GetDevopsRoleSimpleRules(role string) []SimpleRule GetClusterRole(username string) (*rbacv1.ClusterRole, error)
UnBindAllRoles(username string) error
ListRoleBindings(namespace string, role string) ([]*rbacv1.RoleBinding, error) ListRoleBindings(namespace string, role string) ([]*rbacv1.RoleBinding, error)
CreateClusterRoleBinding(username string, clusterRole string) error CreateClusterRoleBinding(username string, clusterRole string) error
ListRoles(namespace string, conditions *params.Conditions, orderBy string, reverse bool, limit int, offset int) (*models.PageableResponse, error)
ListClusterRoles(conditions *params.Conditions, orderBy string, reverse bool, limit int, offset int) (*models.PageableResponse, error)
ListClusterRoleBindings(clusterRole string) ([]*rbacv1.ClusterRoleBinding, error)
GetClusterRoleSimpleRules(clusterRole string) ([]SimpleRule, error)
GetRoleSimpleRules(namespace string, role string) ([]SimpleRule, error)
GetRoles(namespace, username string) ([]*rbacv1.Role, error)
GetClusterPolicyRules(username string) ([]rbacv1.PolicyRule, error)
GetPolicyRules(namespace, username string) ([]rbacv1.PolicyRule, error)
GetWorkspaceRoleSimpleRules(workspace, roleName string) []SimpleRule
GetWorkspaceRole(workspace, username string) (*rbacv1.ClusterRole, error)
GetWorkspaceRoleMap(username string) (map[string]string, error)
} }
type amOperator struct { type amOperator struct {
...@@ -60,7 +67,31 @@ type amOperator struct { ...@@ -60,7 +67,31 @@ type amOperator struct {
resources resource.ResourceGetter resources resource.ResourceGetter
} }
func newAMOperator(informers informers.SharedInformerFactory) AccessManagementInterface { 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) (*rbacv1.ClusterRole, error) {
panic("implement me")
}
func (am *amOperator) UnBindAllRoles(username string) error {
panic("implement me")
}
func NewAMOperator(informers informers.SharedInformerFactory) *amOperator {
resourceGetter := resource.ResourceGetter{} resourceGetter := resource.ResourceGetter{}
resourceGetter.Add(v1alpha2.Role, role.NewRoleSearcher(informers)) resourceGetter.Add(v1alpha2.Role, role.NewRoleSearcher(informers))
resourceGetter.Add(v1alpha2.ClusterRoles, clusterrole.NewClusterRoleSearcher(informers)) resourceGetter.Add(v1alpha2.ClusterRoles, clusterrole.NewClusterRoleSearcher(informers))
...@@ -198,7 +229,7 @@ func (am *amOperator) GetUserClusterRoles(username string) (*rbacv1.ClusterRole, ...@@ -198,7 +229,7 @@ func (am *amOperator) GetUserClusterRoles(username string) (*rbacv1.ClusterRole,
return userFacingClusterRole, clusterRoles, nil return userFacingClusterRole, clusterRoles, nil
} }
func (am *amOperator) GetUserClusterRole(username string) (*rbacv1.ClusterRole, error) { func (am *amOperator) GetClusterRole(username string) (*rbacv1.ClusterRole, error) {
userFacingClusterRole, _, err := am.GetUserClusterRoles(username) userFacingClusterRole, _, err := am.GetUserClusterRoles(username)
if err != nil { if err != nil {
return nil, err return nil, err
...@@ -256,15 +287,15 @@ func (am *amOperator) GetWorkspaceRoleBindings(workspace string) ([]*rbacv1.Clus ...@@ -256,15 +287,15 @@ func (am *amOperator) GetWorkspaceRoleBindings(workspace string) ([]*rbacv1.Clus
return result, nil return result, nil
} }
func (am *amOperator) GetWorkspaceRole(workspace, role string) (*rbacv1.ClusterRole, error) { //func (am *amOperator) GetWorkspaceRole(workspace, role string) (*rbacv1.ClusterRole, error) {
if !sliceutil.HasString(constants.WorkSpaceRoles, role) { // if !sliceutil.HasString(constants.WorkSpaceRoles, role) {
return nil, apierrors.NewNotFound(schema.GroupResource{Resource: "workspace role"}, role) // return nil, apierrors.NewNotFound(schema.GroupResource{Resource: "workspace role"}, role)
} // }
role = fmt.Sprintf("workspace:%s:%s", workspace, strings.TrimPrefix(role, "workspace-")) // role = fmt.Sprintf("workspace:%s:%s", workspace, strings.TrimPrefix(role, "workspace-"))
return am.informers.Rbac().V1().ClusterRoles().Lister().Get(role) // return am.informers.Rbac().V1().ClusterRoles().Lister().Get(role)
} //}
func (am *amOperator) GetUserWorkspaceRoleMap(username string) (map[string]string, error) { func (am *amOperator) GetWorkspaceRoleMap(username string) (map[string]string, error) {
clusterRoleBindings, err := am.informers.Rbac().V1().ClusterRoleBindings().Lister().List(labels.Everything()) clusterRoleBindings, err := am.informers.Rbac().V1().ClusterRoleBindings().Lister().List(labels.Everything())
...@@ -286,7 +317,7 @@ func (am *amOperator) GetUserWorkspaceRoleMap(username string) (map[string]strin ...@@ -286,7 +317,7 @@ func (am *amOperator) GetUserWorkspaceRoleMap(username string) (map[string]strin
} }
func (am *amOperator) GetUserWorkspaceRole(workspace, username string) (*rbacv1.ClusterRole, error) { func (am *amOperator) GetUserWorkspaceRole(workspace, username string) (*rbacv1.ClusterRole, error) {
workspaceRoleMap, err := am.GetUserWorkspaceRoleMap(username) workspaceRoleMap, err := am.GetWorkspaceRoleMap(username)
if err != nil { if err != nil {
return nil, err return nil, err
...@@ -341,55 +372,6 @@ func (am *amOperator) GetClusterRoleBindings(clusterRoleName string) ([]*rbacv1. ...@@ -341,55 +372,6 @@ func (am *amOperator) GetClusterRoleBindings(clusterRoleName string) ([]*rbacv1.
return items, nil return items, nil
} }
func (am *amOperator) ListClusterRoleUsers(clusterRoleName string, conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) {
roleBindings, err := am.GetClusterRoleBindings(clusterRoleName)
if err != nil {
return nil, err
}
users := make([]*User, 0)
for _, roleBinding := range roleBindings {
for _, subject := range roleBinding.Subjects {
if subject.Kind == rbacv1.UserKind && !k8sutil.ContainsUser(users, subject.Name) {
user, err := GetUserInfo(subject.Name)
if ldap.IsErrorWithCode(err, ldap.LDAPResultNoSuchObject) {
continue
}
if err != nil {
klog.Errorln(err)
return nil, err
}
users = append(users, user)
}
}
}
// order & reverse
sort.Slice(users, func(i, j int) bool {
if reverse {
i, j = j, i
}
switch orderBy {
default:
fallthrough
case v1alpha2.Name:
return strings.Compare(users[i].Username, users[j].Username) <= 0
}
})
result := make([]interface{}, 0)
for i, d := range users {
if i >= offset && (limit == -1 || len(result) < limit) {
result = append(result, d)
}
}
return &models.PageableResponse{Items: result, TotalCount: len(users)}, nil
}
func (am *amOperator) ListRoles(namespace string, conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) { func (am *amOperator) ListRoles(namespace string, conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) {
return am.resources.ListResources(namespace, v1alpha2.Roles, conditions, orderBy, reverse, limit, offset) return am.resources.ListResources(namespace, v1alpha2.Roles, conditions, orderBy, reverse, limit, offset)
} }
...@@ -431,92 +413,6 @@ func (am *amOperator) ListClusterRoles(conditions *params.Conditions, orderBy st ...@@ -431,92 +413,6 @@ func (am *amOperator) ListClusterRoles(conditions *params.Conditions, orderBy st
return am.resources.ListResources("", v1alpha2.ClusterRoles, conditions, orderBy, reverse, limit, offset) return am.resources.ListResources("", v1alpha2.ClusterRoles, conditions, orderBy, reverse, limit, offset)
} }
func (am *amOperator) NamespaceUsers(namespaceName string) ([]*User, error) {
namespace, err := am.informers.Core().V1().Namespaces().Lister().Get(namespaceName)
if err != nil {
klog.Errorln(err)
return nil, err
}
roleBindings, err := am.GetRoleBindings(namespaceName, "")
if err != nil {
return nil, err
}
users := make([]*User, 0)
for _, roleBinding := range roleBindings {
// controlled by ks-controller-manager
if roleBinding.Name == NamespaceViewerRoleBindName {
continue
}
for _, subject := range roleBinding.Subjects {
if subject.Kind == rbacv1.UserKind && !k8sutil.ContainsUser(users, subject.Name) {
// show creator
if roleBinding.Name == NamespaceAdminRoleBindName && subject.Name != namespace.Annotations[constants.CreatorAnnotationKey] {
continue
}
user, err := GetUserInfo(subject.Name)
if err != nil {
if ldap.IsErrorWithCode(err, ldap.LDAPResultNoSuchObject) {
continue
}
return nil, err
}
user.Role = roleBinding.RoleRef.Name
user.RoleBindTime = &roleBinding.CreationTimestamp.Time
user.RoleBinding = roleBinding.Name
users = append(users, user)
}
}
}
return users, nil
}
func (am *amOperator) GetUserWorkspaceSimpleRules(workspace, username string) ([]SimpleRule, error) {
clusterRules, err := am.GetUserClusterRules(username)
if err != nil {
return nil, err
}
// cluster-admin
if RulesMatchesRequired(clusterRules, rbacv1.PolicyRule{
Verbs: []string{"*"},
APIGroups: []string{"*"},
Resources: []string{"*"},
}) {
return am.GetWorkspaceRoleSimpleRules(workspace, constants.WorkspaceAdmin), nil
}
workspaceRole, err := am.GetUserWorkspaceRole(workspace, username)
if err != nil {
if apierrors.IsNotFound(err) {
// workspaces-manager
if RulesMatchesRequired(clusterRules, rbacv1.PolicyRule{
Verbs: []string{"*"},
APIGroups: []string{"*"},
Resources: []string{"workspaces", "workspaces/*"},
}) {
return am.GetWorkspaceRoleSimpleRules(workspace, constants.WorkspacesManager), nil
}
return []SimpleRule{}, nil
}
klog.Error(err)
return nil, err
}
return am.GetWorkspaceRoleSimpleRules(workspace, workspaceRole.Annotations[constants.DisplayNameAnnotationKey]), nil
}
func (am *amOperator) GetWorkspaceRoleSimpleRules(workspace, roleName string) []SimpleRule { func (am *amOperator) GetWorkspaceRoleSimpleRules(workspace, roleName string) []SimpleRule {
workspaceRules := make([]SimpleRule, 0) workspaceRules := make([]SimpleRule, 0)
...@@ -583,20 +479,6 @@ func (am *amOperator) GetUserClusterSimpleRules(username string) ([]SimpleRule, ...@@ -583,20 +479,6 @@ func (am *amOperator) GetUserClusterSimpleRules(username string) ([]SimpleRule,
return getClusterSimpleRule(clusterRules), nil return getClusterSimpleRule(clusterRules), nil
} }
func (am *amOperator) GetUserNamespaceSimpleRules(namespace, username string) ([]SimpleRule, error) {
clusterRules, err := am.GetUserClusterRules(username)
if err != nil {
return nil, err
}
rules, err := am.GetUserRules(namespace, username)
if err != nil {
return nil, err
}
rules = append(rules, clusterRules...)
return getSimpleRule(rules), nil
}
// Convert roles to rules // Convert roles to rules
func (am *amOperator) GetRoleSimpleRules(namespace string, roleName string) ([]SimpleRule, error) { func (am *amOperator) GetRoleSimpleRules(namespace string, roleName string) ([]SimpleRule, error) {
...@@ -608,7 +490,7 @@ func (am *amOperator) GetRoleSimpleRules(namespace string, roleName string) ([]S ...@@ -608,7 +490,7 @@ func (am *amOperator) GetRoleSimpleRules(namespace string, roleName string) ([]S
return nil, err return nil, err
} }
return getSimpleRule(role.Rules), nil return ConvertToSimpleRule(role.Rules), nil
} }
func getClusterSimpleRule(policyRules []rbacv1.PolicyRule) []SimpleRule { func getClusterSimpleRule(policyRules []rbacv1.PolicyRule) []SimpleRule {
...@@ -629,7 +511,7 @@ func getClusterSimpleRule(policyRules []rbacv1.PolicyRule) []SimpleRule { ...@@ -629,7 +511,7 @@ func getClusterSimpleRule(policyRules []rbacv1.PolicyRule) []SimpleRule {
return rules return rules
} }
func getSimpleRule(policyRules []rbacv1.PolicyRule) []SimpleRule { func ConvertToSimpleRule(policyRules []rbacv1.PolicyRule) []SimpleRule {
simpleRules := make([]SimpleRule, 0) simpleRules := make([]SimpleRule, 0)
for i := 0; i < len(policy.RoleRuleMapping); i++ { for i := 0; i < len(policy.RoleRuleMapping); i++ {
rule := SimpleRule{Name: policy.RoleRuleMapping[i].Name} rule := SimpleRule{Name: policy.RoleRuleMapping[i].Name}
......
此差异已折叠。
/*
*
* 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 iam
import (
"github.com/golang/mock/gomock"
"kubesphere.io/kubesphere/pkg/simple/client/ldap"
"testing"
)
func TestIMOperator(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
ldappool, err := ldap.NewMockClient(&ldap.Options{
Host: "192.168.0.7:30389",
ManagerDN: "cn=admin,dc=kubesphere,dc=io",
ManagerPassword: "admin",
UserSearchBase: "ou=Users,dc=kubesphere,dc=io",
GroupSearchBase: "ou=Groups,dc=kubesphere,dc=io",
InitialCap: 8,
MaxCap: 64,
}, ctrl, func(client *ldap.MockClient) {
client.EXPECT().Search(gomock.Any()).AnyTimes()
})
if err != nil {
t.Fatal(err)
}
defer ldappool.Close()
im := NewIMOperator(ldappool, Config{})
err = im.Init()
if err != nil {
t.Fatal(err)
}
}
...@@ -28,6 +28,16 @@ const ( ...@@ -28,6 +28,16 @@ const (
KindTokenReview = "TokenReview" KindTokenReview = "TokenReview"
) )
type User struct {
Username string `json:"username"`
Email string `json:"email"`
Lang string `json:"lang,omitempty"`
Description string `json:"description"`
CreateTime time.Time `json:"create_time"`
Groups []string `json:"groups,omitempty"`
Password string `json:"password,omitempty"`
}
type Action struct { type Action struct {
Name string `json:"name"` Name string `json:"name"`
Rules []v1.PolicyRule `json:"rules"` Rules []v1.PolicyRule `json:"rules"`
...@@ -47,15 +57,3 @@ type RoleList struct { ...@@ -47,15 +57,3 @@ type RoleList struct {
ClusterRoles []*v1.ClusterRole `json:"clusterRole" description:"cluster role list"` ClusterRoles []*v1.ClusterRole `json:"clusterRole" description:"cluster role list"`
Roles []*v1.Role `json:"roles" description:"role list"` Roles []*v1.Role `json:"roles" description:"role list"`
} }
type Config struct {
adminEmail string
adminPassword string
authRateLimit string
maxAuthFailed int
authTimeInterval time.Duration
tokenIdleTimeout time.Duration
userInitFile string
enableMultiLogin bool
generateKubeConfig bool
}
...@@ -81,7 +81,6 @@ func TestApplicationOperator_CreateApplication(t *testing.T) { ...@@ -81,7 +81,6 @@ func TestApplicationOperator_CreateApplication(t *testing.T) {
defer ctrl.Finish() defer ctrl.Finish()
for _, test := range tests { for _, test := range tests {
op := openpitrix.NewMockClient(ctrl) op := openpitrix.NewMockClient(ctrl)
objs := namespacesToRuntimeObjects(test.existNamespaces...) objs := namespacesToRuntimeObjects(test.existNamespaces...)
k8s := fake.NewSimpleClientset(objs...) k8s := fake.NewSimpleClientset(objs...)
......
...@@ -41,6 +41,7 @@ type NamespaceInterface interface { ...@@ -41,6 +41,7 @@ type NamespaceInterface interface {
type namespaceSearcher struct { type namespaceSearcher struct {
k8s kubernetes.Interface k8s kubernetes.Interface
informers k8sinformers.SharedInformerFactory informers k8sinformers.SharedInformerFactory
am iam.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) {
...@@ -56,8 +57,8 @@ func (s *namespaceSearcher) CreateNamespace(workspace string, namespace *v1.Name ...@@ -56,8 +57,8 @@ 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) NamespaceInterface { func newNamespaceOperator(k8s kubernetes.Interface, informers k8sinformers.SharedInformerFactory, am iam.AccessManagementInterface) NamespaceInterface {
return &namespaceSearcher{k8s: k8s, informers: informers} return &namespaceSearcher{k8s: k8s, informers: informers, am: am}
} }
func (s *namespaceSearcher) match(match map[string]string, item *v1.Namespace) bool { func (s *namespaceSearcher) match(match map[string]string, item *v1.Namespace) bool {
...@@ -111,7 +112,7 @@ func (s *namespaceSearcher) compare(a, b *v1.Namespace, orderBy string) bool { ...@@ -111,7 +112,7 @@ func (s *namespaceSearcher) compare(a, b *v1.Namespace, orderBy string) bool {
func (s *namespaceSearcher) GetNamespaces(username string) ([]*v1.Namespace, error) { func (s *namespaceSearcher) GetNamespaces(username string) ([]*v1.Namespace, error) {
roles, err := iam.GetUserRoles("", username) roles, err := s.am.GetRoles("", username)
if err != nil { if err != nil {
return nil, err return nil, err
...@@ -143,7 +144,7 @@ func containsNamespace(namespaces []*v1.Namespace, namespace *v1.Namespace) bool ...@@ -143,7 +144,7 @@ func containsNamespace(namespaces []*v1.Namespace, namespace *v1.Namespace) bool
func (s *namespaceSearcher) Search(username string, conditions *params.Conditions, orderBy string, reverse bool) ([]*v1.Namespace, error) { func (s *namespaceSearcher) Search(username string, conditions *params.Conditions, orderBy string, reverse bool) ([]*v1.Namespace, error) {
rules, err := iam.GetUserClusterRules(username) rules, err := s.am.GetClusterPolicyRules(username)
if err != nil { if err != nil {
return nil, err return nil, err
......
...@@ -19,12 +19,16 @@ package tenant ...@@ -19,12 +19,16 @@ package tenant
import ( import (
"k8s.io/api/core/v1" "k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
k8sinformers "k8s.io/client-go/informers" k8sinformers "k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes"
"k8s.io/klog"
"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/constants" "kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/models" "kubesphere.io/kubesphere/pkg/models"
"kubesphere.io/kubesphere/pkg/models/iam"
"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"
"strconv" "strconv"
...@@ -36,14 +40,35 @@ type Interface interface { ...@@ -36,14 +40,35 @@ type Interface interface {
DescribeWorkspace(username, workspace string) (*v1alpha1.Workspace, error) DescribeWorkspace(username, workspace string) (*v1alpha1.Workspace, error)
ListWorkspaces(username string, conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) ListWorkspaces(username string, conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error)
ListNamespaces(username string, conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) ListNamespaces(username string, conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error)
GetWorkspace(workspace string) (*v1alpha1.Workspace, error) ListDevopsProjects(username string, conditions *params.Conditions, orderBy string, reverse bool, limit int, offset int) (*models.PageableResponse, error)
DevOpsProjectOperator GetWorkspaceSimpleRules(workspace, username string) ([]iam.SimpleRule, error)
GetNamespaceSimpleRules(namespace, username string) ([]iam.SimpleRule, error)
CountDevOpsProjects(username string) (uint32, error)
DeleteDevOpsProject(username, projectId string) error
GetUserDevopsSimpleRules(username string, devops string) (interface{}, error)
} }
type tenantOperator struct { type tenantOperator struct {
workspaces WorkspaceInterface workspaces WorkspaceInterface
namespaces NamespaceInterface namespaces NamespaceInterface
DevOpsProjectOperator am iam.AccessManagementInterface
devops DevOpsProjectOperator
}
func (t *tenantOperator) CountDevOpsProjects(username string) (uint32, error) {
return t.devops.GetDevOpsProjectsCount(username)
}
func (t *tenantOperator) DeleteDevOpsProject(username, projectId string) error {
return t.devops.DeleteDevOpsProject(projectId, username)
}
func (t *tenantOperator) GetUserDevopsSimpleRules(username string, projectId string) (interface{}, error) {
return t.devops.GetUserDevOpsSimpleRules(username, projectId)
}
func (t *tenantOperator) ListDevopsProjects(username string, conditions *params.Conditions, orderBy string, reverse bool, limit int, offset int) (*models.PageableResponse, error) {
return t.devops.ListDevOpsProjects(conditions.Match["workspace"], username, conditions, orderBy, reverse, limit, offset)
} }
func (t *tenantOperator) DeleteNamespace(workspace, namespace string) error { func (t *tenantOperator) DeleteNamespace(workspace, namespace string) error {
...@@ -51,9 +76,11 @@ func (t *tenantOperator) DeleteNamespace(workspace, namespace string) error { ...@@ -51,9 +76,11 @@ 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 := iam.NewAMOperator(informers)
return &tenantOperator{ return &tenantOperator{
workspaces: newWorkspaceOperator(client, informers, ksinformers, db), workspaces: newWorkspaceOperator(client, informers, ksinformers, amOperator, db),
namespaces: newNamespaceOperator(client, informers), namespaces: newNamespaceOperator(client, informers, amOperator),
am: amOperator,
} }
} }
...@@ -95,17 +122,69 @@ func (t *tenantOperator) ListWorkspaces(username string, conditions *params.Cond ...@@ -95,17 +122,69 @@ func (t *tenantOperator) ListWorkspaces(username string, conditions *params.Cond
return &models.PageableResponse{Items: result, TotalCount: len(workspaces)}, nil return &models.PageableResponse{Items: result, TotalCount: len(workspaces)}, nil
} }
func (t *tenantOperator) GetWorkspaceSimpleRules(workspace, username string) ([]iam.SimpleRule, error) {
clusterRules, err := t.am.GetClusterPolicyRules(username)
if err != nil {
return nil, err
}
// cluster-admin
if iam.RulesMatchesRequired(clusterRules, rbacv1.PolicyRule{
Verbs: []string{"*"},
APIGroups: []string{"*"},
Resources: []string{"*"},
}) {
return t.am.GetWorkspaceRoleSimpleRules(workspace, constants.WorkspaceAdmin), nil
}
workspaceRole, err := t.am.GetWorkspaceRole(workspace, username)
// workspaces-manager
if iam.RulesMatchesRequired(clusterRules, rbacv1.PolicyRule{
Verbs: []string{"*"},
APIGroups: []string{"*"},
Resources: []string{"workspaces", "workspaces/*"},
}) {
return t.am.GetWorkspaceRoleSimpleRules(workspace, constants.WorkspacesManager), nil
}
if err != nil {
if apierrors.IsNotFound(err) {
return []iam.SimpleRule{}, nil
}
klog.Error(err)
return nil, err
}
return t.am.GetWorkspaceRoleSimpleRules(workspace, workspaceRole.Annotations[constants.DisplayNameAnnotationKey]), nil
}
func (t *tenantOperator) GetNamespaceSimpleRules(namespace, username string) ([]iam.SimpleRule, error) {
clusterRules, err := t.am.GetClusterPolicyRules(username)
if err != nil {
return nil, err
}
rules, err := t.am.GetPolicyRules(namespace, username)
if err != nil {
return nil, err
}
rules = append(rules, clusterRules...)
return iam.ConvertToSimpleRule(rules), nil
}
func (t *tenantOperator) appendAnnotations(username string, workspace *v1alpha1.Workspace) *v1alpha1.Workspace { func (t *tenantOperator) appendAnnotations(username string, workspace *v1alpha1.Workspace) *v1alpha1.Workspace {
workspace = workspace.DeepCopy() workspace = workspace.DeepCopy()
if workspace.Annotations == nil { if workspace.Annotations == nil {
workspace.Annotations = make(map[string]string) workspace.Annotations = make(map[string]string)
} }
ns, err := t.ListNamespaces(username, &params.Conditions{Match: map[string]string{constants.WorkspaceLabelKey: workspace.Name}}, "", false, 1, 0)
if err == nil { if ns, err := t.ListNamespaces(username, &params.Conditions{Match: map[string]string{constants.WorkspaceLabelKey: workspace.Name}}, "", false, 1, 0); err == nil {
workspace.Annotations["kubesphere.io/namespace-count"] = strconv.Itoa(ns.TotalCount) workspace.Annotations["kubesphere.io/namespace-count"] = strconv.Itoa(ns.TotalCount)
} }
devops, err := t.ListDevOpsProjects(workspace.Name, username, &params.Conditions{}, "", false, 1, 0)
if err == nil { if devops, err := t.ListDevopsProjects(username, &params.Conditions{Match: map[string]string{"workspace": workspace.Name}}, "", false, 1, 0); err == nil {
workspace.Annotations["kubesphere.io/devops-count"] = strconv.Itoa(devops.TotalCount) workspace.Annotations["kubesphere.io/devops-count"] = strconv.Itoa(devops.TotalCount)
} }
...@@ -135,7 +214,3 @@ func (t *tenantOperator) ListNamespaces(username string, conditions *params.Cond ...@@ -135,7 +214,3 @@ func (t *tenantOperator) ListNamespaces(username string, conditions *params.Cond
return &models.PageableResponse{Items: result, TotalCount: len(namespaces)}, nil return &models.PageableResponse{Items: result, TotalCount: len(namespaces)}, nil
} }
func (t *tenantOperator) GetWorkspace(workspace string) (*v1alpha1.Workspace, error) {
return t.workspaces.GetWorkspace(workspace)
}
...@@ -46,13 +46,18 @@ import ( ...@@ -46,13 +46,18 @@ import (
"k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/labels"
) )
type InWorkspaceUser struct {
*iam.User
WorkspaceRole string `json:"workspaceRole"`
}
type WorkspaceInterface interface { type WorkspaceInterface interface {
GetWorkspace(workspace string) (*v1alpha1.Workspace, error) GetWorkspace(workspace string) (*v1alpha1.Workspace, error)
SearchWorkspace(username string, conditions *params.Conditions, orderBy string, reverse bool) ([]*v1alpha1.Workspace, error) SearchWorkspace(username string, conditions *params.Conditions, orderBy string, reverse bool) ([]*v1alpha1.Workspace, error)
ListNamespaces(workspace string) ([]*core.Namespace, error) ListNamespaces(workspace string) ([]*core.Namespace, error)
DeleteNamespace(workspace, namespace string) error DeleteNamespace(workspace, namespace string) error
RemoveUser(user, workspace string) error RemoveUser(user, workspace string) error
AddUser(workspace string, user *iam.User) error AddUser(workspace string, user *InWorkspaceUser) error
CountDevopsProjectsInWorkspace(workspace string) (int, error) CountDevopsProjectsInWorkspace(workspace string) (int, error)
CountUsersInWorkspace(workspace string) (int, error) CountUsersInWorkspace(workspace string) (int, error)
CountOrgRoles() (int, error) CountOrgRoles() (int, error)
...@@ -64,17 +69,19 @@ type workspaceOperator struct { ...@@ -64,17 +69,19 @@ type workspaceOperator struct {
client kubernetes.Interface client kubernetes.Interface
informers informers.SharedInformerFactory informers informers.SharedInformerFactory
ksInformers externalversions.SharedInformerFactory ksInformers externalversions.SharedInformerFactory
am iam.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, db *mysql.Database) WorkspaceInterface { func newWorkspaceOperator(client kubernetes.Interface, informers informers.SharedInformerFactory, ksinformers externalversions.SharedInformerFactory, am iam.AccessManagementInterface, db *mysql.Database) WorkspaceInterface {
return &workspaceOperator{ return &workspaceOperator{
client: client, client: client,
informers: informers, informers: informers,
ksInformers: ksinformers, ksInformers: ksinformers,
am: am,
db: db, db: db,
} }
} }
...@@ -104,7 +111,7 @@ func (w *workspaceOperator) DeleteNamespace(workspace string, namespace string) ...@@ -104,7 +111,7 @@ func (w *workspaceOperator) DeleteNamespace(workspace string, namespace string)
} }
func (w *workspaceOperator) RemoveUser(workspace string, username string) error { func (w *workspaceOperator) RemoveUser(workspace string, username string) error {
workspaceRole, err := iam.GetUserWorkspaceRole(workspace, username) workspaceRole, err := w.am.GetWorkspaceRole(workspace, username)
if err != nil { if err != nil {
return err return err
} }
...@@ -117,9 +124,9 @@ func (w *workspaceOperator) RemoveUser(workspace string, username string) error ...@@ -117,9 +124,9 @@ func (w *workspaceOperator) RemoveUser(workspace string, username string) error
return nil return nil
} }
func (w *workspaceOperator) AddUser(workspaceName string, user *iam.User) error { func (w *workspaceOperator) AddUser(workspaceName string, user *InWorkspaceUser) error {
workspaceRole, err := iam.GetUserWorkspaceRole(workspaceName, user.Username) workspaceRole, err := w.am.GetWorkspaceRole(workspaceName, user.Username)
if err != nil && !apierrors.IsNotFound(err) { if err != nil && !apierrors.IsNotFound(err) {
klog.Errorf("get workspace role failed: %+v", err) klog.Errorf("get workspace role failed: %+v", err)
...@@ -215,7 +222,7 @@ func (w *workspaceOperator) CountDevopsProjectsInWorkspace(workspaceName string) ...@@ -215,7 +222,7 @@ func (w *workspaceOperator) CountDevopsProjectsInWorkspace(workspaceName string)
} }
func (w *workspaceOperator) CountUsersInWorkspace(workspace string) (int, error) { func (w *workspaceOperator) CountUsersInWorkspace(workspace string) (int, error) {
count, err := iam.WorkspaceUsersTotalCount(workspace) count, err := w.CountUsersInWorkspace(workspace)
if err != nil { if err != nil {
return 0, err return 0, err
} }
...@@ -285,7 +292,7 @@ func (*workspaceOperator) compare(a, b *v1alpha1.Workspace, orderBy string) bool ...@@ -285,7 +292,7 @@ func (*workspaceOperator) compare(a, b *v1alpha1.Workspace, orderBy string) bool
} }
func (w *workspaceOperator) SearchWorkspace(username string, conditions *params.Conditions, orderBy string, reverse bool) ([]*v1alpha1.Workspace, error) { func (w *workspaceOperator) SearchWorkspace(username string, conditions *params.Conditions, orderBy string, reverse bool) ([]*v1alpha1.Workspace, error) {
rules, err := iam.GetUserClusterRules(username) rules, err := w.am.GetClusterPolicyRules(username)
if err != nil { if err != nil {
return nil, err return nil, err
...@@ -299,7 +306,7 @@ func (w *workspaceOperator) SearchWorkspace(username string, conditions *params. ...@@ -299,7 +306,7 @@ func (w *workspaceOperator) SearchWorkspace(username string, conditions *params.
return nil, err return nil, err
} }
} else { } else {
workspaceRoles, err := iam.GetUserWorkspaceRoleMap(username) workspaceRoles, err := w.am.GetWorkspaceRoleMap(username)
if err != nil { if err != nil {
return nil, err return nil, err
} }
......
...@@ -186,10 +186,10 @@ func newConfig() *Config { ...@@ -186,10 +186,10 @@ func newConfig() *Config {
SonarQubeOptions: sonarqube.NewSonarQubeOptions(), SonarQubeOptions: sonarqube.NewSonarQubeOptions(),
KubernetesOptions: k8s.NewKubernetesOptions(), KubernetesOptions: k8s.NewKubernetesOptions(),
ServiceMeshOptions: servicemesh.NewServiceMeshOptions(), ServiceMeshOptions: servicemesh.NewServiceMeshOptions(),
LdapOptions: ldap.NewLdapOptions(), LdapOptions: ldap.NewOptions(),
RedisOptions: cache.NewRedisOptions(), RedisOptions: cache.NewRedisOptions(),
S3Options: s3.NewS3Options(), S3Options: s3.NewS3Options(),
OpenPitrixOptions: openpitrix.NewOpenPitrixOptions(), OpenPitrixOptions: openpitrix.NewOptions(),
MonitoringOptions: prometheus.NewPrometheusOptions(), MonitoringOptions: prometheus.NewPrometheusOptions(),
KubeSphereOptions: kubesphere.NewKubeSphereOptions(), KubeSphereOptions: kubesphere.NewKubeSphereOptions(),
AlertingOptions: alerting.NewAlertingOptions(), AlertingOptions: alerting.NewAlertingOptions(),
......
...@@ -18,8 +18,7 @@ ...@@ -18,8 +18,7 @@
package errors package errors
import ( import (
"github.com/emicklei/go-restful" "fmt"
"net/http"
) )
type Error struct { type Error struct {
...@@ -28,24 +27,16 @@ type Error struct { ...@@ -28,24 +27,16 @@ type Error struct {
var None = Error{Message: "success"} var None = Error{Message: "success"}
func (e *Error) Error() string { func (e Error) Error() string {
return e.Message return e.Message
} }
func Wrap(err error) Error { func Wrap(err error) error {
return Error{Message: err.Error()} return Error{Message: err.Error()}
} }
func New(message string) Error { func New(format string, args ...interface{}) error {
return Error{Message: message} return Error{Message: fmt.Sprintf(format, args...)}
}
func ParseSvcErr(err error, resp *restful.Response) {
if svcErr, ok := err.(restful.ServiceError); ok {
resp.WriteServiceError(svcErr.Code, svcErr)
} else {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, Wrap(err))
}
} }
func GetServiceErrorCode(err error) int { func GetServiceErrorCode(err error) int {
......
...@@ -38,11 +38,11 @@ func NewClientSetOptions() *ClientSetOptions { ...@@ -38,11 +38,11 @@ func NewClientSetOptions() *ClientSetOptions {
mySQLOptions: mysql.NewMySQLOptions(), mySQLOptions: mysql.NewMySQLOptions(),
redisOptions: cache.NewRedisOptions(), redisOptions: cache.NewRedisOptions(),
kubernetesOptions: k8s.NewKubernetesOptions(), kubernetesOptions: k8s.NewKubernetesOptions(),
ldapOptions: ldap.NewLdapOptions(), ldapOptions: ldap.NewOptions(),
devopsOptions: jenkins.NewDevopsOptions(), devopsOptions: jenkins.NewDevopsOptions(),
sonarqubeOptions: sonarqube.NewSonarQubeOptions(), sonarqubeOptions: sonarqube.NewSonarQubeOptions(),
s3Options: s3.NewS3Options(), s3Options: s3.NewS3Options(),
openPitrixOptions: openpitrix.NewOpenPitrixOptions(), openPitrixOptions: openpitrix.NewOptions(),
prometheusOptions: prometheus.NewPrometheusOptions(), prometheusOptions: prometheus.NewPrometheusOptions(),
kubesphereOptions: kubesphere.NewKubeSphereOptions(), kubesphereOptions: kubesphere.NewKubeSphereOptions(),
elasticSearhOptions: esclient.NewElasticSearchOptions(), elasticSearhOptions: esclient.NewElasticSearchOptions(),
...@@ -114,7 +114,7 @@ type ClientSet struct { ...@@ -114,7 +114,7 @@ type ClientSet struct {
mySQLClient *mysql.Client mySQLClient *mysql.Client
k8sClient k8s.Client k8sClient k8s.Client
ldapClient *ldap.Client ldapClient ldap.Client
devopsClient *jenkins.Client devopsClient *jenkins.Client
sonarQubeClient *sonarqube.Client sonarQubeClient *sonarqube.Client
redisClient cache.Interface redisClient cache.Interface
...@@ -242,7 +242,7 @@ func (cs *ClientSet) SonarQube() (*sonarqube.Client, error) { ...@@ -242,7 +242,7 @@ func (cs *ClientSet) SonarQube() (*sonarqube.Client, error) {
} }
} }
func (cs *ClientSet) Ldap() (*ldap.Client, error) { func (cs *ClientSet) Ldap() (ldap.Client, error) {
var err error var err error
if cs.csoptions.ldapOptions == nil || cs.csoptions.ldapOptions.Host == "" { if cs.csoptions.ldapOptions == nil || cs.csoptions.ldapOptions.Host == "" {
...@@ -256,7 +256,7 @@ func (cs *ClientSet) Ldap() (*ldap.Client, error) { ...@@ -256,7 +256,7 @@ func (cs *ClientSet) Ldap() (*ldap.Client, error) {
defer mutex.Unlock() defer mutex.Unlock()
if cs.ldapClient == nil { if cs.ldapClient == nil {
cs.ldapClient, err = ldap.NewLdapClient(cs.csoptions.ldapOptions, cs.stopCh) cs.ldapClient, err = ldap.NewClient(cs.csoptions.ldapOptions, cs.stopCh)
if err != nil { if err != nil {
return nil, err return nil, err
} }
...@@ -311,7 +311,7 @@ func (cs *ClientSet) OpenPitrix() (openpitrix.Client, error) { ...@@ -311,7 +311,7 @@ func (cs *ClientSet) OpenPitrix() (openpitrix.Client, error) {
if cs.openpitrixClient != nil { if cs.openpitrixClient != nil {
return cs.openpitrixClient, nil return cs.openpitrixClient, nil
} else { } else {
cs.openpitrixClient, err = openpitrix.NewOpenPitrixClient(cs.csoptions.openPitrixOptions) cs.openpitrixClient, err = openpitrix.NewClient(cs.csoptions.openPitrixOptions)
if err != nil { if err != nil {
return nil, err return nil, err
} }
......
...@@ -25,7 +25,7 @@ type channelPool struct { ...@@ -25,7 +25,7 @@ type channelPool struct {
// PoolFactory is a function to create new connections. // PoolFactory is a function to create new connections.
type PoolFactory func(string) (ldap.Client, error) type PoolFactory func(string) (ldap.Client, error)
// NewChannelPool returns a new pool based on buffered channels with an initial // newChannelPool returns a new pool based on buffered channels with an initial
// capacity and maximum capacity. Factory is used when initial capacity is // capacity and maximum capacity. Factory is used when initial capacity is
// greater than zero to fill the pool. A zero initialCap doesn't fill the Pool // greater than zero to fill the pool. A zero initialCap doesn't fill the Pool
// until a new Get() is called. During a Get(), If there is no new connection // until a new Get() is called. During a Get(), If there is no new connection
...@@ -36,7 +36,7 @@ type PoolFactory func(string) (ldap.Client, error) ...@@ -36,7 +36,7 @@ type PoolFactory func(string) (ldap.Client, error)
// of the call is one of those passed, most likely you want to set this to something // of the call is one of those passed, most likely you want to set this to something
// like // like
// []uint8{ldap.LDAPResultTimeLimitExceeded, ldap.ErrorNetwork} // []uint8{ldap.LDAPResultTimeLimitExceeded, ldap.ErrorNetwork}
func NewChannelPool(initialCap, maxCap int, name string, factory PoolFactory, closeAt []uint16) (Pool, error) { func newChannelPool(initialCap, maxCap int, name string, factory PoolFactory, closeAt []uint16) (Pool, error) {
if initialCap < 0 || maxCap <= 0 || initialCap > maxCap { if initialCap < 0 || maxCap <= 0 || initialCap > maxCap {
return nil, errors.New("invalid capacity settings") return nil, errors.New("invalid capacity settings")
} }
...@@ -85,7 +85,7 @@ func (c *channelPool) Get() (*PoolConn, error) { ...@@ -85,7 +85,7 @@ func (c *channelPool) Get() (*PoolConn, error) {
return nil, ErrClosed return nil, ErrClosed
} }
// wrap our connections with our ldap.PoolClient implementation (wrapConn // wrap our connections with our ldap.Client implementation (wrapConn
// method) that puts the connection back to the pool if it's closed. // method) that puts the connection back to the pool if it's closed.
select { select {
case conn := <-conns: case conn := <-conns:
......
...@@ -8,7 +8,7 @@ import ( ...@@ -8,7 +8,7 @@ import (
"github.com/go-ldap/ldap" "github.com/go-ldap/ldap"
) )
// PoolConn implements PoolClient to override the Close() method // PoolConn implements Client to override the Close() method
type PoolConn struct { type PoolConn struct {
Conn ldap.Client Conn ldap.Client
c *channelPool c *channelPool
......
...@@ -20,11 +20,13 @@ package ldap ...@@ -20,11 +20,13 @@ package ldap
import ( import (
"fmt" "fmt"
"github.com/go-ldap/ldap" "github.com/go-ldap/ldap"
"github.com/golang/mock/gomock"
"k8s.io/klog" "k8s.io/klog"
) )
type Client interface { type Client interface {
NewConn() (ldap.Client, error) NewConn() (ldap.Client, error)
Close()
GroupSearchBase() string GroupSearchBase() string
UserSearchBase() string UserSearchBase() string
} }
...@@ -35,8 +37,8 @@ type poolClient struct { ...@@ -35,8 +37,8 @@ type poolClient struct {
} }
// panic if cannot connect to ldap service // panic if cannot connect to ldap service
func NewLdapClient(options *Options, stopCh <-chan struct{}) (Client, error) { func NewClient(options *Options, stopCh <-chan struct{}) (Client, error) {
pool, err := NewChannelPool(8, 64, "kubesphere", func(s string) (ldap.Client, error) { pool, err := newChannelPool(options.InitialCap, options.MaxCap, options.PoolName, func(s string) (ldap.Client, error) {
conn, err := ldap.Dial("tcp", options.Host) conn, err := ldap.Dial("tcp", options.Host)
if err != nil { if err != nil {
return nil, err return nil, err
...@@ -46,7 +48,6 @@ func NewLdapClient(options *Options, stopCh <-chan struct{}) (Client, error) { ...@@ -46,7 +48,6 @@ func NewLdapClient(options *Options, stopCh <-chan struct{}) (Client, error) {
if err != nil { if err != nil {
klog.Error(err) klog.Error(err)
pool.Close()
return nil, err return nil, err
} }
...@@ -57,13 +58,16 @@ func NewLdapClient(options *Options, stopCh <-chan struct{}) (Client, error) { ...@@ -57,13 +58,16 @@ func NewLdapClient(options *Options, stopCh <-chan struct{}) (Client, error) {
go func() { go func() {
<-stopCh <-stopCh
if client.pool != nil { client.Close()
client.pool.Close()
}
}() }()
return client, nil return client, nil
} }
func (l *poolClient) Close() {
if l.pool != nil {
l.pool.Close()
}
}
func (l *poolClient) NewConn() (ldap.Client, error) { func (l *poolClient) NewConn() (ldap.Client, error) {
if l.pool == nil { if l.pool == nil {
...@@ -81,7 +85,7 @@ func (l *poolClient) NewConn() (ldap.Client, error) { ...@@ -81,7 +85,7 @@ func (l *poolClient) NewConn() (ldap.Client, error) {
err = conn.Bind(l.options.ManagerDN, l.options.ManagerPassword) err = conn.Bind(l.options.ManagerDN, l.options.ManagerPassword)
if err != nil { if err != nil {
conn.Close() conn.Close()
klog.Error(err) klog.Errorln(err)
return nil, err return nil, err
} }
return conn, nil return conn, nil
...@@ -94,3 +98,25 @@ func (l *poolClient) GroupSearchBase() string { ...@@ -94,3 +98,25 @@ func (l *poolClient) GroupSearchBase() string {
func (l *poolClient) UserSearchBase() string { func (l *poolClient) UserSearchBase() string {
return l.options.UserSearchBase return l.options.UserSearchBase
} }
func NewMockClient(options *Options, ctrl *gomock.Controller, setup func(client *MockClient)) (Client, error) {
pool, err := newChannelPool(options.InitialCap, options.MaxCap, options.PoolName, func(s string) (ldap.Client, error) {
conn := newMockClient(ctrl)
conn.EXPECT().Bind(gomock.Any(), gomock.Any()).AnyTimes()
conn.EXPECT().Close().AnyTimes()
setup(conn)
return conn, nil
}, []uint16{ldap.LDAPResultAdminLimitExceeded, ldap.ErrorNetwork})
if err != nil {
klog.Error(err)
return nil, err
}
client := &poolClient{
pool: pool,
options: options,
}
return client, nil
}
此差异已折叠。
...@@ -11,11 +11,14 @@ type Options struct { ...@@ -11,11 +11,14 @@ type Options struct {
ManagerPassword string `json:"managerPassword,omitempty" yaml:"managerPassword"` ManagerPassword string `json:"managerPassword,omitempty" yaml:"managerPassword"`
UserSearchBase string `json:"userSearchBase,omitempty" yaml:"userSearchBase"` UserSearchBase string `json:"userSearchBase,omitempty" yaml:"userSearchBase"`
GroupSearchBase string `json:"groupSearchBase,omitempty" yaml:"groupSearchBase"` GroupSearchBase string `json:"groupSearchBase,omitempty" yaml:"groupSearchBase"`
InitialCap int `json:"initialCap,omitempty" yaml:"initialCap"`
MaxCap int `json:"maxCap,omitempty" yaml:"maxCap"`
PoolName string `json:"poolName,omitempty" yaml:"poolName"`
} }
// NewLdapOptions return a default option // NewOptions return a default option
// which host field point to nowhere. // which host field point to nowhere.
func NewLdapOptions() *Options { func NewOptions() *Options {
return &Options{ return &Options{
Host: "", Host: "",
ManagerDN: "cn=admin,dc=example,dc=org", ManagerDN: "cn=admin,dc=example,dc=org",
...@@ -25,7 +28,7 @@ func NewLdapOptions() *Options { ...@@ -25,7 +28,7 @@ func NewLdapOptions() *Options {
} }
func (l *Options) Validate() []error { func (l *Options) Validate() []error {
errors := []error{} var errors []error
return errors return errors
} }
......
...@@ -134,7 +134,7 @@ func newAppManagerClient(endpoint string) (pb.AppManagerClient, error) { ...@@ -134,7 +134,7 @@ func newAppManagerClient(endpoint string) (pb.AppManagerClient, error) {
return pb.NewAppManagerClient(conn), nil return pb.NewAppManagerClient(conn), nil
} }
func NewOpenPitrixClient(options *Options) (Client, error) { func NewClient(options *Options) (Client, error) {
runtimeMangerClient, err := newRuntimeManagerClient(options.RuntimeManagerEndpoint) runtimeMangerClient, err := newRuntimeManagerClient(options.RuntimeManagerEndpoint)
......
...@@ -16,7 +16,7 @@ type Options struct { ...@@ -16,7 +16,7 @@ type Options struct {
RepoIndexerEndpoint string `json:"repoIndexerEndpoint,omitempty" yaml:"repoIndexerEndpoint,omitempty"` RepoIndexerEndpoint string `json:"repoIndexerEndpoint,omitempty" yaml:"repoIndexerEndpoint,omitempty"`
} }
func NewOpenPitrixOptions() *Options { func NewOptions() *Options {
return &Options{} return &Options{}
} }
......
...@@ -20,19 +20,26 @@ package jwtutil ...@@ -20,19 +20,26 @@ package jwtutil
import ( import (
"fmt" "fmt"
"github.com/dgrijalva/jwt-go" "github.com/dgrijalva/jwt-go"
"os"
) )
const secretEnv = "JWT_SECRET" const secretEnv = "JWT_SECRET"
var secret []byte var secretKey []byte
func Setup(key string) { func init() {
secret = []byte(key) if secret := os.Getenv(secretEnv); secret != "" {
Setup(secret)
}
}
func Setup(secret string) {
secretKey = []byte(secret)
} }
func MustSigned(claims jwt.MapClaims) string { func MustSigned(claims jwt.MapClaims) string {
uToken := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) uToken := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
token, err := uToken.SignedString(secret) token, err := uToken.SignedString(secretKey)
if err != nil { if err != nil {
panic(err) panic(err)
} }
...@@ -41,7 +48,7 @@ func MustSigned(claims jwt.MapClaims) string { ...@@ -41,7 +48,7 @@ func MustSigned(claims jwt.MapClaims) string {
func provideKey(token *jwt.Token) (interface{}, error) { func provideKey(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); ok { if _, ok := token.Method.(*jwt.SigningMethodHMAC); ok {
return secret, nil return secretKey, nil
} else { } else {
return nil, fmt.Errorf("expect token signed with HMAC but got %v", token.Header["alg"]) return nil, fmt.Errorf("expect token signed with HMAC but got %v", token.Header["alg"])
} }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册