提交 5c4efd53 编写于 作者: H hongming 提交者: zryfish

refactor tenant api

Signed-off-by: Nhongming <talonwan@yunify.com>
上级 71633730
......@@ -192,9 +192,10 @@ func addWebService(c *restful.Container) error {
Param(ws.PathParameter("workspace", "workspace name")).
Doc("Add user to workspace").
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.POST("/workspaces/{workspace}/members").
ws.Route(ws.DELETE("/workspaces/{workspace}/members/{username}").
To(iam.RemoveUser).
Param(ws.PathParameter("workspace", "workspace name")).
Param(ws.PathParameter("name", "username")).
Doc("Remove user from workspace").
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/workspaces/{workspace}/members/{username}").
......
......@@ -91,7 +91,7 @@ func addWebService(c *restful.Container) error {
To(resources.ApplicationHandler).
Writes(models.PageableResponse{}).
Metadata(restfulspec.KeyOpenAPITags, tags).
Doc("Cluster level resource query").
Doc("List applications in cluster").
Param(webservice.QueryParameter(params.ConditionsParam, "query conditions").
Required(false).
DataFormat("key=value,key~value").
......@@ -103,9 +103,24 @@ func addWebService(c *restful.Container) error {
DataFormat("limit=%d,page=%d").
DefaultValue("limit=10,page=1")))
webservice.Route(webservice.GET("/namespaces/{namespace}/applications").
To(resources.NamespacedApplicationHandler).
Writes(models.PageableResponse{}).
Metadata(restfulspec.KeyOpenAPITags, tags).
Doc("List applications").
Param(webservice.QueryParameter(params.ConditionsParam, "query conditions").
Required(false).
DataFormat("key=value,key~value").
DefaultValue("")).
Param(webservice.PathParameter("namespace", "namespace")).
Param(webservice.QueryParameter(params.PagingParam, "page").
Required(false).
DataFormat("limit=%d,page=%d").
DefaultValue("limit=10,page=1")))
webservice.Route(webservice.GET("/storageclasses/{storageclass}/persistentvolumeclaims").
To(resources.GetPvcListBySc).
Doc("get user's kubectl pod").
Doc("query persistent volume claims by storageclass").
Param(webservice.PathParameter("username", "username")).
Metadata(restfulspec.KeyOpenAPITags, tags))
......
......@@ -42,6 +42,10 @@ func addWebService(c *restful.Container) error {
To(tenant.ListWorkspaces).
Doc("List workspace by user").
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/workspaces/{workspace}").
To(tenant.DescribeWorkspace).
Doc("Get workspace detail").
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/workspaces/{workspace}/rules").
To(tenant.ListWorkspaceRules).
Param(ws.PathParameter("workspace", "workspace name")).
......@@ -96,7 +100,7 @@ func addWebService(c *restful.Container) error {
Param(ws.PathParameter("workspace", "workspace name")).
Doc("Create devops project").
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.DELETE("/workspaces/{workspace}/devops").
ws.Route(ws.DELETE("/workspaces/{workspace}/devops/{id}").
To(tenant.DeleteDevopsProject).
Param(ws.PathParameter("workspace", "workspace name")).
Doc("Delete devops project").
......
......@@ -41,7 +41,7 @@ func addWebService(c *restful.Container) error {
tags := []string{"Terminal"}
webservice.Route(webservice.GET("/namespace/{namespace}/pods/{pods}").
webservice.Route(webservice.GET("/namespaces/{namespace}/pods/{pods}").
To(terminal.CreateTerminalSession).
Doc("create terminal session").
Metadata(restfulspec.KeyOpenAPITags, tags).
......
......@@ -142,7 +142,7 @@ func ListGroupUsers(req *restful.Request, resp *restful.Response) {
for i := 0; i < len(group.Members); i++ {
name := group.Members[i]
user, err := iam.DescribeUser(name)
user, err := iam.GetUserInfo(name)
if err != nil {
if ldap.IsErrorWithCode(err, ldap.LDAPResultNoSuchObject) {
......
......@@ -234,22 +234,11 @@ func ListUsers(req *restful.Request, resp *restful.Response) {
conditions, err := params.ParseConditions(req.QueryParameter(params.ConditionsParam))
orderBy := req.QueryParameter(params.OrderByParam)
reverse := params.ParseReverse(req)
names := params.ParseArray(req.QueryParameter(params.NameParam))
if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
return
}
if len(names) > 0 {
users, err := iam.ListUsersByName(names)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
return
}
resp.WriteAsJson(users)
return
}
users, err := iam.ListUsers(conditions, orderBy, reverse, limit, offset)
......
......@@ -91,7 +91,7 @@ func DescribeWorkspaceUser(req *restful.Request, resp *restful.Response) {
return
}
user, err := iam.DescribeUser(username)
user, err := iam.GetUserInfo(username)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
......@@ -132,15 +132,9 @@ func InviteUser(req *restful.Request, resp *restful.Response) {
func RemoveUser(req *restful.Request, resp *restful.Response) {
workspace := req.PathParameter("workspace")
var user models.User
err := req.ReadEntity(&user)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
return
}
err = workspaces.InviteUser(workspace, &user)
username := req.PathParameter("username")
err := workspaces.RemoveUser(workspace, username)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
......
......@@ -19,10 +19,11 @@ package resources
import (
"github.com/emicklei/go-restful"
"k8s.io/api/core/v1"
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/errors"
"kubesphere.io/kubesphere/pkg/models/applications"
//"kubesphere.io/kubesphere/pkg/models/applications"
"kubesphere.io/kubesphere/pkg/models/resources"
"kubesphere.io/kubesphere/pkg/params"
"net/http"
)
......@@ -58,3 +59,42 @@ func ApplicationHandler(req *restful.Request, resp *restful.Response) {
resp.WriteAsJson(result)
}
func NamespacedApplicationHandler(req *restful.Request, resp *restful.Response) {
limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam))
namespaceName := req.PathParameter("namespace")
conditions, err := params.ParseConditions(req.QueryParameter(params.ConditionsParam))
if err != nil {
if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
return
}
}
namespace, err := resources.GetResource("", resources.Namespaces, namespaceName)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
var runtimeId string
if ns, ok := namespace.(*v1.Namespace); ok {
runtimeId = ns.Annotations[constants.OpenPitrixRuntimeAnnotationKey]
}
if runtimeId == "" {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.New("openpitrix runtime not init"))
return
}
result, err := applications.ListApplication(runtimeId, conditions, limit, offset)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(result)
}
......@@ -19,7 +19,6 @@ package resources
import (
"github.com/emicklei/go-restful"
"kubesphere.io/kubesphere/pkg/models"
"kubesphere.io/kubesphere/pkg/models/resources"
"net/http"
......@@ -34,7 +33,6 @@ func ListResources(req *restful.Request, resp *restful.Response) {
orderBy := req.QueryParameter(params.OrderByParam)
limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam))
reverse := params.ParseReverse(req)
names := params.ParseArray(req.QueryParameter(params.NameParam))
if orderBy == "" {
orderBy = resources.CreateTime
......@@ -46,12 +44,7 @@ func ListResources(req *restful.Request, resp *restful.Response) {
return
}
var result *models.PageableResponse
if len(names) > 0 {
result, err = resources.ListResourcesByName(namespace, resourceName, names)
} else {
result, err = resources.ListResources(namespace, resourceName, conditions, orderBy, reverse, limit, offset)
}
result, err := resources.ListResources(namespace, resourceName, conditions, orderBy, reverse, limit, offset)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
......
......@@ -21,6 +21,7 @@ package routers
import (
"fmt"
"github.com/emicklei/go-restful"
k8serr "k8s.io/apimachinery/pkg/api/errors"
"net/http"
"kubesphere.io/kubesphere/pkg/errors"
......@@ -57,7 +58,11 @@ func GetRouter(request *restful.Request, response *restful.Response) {
router, err := routers.GetRouter(namespace)
if err != nil {
response.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
if k8serr.IsNotFound(err) {
response.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err))
} else {
response.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
}
return
}
......
......@@ -19,6 +19,7 @@ package tenant
import (
"github.com/emicklei/go-restful"
"github.com/golang/glog"
"k8s.io/api/core/v1"
k8serr "k8s.io/apimachinery/pkg/api/errors"
"kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1"
......@@ -26,10 +27,11 @@ import (
"kubesphere.io/kubesphere/pkg/errors"
"kubesphere.io/kubesphere/pkg/models"
"kubesphere.io/kubesphere/pkg/models/iam"
"kubesphere.io/kubesphere/pkg/models/resources"
"kubesphere.io/kubesphere/pkg/models/tenant"
"kubesphere.io/kubesphere/pkg/models/workspaces"
"kubesphere.io/kubesphere/pkg/params"
"log"
"kubesphere.io/kubesphere/pkg/simple/client/kubesphere"
"net/http"
)
......@@ -54,6 +56,11 @@ func ListWorkspaces(req *restful.Request, resp *restful.Response) {
limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam))
reverse := params.ParseReverse(req)
if orderBy == "" {
orderBy = resources.CreateTime
reverse = true
}
if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
return
......@@ -69,6 +76,20 @@ func ListWorkspaces(req *restful.Request, resp *restful.Response) {
resp.WriteAsJson(result)
}
func DescribeWorkspace(req *restful.Request, resp *restful.Response) {
username := req.HeaderParameter(constants.UserNameHeader)
workspaceName := req.PathParameter("workspace")
result, err := tenant.DescribeWorkspace(username, workspaceName)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(result)
}
func ListNamespaces(req *restful.Request, resp *restful.Response) {
workspace := req.PathParameter("workspace")
username := req.PathParameter("username")
......@@ -88,7 +109,7 @@ func ListNamespaces(req *restful.Request, resp *restful.Response) {
return
}
conditions.Match["kubesphere.io/workspace"] = workspace
conditions.Match[constants.WorkspaceLabelKey] = workspace
result, err := tenant.ListNamespaces(username, conditions, orderBy, reverse, limit, offset)
......@@ -188,18 +209,24 @@ func ListDevopsProjects(req *restful.Request, resp *restful.Response) {
func DeleteDevopsProject(req *restful.Request, resp *restful.Response) {
devops := req.PathParameter("id")
workspace := req.PathParameter("workspace")
force := req.QueryParameter("force")
workspaceName := req.PathParameter("workspace")
username := req.HeaderParameter(constants.UserNameHeader)
err := workspaces.UnBindDevopsProject(workspace, devops)
_, err := tenant.GetWorkspace(workspaceName)
if err != nil && force != "true" {
if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
return
}
err = kubesphere.Client().DeleteDevopsProject(username, devops)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
err = workspaces.DeleteDevopsProject(username, devops)
err = workspaces.UnBindDevopsProject(workspaceName, devops)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
......@@ -211,7 +238,7 @@ func DeleteDevopsProject(req *restful.Request, resp *restful.Response) {
func CreateDevopsProject(req *restful.Request, resp *restful.Response) {
workspace := req.PathParameter("workspace")
workspaceName := req.PathParameter("workspace")
username := req.HeaderParameter(constants.UserNameHeader)
var devops models.DevopsProject
......@@ -223,8 +250,8 @@ func CreateDevopsProject(req *restful.Request, resp *restful.Response) {
return
}
log.Println("create workspace", username, workspace, devops)
project, err := workspaces.CreateDevopsProject(username, workspace, devops)
glog.Infoln("create workspace", username, workspaceName, devops)
project, err := workspaces.CreateDevopsProject(username, workspaceName, &devops)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
......@@ -232,7 +259,6 @@ func CreateDevopsProject(req *restful.Request, resp *restful.Response) {
}
resp.WriteAsJson(project)
}
func ListNamespaceRules(req *restful.Request, resp *restful.Response) {
......
......@@ -47,7 +47,5 @@ const (
var (
WorkSpaceRoles = []string{WorkspaceAdmin, WorkspaceRegular, WorkspaceViewer}
SystemWorkspace = "system-workspace"
DevopsAPIServer = "ks-devops.kubesphere-devops-system.svc"
SystemNamespaces = []string{KubeSphereNamespace, OpenPitrixNamespace, KubeSystemNamespace}
)
......@@ -153,9 +153,9 @@ func (r *ReconcileWorkspace) Reconcile(request reconcile.Request) (reconcile.Res
return reconcile.Result{}, err
}
if err = r.createGroup(instance); err != nil {
return reconcile.Result{}, err
}
//if err = r.createGroup(instance); err != nil {
// return reconcile.Result{}, err
//}
if err = r.createWorkspaceRoleBindings(instance); err != nil {
return reconcile.Result{}, err
......@@ -369,7 +369,7 @@ func (r *ReconcileWorkspace) createWorkspaceRoleBindings(instance *tenantv1alpha
regularRoleBinding := &rbac.ClusterRoleBinding{}
regularRoleBinding.Name = getWorkspaceRegularRoleBindingName(instance.Name)
regularRoleBinding.Labels = map[string]string{constants.WorkspaceLabelKey: instance.Name}
regularRoleBinding.RoleRef = rbac.RoleRef{APIGroup: "rbac.authorization.k8s.io", Kind: "ClusterRole", Name: getWorkspaceViewerRoleName(instance.Name)}
regularRoleBinding.RoleRef = rbac.RoleRef{APIGroup: "rbac.authorization.k8s.io", Kind: "ClusterRole", Name: getWorkspaceRegularRoleName(instance.Name)}
regularRoleBinding.Subjects = []rbac.Subject{}
if err = controllerutil.SetControllerReference(instance, regularRoleBinding, r.scheme); err != nil {
......
......@@ -18,20 +18,17 @@
package iam
import (
"encoding/json"
"errors"
"fmt"
"github.com/golang/glog"
"io/ioutil"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"kubesphere.io/kubesphere/pkg/informers"
"kubesphere.io/kubesphere/pkg/models/resources"
"kubesphere.io/kubesphere/pkg/params"
"kubesphere.io/kubesphere/pkg/simple/client/k8s"
"kubesphere.io/kubesphere/pkg/simple/client/kubesphere"
"kubesphere.io/kubesphere/pkg/utils/k8sutil"
"kubesphere.io/kubesphere/pkg/utils/sliceutil"
"net/http"
"sort"
"strings"
......@@ -47,7 +44,7 @@ import (
const ClusterRoleKind = "ClusterRole"
func GetUserDevopsSimpleRules(username, projectId string) ([]models.SimpleRule, error) {
role, err := GetUserDevopsRole(projectId, username)
role, err := kubesphere.Client().GetUserDevopsRole(username, projectId)
if err != nil {
return nil, err
......@@ -100,53 +97,6 @@ func GetDevopsRoleSimpleRules(role string) []models.SimpleRule {
return rules
}
func GetUserDevopsRole(projectId string, username string) (string, error) {
//Hard fix
if username == "admin" {
return "owner", nil
}
req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("http://%s/api/v1alpha/projects/%s/members", constants.DevopsAPIServer, projectId), nil)
if err != nil {
return "", err
}
req.Header.Set(constants.UserNameHeader, username)
resp, err := http.DefaultClient.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", err
}
if resp.StatusCode > 200 {
return "", errors.New(string(data))
}
var result []map[string]string
err = json.Unmarshal(data, &result)
if err != nil {
return "", err
}
for _, item := range result {
if item["username"] == username {
return item["role"], nil
}
}
return "", nil
}
// Get user roles in namespace
func GetUserRoles(namespace, username string) ([]*v1.Role, error) {
clusterRoleLister := informers.SharedInformerFactory().Rbac().V1().ClusterRoles().Lister()
......@@ -267,13 +217,6 @@ func GetUserRules(namespace, username string) ([]v1.PolicyRule, error) {
return rules, nil
}
func isUserFacingClusterRole(role *v1.ClusterRole) bool {
if role.Labels[constants.CreatorLabelKey] != "" {
return true
}
return false
}
func GetWorkspaceRoleBindings(workspace string) ([]*v1.ClusterRoleBinding, error) {
clusterRoleBindings, err := informers.SharedInformerFactory().Rbac().V1().ClusterRoleBindings().Lister().List(labels.Everything())
......@@ -386,7 +329,7 @@ func ListClusterRoleUsers(clusterRoleName string, conditions *params.Conditions,
for _, roleBinding := range roleBindings {
for _, subject := range roleBinding.Subjects {
if subject.Kind == v1.UserKind && !k8sutil.ContainsUser(users, subject.Name) {
user, err := DescribeUser(subject.Name)
user, err := GetUserInfo(subject.Name)
if ldap.IsErrorWithCode(err, ldap.LDAPResultNoSuchObject) {
continue
}
......@@ -436,7 +379,7 @@ func RoleUsers(namespace string, roleName string) ([]*models.User, error) {
for _, roleBinding := range roleBindings {
for _, subject := range roleBinding.Subjects {
if subject.Kind == v1.UserKind && !k8sutil.ContainsUser(users, subject.Name) {
user, err := DescribeUser(subject.Name)
user, err := GetUserInfo(subject.Name)
if err != nil {
if ldap.IsErrorWithCode(err, ldap.LDAPResultNoSuchObject) {
......@@ -491,10 +434,14 @@ func NamespaceUsers(namespaceName string) ([]*models.User, error) {
users := make([]*models.User, 0)
for _, roleBinding := range roleBindings {
// controlled by ks-controller-manager
if roleBinding.Name == "admin" || roleBinding.Name == "viewer" {
continue
}
for _, subject := range roleBinding.Subjects {
if subject.Kind == v1.UserKind && !k8sutil.ContainsUser(users, subject.Name) {
user, err := DescribeUser(subject.Name)
user, err := GetUserInfo(subject.Name)
if err != nil {
if ldap.IsErrorWithCode(err, ldap.LDAPResultNoSuchObject) {
......
......@@ -26,6 +26,7 @@ import (
"kubesphere.io/kubesphere/pkg/simple/client/k8s"
"kubesphere.io/kubesphere/pkg/simple/client/redis"
"kubesphere.io/kubesphere/pkg/utils/k8sutil"
"kubesphere.io/kubesphere/pkg/utils/sliceutil"
"regexp"
"sort"
"strconv"
......@@ -232,7 +233,7 @@ func ListUsersByName(names []string) (*models.PageableResponse, error) {
for _, name := range names {
if !k8sutil.ContainsUser(users, name) {
user, err := DescribeUser(name)
user, err := GetUserInfo(name)
if err != nil {
if ldap.IsErrorWithCode(err, ldap.LDAPResultNoSuchObject) {
continue
......@@ -252,6 +253,30 @@ func ListUsersByName(names []string) (*models.PageableResponse, error) {
return &models.PageableResponse{Items: items, TotalCount: len(items)}, nil
}
func ListUserByEmail(email []string) (*models.PageableResponse, error) {
users := make([]*models.User, 0)
for _, mail := range email {
user, err := GetUserInfoByEmail(mail)
if err != nil {
if ldap.IsErrorWithCode(err, ldap.LDAPResultNoSuchObject) {
continue
}
return nil, err
}
if !k8sutil.ContainsUser(users, user.Username) {
users = append(users, user)
}
}
items := make([]interface{}, 0)
for _, u := range users {
items = append(items, u)
}
return &models.PageableResponse{Items: items, TotalCount: len(items)}, nil
}
func ListUsers(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) {
conn, err := ldapclient.Client()
......@@ -272,6 +297,22 @@ func ListUsers(conditions *params.Conditions, orderBy string, reverse bool, limi
filter = fmt.Sprintf("(&(objectClass=inetOrgPerson)(|(uid=*%s*)(mail=*%s*)(description=*%s*)))", keyword, keyword, keyword)
}
if username := conditions.Match["username"]; username != "" {
uidFilter := ""
for _, username := range strings.Split(username, "|") {
uidFilter += fmt.Sprintf("(uid=%s)", username)
}
filter = fmt.Sprintf("(&(objectClass=inetOrgPerson)(|%s))", uidFilter)
}
if email := conditions.Match["email"]; email != "" {
emailFilter := ""
for _, username := range strings.Split(email, "|") {
emailFilter += fmt.Sprintf("(mail=%s)", username)
}
filter = fmt.Sprintf("(&(objectClass=inetOrgPerson)(|%s))", emailFilter)
}
for {
userSearchRequest := ldap.NewSearchRequest(
ldapclient.UserSearchBase,
......@@ -331,26 +372,13 @@ func ListUsers(conditions *params.Conditions, orderBy string, reverse bool, limi
if i >= offset && len(items) < limit {
avatar, err := getAvatar(user.Username)
if err != nil {
return nil, err
}
user.AvatarUrl = avatar
lastLoginTime, err := getLastLoginTime(user.Username)
if err != nil {
return nil, err
}
user.LastLoginTime = lastLoginTime
user.AvatarUrl = getAvatar(user.Username)
user.LastLoginTime = getLastLoginTime(user.Username)
clusterRole, err := GetUserClusterRole(user.Username)
if err != nil {
return nil, err
}
user.ClusterRole = clusterRole.Name
items = append(items, user)
}
}
......@@ -373,23 +401,47 @@ func DescribeUser(username string) (*models.User, error) {
}
user.Groups = groups
user.AvatarUrl = getAvatar(username)
avatar, err := getAvatar(username)
return user, nil
}
func GetUserInfoByEmail(mail string) (*models.User, error) {
conn, err := ldapclient.Client()
if err != nil {
return nil, err
}
user.AvatarUrl = avatar
userSearchRequest := ldap.NewSearchRequest(
ldapclient.UserSearchBase,
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
fmt.Sprintf("(&(objectClass=inetOrgPerson)(mail=%s))", mail),
[]string{"uid", "description", "preferredLanguage", "createTimestamp"},
nil,
)
lastLoginTime, err := getLastLoginTime(username)
result, err := conn.Search(userSearchRequest)
if err != nil {
return nil, err
}
user.LastLoginTime = lastLoginTime
if len(result.Entries) != 1 {
return nil, ldap.NewError(ldap.LDAPResultNoSuchObject, fmt.Errorf("user %s does not exist", mail))
}
username := result.Entries[0].GetAttributeValue("uid")
description := result.Entries[0].GetAttributeValue("description")
lang := result.Entries[0].GetAttributeValue("preferredLanguage")
createTimestamp, _ := time.Parse("20060102150405Z", result.Entries[0].GetAttributeValue("createTimestamp"))
user := &models.User{Username: username, Email: mail, Description: description, Lang: lang, CreateTime: createTimestamp}
user.LastLoginTime = getLastLoginTime(username)
clusterRole, err := GetUserClusterRole(user.Username)
if err != nil {
return nil, err
}
user.ClusterRole = clusterRole.Name
return user, nil
}
......@@ -426,6 +478,8 @@ func GetUserInfo(username string) (*models.User, error) {
createTimestamp, _ := time.Parse("20060102150405Z", result.Entries[0].GetAttributeValue("createTimestamp"))
user := &models.User{Username: username, Email: email, Description: description, Lang: lang, CreateTime: createTimestamp}
user.LastLoginTime = getLastLoginTime(username)
return user, nil
}
......@@ -462,17 +516,18 @@ func GetUserGroups(username string) ([]string, error) {
return groups, nil
}
func getLastLoginTime(username string) (string, error) {
func getLastLoginTime(username string) string {
lastLogin, err := redis.Client().LRange(fmt.Sprintf("kubesphere:users:%s:login-log", username), -1, -1).Result()
if err != nil {
return "", err
return ""
}
if len(lastLogin) > 0 {
return strings.Split(lastLogin[0], ",")[0], nil
return strings.Split(lastLogin[0], ",")[0]
}
return "", nil
return ""
}
func setAvatar(username, avatar string) error {
......@@ -480,20 +535,21 @@ func setAvatar(username, avatar string) error {
return err
}
func getAvatar(username string) (string, error) {
func getAvatar(username string) string {
avatar, err := redis.Client().HMGet("kubesphere:users:avatar", username).Result()
if err != nil {
return "", err
return ""
}
if len(avatar) > 0 {
if url, ok := avatar[0].(string); ok {
return url, nil
return url
}
}
return "", nil
return ""
}
func DeleteUser(username string) error {
......@@ -811,7 +867,7 @@ func UpdateUser(user *models.User) (*models.User, error) {
return nil, err
}
return DescribeUser(user.Username)
return GetUserInfo(user.Username)
}
func DeleteGroup(path string) error {
......@@ -1105,13 +1161,15 @@ func ListWorkspaceUsers(workspace string, conditions *params.Conditions, orderBy
for _, roleBinding := range workspaceRoleBindings {
for _, subject := range roleBinding.Subjects {
if subject.Kind == v1.UserKind && !k8sutil.ContainsUser(users, subject.Name) {
user, err := DescribeUser(subject.Name)
user, err := GetUserInfo(subject.Name)
if err != nil {
return nil, err
}
prefix := fmt.Sprintf("workspace:%s:", workspace)
user.WorkspaceRole = fmt.Sprintf("workspace-%s", strings.TrimPrefix(roleBinding.Name, prefix))
users = append(users, user)
if matchConditions(conditions, user) {
users = append(users, user)
}
}
}
}
......@@ -1141,3 +1199,31 @@ func ListWorkspaceUsers(workspace string, conditions *params.Conditions, orderBy
return &models.PageableResponse{Items: result, TotalCount: len(users)}, nil
}
func matchConditions(conditions *params.Conditions, user *models.User) bool {
for k, v := range conditions.Match {
switch k {
case "keyword":
if !strings.Contains(user.Username, v) &&
!strings.Contains(user.Email, v) &&
!strings.Contains(user.Description, v) {
return false
}
case "name":
names := strings.Split(v, "|")
if !sliceutil.HasString(names, user.Username) {
return false
}
case "email":
email := strings.Split(v, "|")
if !sliceutil.HasString(email, user.Email) {
return false
}
case "role":
if user.WorkspaceRole != v {
return false
}
}
}
return true
}
......@@ -126,6 +126,18 @@ var (
},
},
},
{
Name: "logging",
Actions: []models.Action{
{Name: "view",
Rules: []v1.PolicyRule{{
Verbs: []string{"get", "list"},
APIGroups: []string{"logging.kubesphere.io"},
Resources: []string{"*"},
}},
},
},
},
{
Name: "accounts",
Actions: []models.Action{
......@@ -683,8 +695,8 @@ var (
Rules: []v1.PolicyRule{
{
Verbs: []string{"get"},
APIGroups: []string{"resources.kubesphere.io"},
Resources: []string{"pod/terminal"},
APIGroups: []string{"terminal.kubesphere.io"},
Resources: []string{"pods"},
},
},
},
......
......@@ -18,6 +18,7 @@
package resources
import (
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/informers"
"kubesphere.io/kubesphere/pkg/params"
"kubesphere.io/kubesphere/pkg/utils/k8sutil"
......@@ -55,6 +56,12 @@ func (*clusterRoleSearcher) match(match map[string]string, item *rbac.ClusterRol
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
return false
}
case "userfacing":
if v == "true" {
if !isUserFacingClusterRole(item) {
return false
}
}
default:
if item.Labels[k] != v {
return false
......@@ -134,3 +141,10 @@ func (s *clusterRoleSearcher) search(namespace string, conditions *params.Condit
}
return r, nil
}
func isUserFacingClusterRole(role *rbac.ClusterRole) bool {
if role.Labels[constants.CreatorLabelKey] != "" && role.Labels[constants.WorkspaceLabelKey] == "" {
return true
}
return false
}
......@@ -103,24 +103,15 @@ type resourceSearchInterface interface {
search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error)
}
func ListResourcesByName(namespace, resource string, names []string) (*models.PageableResponse, error) {
items := make([]interface{}, 0)
func GetResource(namespace, resource, name string) (interface{}, error) {
if searcher, ok := resources[resource]; ok {
for _, name := range names {
item, err := searcher.get(namespace, name)
if err != nil {
return nil, err
}
items = append(items, item)
resource, err := searcher.get(namespace, name)
if err != nil {
return nil, err
}
} else {
return nil, fmt.Errorf("not found")
return resource, nil
}
return &models.PageableResponse{TotalCount: len(items), Items: items}, nil
return nil, fmt.Errorf("resource %s not found", resource)
}
func ListResources(namespace, resource string, conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) {
......
......@@ -22,6 +22,7 @@ import (
"fmt"
"github.com/golang/glog"
"io/ioutil"
"k8s.io/apimachinery/pkg/api/errors"
"kubesphere.io/kubesphere/pkg/simple/client/k8s"
"sort"
......@@ -127,7 +128,7 @@ func GetRouter(namespace string) (*corev1.Service, error) {
}
}
return nil, fmt.Errorf("resources not found %s", serviceName)
return nil, errors.NewNotFound(corev1.Resource("service"), serviceName)
}
// Load all resource yamls
......
......@@ -18,15 +18,10 @@
package tenant
import (
"encoding/json"
"fmt"
"io/ioutil"
"kubesphere.io/kubesphere/pkg/constants"
kserr "kubesphere.io/kubesphere/pkg/errors"
"kubesphere.io/kubesphere/pkg/models"
"kubesphere.io/kubesphere/pkg/params"
"kubesphere.io/kubesphere/pkg/simple/client/kubesphere"
"kubesphere.io/kubesphere/pkg/simple/client/mysql"
"net/http"
"sort"
"strings"
)
......@@ -41,79 +36,55 @@ func ListDevopsProjects(workspace, username string, conditions *params.Condition
return nil, err
}
devOpsProjects := make([]models.DevopsProject, 0)
request, _ := http.NewRequest(http.MethodGet, fmt.Sprintf("http://%s/api/v1alpha/projects", constants.DevopsAPIServer), nil)
request.Header.Add(constants.UserNameHeader, username)
resp, err := http.DefaultClient.Do(request)
if err != nil {
return nil, err
}
defer resp.Body.Close()
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
if resp.StatusCode > 200 {
return nil, kserr.Parse(data)
}
err = json.Unmarshal(data, &devOpsProjects)
projects, err := kubesphere.Client().ListDevopsProjects(username)
if err != nil {
return nil, err
}
if keyword := conditions.Match["keyword"]; keyword != "" {
for i := 0; i < len(devOpsProjects); i++ {
if !strings.Contains(devOpsProjects[i].Name, keyword) {
devOpsProjects = append(devOpsProjects[:i], devOpsProjects[i+1:]...)
for i := 0; i < len(projects); i++ {
if !strings.Contains(projects[i].Name, keyword) {
projects = append(projects[:i], projects[i+1:]...)
i--
}
}
}
sort.Slice(devOpsProjects, func(i, j int) bool {
sort.Slice(projects, func(i, j int) bool {
if reverse {
tmp := i
i = j
j = tmp
}
switch orderBy {
case "name":
if reverse {
return devOpsProjects[i].Name < devOpsProjects[j].Name
} else {
return devOpsProjects[i].Name > devOpsProjects[j].Name
}
return projects[i].Name > projects[j].Name
default:
if reverse {
return devOpsProjects[i].CreateTime.After(*devOpsProjects[j].CreateTime)
} else {
return devOpsProjects[i].CreateTime.Before(*devOpsProjects[j].CreateTime)
}
return projects[i].CreateTime.Before(*projects[j].CreateTime)
}
})
for i := 0; i < len(devOpsProjects); i++ {
for i := 0; i < len(projects); i++ {
inWorkspace := false
for _, binding := range workspaceDOPBindings {
if binding.DevOpsProject == *devOpsProjects[i].ProjectId {
if binding.DevOpsProject == projects[i].ProjectId {
inWorkspace = true
}
}
if !inWorkspace {
devOpsProjects = append(devOpsProjects[:i], devOpsProjects[i+1:]...)
projects = append(projects[:i], projects[i+1:]...)
i--
}
}
// limit offset
result := make([]interface{}, 0)
for i, v := range devOpsProjects {
for i, v := range projects {
if len(result) < limit && i >= offset {
result = append(result, v)
}
}
return &models.PageableResponse{Items: result, TotalCount: len(devOpsProjects)}, nil
return &models.PageableResponse{Items: result, TotalCount: len(projects)}, nil
}
......@@ -21,6 +21,7 @@ import (
"k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1"
"k8s.io/apimachinery/pkg/labels"
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/informers"
"kubesphere.io/kubesphere/pkg/models/iam"
"kubesphere.io/kubesphere/pkg/params"
......@@ -35,6 +36,14 @@ type namespaceSearcher struct {
func (*namespaceSearcher) match(match map[string]string, item *v1.Namespace) bool {
for k, v := range match {
switch k {
case "name":
if item.Name != v && item.Labels[constants.DisplayNameLabelKey] != v {
return false
}
case "keyword":
if !strings.Contains(item.Name, v) && !contains(item.Labels, "", v) && !contains(item.Annotations, "", v) {
return false
}
default:
if item.Labels[k] != v {
return false
......
......@@ -19,7 +19,9 @@ package tenant
import (
"k8s.io/api/core/v1"
"kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1"
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/informers"
"kubesphere.io/kubesphere/pkg/models"
ws "kubesphere.io/kubesphere/pkg/models/workspaces"
"kubesphere.io/kubesphere/pkg/params"
......@@ -45,6 +47,18 @@ func CreateNamespace(workspaceName string, namespace *v1.Namespace, username str
return k8s.Client().CoreV1().Namespaces().Create(namespace)
}
func DescribeWorkspace(username, workspaceName string) (*v1alpha1.Workspace, error) {
workspace, err := informers.KsSharedInformerFactory().Tenant().V1alpha1().Workspaces().Lister().Get(workspaceName)
if err != nil {
return nil, err
}
workspace = appendAnnotations(username, workspace)
return workspace, nil
}
func ListWorkspaces(username string, conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) {
workspaces, err := workspaces.search(username, conditions, orderBy, reverse)
......@@ -57,25 +71,7 @@ func ListWorkspaces(username string, conditions *params.Conditions, orderBy stri
result := make([]interface{}, 0)
for i, workspace := range workspaces {
if len(result) < limit && i >= offset {
workspace := workspace.DeepCopy()
ns, err := ListNamespaces(username, &params.Conditions{Match: map[string]string{"kubesphere.io/workspace": workspace.Name}}, "", false, 1, 0)
if err != nil {
return nil, err
}
if workspace.Annotations == nil {
workspace.Annotations = make(map[string]string)
}
workspace.Annotations["kubesphere.io/namespace-count"] = strconv.Itoa(ns.TotalCount)
devops, err := ListDevopsProjects(workspace.Name, username, &params.Conditions{}, "", false, 1, 0)
if err != nil {
return nil, err
}
workspace.Annotations["kubesphere.io/devops-count"] = strconv.Itoa(devops.TotalCount)
userCount, err := ws.WorkspaceUserCount(workspace.Name)
if err != nil {
return nil, err
}
workspace.Annotations["kubesphere.io/member-count"] = strconv.Itoa(userCount)
workspace := appendAnnotations(username, workspace)
result = append(result, workspace)
}
}
......@@ -83,6 +79,32 @@ func ListWorkspaces(username string, conditions *params.Conditions, orderBy stri
return &models.PageableResponse{Items: result, TotalCount: len(workspaces)}, nil
}
func appendAnnotations(username string, workspace *v1alpha1.Workspace) *v1alpha1.Workspace {
workspace = workspace.DeepCopy()
if workspace.Annotations == nil {
workspace.Annotations = make(map[string]string)
}
ns, err := ListNamespaces(username, &params.Conditions{Match: map[string]string{constants.WorkspaceLabelKey: workspace.Name}}, "", false, 1, 0)
if err == nil {
workspace.Annotations["kubesphere.io/namespace-count"] = strconv.Itoa(ns.TotalCount)
} else {
workspace.Annotations["kubesphere.io/namespace-count"] = "-1"
}
devops, err := ListDevopsProjects(workspace.Name, username, &params.Conditions{}, "", false, 1, 0)
if err == nil {
workspace.Annotations["kubesphere.io/devops-count"] = strconv.Itoa(devops.TotalCount)
} else {
workspace.Annotations["kubesphere.io/devops-count"] = "-1"
}
userCount, err := ws.WorkspaceUserCount(workspace.Name)
if err == nil {
workspace.Annotations["kubesphere.io/member-count"] = strconv.Itoa(userCount)
} else {
workspace.Annotations["kubesphere.io/member-count"] = "-1"
}
return workspace
}
func ListNamespaces(username string, conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) {
namespaces, err := namespaces.search(username, conditions, orderBy, reverse)
......
......@@ -40,6 +40,10 @@ func (*workspaceSearcher) match(match map[string]string, item *v1alpha1.Workspac
if item.Name != v && item.Labels[constants.DisplayNameLabelKey] != v {
return false
}
case "keyword":
if !strings.Contains(item.Name, v) && !contains(item.Labels, "", v) && !contains(item.Annotations, "", v) {
return false
}
default:
if item.Labels[k] != v {
return false
......@@ -128,3 +132,16 @@ func (s *workspaceSearcher) search(username string, conditions *params.Condition
func GetWorkspace(workspaceName string) (*v1alpha1.Workspace, error) {
return informers.KsSharedInformerFactory().Tenant().V1alpha1().Workspaces().Lister().Get(workspaceName)
}
func contains(m map[string]string, key, value string) bool {
for k, v := range m {
if key == "" {
if strings.Contains(k, value) || strings.Contains(v, value) {
return true
}
} else if k == key && strings.Contains(v, value) {
return true
}
}
return false
}
......@@ -42,7 +42,7 @@ type WorkspaceDPBinding struct {
}
type DevopsProject struct {
ProjectId *string `json:"project_id,omitempty"`
ProjectId string `json:"project_id,omitempty"`
Name string `json:"name"`
Description string `json:"description"`
Creator string `json:"creator"`
......
......@@ -18,41 +18,29 @@
package workspaces
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"k8s.io/apimachinery/pkg/runtime/schema"
"kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1"
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/informers"
"kubesphere.io/kubesphere/pkg/models"
"kubesphere.io/kubesphere/pkg/models/iam"
"kubesphere.io/kubesphere/pkg/models/resources"
"kubesphere.io/kubesphere/pkg/params"
"kubesphere.io/kubesphere/pkg/simple/client/k8s"
"kubesphere.io/kubesphere/pkg/simple/client/ldap"
"kubesphere.io/kubesphere/pkg/simple/client/kubesphere"
"kubesphere.io/kubesphere/pkg/simple/client/mysql"
"kubesphere.io/kubesphere/pkg/utils/k8sutil"
"kubesphere.io/kubesphere/pkg/utils/sliceutil"
"net/http"
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/informers"
"kubesphere.io/kubesphere/pkg/models"
"kubesphere.io/kubesphere/pkg/models/iam"
"strings"
"github.com/jinzhu/gorm"
core "k8s.io/api/core/v1"
"errors"
"github.com/golang/glog"
"k8s.io/api/rbac/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"sort"
kserr "kubesphere.io/kubesphere/pkg/errors"
)
func UnBindDevopsProject(workspace string, devops string) error {
......@@ -60,191 +48,26 @@ func UnBindDevopsProject(workspace string, devops string) error {
return db.Delete(&models.WorkspaceDPBinding{Workspace: workspace, DevOpsProject: devops}).Error
}
func DeleteDevopsProject(username string, devops string) error {
request, _ := http.NewRequest(http.MethodDelete, fmt.Sprintf("http://%s/api/v1alpha/projects/%s", constants.DevopsAPIServer, devops), nil)
request.Header.Add("X-Token-Username", username)
result, err := http.DefaultClient.Do(request)
if err != nil {
return err
}
defer result.Body.Close()
data, err := ioutil.ReadAll(result.Body)
if err != nil {
return err
}
if result.StatusCode > 200 {
return kserr.Parse(data)
}
return nil
}
func CreateDevopsProject(username string, workspace string, devops models.DevopsProject) (*models.DevopsProject, error) {
data, err := json.Marshal(devops)
if err != nil {
return nil, err
}
request, _ := http.NewRequest(http.MethodPost, fmt.Sprintf("http://%s/api/v1alpha/projects", constants.DevopsAPIServer), bytes.NewReader(data))
request.Header.Add("X-Token-Username", username)
request.Header.Add("Content-Type", "application/json")
result, err := http.DefaultClient.Do(request)
if err != nil {
return nil, err
}
defer result.Body.Close()
data, err = ioutil.ReadAll(result.Body)
if err != nil {
return nil, err
}
func CreateDevopsProject(username string, workspace string, devops *models.DevopsProject) (*models.DevopsProject, error) {
if result.StatusCode > 200 {
return nil, kserr.Parse(data)
}
var project models.DevopsProject
err = json.Unmarshal(data, &project)
created, err := kubesphere.Client().CreateDevopsProject(username, devops)
if err != nil {
return nil, err
}
err = BindingDevopsProject(workspace, *project.ProjectId)
err = BindingDevopsProject(workspace, created.ProjectId)
if err != nil {
DeleteDevopsProject(username, *project.ProjectId)
return nil, err
}
go createDefaultDevopsRoleBinding(workspace, project)
return &project, nil
}
func createDefaultDevopsRoleBinding(workspace string, project models.DevopsProject) error {
admins := []string{""}
for _, admin := range admins {
createDevopsRoleBinding(workspace, *project.ProjectId, admin, constants.DevopsOwner)
}
viewers := []string{""}
for _, viewer := range viewers {
createDevopsRoleBinding(workspace, *project.ProjectId, viewer, constants.DevopsReporter)
}
return nil
}
func createDevopsRoleBinding(workspace string, projectId string, user string, role string) {
projects := make([]string, 0)
if projectId != "" {
projects = append(projects, projectId)
} else {
p, err := GetDevOpsProjects(workspace)
if err != nil {
glog.Warning("create devops role binding failed", workspace, projectId, user, role)
return
}
projects = append(projects, p...)
}
for _, project := range projects {
data := []byte(fmt.Sprintf(`{"username":"%s","role":"%s"}`, user, role))
request, _ := http.NewRequest(http.MethodPost, fmt.Sprintf("http://%s/api/v1alpha/projects/%s/members", constants.DevopsAPIServer, project), bytes.NewReader(data))
request.Header.Add("Content-Type", "application/json")
request.Header.Add("X-Token-Username", "admin")
resp, err := http.DefaultClient.Do(request)
if err != nil || resp.StatusCode > 200 {
glog.Warning(fmt.Sprintf("create devops role binding failed %s,%s,%s,%s", workspace, project, user, role))
}
if resp != nil {
resp.Body.Close()
}
}
}
func ListNamespaceByUser(workspaceName string, username string, keyword string, orderBy string, reverse bool, limit int, offset int) (int, []*core.Namespace, error) {
namespaces, err := Namespaces(workspaceName)
if err != nil {
return 0, nil, err
}
if keyword != "" {
for i := 0; i < len(namespaces); i++ {
if !strings.Contains(namespaces[i].Name, keyword) {
namespaces = append(namespaces[:i], namespaces[i+1:]...)
i--
}
}
}
sort.Slice(namespaces, func(i, j int) bool {
switch orderBy {
case "name":
if reverse {
return namespaces[i].Name < namespaces[j].Name
} else {
return namespaces[i].Name > namespaces[j].Name
}
default:
if reverse {
return namespaces[i].CreationTimestamp.Time.After(namespaces[j].CreationTimestamp.Time)
} else {
return namespaces[i].CreationTimestamp.Time.Before(namespaces[j].CreationTimestamp.Time)
}
}
})
rules, err := iam.GetUserClusterRules(username)
if err != nil {
return 0, nil, err
}
namespacesManager := v1.PolicyRule{APIGroups: []string{"kubesphere.io"}, ResourceNames: []string{workspaceName}, Verbs: []string{"get"}, Resources: []string{"workspaces/namespaces"}}
if !iam.RulesMatchesRequired(rules, namespacesManager) {
for i := 0; i < len(namespaces); i++ {
roles, err := iam.GetUserRoles(namespaces[i].Name, username)
if err != nil {
return 0, nil, err
}
rules := make([]v1.PolicyRule, 0)
for _, role := range roles {
rules = append(rules, role.Rules...)
}
if !iam.RulesMatchesRequired(rules, v1.PolicyRule{APIGroups: []string{""}, ResourceNames: []string{namespaces[i].Name}, Verbs: []string{"get"}, Resources: []string{"namespaces"}}) {
namespaces = append(namespaces[:i], namespaces[i+1:]...)
i--
}
}
}
if len(namespaces) < offset {
return len(namespaces), namespaces, nil
} else if len(namespaces) < limit+offset {
return len(namespaces), namespaces[offset:], nil
} else {
return len(namespaces), namespaces[offset : limit+offset], nil
}
return created, nil
}
func Namespaces(workspaceName string) ([]*core.Namespace, error) {
namespaceLister := informers.SharedInformerFactory().Core().V1().Namespaces().Lister()
namespaces, err := namespaceLister.List(labels.SelectorFromSet(labels.Set{"kubesphere.io/workspace": workspaceName}))
namespaces, err := namespaceLister.List(labels.SelectorFromSet(labels.Set{constants.WorkspaceLabelKey: workspaceName}))
if err != nil {
return nil, err
......@@ -281,161 +104,18 @@ func DeleteNamespace(workspace string, namespaceName string) error {
}
}
func Delete(workspace *models.Workspace) error {
err := release(workspace)
func RemoveUser(workspaceName string, username string) error {
workspaceRole, err := iam.GetUserWorkspaceRole(workspaceName, username)
if err != nil {
return err
}
err = iam.DeleteGroup(workspace.Name)
err = DeleteWorkspaceRoleBinding(workspaceName, username, workspaceRole.Labels[constants.DisplayNameLabelKey])
if err != nil {
return err
}
return nil
}
// TODO
func release(workspace *models.Workspace) error {
for _, namespace := range workspace.Namespaces {
err := DeleteNamespace(workspace.Name, namespace)
if err != nil && !apierrors.IsNotFound(err) {
return err
}
}
for _, devops := range workspace.DevopsProjects {
err := DeleteDevopsProject("admin", devops)
if err != nil && !strings.Contains(err.Error(), "not found") {
return err
}
}
err := workspaceRoleRelease(workspace.Name)
return err
}
func workspaceRoleRelease(workspace string) error {
k8sClient := k8s.Client()
deletePolicy := meta_v1.DeletePropagationForeground
for _, role := range constants.WorkSpaceRoles {
err := k8sClient.RbacV1().ClusterRoles().Delete(fmt.Sprintf("system:%s:%s", workspace, role), &meta_v1.DeleteOptions{PropagationPolicy: &deletePolicy})
if err != nil && !apierrors.IsNotFound(err) {
return err
}
}
for _, role := range constants.WorkSpaceRoles {
err := k8sClient.RbacV1().ClusterRoleBindings().Delete(fmt.Sprintf("system:%s:%s", workspace, role), &meta_v1.DeleteOptions{PropagationPolicy: &deletePolicy})
if err != nil && !apierrors.IsNotFound(err) {
return err
}
}
return nil
}
func Edit(workspace *models.Workspace) (*models.Workspace, error) {
group, err := iam.UpdateGroup(&workspace.Group)
if err != nil {
return nil, err
}
workspace.Group = *group
return workspace, nil
}
func DescribeWorkspace(workspaceName string) (*v1alpha1.Workspace, error) {
workspace, err := informers.KsSharedInformerFactory().Tenant().V1alpha1().Workspaces().Lister().Get(workspaceName)
if err != nil {
return nil, err
}
return workspace, nil
}
func fetch(names []string) ([]*models.Workspace, error) {
if names != nil && len(names) == 0 {
return make([]*models.Workspace, 0), nil
}
var groups []models.Group
var err error
if names == nil {
groups, err = iam.ChildList("")
if err != nil {
return nil, err
}
} else {
conn, err := ldap.Client()
if err != nil {
return nil, err
}
defer conn.Close()
for _, name := range names {
group, err := iam.DescribeGroup(name)
if err != nil {
return nil, err
}
groups = append(groups, *group)
}
}
db := mysql.Client()
workspaces := make([]*models.Workspace, 0)
for _, group := range groups {
workspace, err := convertGroupToWorkspace(db, group)
if err != nil {
return nil, err
}
workspaces = append(workspaces, workspace)
}
return workspaces, nil
}
func convertGroupToWorkspace(db *gorm.DB, group models.Group) (*models.Workspace, error) {
namespaces, err := Namespaces(group.Name)
if err != nil {
return nil, err
}
namespacesNames := make([]string, 0)
for _, namespace := range namespaces {
namespacesNames = append(namespacesNames, namespace.Name)
}
var workspaceDOPBindings []models.WorkspaceDPBinding
if err := db.Where("workspace = ?", group.Name).Find(&workspaceDOPBindings).Error; err != nil {
return nil, err
}
devOpsProjects := make([]string, 0)
for _, workspaceDOPBinding := range workspaceDOPBindings {
devOpsProjects = append(devOpsProjects, workspaceDOPBinding.DevOpsProject)
}
workspace := models.Workspace{Group: group}
workspace.Namespaces = namespacesNames
workspace.DevopsProjects = devOpsProjects
return &workspace, nil
}
func InviteUser(workspaceName string, user *models.User) error {
workspaceRole, err := iam.GetUserWorkspaceRole(workspaceName, user.Username)
......@@ -463,7 +143,6 @@ func CreateWorkspaceRoleBinding(workspace, username string, role string) error {
}
roleBindingName := fmt.Sprintf("workspace:%s:%s", workspace, strings.TrimPrefix(role, "workspace-"))
workspaceRoleBinding, err := informers.SharedInformerFactory().Rbac().V1().ClusterRoleBindings().Lister().Get(roleBindingName)
workspaceRoleBinding = workspaceRoleBinding.DeepCopy()
if err != nil {
......@@ -487,11 +166,10 @@ func DeleteWorkspaceRoleBinding(workspace, username string, role string) error {
roleBindingName := fmt.Sprintf("workspace:%s:%s", workspace, strings.TrimPrefix(role, "workspace-"))
workspaceRoleBinding, err := informers.SharedInformerFactory().Rbac().V1().ClusterRoleBindings().Lister().Get(roleBindingName)
workspaceRoleBinding = workspaceRoleBinding.DeepCopy()
if err != nil {
return err
}
workspaceRoleBinding = workspaceRoleBinding.DeepCopy()
for i, v := range workspaceRoleBinding.Subjects {
if v.Kind == v1.UserKind && v.Name == username {
......
......@@ -82,18 +82,6 @@ func ParseReverse(req *restful.Request) bool {
return b
}
func ParseArray(str string) []string {
arr := make([]string, 0)
for _, item := range strings.Split(str, ",") {
if item = strings.TrimSpace(item); item != "" {
arr = append(arr, item)
}
}
return arr
}
type Conditions struct {
Match map[string]string
Fuzzy map[string]string
......
/*
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 kubesphere
import (
"bytes"
"encoding/json"
"fmt"
"github.com/golang/glog"
"io/ioutil"
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/models"
"net/http"
)
func (c client) DeleteDevopsProject(username string, projectId string) error {
request, _ := http.NewRequest(http.MethodDelete, fmt.Sprintf("%s/api/v1alpha/projects/%s", devopsAPIServer, projectId), nil)
if username == "" {
username = constants.AdminUserName
}
request.Header.Add("X-Token-Username", username)
resp, err := c.client.Do(request)
if err != nil {
return err
}
defer resp.Body.Close()
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
if resp.StatusCode > http.StatusOK {
return Error{resp.StatusCode, string(data)}
}
return nil
}
func (c client) GetUserDevopsRole(username string, projectId string) (string, error) {
if username == "admin" {
return "owner", nil
}
req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/api/v1alpha/projects/%s/members", devopsAPIServer, projectId), nil)
if err != nil {
return "", err
}
req.Header.Set(constants.UserNameHeader, username)
resp, err := c.client.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", err
}
if resp.StatusCode > http.StatusOK {
return "", Error{resp.StatusCode, string(data)}
}
var result []map[string]string
err = json.Unmarshal(data, &result)
if err != nil {
return "", err
}
for _, item := range result {
if item["username"] == username {
return item["role"], nil
}
}
return "", nil
}
func (c client) CreateDevopsProject(username string, project *models.DevopsProject) (*models.DevopsProject, error) {
data, err := json.Marshal(project)
if err != nil {
return nil, err
}
request, _ := http.NewRequest(http.MethodPost, fmt.Sprintf("%s/api/v1alpha/projects", devopsAPIServer), bytes.NewReader(data))
request.Header.Add("X-Token-Username", username)
request.Header.Add("Content-Type", "application/json")
resp, err := c.client.Do(request)
if err != nil {
return nil, err
}
defer resp.Body.Close()
data, err = ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
if resp.StatusCode > http.StatusOK {
return nil, Error{resp.StatusCode, string(data)}
}
var created models.DevopsProject
err = json.Unmarshal(data, &created)
if err != nil {
return nil, err
}
return &created, nil
}
func (c client) CreateDevopsRoleBinding(projectId string, user string, role string) {
projects := make([]string, 0)
projects = append(projects, projectId)
for _, project := range projects {
data := []byte(fmt.Sprintf(`{"username":"%s","role":"%s"}`, user, role))
request, _ := http.NewRequest(http.MethodPost, fmt.Sprintf("%s/api/v1alpha/projects/%s/members", devopsAPIServer, project), bytes.NewReader(data))
request.Header.Add("Content-Type", "application/json")
request.Header.Add("X-Token-Username", "admin")
resp, err := c.client.Do(request)
if err != nil || resp.StatusCode > 200 {
glog.Warning(fmt.Sprintf("create devops role binding failed %s,%s,%s", project, user, role))
}
if resp != nil {
resp.Body.Close()
}
}
}
func (c client) ListDevopsProjects(username string) ([]models.DevopsProject, error) {
projects := make([]models.DevopsProject, 0)
request, _ := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/api/v1alpha/projects", devopsAPIServer), nil)
request.Header.Add(constants.UserNameHeader, username)
resp, err := c.client.Do(request)
if err != nil {
return nil, err
}
defer resp.Body.Close()
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
if resp.StatusCode > http.StatusOK {
return nil, Error{resp.StatusCode, string(data)}
}
err = json.Unmarshal(data, &projects)
if err != nil {
return nil, err
}
return projects, nil
}
......@@ -32,6 +32,7 @@ import (
var (
accountAPIServer string
devopsAPIServer string
once sync.Once
c client
)
......@@ -41,6 +42,11 @@ type Interface interface {
UpdateGroup(group *models.Group) (*models.Group, error)
DescribeGroup(name string) (*models.Group, error)
DeleteGroup(name string) error
DeleteDevopsProject(username string, projectId string) error
GetUserDevopsRole(username string, projectId string) (string, error)
CreateDevopsProject(username string, project *models.DevopsProject) (*models.DevopsProject, error)
CreateDevopsRoleBinding(projectId string, user string, role string)
ListDevopsProjects(username string) ([]models.DevopsProject, error)
}
type client struct {
......@@ -49,6 +55,7 @@ type client struct {
func init() {
flag.StringVar(&accountAPIServer, "ks-account-api-server", "http://ks-account.kubesphere-system.svc", "kubesphere account api server")
flag.StringVar(&devopsAPIServer, "ks-devops-api-server", "http://ks-devops.kubesphere-devops-system.svc", "kubesphere devops api server")
}
func Client() Interface {
......
......@@ -273,11 +273,10 @@ func makeHttpRequest(method, url, data string) ([]byte, error) {
return nil, err
}
httpClient := &http.Client{}
resp, err := httpClient.Do(req)
resp, err := http.DefaultClient.Do(req)
if err != nil {
err := fmt.Errorf("Request to %s failed, method: %s, reason: %s ", url, method, err)
err := fmt.Errorf("Request to %s failed, method: %s,token: %s, reason: %s ", url, method, openpitrixProxyToken, err)
glog.Error(err)
return nil, err
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册