[WIP] API refactor (#1737)

* refactor openpitrix API

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

* add openpitrix mock client

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

* refactor tenant API

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

* refactor IAM API

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

* refactor IAM API

Signed-off-by: hongming <talonwan@yunify.com>
This commit is contained in:
hongming
2020-01-13 13:36:21 +08:00
committed by zryfish
parent c40d1542a2
commit 71849f028f
66 changed files with 5415 additions and 4366 deletions

View File

@@ -16,185 +16,3 @@
*/
package iam
import (
"github.com/emicklei/go-restful"
"k8s.io/api/rbac/v1"
k8serr "k8s.io/apimachinery/pkg/api/errors"
"kubesphere.io/kubesphere/pkg/server/params"
"net/http"
"sort"
"kubesphere.io/kubesphere/pkg/models/iam"
"kubesphere.io/kubesphere/pkg/models/iam/policy"
"kubesphere.io/kubesphere/pkg/server/errors"
)
type RoleList struct {
ClusterRoles []*v1.ClusterRole `json:"clusterRole" description:"cluster role list"`
Roles []*v1.Role `json:"roles" description:"role list"`
}
func ListRoleUsers(req *restful.Request, resp *restful.Response) {
roleName := req.PathParameter("role")
namespace := req.PathParameter("namespace")
users, err := iam.RoleUsers(namespace, roleName)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(users)
}
func 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 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 ListNamespaceUsers(req *restful.Request, resp *restful.Response) {
namespace := req.PathParameter("namespace")
users, err := iam.NamespaceUsers(namespace)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
// sort by time by default
sort.Slice(users, func(i, j int) bool {
return users[i].RoleBindTime.After(*users[j].RoleBindTime)
})
resp.WriteAsJson(users)
}
func ListUserRoles(req *restful.Request, resp *restful.Response) {
username := req.PathParameter("user")
roles, err := iam.GetUserRoles("", username)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
_, clusterRoles, err := iam.GetUserClusterRoles(username)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
roleList := RoleList{}
roleList.Roles = roles
roleList.ClusterRoles = clusterRoles
resp.WriteAsJson(roleList)
}
func RulesMapping(req *restful.Request, resp *restful.Response) {
rules := policy.RoleRuleMapping
resp.WriteAsJson(rules)
}
func ClusterRulesMapping(req *restful.Request, resp *restful.Response) {
rules := policy.ClusterRoleRuleMapping
resp.WriteAsJson(rules)
}
func ListClusterRoleRules(req *restful.Request, resp *restful.Response) {
clusterRoleName := req.PathParameter("clusterrole")
rules, err := iam.GetClusterRoleSimpleRules(clusterRoleName)
if err != nil {
resp.WriteError(http.StatusInternalServerError, err)
return
}
resp.WriteAsJson(rules)
}
func 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)
}
func ListRoleRules(req *restful.Request, resp *restful.Response) {
namespaceName := req.PathParameter("namespace")
roleName := req.PathParameter("role")
rules, err := iam.GetRoleSimpleRules(namespaceName, roleName)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(rules)
}

View File

