code refactor (#1786)

* implement LDAP mock client

Signed-off-by: hongming <talonwan@yunify.com>

* update

Signed-off-by: hongming <talonwan@yunify.com>

* update

Signed-off-by: hongming <talonwan@yunify.com>

* resolve conflict

Signed-off-by: hongming <talonwan@yunify.com>
This commit is contained in:
hongming
2020-02-24 15:39:36 +08:00
committed by GitHub
parent 96aee0e60b
commit abf9fee845
39 changed files with 1338 additions and 2467 deletions

View File

@@ -2,21 +2,26 @@ package v1alpha2
import (
"errors"
"fmt"
"github.com/dgrijalva/jwt-go"
"github.com/emicklei/go-restful"
"github.com/go-ldap/ldap"
rbacv1 "k8s.io/api/rbac/v1"
k8serr "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/klog"
"kubesphere.io/kubesphere/pkg/api"
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/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/simple/client/k8s"
ldappool "kubesphere.io/kubesphere/pkg/simple/client/ldap"
"kubesphere.io/kubesphere/pkg/utils/iputil"
"kubesphere.io/kubesphere/pkg/utils/jwtutil"
"net/http"
"sort"
)
type iamHandler struct {
@@ -24,29 +29,34 @@ type iamHandler struct {
imOperator iam.IdentityManagementInterface
}
func newIAMHandler() *iamHandler {
return &iamHandler{}
func newIAMHandler(k8sClient k8s.Client, ldapClient ldappool.Client, options iam.Config) *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) {
var tokenReview iamv1alpha2.TokenReview
err := req.ReadEntity(&tokenReview)
if err != nil {
klog.V(4).Infoln(err)
api.HandleBadRequest(resp, err)
return
}
if tokenReview.Spec == nil {
api.HandleBadRequest(resp, errors.New("token must not be null"))
if err = tokenReview.Validate(); err != nil {
klog.V(4).Infoln(err)
api.HandleBadRequest(resp, err)
return
}
uToken := tokenReview.Spec.Token
token, err := jwtutil.ValidateToken(uToken)
token, err := jwtutil.ValidateToken(tokenReview.Spec.Token)
if err != nil {
failed := iamv1alpha2.TokenReview{APIVersion: tokenReview.APIVersion,
@@ -59,18 +69,24 @@ func (h *iamHandler) TokenReviewHandler(req *restful.Request, resp *restful.Resp
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)
if !ok {
api.HandleBadRequest(resp, errors.New("username not found"))
api.HandleBadRequest(resp, errors.New("invalid token"))
return
}
user, err := h.imOperator.DescribeUser(username)
if err != nil {
klog.Errorln(err)
api.HandleInternalError(resp, err)
return
}
@@ -92,7 +108,9 @@ func (h *iamHandler) Login(req *restful.Request, resp *restful.Response) {
err := req.ReadEntity(&loginRequest)
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
}
@@ -101,10 +119,12 @@ func (h *iamHandler) Login(req *restful.Request, resp *restful.Response) {
token, err := h.imOperator.Login(loginRequest.Username, loginRequest.Password, ip)
if err != nil {
if serviceError, ok := err.(restful.ServiceError); ok {
resp.WriteHeaderAndEntity(serviceError.Code, errors.New(serviceError.Message))
if err == iam.AuthRateLimitExceeded {
klog.V(4).Infoln(err)
resp.WriteHeaderAndEntity(http.StatusTooManyRequests, err)
return
}
klog.V(4).Infoln(err)
resp.WriteHeaderAndEntity(http.StatusUnauthorized, err)
return
}
@@ -113,14 +133,16 @@ func (h *iamHandler) Login(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)
if err != nil {
klog.V(4).Infoln(err)
api.HandleBadRequest(resp, err)
return
}
if err := createRequest.Validate(); err != nil {
klog.V(4).Infoln(err)
api.HandleBadRequest(resp, err)
return
}
@@ -128,23 +150,223 @@ func (h *iamHandler) CreateUser(req *restful.Request, resp *restful.Response) {
created, err := h.imOperator.CreateUser(createRequest.User)
if err != nil {
if ldap.IsErrorWithCode(err, ldap.LDAPResultEntryAlreadyExists) {
resp.WriteHeaderAndEntity(http.StatusConflict, kserr.Wrap(err))
if err == iam.UserAlreadyExists {
klog.V(4).Infoln(err)
resp.WriteHeaderAndEntity(http.StatusConflict, err)
return
}
klog.Errorln(err)
api.HandleInternalError(resp, err)
return
}
err := h.amOperator.CreateClusterRoleBinding(created.Username, createRequest.ClusterRole)
err = h.amOperator.CreateClusterRoleBinding(created.Username, createRequest.ClusterRole)
if err != nil {
klog.Errorln(err)
api.HandleInternalError(resp, err)
return
}
resp.WriteEntity(created)
}
func (h *iamHandler) DeleteUser(req *restful.Request, resp *restful.Response) {
username := req.PathParameter("user")
operator := req.HeaderParameter(constants.UserNameHeader)
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 {
klog.Errorln(err)
api.HandleInternalError(resp, err)
return
}
err = h.imOperator.DeleteUser(username)
// TODO release user resources
if err != nil {
klog.Errorln(err)
api.HandleInternalError(resp, err)
return
}
resp.WriteEntity(apierr.None)
}
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)
}
func (h *iamHandler) ListUsers(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)
if err != nil {
klog.V(4).Infoln(err)
api.HandleBadRequest(resp, err)
return
}
result, err := h.imOperator.ListUsers(conditions, orderBy, reverse, limit, offset)
if err != nil {
klog.Errorln(err)
api.HandleInternalError(resp, err)
return
}
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) {
namespace := req.PathParameter("namespace")
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)
if err != nil {
klog.V(4).Infoln(err)
api.HandleBadRequest(resp, err)
return
}
result, err := h.amOperator.ListRoles(namespace, conditions, orderBy, reverse, limit, offset)
if err != nil {
klog.Errorln(err)
api.HandleInternalError(resp, err)
return
}
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)
if err != nil {
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")
@@ -152,6 +374,7 @@ func (h *iamHandler) ListRoleUsers(req *restful.Request, resp *restful.Response)
roleBindings, err := h.amOperator.ListRoleBindings(namespace, role)
if err != nil {
klog.Errorln(err)
api.HandleInternalError(resp, err)
return
}
@@ -159,12 +382,13 @@ func (h *iamHandler) ListRoleUsers(req *restful.Request, resp *restful.Response)
for _, roleBinding := range roleBindings {
for _, subject := range roleBinding.Subjects {
if subject.Kind == rbacv1.UserKind {
user, err := h.imOperator.GetUserInfo(subject.Name)
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
}
@@ -176,152 +400,133 @@ func (h *iamHandler) ListRoleUsers(req *restful.Request, resp *restful.Response)
resp.WriteEntity(result)
}
func (h *iamHandler) ListClusterRoles(req *restful.Request, resp *restful.Response) {
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.ListClusterRoles(conditions, orderBy, reverse, limit, offset)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(result)
}
func (h *iamHandler) ListRoles(req *restful.Request, resp *restful.Response) {
namespace := req.PathParameter("namespace")
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.ListRoles(namespace, conditions, orderBy, reverse, limit, offset)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(result)
}
// List users by namespace
func (h *iamHandler) ListNamespaceUsers(req *restful.Request, resp *restful.Response) {
namespace := req.PathParameter("namespace")
users, err := iam.NamespaceUsers(namespace)
roleBindings, err := h.amOperator.ListRoleBindings(namespace, "")
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
klog.Errorln(err)
api.HandleInternalError(resp, err)
return
}
// sort by time by default
sort.Slice(users, func(i, j int) bool {
return users[i].RoleBindTime.After(*users[j].RoleBindTime)
})
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.WriteAsJson(users)
resp.WriteEntity(result)
}
func (h *iamHandler) ListUserRoles(req *restful.Request, resp *restful.Response) {
username := req.PathParameter("user")
roles, err := iam.GetUserRoles("", username)
func (h *iamHandler) ListClusterRoleUsers(req *restful.Request, resp *restful.Response) {
clusterRole := req.PathParameter("clusterrole")
clusterRoleBindings, err := h.amOperator.ListClusterRoleBindings(clusterRole)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
klog.Errorln(err)
api.HandleInternalError(resp, err)
return
}
_, clusterRoles, err := iam.GetUserClusterRoles(username)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
result := make([]*iam.User, 0)
for _, roleBinding := range clusterRoleBindings {
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)
}
}
}
roleList := RoleList{}
roleList.Roles = roles
roleList.ClusterRoles = clusterRoles
resp.WriteAsJson(roleList)
resp.WriteEntity(result)
}
func (h *iamHandler) RulesMapping(req *restful.Request, resp *restful.Response) {
rules := policy.RoleRuleMapping
resp.WriteAsJson(rules)
resp.WriteEntity(rules)
}
func (h *iamHandler) ClusterRulesMapping(req *restful.Request, resp *restful.Response) {
rules := policy.ClusterRoleRuleMapping
resp.WriteAsJson(rules)
resp.WriteEntity(rules)
}
func (h *iamHandler) ListClusterRoleRules(req *restful.Request, resp *restful.Response) {
clusterRoleName := req.PathParameter("clusterrole")
rules, err := iam.GetClusterRoleSimpleRules(clusterRoleName)
clusterRole := req.PathParameter("clusterrole")
rules, err := h.amOperator.GetClusterRoleSimpleRules(clusterRole)
if err != nil {
resp.WriteError(http.StatusInternalServerError, err)
klog.Errorln(err)
api.HandleInternalError(resp, err)
return
}
resp.WriteAsJson(rules)
}
func (h *iamHandler) ListClusterRoleUsers(req *restful.Request, resp *restful.Response) {
clusterRoleName := req.PathParameter("clusterrole")
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.ListClusterRoleUsers(clusterRoleName, conditions, orderBy, reverse, limit, offset)
if err != nil {
if k8serr.IsNotFound(err) {
resp.WriteError(http.StatusNotFound, err)
} else {
resp.WriteError(http.StatusInternalServerError, err)
}
return
}
resp.WriteAsJson(result)
resp.WriteEntity(rules)
}
func (h *iamHandler) ListRoleRules(req *restful.Request, resp *restful.Response) {
namespaceName := req.PathParameter("namespace")
roleName := req.PathParameter("role")
namespace := req.PathParameter("namespace")
role := req.PathParameter("role")
rules, err := iam.GetRoleSimpleRules(namespaceName, roleName)
rules, err := h.amOperator.GetRoleSimpleRules(namespace, role)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
klog.Errorln(err)
api.HandleInternalError(resp, err)
return
}
resp.WriteAsJson(rules)
resp.WriteEntity(rules)
}
func (h *iamHandler) ListWorkspaceRoles(request *restful.Request, response *restful.Response) {
panic("implement me")
}
func (h *iamHandler) DescribeWorkspaceRole(request *restful.Request, response *restful.Response) {
panic("implement me")
}
func (h *iamHandler) ListWorkspaceRoleRules(request *restful.Request, response *restful.Response) {
panic("implement me")
}
func (h *iamHandler) ListWorkspaceUsers(request *restful.Request, response *restful.Response) {
panic("implement me")
}
func (h *iamHandler) InviteUser(request *restful.Request, response *restful.Response) {
panic("implement me")
}
func (h *iamHandler) RemoveUser(request *restful.Request, response *restful.Response) {
panic("implement me")
}
func (h *iamHandler) DescribeWorkspaceUser(request *restful.Request, response *restful.Response) {
panic("implement me")
}