add workspace api
This commit is contained in:
@@ -39,6 +39,7 @@ import (
|
||||
"kubesphere.io/kubesphere/pkg/apis/v1alpha/users"
|
||||
"kubesphere.io/kubesphere/pkg/apis/v1alpha/volumes"
|
||||
"kubesphere.io/kubesphere/pkg/apis/v1alpha/workloadstatus"
|
||||
"kubesphere.io/kubesphere/pkg/apis/v1alpha/workspaces"
|
||||
_ "kubesphere.io/kubesphere/pkg/filter/container"
|
||||
)
|
||||
|
||||
@@ -67,7 +68,7 @@ func init() {
|
||||
statefulsets.Register(ws, "/namespaces/{namespace}/statefulsets/{statefulset}/revisions/{revision}")
|
||||
resources.Register(ws, "/resources")
|
||||
monitoring.Register(ws, "/monitoring")
|
||||
|
||||
workspaces.Register(ws, "/workspaces")
|
||||
// add webservice to default container
|
||||
restful.Add(ws)
|
||||
|
||||
|
||||
430
pkg/apis/v1alpha/workspaces/workspaces.go
Normal file
430
pkg/apis/v1alpha/workspaces/workspaces.go
Normal file
@@ -0,0 +1,430 @@
|
||||
package workspaces
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/emicklei/go-restful"
|
||||
"k8s.io/api/core/v1"
|
||||
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"k8s.io/kubernetes/pkg/util/slice"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/constants"
|
||||
"kubesphere.io/kubesphere/pkg/models/iam"
|
||||
"kubesphere.io/kubesphere/pkg/models/workspaces"
|
||||
)
|
||||
|
||||
func Register(ws *restful.WebService, subPath string) {
|
||||
ws.Route(ws.GET(subPath).To(WorkspaceListHandler))
|
||||
ws.Route(ws.POST(subPath).To(WorkspaceCreateHandler))
|
||||
ws.Route(ws.DELETE(subPath + "/{name}").To(DeleteWorkspaceHandler))
|
||||
ws.Route(ws.GET(subPath + "/{name}").To(WorkspaceDetailHandler))
|
||||
ws.Route(ws.PUT(subPath + "/{name}").To(WorkspaceEditHandler))
|
||||
ws.Route(ws.GET(subPath + "/{name}/namespaces").To(NamespaceHandler))
|
||||
ws.Route(ws.POST(subPath + "/{name}/namespaces").To(NamespaceCreateHandler))
|
||||
ws.Route(ws.DELETE(subPath + "/{name}/namespaces/{namespace}").To(NamespaceDeleteHandler))
|
||||
ws.Route(ws.GET(subPath + "/{name}/devops").To(DevOpsProjectHandler))
|
||||
ws.Route(ws.POST(subPath + "/{name}/devops").To(DevOpsProjectCreateHandler))
|
||||
ws.Route(ws.DELETE(subPath + "/{name}/devops/{id}").To(DevOpsProjectDeleteHandler))
|
||||
ws.Route(ws.GET(subPath + "/{name}/members").To(MembersHandler))
|
||||
ws.Route(ws.GET(subPath + "/{name}/members/{member}").To(MemberHandler))
|
||||
ws.Route(ws.GET(subPath + "/{name}/roles").To(RolesHandler))
|
||||
ws.Route(ws.GET(subPath + "/{name}/roles/{role}").To(RoleHandler))
|
||||
ws.Route(ws.POST(subPath + "/{name}/members").To(MembersInviteHandler))
|
||||
ws.Route(ws.DELETE(subPath + "/{name}/members").To(MembersRemoveHandler))
|
||||
}
|
||||
|
||||
func RoleHandler(req *restful.Request, resp *restful.Response) {
|
||||
workspaceName := req.PathParameter("name")
|
||||
roleName := req.PathParameter("role")
|
||||
|
||||
if !slice.ContainsString(workspaces.WorkSpaceRoles, roleName, nil) {
|
||||
resp.WriteHeaderAndEntity(http.StatusNotFound, constants.MessageResponse{Message: fmt.Sprintf("role %s not found", roleName)})
|
||||
return
|
||||
}
|
||||
|
||||
role, rules, err := iam.WorkspaceRoleRules(workspaceName, roleName)
|
||||
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
users, err := iam.WorkspaceRoleUsers(workspaceName, roleName)
|
||||
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
resp.WriteEntity(map[string]interface{}{"role": role, "rules": rules, "users": users})
|
||||
}
|
||||
|
||||
func RolesHandler(req *restful.Request, resp *restful.Response) {
|
||||
|
||||
name := req.PathParameter("name")
|
||||
|
||||
workspace, err := workspaces.Detail(name)
|
||||
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
roles, err := workspaces.Roles(workspace)
|
||||
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
resp.WriteEntity(roles)
|
||||
}
|
||||
|
||||
func MembersHandler(req *restful.Request, resp *restful.Response) {
|
||||
workspace := req.PathParameter("name")
|
||||
|
||||
users, err := workspaces.GetWorkspaceMembers(workspace)
|
||||
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
resp.WriteEntity(users)
|
||||
}
|
||||
|
||||
func MemberHandler(req *restful.Request, resp *restful.Response) {
|
||||
workspace := req.PathParameter("name")
|
||||
username := req.PathParameter("member")
|
||||
|
||||
user, err := iam.GetUser(username)
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
namespaces, err := workspaces.Namespaces(workspace)
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
user.WorkspaceRole = user.WorkspaceRoles[workspace]
|
||||
|
||||
roles := make(map[string]string)
|
||||
|
||||
for _, namespace := range namespaces {
|
||||
if role := user.Roles[namespace.Name]; role != "" {
|
||||
roles[namespace.Name] = role
|
||||
}
|
||||
}
|
||||
|
||||
user.Roles = roles
|
||||
user.Rules = nil
|
||||
user.WorkspaceRules = nil
|
||||
user.WorkspaceRoles = nil
|
||||
user.ClusterRules = nil
|
||||
resp.WriteEntity(user)
|
||||
}
|
||||
|
||||
func MembersInviteHandler(req *restful.Request, resp *restful.Response) {
|
||||
var users []workspaces.UserInvite
|
||||
workspace := req.PathParameter("name")
|
||||
err := req.ReadEntity(&users)
|
||||
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
err = workspaces.Invite(workspace, users)
|
||||
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
resp.WriteHeaderAndEntity(http.StatusOK, constants.MessageResponse{Message: "success"})
|
||||
}
|
||||
|
||||
func MembersRemoveHandler(req *restful.Request, resp *restful.Response) {
|
||||
query := req.QueryParameter("name")
|
||||
workspace := req.PathParameter("name")
|
||||
|
||||
names := strings.Split(query, ",")
|
||||
|
||||
err := workspaces.RemoveMembers(workspace, names)
|
||||
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
resp.WriteHeaderAndEntity(http.StatusOK, constants.MessageResponse{Message: "success"})
|
||||
}
|
||||
|
||||
func NamespaceDeleteHandler(req *restful.Request, resp *restful.Response) {
|
||||
namespace := req.PathParameter("namespace")
|
||||
workspace := req.PathParameter("name")
|
||||
force := req.QueryParameter("force")
|
||||
err := workspaces.UnBindNamespace(workspace, namespace)
|
||||
|
||||
if err != nil && force != "true" {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
err = workspaces.DeleteNamespace(namespace)
|
||||
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
resp.WriteHeaderAndEntity(http.StatusOK, constants.MessageResponse{Message: "success"})
|
||||
}
|
||||
|
||||
func DevOpsProjectDeleteHandler(req *restful.Request, resp *restful.Response) {
|
||||
devops := req.PathParameter("id")
|
||||
workspace := req.PathParameter("name")
|
||||
force := req.QueryParameter("force")
|
||||
username := req.HeaderParameter("X-Token-Username")
|
||||
|
||||
err := workspaces.UnBindDevopsProject(workspace, devops)
|
||||
|
||||
if err != nil && force != "true" {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
err = workspaces.DeleteDevopsProject(username, devops)
|
||||
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
resp.WriteEntity(constants.MessageResponse{Message: "success"})
|
||||
}
|
||||
|
||||
func DevOpsProjectCreateHandler(req *restful.Request, resp *restful.Response) {
|
||||
|
||||
workspace := req.PathParameter("name")
|
||||
username := req.HeaderParameter("X-Token-Username")
|
||||
|
||||
var devops workspaces.DevopsProject
|
||||
|
||||
err := req.ReadEntity(&devops)
|
||||
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusBadRequest, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
project, err := workspaces.CreateDevopsProject(username, devops)
|
||||
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if project.ProjectId == nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: "project create failed"})
|
||||
} else {
|
||||
err = workspaces.BindingDevopsProject(workspace, *project.ProjectId)
|
||||
|
||||
if err != nil {
|
||||
workspaces.DeleteDevopsProject(username, *project.ProjectId)
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
resp.WriteEntity(project)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func NamespaceCreateHandler(req *restful.Request, resp *restful.Response) {
|
||||
workspace := req.PathParameter("name")
|
||||
username := req.HeaderParameter("X-Token-Username")
|
||||
|
||||
namespace := &v1.Namespace{}
|
||||
|
||||
err := req.ReadEntity(namespace)
|
||||
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusBadRequest, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if namespace.Annotations == nil {
|
||||
namespace.Annotations = make(map[string]string, 0)
|
||||
}
|
||||
|
||||
namespace.Annotations["creator"] = username
|
||||
namespace.Annotations["workspace"] = workspace
|
||||
|
||||
namespace, err = workspaces.CreateNamespace(namespace)
|
||||
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusBadRequest, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
err = workspaces.BindingNamespace(workspace, namespace.Name)
|
||||
|
||||
if err != nil {
|
||||
workspaces.DeleteNamespace(namespace.Name)
|
||||
resp.WriteHeaderAndEntity(http.StatusBadRequest, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
resp.WriteEntity(namespace)
|
||||
}
|
||||
|
||||
func DevOpsProjectHandler(req *restful.Request, resp *restful.Response) {
|
||||
|
||||
workspace := req.PathParameter("name")
|
||||
|
||||
devOpsProjects, err := workspaces.DevopsProjects(workspace)
|
||||
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
resp.WriteEntity(devOpsProjects)
|
||||
}
|
||||
|
||||
func NamespaceHandler(req *restful.Request, resp *restful.Response) {
|
||||
|
||||
workspace := req.PathParameter("name")
|
||||
|
||||
namespaces, err := workspaces.Namespaces(workspace)
|
||||
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
resp.WriteEntity(namespaces)
|
||||
}
|
||||
func WorkspaceCreateHandler(req *restful.Request, resp *restful.Response) {
|
||||
var workspace workspaces.Workspace
|
||||
username := req.HeaderParameter("X-Token-Username")
|
||||
err := req.ReadEntity(&workspace)
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusBadRequest, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
if workspace.Name == "" || strings.Contains(workspace.Name, ":") {
|
||||
resp.WriteHeaderAndEntity(http.StatusBadRequest, constants.MessageResponse{Message: "invalid workspace name"})
|
||||
return
|
||||
}
|
||||
|
||||
workspace.Path = workspace.Name
|
||||
workspace.Members = nil
|
||||
|
||||
workspace.Creator = username
|
||||
|
||||
created, err := workspaces.Create(&workspace)
|
||||
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
resp.WriteEntity(created)
|
||||
|
||||
}
|
||||
|
||||
func DeleteWorkspaceHandler(req *restful.Request, resp *restful.Response) {
|
||||
name := req.PathParameter("name")
|
||||
|
||||
if name == "" || strings.Contains(name, ":") {
|
||||
resp.WriteHeaderAndEntity(http.StatusBadRequest, constants.MessageResponse{Message: "invalid workspace name"})
|
||||
return
|
||||
}
|
||||
|
||||
workspace, err := workspaces.Detail(name)
|
||||
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
err = workspaces.Delete(workspace)
|
||||
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
resp.WriteEntity(constants.MessageResponse{Message: "success"})
|
||||
}
|
||||
func WorkspaceEditHandler(req *restful.Request, resp *restful.Response) {
|
||||
var workspace workspaces.Workspace
|
||||
name := req.PathParameter("name")
|
||||
err := req.ReadEntity(&workspace)
|
||||
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusBadRequest, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if name != workspace.Name {
|
||||
resp.WriteError(http.StatusBadRequest, fmt.Errorf("the name of workspace (%s) does not match the name on the URL (%s)", workspace.Name, name))
|
||||
return
|
||||
}
|
||||
|
||||
if workspace.Name == "" || strings.Contains(workspace.Name, ":") {
|
||||
resp.WriteHeaderAndEntity(http.StatusBadRequest, constants.MessageResponse{Message: "invalid workspace name"})
|
||||
return
|
||||
}
|
||||
|
||||
workspace.Path = workspace.Name
|
||||
|
||||
workspace.Members = nil
|
||||
|
||||
edited, err := workspaces.Edit(&workspace)
|
||||
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
resp.WriteEntity(edited)
|
||||
}
|
||||
func WorkspaceDetailHandler(req *restful.Request, resp *restful.Response) {
|
||||
|
||||
name := req.PathParameter("name")
|
||||
|
||||
workspace, err := workspaces.Detail(name)
|
||||
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
resp.WriteEntity(workspace)
|
||||
}
|
||||
|
||||
func WorkspaceListHandler(req *restful.Request, resp *restful.Response) {
|
||||
|
||||
var names []string
|
||||
|
||||
if query := req.QueryParameter("name"); query != "" {
|
||||
names = strings.Split(query, ",")
|
||||
}
|
||||
|
||||
list, err := workspaces.List(names)
|
||||
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
resp.WriteEntity(list)
|
||||
}
|
||||
@@ -43,6 +43,7 @@ import (
|
||||
"kubesphere.io/kubesphere/pkg/constants"
|
||||
"kubesphere.io/kubesphere/pkg/models"
|
||||
"kubesphere.io/kubesphere/pkg/models/controllers"
|
||||
"kubesphere.io/kubesphere/pkg/models/workspaces"
|
||||
"kubesphere.io/kubesphere/pkg/options"
|
||||
)
|
||||
|
||||
@@ -92,6 +93,16 @@ func preCheck() error {
|
||||
models.CreateKubectlDeploy(constants.AdminUserName)
|
||||
return nil
|
||||
}
|
||||
|
||||
db := client.NewSharedDBClient()
|
||||
defer db.Close()
|
||||
if !db.HasTable(&workspaces.WorkspaceNSBinding{}) {
|
||||
db.CreateTable(&workspaces.WorkspaceNSBinding{})
|
||||
}
|
||||
|
||||
if !db.HasTable(&workspaces.WorkspaceDPBinding{}) {
|
||||
db.CreateTable(&workspaces.WorkspaceDPBinding{})
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -40,14 +40,29 @@ const (
|
||||
DataHome = "/etc/kubesphere"
|
||||
IngressControllerFolder = DataHome + "/ingress-controller"
|
||||
IngressControllerPrefix = "kubesphere-router-"
|
||||
DevopsAPIServerEnv = "DEVOPS_API_SERVER"
|
||||
AccountAPIServerEnv = "ACCOUNT_API_SERVER"
|
||||
DevopsProxyTokenEnv = "DEVOPS_PROXY_TOKEN"
|
||||
OpenPitrixProxyTokenEnv = "OPENPITRIX_PROXY_TOKEN"
|
||||
)
|
||||
|
||||
var (
|
||||
DevopsAPIServer = "ks-devops-apiserver.kubesphere-system.svc"
|
||||
AccountAPIServer = "ks-account.kubesphere-system.svc"
|
||||
DevopsProxyToken = ""
|
||||
OpenPitrixProxyToken = ""
|
||||
)
|
||||
|
||||
func init() {
|
||||
if env := os.Getenv(DevopsAPIServerEnv); env != "" {
|
||||
DevopsAPIServer = env
|
||||
}
|
||||
if env := os.Getenv(AccountAPIServerEnv); env != "" {
|
||||
AccountAPIServer = env
|
||||
}
|
||||
if env := os.Getenv(DevopsProxyTokenEnv); env != "" {
|
||||
DevopsProxyToken = env
|
||||
}
|
||||
if env := os.Getenv(OpenPitrixProxyTokenEnv); env != "" {
|
||||
OpenPitrixProxyToken = env
|
||||
}
|
||||
|
||||
@@ -1,18 +1,152 @@
|
||||
package iam
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"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/kubernetes/pkg/util/slice"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/client"
|
||||
"kubesphere.io/kubesphere/pkg/constants"
|
||||
ksErr "kubesphere.io/kubesphere/pkg/util/errors"
|
||||
)
|
||||
|
||||
const ClusterRoleKind = "ClusterRole"
|
||||
|
||||
// Get user list based on workspace role
|
||||
func WorkspaceRoleUsers(workspace string, roleName string) ([]User, error) {
|
||||
|
||||
k8sClient := client.NewK8sClient()
|
||||
|
||||
roleBinding, err := k8sClient.RbacV1().ClusterRoleBindings().Get(fmt.Sprintf("system:%s:%s", workspace, roleName), meta_v1.GetOptions{})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
names := make([]string, 0)
|
||||
|
||||
for _, subject := range roleBinding.Subjects {
|
||||
if subject.Kind == v1.UserKind {
|
||||
names = append(names, subject.Name)
|
||||
}
|
||||
}
|
||||
|
||||
users, err := GetUsers(names)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for i := 0; i < len(users); i++ {
|
||||
users[i].WorkspaceRole = roleName
|
||||
}
|
||||
|
||||
return users, nil
|
||||
}
|
||||
|
||||
func GetUsers(names []string) ([]User, error) {
|
||||
var users []User
|
||||
|
||||
if names == nil || len(names) == 0 {
|
||||
return make([]User, 0), nil
|
||||
}
|
||||
|
||||
result, err := http.Get(fmt.Sprintf("http://%s/apis/account.kubesphere.io/v1alpha1/users?name=%s", constants.AccountAPIServer, strings.Join(names, ",")))
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data, err := ioutil.ReadAll(result.Body)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if result.StatusCode > 200 {
|
||||
return nil, ksErr.Wrap(data)
|
||||
}
|
||||
|
||||
err = json.Unmarshal(data, &users)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return users, nil
|
||||
}
|
||||
|
||||
func GetUser(name string) (*User, error) {
|
||||
|
||||
result, err := http.Get(fmt.Sprintf("http://%s/apis/account.kubesphere.io/v1alpha1/users/%s", constants.AccountAPIServer, name))
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data, err := ioutil.ReadAll(result.Body)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if result.StatusCode > 200 {
|
||||
return nil, ksErr.Wrap(data)
|
||||
}
|
||||
|
||||
var user User
|
||||
|
||||
err = json.Unmarshal(data, &user)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
// Get rules
|
||||
func WorkspaceRoleRules(workspace string, roleName string) (*v1.ClusterRole, []Rule, error) {
|
||||
k8sClient := client.NewK8sClient()
|
||||
|
||||
role, err := k8sClient.RbacV1().ClusterRoles().Get(fmt.Sprintf("system:%s:%s", workspace, roleName), meta_v1.GetOptions{})
|
||||
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
for i := 0; i < len(role.Rules); i++ {
|
||||
role.Rules[i].ResourceNames = nil
|
||||
}
|
||||
|
||||
rules := make([]Rule, 0)
|
||||
for i := 0; i < len(WorkspaceRoleRuleMapping); i++ {
|
||||
rule := Rule{Name: WorkspaceRoleRuleMapping[i].Name}
|
||||
rule.Actions = make([]Action, 0)
|
||||
for j := 0; j < len(WorkspaceRoleRuleMapping[i].Actions); j++ {
|
||||
if actionValidate(role.Rules, WorkspaceRoleRuleMapping[i].Actions[j]) {
|
||||
rule.Actions = append(rule.Actions, WorkspaceRoleRuleMapping[i].Actions[j])
|
||||
}
|
||||
}
|
||||
if len(rule.Actions) > 0 {
|
||||
rules = append(rules, rule)
|
||||
}
|
||||
}
|
||||
|
||||
role.Name = roleName
|
||||
|
||||
return role, rules, nil
|
||||
}
|
||||
|
||||
func GetUserNamespaces(username string, requiredRule v1.PolicyRule) (allNamespace bool, namespaces []string, err error) {
|
||||
|
||||
clusterRoles, err := GetClusterRoles(username)
|
||||
@@ -247,11 +381,18 @@ func GetClusterRoles(username string) ([]v1.ClusterRole, error) {
|
||||
if role.Annotations == nil {
|
||||
role.Annotations = make(map[string]string, 0)
|
||||
}
|
||||
|
||||
role.Annotations["rbac.authorization.k8s.io/clusterrolebinding"] = roleBinding.Name
|
||||
|
||||
if roleBinding.Annotations != nil &&
|
||||
roleBinding.Annotations["rbac.authorization.k8s.io/clusterrole"] == roleBinding.RoleRef.Name {
|
||||
role.Annotations["rbac.authorization.k8s.io/clusterrole"] = "true"
|
||||
}
|
||||
|
||||
roles = append(roles, *role)
|
||||
break
|
||||
} else if apierrors.IsNotFound(err) {
|
||||
glog.Infoln(err.Error())
|
||||
log.Println(err)
|
||||
break
|
||||
} else {
|
||||
return nil, err
|
||||
@@ -303,21 +444,172 @@ func ruleValidate(rules []v1.PolicyRule, rule v1.PolicyRule) bool {
|
||||
|
||||
func verbValidate(rules []v1.PolicyRule, apiGroup string, nonResourceURL string, resource string, resourceName string, verb string) bool {
|
||||
for _, rule := range rules {
|
||||
if slice.ContainsString(rule.APIGroups, apiGroup, nil) || slice.ContainsString(rule.APIGroups, v1.APIGroupAll, nil) {
|
||||
if slice.ContainsString(rule.Verbs, verb, nil) || slice.ContainsString(rule.Verbs, v1.VerbAll, nil) {
|
||||
if nonResourceURL == "" {
|
||||
if slice.ContainsString(rule.Resources, resource, nil) || slice.ContainsString(rule.Resources, v1.ResourceAll, nil) {
|
||||
if resourceName == "" {
|
||||
return true
|
||||
} else if slice.ContainsString(rule.ResourceNames, resourceName, nil) || slice.ContainsString(rule.Resources, v1.ResourceAll, nil) {
|
||||
|
||||
if nonResourceURL == "" {
|
||||
if slice.ContainsString(rule.APIGroups, apiGroup, nil) ||
|
||||
slice.ContainsString(rule.APIGroups, v1.APIGroupAll, nil) {
|
||||
if slice.ContainsString(rule.Verbs, verb, nil) ||
|
||||
slice.ContainsString(rule.Verbs, v1.VerbAll, nil) {
|
||||
if slice.ContainsString(rule.Resources, v1.ResourceAll, nil) {
|
||||
return true
|
||||
} else if slice.ContainsString(rule.Resources, resource, nil) {
|
||||
if len(rule.ResourceNames) > 0 {
|
||||
if slice.ContainsString(rule.ResourceNames, resourceName, nil) {
|
||||
return true
|
||||
}
|
||||
} else if resourceName == "" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
} else if slice.ContainsString(rule.NonResourceURLs, nonResourceURL, nil) || slice.ContainsString(rule.NonResourceURLs, v1.NonResourceAll, nil) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
} else if slice.ContainsString(rule.NonResourceURLs, nonResourceURL, nil) ||
|
||||
slice.ContainsString(rule.NonResourceURLs, v1.NonResourceAll, nil) {
|
||||
if slice.ContainsString(rule.Verbs, verb, nil) ||
|
||||
slice.ContainsString(rule.Verbs, v1.VerbAll, nil) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func GetUserRules(username string) (map[string][]Rule, error) {
|
||||
|
||||
items := make(map[string][]Rule, 0)
|
||||
userRoles, err := GetRoles(username)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rulesMapping := make(map[string][]v1.PolicyRule, 0)
|
||||
|
||||
for _, role := range userRoles {
|
||||
rules := rulesMapping[role.Namespace]
|
||||
if rules == nil {
|
||||
rules = make([]v1.PolicyRule, 0)
|
||||
}
|
||||
rules = append(rules, role.Rules...)
|
||||
rulesMapping[role.Namespace] = rules
|
||||
}
|
||||
|
||||
for namespace, policyRules := range rulesMapping {
|
||||
rules := convertToRules(policyRules)
|
||||
if len(rules) > 0 {
|
||||
items[namespace] = rules
|
||||
}
|
||||
}
|
||||
|
||||
return items, nil
|
||||
}
|
||||
|
||||
func convertToRules(policyRules []v1.PolicyRule) []Rule {
|
||||
rules := make([]Rule, 0)
|
||||
|
||||
for i := 0; i < (len(RoleRuleGroup)); i++ {
|
||||
rule := Rule{Name: RoleRuleGroup[i].Name}
|
||||
rule.Actions = make([]Action, 0)
|
||||
for j := 0; j < (len(RoleRuleGroup[i].Actions)); j++ {
|
||||
if actionValidate(policyRules, RoleRuleGroup[i].Actions[j]) {
|
||||
rule.Actions = append(rule.Actions, RoleRuleGroup[i].Actions[j])
|
||||
}
|
||||
}
|
||||
|
||||
if len(rule.Actions) > 0 {
|
||||
rules = append(rules, rule)
|
||||
}
|
||||
}
|
||||
|
||||
return rules
|
||||
}
|
||||
|
||||
func GetUserClusterRules(username string) ([]Rule, error) {
|
||||
|
||||
rules := make([]Rule, 0)
|
||||
|
||||
clusterRoles, err := GetClusterRoles(username)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
clusterRules := make([]v1.PolicyRule, 0)
|
||||
|
||||
for _, role := range clusterRoles {
|
||||
clusterRules = append(clusterRules, role.Rules...)
|
||||
}
|
||||
|
||||
for i := 0; i < (len(ClusterRoleRuleGroup)); i++ {
|
||||
rule := Rule{Name: ClusterRoleRuleGroup[i].Name}
|
||||
rule.Actions = make([]Action, 0)
|
||||
for j := 0; j < (len(ClusterRoleRuleGroup[i].Actions)); j++ {
|
||||
if actionValidate(clusterRules, ClusterRoleRuleGroup[i].Actions[j]) {
|
||||
rule.Actions = append(rule.Actions, ClusterRoleRuleGroup[i].Actions[j])
|
||||
}
|
||||
}
|
||||
if len(rule.Actions) > 0 {
|
||||
rules = append(rules, rule)
|
||||
}
|
||||
}
|
||||
|
||||
return rules, nil
|
||||
}
|
||||
|
||||
func GetClusterRoleRules(name string) ([]Rule, error) {
|
||||
|
||||
clusterRole, err := GetClusterRole(name)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rules := make([]Rule, 0)
|
||||
|
||||
for i := 0; i < len(ClusterRoleRuleGroup); i++ {
|
||||
rule := Rule{Name: ClusterRoleRuleGroup[i].Name}
|
||||
rule.Actions = make([]Action, 0)
|
||||
for j := 0; j < (len(ClusterRoleRuleGroup[i].Actions)); j++ {
|
||||
if actionValidate(clusterRole.Rules, ClusterRoleRuleGroup[i].Actions[j]) {
|
||||
rule.Actions = append(rule.Actions, ClusterRoleRuleGroup[i].Actions[j])
|
||||
}
|
||||
}
|
||||
if len(rule.Actions) > 0 {
|
||||
rules = append(rules, rule)
|
||||
}
|
||||
}
|
||||
|
||||
return rules, nil
|
||||
}
|
||||
|
||||
func GetRoleRules(namespace string, name string) ([]Rule, error) {
|
||||
role, err := GetRole(namespace, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rules := make([]Rule, 0)
|
||||
for i := 0; i < len(RoleRuleGroup); i++ {
|
||||
rule := Rule{Name: RoleRuleGroup[i].Name}
|
||||
rule.Actions = make([]Action, 0)
|
||||
for j := 0; j < len(RoleRuleGroup[i].Actions); j++ {
|
||||
if actionValidate(role.Rules, RoleRuleGroup[i].Actions[j]) {
|
||||
rule.Actions = append(rule.Actions, RoleRuleGroup[i].Actions[j])
|
||||
}
|
||||
}
|
||||
if len(rule.Actions) > 0 {
|
||||
rules = append(rules, rule)
|
||||
}
|
||||
}
|
||||
return rules, nil
|
||||
}
|
||||
|
||||
func actionValidate(rules []v1.PolicyRule, action Action) bool {
|
||||
for _, rule := range action.Rules {
|
||||
if !ruleValidate(rules, rule) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -29,16 +29,6 @@ const (
|
||||
clusterRulesConfigPath = "/etc/kubesphere/rules/clusterrules.json"
|
||||
)
|
||||
|
||||
type Action struct {
|
||||
Name string `json:"name"`
|
||||
Rules []v1.PolicyRule `json:"rules"`
|
||||
}
|
||||
|
||||
type Rule struct {
|
||||
Name string `json:"name"`
|
||||
Actions []Action `json:"actions"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
rulesConfig, err := ioutil.ReadFile(rulesConfigPath)
|
||||
if err == nil {
|
||||
@@ -61,6 +51,101 @@ func init() {
|
||||
}
|
||||
|
||||
var (
|
||||
WorkspaceRoleRuleMapping = []Rule{
|
||||
{
|
||||
Name: "workspaces",
|
||||
Actions: []Action{
|
||||
{Name: "edit",
|
||||
Rules: []v1.PolicyRule{
|
||||
{
|
||||
Verbs: []string{"update", "patch"},
|
||||
APIGroups: []string{"kubesphere.io"},
|
||||
Resources: []string{"workspaces"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{Name: "delete",
|
||||
Rules: []v1.PolicyRule{
|
||||
{
|
||||
Verbs: []string{"delete"},
|
||||
APIGroups: []string{"kubesphere.io"},
|
||||
Resources: []string{"workspaces"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
{Name: "members",
|
||||
Actions: []Action{
|
||||
{Name: "view",
|
||||
Rules: []v1.PolicyRule{
|
||||
{
|
||||
Verbs: []string{"list"},
|
||||
APIGroups: []string{"kubesphere.io"},
|
||||
Resources: []string{"workspaces/members"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "devops",
|
||||
Actions: []Action{
|
||||
{Name: "view",
|
||||
Rules: []v1.PolicyRule{
|
||||
{
|
||||
Verbs: []string{"list"},
|
||||
APIGroups: []string{"kubesphere.io"},
|
||||
Resources: []string{"workspaces/devops"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{Name: "create",
|
||||
Rules: []v1.PolicyRule{
|
||||
{
|
||||
Verbs: []string{"create"},
|
||||
APIGroups: []string{"kubesphere.io"},
|
||||
Resources: []string{"workspaces/devops"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "projects",
|
||||
Actions: []Action{
|
||||
{Name: "view",
|
||||
Rules: []v1.PolicyRule{
|
||||
{
|
||||
Verbs: []string{"list"},
|
||||
APIGroups: []string{"kubesphere.io"},
|
||||
Resources: []string{"workspaces/namespaces"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{Name: "create",
|
||||
Rules: []v1.PolicyRule{
|
||||
{
|
||||
Verbs: []string{"create"},
|
||||
APIGroups: []string{"kubesphere.io"},
|
||||
Resources: []string{"workspaces/namespaces"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "registries",
|
||||
Actions: []Action{
|
||||
{Name: "view"},
|
||||
{Name: "create"},
|
||||
{Name: "edit"},
|
||||
{Name: "delete"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
ClusterRoleRuleGroup = []Rule{{
|
||||
Name: "projects",
|
||||
Actions: []Action{
|
||||
|
||||
@@ -1,161 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 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 (
|
||||
"k8s.io/api/rbac/v1"
|
||||
)
|
||||
|
||||
func GetUserRules(username string) (map[string][]Rule, error) {
|
||||
|
||||
items := make(map[string][]Rule, 0)
|
||||
userRoles, err := GetRoles(username)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rulesMapping := make(map[string][]v1.PolicyRule, 0)
|
||||
|
||||
for _, role := range userRoles {
|
||||
rules := rulesMapping[role.Namespace]
|
||||
if rules == nil {
|
||||
rules = make([]v1.PolicyRule, 0)
|
||||
}
|
||||
rules = append(rules, role.Rules...)
|
||||
rulesMapping[role.Namespace] = rules
|
||||
}
|
||||
|
||||
for namespace, policyRules := range rulesMapping {
|
||||
rules := convertToRules(policyRules)
|
||||
if len(rules) > 0 {
|
||||
items[namespace] = rules
|
||||
}
|
||||
}
|
||||
|
||||
return items, nil
|
||||
}
|
||||
|
||||
func convertToRules(policyRules []v1.PolicyRule) []Rule {
|
||||
rules := make([]Rule, 0)
|
||||
|
||||
for i := 0; i < (len(RoleRuleGroup)); i++ {
|
||||
rule := Rule{Name: RoleRuleGroup[i].Name}
|
||||
rule.Actions = make([]Action, 0)
|
||||
for j := 0; j < (len(RoleRuleGroup[i].Actions)); j++ {
|
||||
if actionValidate(policyRules, RoleRuleGroup[i].Actions[j]) {
|
||||
rule.Actions = append(rule.Actions, RoleRuleGroup[i].Actions[j])
|
||||
}
|
||||
}
|
||||
|
||||
if len(rule.Actions) > 0 {
|
||||
rules = append(rules, rule)
|
||||
}
|
||||
}
|
||||
|
||||
return rules
|
||||
}
|
||||
|
||||
func GetUserClusterRules(username string) ([]Rule, error) {
|
||||
|
||||
rules := make([]Rule, 0)
|
||||
|
||||
clusterRoles, err := GetClusterRoles(username)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
clusterRules := make([]v1.PolicyRule, 0)
|
||||
|
||||
for _, role := range clusterRoles {
|
||||
clusterRules = append(clusterRules, role.Rules...)
|
||||
}
|
||||
|
||||
for i := 0; i < (len(ClusterRoleRuleGroup)); i++ {
|
||||
rule := Rule{Name: ClusterRoleRuleGroup[i].Name}
|
||||
rule.Actions = make([]Action, 0)
|
||||
for j := 0; j < (len(ClusterRoleRuleGroup[i].Actions)); j++ {
|
||||
if actionValidate(clusterRules, ClusterRoleRuleGroup[i].Actions[j]) {
|
||||
rule.Actions = append(rule.Actions, ClusterRoleRuleGroup[i].Actions[j])
|
||||
}
|
||||
}
|
||||
if len(rule.Actions) > 0 {
|
||||
rules = append(rules, rule)
|
||||
}
|
||||
}
|
||||
|
||||
return rules, nil
|
||||
}
|
||||
|
||||
func GetClusterRoleRules(name string) ([]Rule, error) {
|
||||
|
||||
clusterRole, err := GetClusterRole(name)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rules := make([]Rule, 0)
|
||||
|
||||
for i := 0; i < len(ClusterRoleRuleGroup); i++ {
|
||||
rule := Rule{Name: ClusterRoleRuleGroup[i].Name}
|
||||
rule.Actions = make([]Action, 0)
|
||||
for j := 0; j < (len(ClusterRoleRuleGroup[i].Actions)); j++ {
|
||||
if actionValidate(clusterRole.Rules, ClusterRoleRuleGroup[i].Actions[j]) {
|
||||
rule.Actions = append(rule.Actions, ClusterRoleRuleGroup[i].Actions[j])
|
||||
}
|
||||
}
|
||||
if len(rule.Actions) > 0 {
|
||||
rules = append(rules, rule)
|
||||
}
|
||||
}
|
||||
|
||||
return rules, nil
|
||||
}
|
||||
|
||||
func GetRoleRules(namespace string, name string) ([]Rule, error) {
|
||||
role, err := GetRole(namespace, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rules := make([]Rule, 0)
|
||||
for i := 0; i < len(RoleRuleGroup); i++ {
|
||||
rule := Rule{Name: RoleRuleGroup[i].Name}
|
||||
rule.Actions = make([]Action, 0)
|
||||
for j := 0; j < len(RoleRuleGroup[i].Actions); j++ {
|
||||
if actionValidate(role.Rules, RoleRuleGroup[i].Actions[j]) {
|
||||
rule.Actions = append(rule.Actions, RoleRuleGroup[i].Actions[j])
|
||||
}
|
||||
}
|
||||
if len(rule.Actions) > 0 {
|
||||
rules = append(rules, rule)
|
||||
}
|
||||
}
|
||||
return rules, nil
|
||||
}
|
||||
|
||||
func actionValidate(rules []v1.PolicyRule, action Action) bool {
|
||||
for _, rule := range action.Rules {
|
||||
if !ruleValidate(rules, rule) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
41
pkg/models/iam/types.go
Normal file
41
pkg/models/iam/types.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package iam
|
||||
|
||||
import (
|
||||
"k8s.io/api/rbac/v1"
|
||||
)
|
||||
|
||||
type Action struct {
|
||||
Name string `json:"name"`
|
||||
Rules []v1.PolicyRule `json:"rules"`
|
||||
}
|
||||
|
||||
type Rule struct {
|
||||
Name string `json:"name"`
|
||||
Actions []Action `json:"actions"`
|
||||
}
|
||||
|
||||
type SimpleRule struct {
|
||||
Name string `json:"name"`
|
||||
Actions []string `json:"actions"`
|
||||
}
|
||||
|
||||
type User struct {
|
||||
Username string `json:"username"`
|
||||
//UID string `json:"uid"`
|
||||
Groups []string `json:"groups"`
|
||||
Password string `json:"password,omitempty"`
|
||||
//Extra map[string]interface{} `json:"extra"`
|
||||
AvatarUrl string `json:"avatar_url"`
|
||||
Description string `json:"description"`
|
||||
Email string `json:"email"`
|
||||
LastLoginTime string `json:"last_login_time"`
|
||||
Status int `json:"status"`
|
||||
ClusterRole string `json:"cluster_role"`
|
||||
ClusterRules []SimpleRule `json:"cluster_rules,omitempty"`
|
||||
Roles map[string]string `json:"roles,omitempty"`
|
||||
Rules map[string][]SimpleRule `json:"rules,omitempty"`
|
||||
Role string `json:"role,omitempty"`
|
||||
WorkspaceRoles map[string]string `json:"workspace_roles,omitempty"`
|
||||
WorkspaceRole string `json:"workspace_role,omitempty"`
|
||||
WorkspaceRules map[string][]SimpleRule `json:"workspace_rules,omitempty"`
|
||||
}
|
||||
46
pkg/models/workspaces/types.go
Normal file
46
pkg/models/workspaces/types.go
Normal file
@@ -0,0 +1,46 @@
|
||||
package workspaces
|
||||
|
||||
import "time"
|
||||
|
||||
type Workspace struct {
|
||||
Group `json:",inline"`
|
||||
Namespaces []string `json:"namespaces,omitempty"`
|
||||
DevopsProjects []string `json:"devops_projects,omitempty"`
|
||||
}
|
||||
|
||||
type UserInvite struct {
|
||||
Username string `json:"username"`
|
||||
Role string `json:"role"`
|
||||
}
|
||||
|
||||
type Group struct {
|
||||
Path string `json:"path"`
|
||||
Name string `json:"name"`
|
||||
Gid string `json:"gid"`
|
||||
Members []string `json:"members"`
|
||||
Logo string `json:"logo"`
|
||||
Creator string `json:"creator"`
|
||||
CreateTime string `json:"create_time"`
|
||||
ChildGroups []string `json:"child_groups,omitempty"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
type WorkspaceNSBinding struct {
|
||||
Workspace string `gorm:"primary_key"`
|
||||
Namespace string `gorm:"primary_key"`
|
||||
}
|
||||
|
||||
type WorkspaceDPBinding struct {
|
||||
Workspace string `gorm:"primary_key"`
|
||||
DevOpsProject string `gorm:"primary_key"`
|
||||
}
|
||||
|
||||
type DevopsProject struct {
|
||||
ProjectId *string `json:"project_id,omitempty"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Creator string `json:"creator"`
|
||||
CreateTime *time.Time `json:"create_time,omitempty"`
|
||||
Status *string `json:"status"`
|
||||
Visibility *string `json:"visibility,omitempty"`
|
||||
}
|
||||
859
pkg/models/workspaces/workspaces.go
Normal file
859
pkg/models/workspaces/workspaces.go
Normal file
@@ -0,0 +1,859 @@
|
||||
package workspaces
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
"github.com/jinzhu/gorm"
|
||||
core "k8s.io/api/core/v1"
|
||||
k8sErr "k8s.io/apimachinery/pkg/api/errors"
|
||||
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"github.com/emicklei/go-restful"
|
||||
"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"
|
||||
v13 "k8s.io/client-go/listers/rbac/v1"
|
||||
"k8s.io/kubernetes/pkg/util/slice"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/client"
|
||||
"kubesphere.io/kubesphere/pkg/constants"
|
||||
"kubesphere.io/kubesphere/pkg/models/controllers"
|
||||
"kubesphere.io/kubesphere/pkg/models/iam"
|
||||
ksErr "kubesphere.io/kubesphere/pkg/util/errors"
|
||||
)
|
||||
|
||||
var WorkSpaceRoles = []string{"admin", "operator", "viewer"}
|
||||
|
||||
func UnBindNamespace(workspace string, namespace string) error {
|
||||
db := client.NewSharedDBClient()
|
||||
defer db.Close()
|
||||
return db.Delete(&WorkspaceNSBinding{Workspace: workspace, Namespace: namespace}).Error
|
||||
}
|
||||
|
||||
func UnBindDevopsProject(workspace string, devops string) error {
|
||||
db := client.NewSharedDBClient()
|
||||
defer db.Close()
|
||||
return db.Delete(&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
|
||||
}
|
||||
data, err := ioutil.ReadAll(result.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if result.StatusCode > 200 {
|
||||
return ksErr.Wrap(data)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func CreateDevopsProject(username string, devops DevopsProject) (*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
|
||||
}
|
||||
|
||||
data, err = ioutil.ReadAll(result.Body)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if result.StatusCode > 200 {
|
||||
return nil, ksErr.Wrap(data)
|
||||
}
|
||||
|
||||
var project DevopsProject
|
||||
|
||||
err = json.Unmarshal(data, &project)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &project, nil
|
||||
}
|
||||
|
||||
func Namespaces(workspace string) ([]*core.Namespace, error) {
|
||||
db := client.NewSharedDBClient()
|
||||
defer db.Close()
|
||||
|
||||
var workspaceNSBindings []WorkspaceNSBinding
|
||||
|
||||
if err := db.Where("workspace = ?", workspace).Find(&workspaceNSBindings).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
namespaces := make([]*core.Namespace, 0)
|
||||
|
||||
for _, workspaceNSBinding := range workspaceNSBindings {
|
||||
namespace, err := client.NewK8sClient().CoreV1().Namespaces().Get(workspaceNSBinding.Namespace, meta_v1.GetOptions{})
|
||||
if err != nil {
|
||||
if k8sErr.IsNotFound(err) {
|
||||
db.Delete(&WorkspaceNSBinding{Workspace: workspace, Namespace: workspaceNSBinding.Namespace})
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
namespaces = append(namespaces, namespace)
|
||||
}
|
||||
}
|
||||
|
||||
return namespaces, nil
|
||||
}
|
||||
|
||||
func BindingDevopsProject(workspace string, devops string) error {
|
||||
db := client.NewSharedDBClient()
|
||||
defer db.Close()
|
||||
return db.Create(&WorkspaceDPBinding{Workspace: workspace, DevOpsProject: devops}).Error
|
||||
}
|
||||
|
||||
func DeleteNamespace(namespace string) error {
|
||||
deletePolicy := meta_v1.DeletePropagationBackground
|
||||
err := client.NewK8sClient().CoreV1().Namespaces().Delete(namespace, &meta_v1.DeleteOptions{PropagationPolicy: &deletePolicy})
|
||||
return err
|
||||
}
|
||||
|
||||
func Delete(workspace *Workspace) error {
|
||||
|
||||
err := release(workspace)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(http.MethodDelete, fmt.Sprintf("http://%s/apis/account.kubesphere.io/v1alpha1/groups/%s", constants.AccountAPIServer, workspace.Name), nil)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
result, err := http.DefaultClient.Do(req)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
data, err := ioutil.ReadAll(result.Body)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if result.StatusCode > 200 {
|
||||
return ksErr.Wrap(data)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func release(workspace *Workspace) error {
|
||||
for _, namespace := range workspace.Namespaces {
|
||||
err := DeleteNamespace(namespace)
|
||||
if err != nil && !apierrors.IsNotFound(err) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for _, devops := range workspace.DevopsProjects {
|
||||
err := DeleteDevopsProject(workspace.Creator, devops)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err := workspaceRoleRelease(workspace.Name)
|
||||
|
||||
return err
|
||||
}
|
||||
func workspaceRoleRelease(workspace string) error {
|
||||
k8sClient := client.NewK8sClient()
|
||||
deletePolicy := meta_v1.DeletePropagationForeground
|
||||
|
||||
for _, role := range 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 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 Create(workspace *Workspace) (*Workspace, error) {
|
||||
|
||||
data, err := json.Marshal(workspace)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result, err := http.Post(fmt.Sprintf("http://%s/apis/account.kubesphere.io/v1alpha1/groups", constants.AccountAPIServer), restful.MIME_JSON, bytes.NewReader(data))
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data, err = ioutil.ReadAll(result.Body)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if result.StatusCode > 200 {
|
||||
return nil, ksErr.Wrap(data)
|
||||
}
|
||||
|
||||
var created Workspace
|
||||
|
||||
err = json.Unmarshal(data, &created)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
created.Members = make([]string, 0)
|
||||
created.Namespaces = make([]string, 0)
|
||||
created.DevopsProjects = make([]string, 0)
|
||||
|
||||
go WorkspaceRoleInit(workspace)
|
||||
|
||||
return &created, nil
|
||||
}
|
||||
|
||||
func Edit(workspace *Workspace) (*Workspace, error) {
|
||||
|
||||
data, err := json.Marshal(workspace)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(http.MethodPut, fmt.Sprintf("http://%s/apis/account.kubesphere.io/v1alpha1/groups/%s", constants.AccountAPIServer, workspace.Name), bytes.NewReader(data))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result, err := http.DefaultClient.Do(req)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data, err = ioutil.ReadAll(result.Body)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if result.StatusCode > 200 {
|
||||
return nil, ksErr.Wrap(data)
|
||||
}
|
||||
|
||||
var edited Workspace
|
||||
|
||||
err = json.Unmarshal(data, &edited)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &edited, nil
|
||||
}
|
||||
|
||||
func Detail(name string) (*Workspace, error) {
|
||||
|
||||
result, err := http.Get(fmt.Sprintf("http://%s/apis/account.kubesphere.io/v1alpha1/groups/%s", constants.AccountAPIServer, name))
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data, err := ioutil.ReadAll(result.Body)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if result.StatusCode > 200 {
|
||||
return nil, ksErr.Wrap(data)
|
||||
}
|
||||
|
||||
var group Group
|
||||
|
||||
err = json.Unmarshal(data, &group)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
db := client.NewSharedDBClient()
|
||||
defer db.Close()
|
||||
|
||||
workspace, err := convertGroupToWorkspace(db, group)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return workspace, nil
|
||||
}
|
||||
|
||||
func List(names []string) ([]*Workspace, error) {
|
||||
|
||||
url := fmt.Sprintf("http://%s/apis/account.kubesphere.io/v1alpha1/groups", constants.AccountAPIServer)
|
||||
|
||||
if names != nil {
|
||||
url = url + "?path=" + strings.Join(names, ",")
|
||||
}
|
||||
|
||||
result, err := http.Get(url)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data, err := ioutil.ReadAll(result.Body)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if result.StatusCode > 200 {
|
||||
return nil, ksErr.Wrap(data)
|
||||
}
|
||||
|
||||
var groups []Group
|
||||
|
||||
err = json.Unmarshal(data, &groups)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
db := client.NewSharedDBClient()
|
||||
|
||||
defer db.Close()
|
||||
|
||||
workspaces := make([]*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 DevopsProjects(workspace string) ([]DevopsProject, error) {
|
||||
|
||||
db := client.NewSharedDBClient()
|
||||
defer db.Close()
|
||||
|
||||
var workspaceDOPBindings []WorkspaceDPBinding
|
||||
|
||||
if err := db.Where("workspace = ?", workspace).Find(&workspaceDOPBindings).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
devOpsProjects := make([]DevopsProject, 0)
|
||||
|
||||
for _, workspaceDOPBinding := range workspaceDOPBindings {
|
||||
request, _ := http.NewRequest(http.MethodGet, fmt.Sprintf("http://%s/api/v1alpha/projects/%s", constants.DevopsAPIServer, workspaceDOPBinding.DevOpsProject), nil)
|
||||
request.Header.Add("X-Token-Username", "admin")
|
||||
|
||||
result, err := http.DefaultClient.Do(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data, err := ioutil.ReadAll(result.Body)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if result.StatusCode == 403 || result.StatusCode == 404 {
|
||||
if err := db.Delete(&workspaceDOPBinding).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if result.StatusCode > 200 {
|
||||
return nil, ksErr.Wrap(data)
|
||||
}
|
||||
|
||||
var project DevopsProject
|
||||
|
||||
err = json.Unmarshal(data, &project)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
devOpsProjects = append(devOpsProjects, project)
|
||||
}
|
||||
|
||||
return devOpsProjects, nil
|
||||
|
||||
}
|
||||
func convertGroupToWorkspace(db *gorm.DB, group Group) (*Workspace, error) {
|
||||
var workspaceNSBindings []WorkspaceNSBinding
|
||||
|
||||
if err := db.Where("workspace = ?", group.Name).Find(&workspaceNSBindings).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
namespaces := make([]string, 0)
|
||||
|
||||
for _, workspaceNSBinding := range workspaceNSBindings {
|
||||
namespaces = append(namespaces, workspaceNSBinding.Namespace)
|
||||
}
|
||||
|
||||
var workspaceDOPBindings []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 := Workspace{Group: group}
|
||||
workspace.Namespaces = namespaces
|
||||
workspace.DevopsProjects = devOpsProjects
|
||||
return &workspace, nil
|
||||
}
|
||||
|
||||
func CreateNamespace(namespace *core.Namespace) (*core.Namespace, error) {
|
||||
return client.NewK8sClient().CoreV1().Namespaces().Create(namespace)
|
||||
}
|
||||
|
||||
func BindingNamespace(workspace string, namespace string) error {
|
||||
db := client.NewSharedDBClient()
|
||||
defer db.Close()
|
||||
return db.Create(&WorkspaceNSBinding{Workspace: workspace, Namespace: namespace}).Error
|
||||
}
|
||||
|
||||
func Invite(workspaceName string, users []UserInvite) error {
|
||||
for _, user := range users {
|
||||
if !slice.ContainsString(WorkSpaceRoles, user.Role, nil) {
|
||||
return fmt.Errorf("role %s not exist", user.Role)
|
||||
}
|
||||
}
|
||||
|
||||
workspace, err := Detail(workspaceName)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, user := range users {
|
||||
if !slice.ContainsString(workspace.Members, user.Username, nil) {
|
||||
workspace.Members = append(workspace.Members, user.Username)
|
||||
}
|
||||
}
|
||||
|
||||
workspace, err = Edit(workspace)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, user := range users {
|
||||
err := CreateWorkspaceRoleBinding(workspace, user.Username, user.Role)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func RemoveMembers(workspaceName string, users []string) error {
|
||||
|
||||
workspace, err := Detail(workspaceName)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = UnbindWorkspace(workspace, users)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
//func checkUserExist(username string) (bool, error) {
|
||||
// result, err := http.Get(fmt.Sprintf("http://%s/apis/account.kubesphere.io/v1alpha1/users?check=%s", constants.AccountAPIServer, username))
|
||||
//
|
||||
// if err != nil {
|
||||
// return false, err
|
||||
// }
|
||||
//
|
||||
// data, err := ioutil.ReadAll(result.Body)
|
||||
//
|
||||
// if err != nil {
|
||||
// return false, err
|
||||
// }
|
||||
//
|
||||
// if result.StatusCode > 200 {
|
||||
// return false, ksErr.Wrap(data)
|
||||
// }
|
||||
//
|
||||
// var r map[string]bool
|
||||
//
|
||||
// err = json.Unmarshal(data, &r)
|
||||
//
|
||||
// if err != nil {
|
||||
// return false, err
|
||||
// }
|
||||
//
|
||||
// return r["exist"], nil
|
||||
//
|
||||
//}
|
||||
|
||||
func Roles(workspace *Workspace) ([]*v1.ClusterRole, error) {
|
||||
roles := make([]*v1.ClusterRole, 0)
|
||||
|
||||
k8sClient := client.NewK8sClient()
|
||||
|
||||
for _, name := range WorkSpaceRoles {
|
||||
role, err := k8sClient.RbacV1().ClusterRoles().Get(fmt.Sprintf("system:%s:%s", workspace.Name, name), meta_v1.GetOptions{})
|
||||
|
||||
if err != nil {
|
||||
if apierrors.IsNotFound(err) {
|
||||
go WorkspaceRoleInit(workspace)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
role.Name = name
|
||||
roles = append(roles, role)
|
||||
}
|
||||
|
||||
return roles, nil
|
||||
}
|
||||
|
||||
func GetWorkspaceMembers(workspace string) ([]iam.User, error) {
|
||||
|
||||
result, err := http.Get(fmt.Sprintf("http://%s/apis/account.kubesphere.io/v1alpha1/groups/%s/users", constants.AccountAPIServer, workspace))
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data, err := ioutil.ReadAll(result.Body)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if result.StatusCode > 200 {
|
||||
return nil, ksErr.Wrap(data)
|
||||
}
|
||||
|
||||
var users []iam.User
|
||||
|
||||
err = json.Unmarshal(data, &users)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return users, nil
|
||||
|
||||
}
|
||||
|
||||
func WorkspaceRoleInit(workspace *Workspace) error {
|
||||
k8sClient := client.NewK8sClient()
|
||||
|
||||
admin := new(v1.ClusterRole)
|
||||
admin.Name = fmt.Sprintf("system:%s:admin", workspace.Name)
|
||||
admin.Kind = iam.ClusterRoleKind
|
||||
admin.Rules = []v1.PolicyRule{
|
||||
{
|
||||
Verbs: []string{"*"},
|
||||
APIGroups: []string{"kubesphere.io"},
|
||||
ResourceNames: []string{workspace.Name},
|
||||
Resources: []string{"workspaces", "workspaces/namespaces", "workspaces/members", "workspaces/devops", "workspaces/registries"},
|
||||
},
|
||||
}
|
||||
|
||||
admin.Labels = map[string]string{"creator": "system"}
|
||||
|
||||
operator := new(v1.ClusterRole)
|
||||
operator.Name = fmt.Sprintf("system:%s:operator", workspace.Name)
|
||||
operator.Kind = iam.ClusterRoleKind
|
||||
operator.Rules = []v1.PolicyRule{
|
||||
{
|
||||
Verbs: []string{"get"},
|
||||
APIGroups: []string{"kubesphere.io"},
|
||||
Resources: []string{"workspaces"},
|
||||
ResourceNames: []string{workspace.Name},
|
||||
}, {
|
||||
Verbs: []string{"list", "create"},
|
||||
APIGroups: []string{"kubesphere.io"},
|
||||
Resources: []string{"workspaces/namespaces", "workspaces/devops"},
|
||||
ResourceNames: []string{workspace.Name},
|
||||
}, {
|
||||
Verbs: []string{"list"},
|
||||
APIGroups: []string{"kubesphere.io"},
|
||||
Resources: []string{"workspaces/members", "workspaces/registries"},
|
||||
ResourceNames: []string{workspace.Name},
|
||||
},
|
||||
}
|
||||
operator.Labels = map[string]string{"creator": "system"}
|
||||
|
||||
viewer := new(v1.ClusterRole)
|
||||
viewer.Name = fmt.Sprintf("system:%s:viewer", workspace.Name)
|
||||
viewer.Kind = iam.ClusterRoleKind
|
||||
viewer.Rules = []v1.PolicyRule{
|
||||
{
|
||||
Verbs: []string{"get"},
|
||||
APIGroups: []string{"kubesphere.io"},
|
||||
Resources: []string{"workspaces"},
|
||||
ResourceNames: []string{workspace.Name},
|
||||
}, {
|
||||
Verbs: []string{"list"},
|
||||
APIGroups: []string{"kubesphere.io"},
|
||||
Resources: []string{"workspaces/namespaces", "workspaces/members", "workspaces/devops", "workspaces/registries"},
|
||||
ResourceNames: []string{workspace.Name},
|
||||
},
|
||||
}
|
||||
viewer.Labels = map[string]string{"creator": "system"}
|
||||
|
||||
_, err := k8sClient.RbacV1().ClusterRoles().Create(admin)
|
||||
if err != nil {
|
||||
if !apierrors.IsAlreadyExists(err) {
|
||||
log.Println("cluster role create failed", admin.Name, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
adminRoleBinding := new(v1.ClusterRoleBinding)
|
||||
adminRoleBinding.Name = admin.Name
|
||||
adminRoleBinding.RoleRef = v1.RoleRef{Kind: "ClusterRole", Name: admin.Name}
|
||||
adminRoleBinding.Subjects = []v1.Subject{{Kind: v1.UserKind, Name: workspace.Creator}}
|
||||
_, err = k8sClient.RbacV1().ClusterRoleBindings().Create(adminRoleBinding)
|
||||
if err != nil {
|
||||
if !apierrors.IsAlreadyExists(err) {
|
||||
log.Println("cluster rolebinding create failed", adminRoleBinding.Name, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
_, err = k8sClient.RbacV1().ClusterRoles().Create(operator)
|
||||
if err != nil {
|
||||
if !apierrors.IsAlreadyExists(err) {
|
||||
log.Println("cluster role create failed", viewer.Name, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
operatorRoleBinding := new(v1.ClusterRoleBinding)
|
||||
operatorRoleBinding.Name = operator.Name
|
||||
operatorRoleBinding.RoleRef = v1.RoleRef{Kind: "ClusterRole", Name: operator.Name}
|
||||
operatorRoleBinding.Subjects = make([]v1.Subject, 0)
|
||||
_, err = k8sClient.RbacV1().ClusterRoleBindings().Create(operatorRoleBinding)
|
||||
if err != nil {
|
||||
if !apierrors.IsAlreadyExists(err) {
|
||||
log.Println("cluster rolebinding create failed", operatorRoleBinding.Name, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
_, err = k8sClient.RbacV1().ClusterRoles().Create(viewer)
|
||||
if err != nil {
|
||||
if !apierrors.IsAlreadyExists(err) {
|
||||
log.Println("cluster role create failed", viewer.Name, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
viewerRoleBinding := new(v1.ClusterRoleBinding)
|
||||
viewerRoleBinding.Name = viewer.Name
|
||||
viewerRoleBinding.RoleRef = v1.RoleRef{Kind: "ClusterRole", Name: viewer.Name}
|
||||
viewerRoleBinding.Subjects = make([]v1.Subject, 0)
|
||||
_, err = k8sClient.RbacV1().ClusterRoleBindings().Create(viewerRoleBinding)
|
||||
if err != nil {
|
||||
if !apierrors.IsAlreadyExists(err) {
|
||||
log.Println("cluster rolebinding create failed", viewerRoleBinding.Name, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func unbindWorkspaceRole(workspace string, users []string) error {
|
||||
k8sClient := client.NewK8sClient()
|
||||
|
||||
for _, name := range WorkSpaceRoles {
|
||||
roleBinding, err := k8sClient.RbacV1().ClusterRoleBindings().Get(fmt.Sprintf("system:%s:%s", workspace, name), meta_v1.GetOptions{})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
modify := false
|
||||
|
||||
for i := 0; i < len(roleBinding.Subjects); i++ {
|
||||
if roleBinding.Subjects[i].Kind == v1.UserKind && slice.ContainsString(users, roleBinding.Subjects[i].Name, nil) {
|
||||
roleBinding.Subjects = append(roleBinding.Subjects[:i], roleBinding.Subjects[i+1:]...)
|
||||
i--
|
||||
modify = true
|
||||
}
|
||||
}
|
||||
|
||||
if modify {
|
||||
roleBinding, err = k8sClient.RbacV1().ClusterRoleBindings().Update(roleBinding)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func unbindNamespacesRole(namespaces []string, users []string) error {
|
||||
|
||||
lister := controllers.ResourceControllers.Controllers[controllers.Namespaces].Lister().(v13.RoleBindingLister)
|
||||
|
||||
k8sClient := client.NewK8sClient()
|
||||
for _, namespace := range namespaces {
|
||||
|
||||
roleBindings, err := lister.RoleBindings(namespace).List(labels.Everything())
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, roleBinding := range roleBindings {
|
||||
|
||||
modify := false
|
||||
for i := 0; i < len(roleBinding.Subjects); i++ {
|
||||
if roleBinding.Subjects[i].Kind == v1.UserKind && slice.ContainsString(users, roleBinding.Subjects[i].Name, nil) {
|
||||
roleBinding.Subjects = append(roleBinding.Subjects[:i], roleBinding.Subjects[i+1:]...)
|
||||
modify = true
|
||||
}
|
||||
}
|
||||
if modify {
|
||||
_, err := k8sClient.RbacV1().RoleBindings(namespace).Update(roleBinding)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func UnbindWorkspace(workspace *Workspace, users []string) error {
|
||||
|
||||
err := unbindNamespacesRole(workspace.Namespaces, users)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = unbindWorkspaceRole(workspace.Name, users)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func CreateWorkspaceRoleBinding(workspace *Workspace, username string, role string) error {
|
||||
|
||||
k8sClient := client.NewK8sClient()
|
||||
|
||||
for _, roleName := range WorkSpaceRoles {
|
||||
roleBinding, err := k8sClient.RbacV1().ClusterRoleBindings().Get(fmt.Sprintf("system:%s:%s", workspace.Name, roleName), meta_v1.GetOptions{})
|
||||
|
||||
if err != nil {
|
||||
if apierrors.IsNotFound(err) {
|
||||
go WorkspaceRoleInit(workspace)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
modify := false
|
||||
|
||||
for i, v := range roleBinding.Subjects {
|
||||
if v.Kind == v1.UserKind && v.Name == username {
|
||||
if roleName == role {
|
||||
return nil
|
||||
} else {
|
||||
modify = true
|
||||
roleBinding.Subjects = append(roleBinding.Subjects[:i], roleBinding.Subjects[i+1:]...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if roleName == role {
|
||||
modify = true
|
||||
roleBinding.Subjects = append(roleBinding.Subjects, v1.Subject{Kind: v1.UserKind, Name: username})
|
||||
}
|
||||
|
||||
if !modify {
|
||||
continue
|
||||
}
|
||||
|
||||
_, err = k8sClient.RbacV1().ClusterRoleBindings().Update(roleBinding)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
20
pkg/util/errors/errors.go
Normal file
20
pkg/util/errors/errors.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package errors
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
)
|
||||
|
||||
func Wrap(data []byte) error {
|
||||
var j map[string]string
|
||||
err := json.Unmarshal(data, &j)
|
||||
if err != nil {
|
||||
return errors.New(string(data))
|
||||
} else if message := j["message"]; message != "" {
|
||||
return errors.New(message)
|
||||
} else if message := j["Error"]; message != "" {
|
||||
return errors.New(message)
|
||||
} else {
|
||||
return errors.New(string(data))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user