@@ -19,38 +19,15 @@ package iam
import (
"fmt"
"github.com/dgrijalva/jwt-go"
"github.com/emicklei/go-restful"
"k8s.io/klog"
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"
"kubesphere.io/kubesphere/pkg/utils/jwtutil"
"net/http"
)
type Spec struct {
Token string `json:"token" description:"access token"`
}
type Status struct {
Authenticated bool `json:"authenticated" description:"is authenticated"`
User map[string]interface{} `json:"user,omitempty" description:"user info"`
}
type TokenReview struct {
APIVersion string `json:"apiVersion" description:"Kubernetes API version"`
Kind string `json:"kind" description:"kind of the API object"`
Spec *Spec `json:"spec,omitempty"`
Status *Status `json:"status,omitempty" description:"token review status"`
}
type LoginRequest struct {
Username string `json:"username" description:"username"`
Password string `json:"password" description:"password"`
}
type OAuthRequest struct {
GrantType string `json:"grant_type"`
Username string `json:"username,omitempty" description:"username"`
@@ -58,36 +35,6 @@ type OAuthRequest struct {
RefreshToken string `json:"refresh_token,omitempty"`
}
const (
KindTokenReview = "TokenReview"
)
func Login(req *restful.Request, resp *restful.Response) {
var loginRequest LoginRequest
err := req.ReadEntity(&loginRequest)
if err != nil || loginRequest.Username == "" || loginRequest.Password == "" {
resp.WriteHeaderAndEntity(http.StatusUnauthorized, errors.New("incorrect username or password"))
return
}
ip := iputil.RemoteIp(req.Request)
token, err := iam.Login(loginRequest.Username, loginRequest.Password, ip)
if err != nil {
if serviceError, ok := err.(restful.ServiceError); ok {
resp.WriteHeaderAndEntity(serviceError.Code, errors.New(serviceError.Message))
return
}
resp.WriteHeaderAndEntity(http.StatusUnauthorized, errors.Wrap(err))
return
}
resp.WriteAsJson(token)
}
func OAuth(req *restful.Request, resp *restful.Response) {
authRequest := &OAuthRequest{}
@@ -120,72 +67,3 @@ func OAuth(req *restful.Request, resp *restful.Response) {
resp.WriteEntity(result)
}
// k8s token review
func TokenReviewHandler(req *restful.Request, resp *restful.Response) {
var tokenReview TokenReview
err := req.ReadEntity(&tokenReview)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
return
}
if tokenReview.Spec == nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.New("token must not be null"))
return
}
uToken := tokenReview.Spec.Token
token, err := jwtutil.ValidateToken(uToken)
if err != nil {
klog.Errorln("token review failed", uToken, err)
failed := TokenReview{APIVersion: tokenReview.APIVersion,
Kind: KindTokenReview,
Status: &Status{
Authenticated: false,
},
}
resp.WriteAsJson(failed)
return
}
claims := token.Claims.(jwt.MapClaims)
username, ok := claims["username"].(string)
if !ok {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.New("username not found"))
return
}
user, err := iam.GetUserInfo(username)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
groups, err := iam.GetUserGroups(username)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
user.Groups = groups
success := TokenReview{APIVersion: tokenReview.APIVersion,
Kind: KindTokenReview,
Status: &Status{
Authenticated: true,
User: map[string]interface{}{"username": user.Username, "uid": user.Username, "groups": user.Groups},
},
}
resp.WriteAsJson(success)
return
}

View File

@@ -136,7 +136,7 @@ func ListGroupUsers(req *restful.Request, resp *restful.Response) {
return
}
users := make([]*models.User, 0)
users := make([]*iam.User, 0)
modify := false

View File

@@ -29,60 +29,10 @@ import (
"github.com/go-ldap/ldap"
rbacv1 "k8s.io/api/rbac/v1"
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/models"
"kubesphere.io/kubesphere/pkg/models/iam"
"kubesphere.io/kubesphere/pkg/server/errors"
)
func CreateUser(req *restful.Request, resp *restful.Response) {
var user models.User
err := req.ReadEntity(&user)
if err != nil {
klog.Info(err)
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
return
}
if user.Username == "" {
err = fmt.Errorf("invalid username: %s", user.Username)
klog.Info(err, user.Username)
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
return
}
// Parses a single RFC 5322 address, e.g. "Barry Gibbs <bg@example.com>"
if _, err = mail.ParseAddress(user.Email); err != nil {
err = fmt.Errorf("invalid email: %s", user.Email)
klog.Info(err, user.Email)
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
return
}
if len(user.Password) < 6 {
err = fmt.Errorf("invalid password")
klog.Info(err)
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
return
}
created, err := iam.CreateUser(&user)
if err != nil {
if ldap.IsErrorWithCode(err, ldap.LDAPResultEntryAlreadyExists) {
klog.Info(err)
resp.WriteHeaderAndEntity(http.StatusConflict, errors.Wrap(err))
return
}
klog.Info(err)
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(created)
}
func DeleteUser(req *restful.Request, resp *restful.Response) {
username := req.PathParameter("user")
@@ -110,7 +60,7 @@ func UpdateUser(req *restful.Request, resp *restful.Response) {
usernameInPath := req.PathParameter("user")
usernameInHeader := req.HeaderParameter(constants.UserNameHeader)
var user models.User
var user iam.User
err := req.ReadEntity(&user)
@@ -254,8 +204,8 @@ func DescribeUser(req *restful.Request, resp *restful.Response) {
}
result := struct {
*models.User
ClusterRules []models.SimpleRule `json:"cluster_rules"`
*iam.User
ClusterRules []iam.SimpleRule `json:"cluster_rules"`
}{
User: user,
ClusterRules: clusterRules,

View File

@@ -21,7 +21,6 @@ import (
"github.com/emicklei/go-restful"
k8serr "k8s.io/apimachinery/pkg/api/errors"
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/models"
"kubesphere.io/kubesphere/pkg/models/iam"
"kubesphere.io/kubesphere/pkg/models/workspaces"
"kubesphere.io/kubesphere/pkg/server/errors"
@@ -113,7 +112,7 @@ func ListDevopsRoleRules(req *restful.Request, resp *restful.Response) {
func InviteUser(req *restful.Request, resp *restful.Response) {
workspace := req.PathParameter("workspace")
var user models.User
var user iam.User
err := req.ReadEntity(&user)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))