feat: kubesphere 4.0 (#6115)

* feat: kubesphere 4.0

Signed-off-by: ci-bot <ci-bot@kubesphere.io>

* feat: kubesphere 4.0

Signed-off-by: ci-bot <ci-bot@kubesphere.io>

---------

Signed-off-by: ci-bot <ci-bot@kubesphere.io>
Co-authored-by: ks-ci-bot <ks-ci-bot@example.com>
Co-authored-by: joyceliu <joyceliu@yunify.com>
This commit is contained in:
KubeSphere CI Bot
2024-09-06 11:05:52 +08:00
committed by GitHub
parent b5015ec7b9
commit 447a51f08b
8557 changed files with 546695 additions and 1146174 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -1,643 +0,0 @@
/*
Copyright 2019 The KubeSphere Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1alpha2
import (
"net/http"
"kubesphere.io/kubesphere/pkg/apiserver/authorization/authorizer"
restfulspec "github.com/emicklei/go-restful-openapi/v2"
"github.com/emicklei/go-restful/v3"
rbacv1 "k8s.io/api/rbac/v1"
v1 "k8s.io/api/rbac/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
iamv1alpha2 "kubesphere.io/api/iam/v1alpha2"
"kubesphere.io/kubesphere/pkg/api"
"kubesphere.io/kubesphere/pkg/apiserver/runtime"
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/models/iam/am"
"kubesphere.io/kubesphere/pkg/models/iam/group"
"kubesphere.io/kubesphere/pkg/models/iam/im"
"kubesphere.io/kubesphere/pkg/server/errors"
)
const (
GroupName = "iam.kubesphere.io"
)
var GroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha2"}
func AddToContainer(container *restful.Container, im im.IdentityManagementInterface, am am.AccessManagementInterface, group group.GroupOperator, authorizer authorizer.Authorizer) error {
ws := runtime.NewWebService(GroupVersion)
handler := newIAMHandler(im, am, group, authorizer)
// users
ws.Route(ws.POST("/users").
To(handler.CreateUser).
Doc("Create a global user account.").
Returns(http.StatusOK, api.StatusOK, iamv1alpha2.User{}).
Reads(iamv1alpha2.User{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.UserTag}))
ws.Route(ws.DELETE("/users/{user}").
To(handler.DeleteUser).
Doc("Delete the specified user.").
Param(ws.PathParameter("user", "username")).
Returns(http.StatusOK, api.StatusOK, errors.None).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.UserTag}))
ws.Route(ws.PUT("/users/{user}").
To(handler.UpdateUser).
Doc("Update user profile.").
Reads(iamv1alpha2.User{}).
Param(ws.PathParameter("user", "username")).
Returns(http.StatusOK, api.StatusOK, iamv1alpha2.User{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.UserTag}))
ws.Route(ws.PUT("/users/{user}/password").
To(handler.ModifyPassword).
Doc("Reset password of the specified user.").
Reads(PasswordReset{}).
Param(ws.PathParameter("user", "username")).
Returns(http.StatusOK, api.StatusOK, errors.None).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.UserTag}))
ws.Route(ws.GET("/users/{user}").
To(handler.DescribeUser).
Doc("Retrieve user details.").
Param(ws.PathParameter("user", "username")).
Returns(http.StatusOK, api.StatusOK, iamv1alpha2.User{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.UserTag}))
ws.Route(ws.GET("/users").
To(handler.ListUsers).
Doc("List all users.").
Returns(http.StatusOK, api.StatusOK, api.ListResult{Items: []interface{}{iamv1alpha2.User{}}}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.UserTag}))
ws.Route(ws.GET("/users/{user}/loginrecords").
To(handler.ListUserLoginRecords).
Param(ws.PathParameter("user", "username of the user")).
Doc("List login records of the specified user.").
Returns(http.StatusOK, api.StatusOK, api.ListResult{Items: []interface{}{iamv1alpha2.LoginRecord{}}}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.UserResourceTag}))
// clustermembers
ws.Route(ws.POST("/clustermembers").
To(handler.CreateClusterMembers).
Doc("Add members to current cluster in bulk.").
Reads([]Member{}).
Returns(http.StatusOK, api.StatusOK, []Member{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.ClusterMemberTag}))
ws.Route(ws.DELETE("/clustermembers/{clustermember}").
To(handler.RemoveClusterMember).
Doc("Delete a member from current cluster.").
Param(ws.PathParameter("clustermember", "cluster member's username")).
Returns(http.StatusOK, api.StatusOK, errors.None).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.ClusterMemberTag}))
ws.Route(ws.PUT("/clustermembers/{clustermember}").
To(handler.UpdateClusterMember).
Doc("Update the cluster role bind of the member.").
Reads(Member{}).
Returns(http.StatusOK, api.StatusOK, Member{}).
Param(ws.PathParameter("clustermember", "cluster member's username")).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.ClusterMemberTag}))
ws.Route(ws.GET("/clustermembers/{clustermember}").
To(handler.DescribeClusterMember).
Doc("Retrieve the cluster role of the specified member.").
Param(ws.PathParameter("clustermember", "cluster member's username")).
Returns(http.StatusOK, api.StatusOK, iamv1alpha2.User{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.ClusterMemberTag}))
ws.Route(ws.GET("/clustermembers").
To(handler.ListClusterMembers).
Doc("List all members in cluster.").
Returns(http.StatusOK, api.StatusOK, api.ListResult{Items: []interface{}{iamv1alpha2.User{}}}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.ClusterMemberTag}))
ws.Route(ws.GET("/workspaces/{workspace}/workspacemembers").
To(handler.ListWorkspaceMembers).
Doc("List all members in the specified workspace.").
Param(ws.PathParameter("workspace", "workspace name")).
Returns(http.StatusOK, api.StatusOK, api.ListResult{Items: []interface{}{iamv1alpha2.User{}}}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.WorkspaceMemberTag}))
ws.Route(ws.GET("/workspaces/{workspace}/workspacemembers/{workspacemember}").
To(handler.DescribeWorkspaceMember).
Doc("Retrieve the workspace role of the specified member.").
Param(ws.PathParameter("workspace", "workspace name")).
Param(ws.PathParameter("workspacemember", "workspace member's username")).
Returns(http.StatusOK, api.StatusOK, iamv1alpha2.User{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.WorkspaceMemberTag}))
ws.Route(ws.POST("/workspaces/{workspace}/workspacemembers").
To(handler.CreateWorkspaceMembers).
Doc("Add members to current cluster in bulk.").
Reads([]Member{}).
Returns(http.StatusOK, api.StatusOK, []Member{}).
Param(ws.PathParameter("workspace", "workspace name")).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.WorkspaceMemberTag}))
ws.Route(ws.PUT("/workspaces/{workspace}/workspacemembers/{workspacemember}").
To(handler.UpdateWorkspaceMember).
Doc("Update the workspace role bind of the member.").
Reads(Member{}).
Returns(http.StatusOK, api.StatusOK, Member{}).
Param(ws.PathParameter("workspace", "workspace name")).
Param(ws.PathParameter("workspacemember", "workspace member's username")).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.WorkspaceMemberTag}))
ws.Route(ws.DELETE("/workspaces/{workspace}/workspacemembers/{workspacemember}").
To(handler.RemoveWorkspaceMember).
Doc("Delete a member from the workspace.").
Param(ws.PathParameter("workspace", "workspace name")).
Param(ws.PathParameter("workspacemember", "workspace member's username")).
Returns(http.StatusOK, api.StatusOK, errors.None).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.WorkspaceMemberTag}))
ws.Route(ws.GET("/namespaces/{namespace}/members").
To(handler.ListNamespaceMembers).
Doc("List all members in the specified namespace.").
Param(ws.PathParameter("namespace", "namespace")).
Returns(http.StatusOK, api.StatusOK, api.ListResult{Items: []interface{}{iamv1alpha2.User{}}}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.NamespaceMemberTag}))
ws.Route(ws.GET("/namespaces/{namespace}/members/{member}").
To(handler.DescribeNamespaceMember).
Doc("Retrieve the role of the specified member.").
Param(ws.PathParameter("namespace", "namespace")).
Param(ws.PathParameter("member", "namespace member's username")).
Returns(http.StatusOK, api.StatusOK, iamv1alpha2.User{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.NamespaceMemberTag}))
ws.Route(ws.POST("/namespaces/{namespace}/members").
To(handler.CreateNamespaceMembers).
Doc("Add members to the namespace in bulk.").
Reads([]Member{}).
Returns(http.StatusOK, api.StatusOK, []Member{}).
Param(ws.PathParameter("namespace", "namespace")).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.NamespaceMemberTag}))
ws.Route(ws.PUT("/namespaces/{namespace}/members/{member}").
To(handler.UpdateNamespaceMember).
Doc("Update the role bind of the member.").
Reads(Member{}).
Returns(http.StatusOK, api.StatusOK, Member{}).
Param(ws.PathParameter("namespace", "namespace")).
Param(ws.PathParameter("member", "namespace member's username")).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.NamespaceMemberTag}))
ws.Route(ws.DELETE("/namespaces/{namespace}/members/{member}").
To(handler.RemoveNamespaceMember).
Doc("Delete a member from the namespace.").
Param(ws.PathParameter("namespace", "namespace")).
Param(ws.PathParameter("member", "namespace member's username")).
Returns(http.StatusOK, api.StatusOK, errors.None).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.NamespaceMemberTag}))
ws.Route(ws.GET("/devops/{devops}/members").
To(handler.ListNamespaceMembers).
Doc("List all members in the specified devops project.").
Param(ws.PathParameter("devops", "devops project name")).
Returns(http.StatusOK, api.StatusOK, api.ListResult{Items: []interface{}{iamv1alpha2.User{}}}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsProjectMemberTag}))
ws.Route(ws.GET("/devops/{devops}/members/{member}").
To(handler.DescribeNamespaceMember).
Doc("Retrieve devops project member details.").
Param(ws.PathParameter("devops", "devops project name")).
Param(ws.PathParameter("member", "devops project member's username")).
Returns(http.StatusOK, api.StatusOK, iamv1alpha2.User{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsProjectMemberTag}))
ws.Route(ws.POST("/devops/{devops}/members").
To(handler.CreateNamespaceMembers).
Doc("Add members to the DevOps project in bulk.").
Reads([]Member{}).
Returns(http.StatusOK, api.StatusOK, []Member{}).
Param(ws.PathParameter("devops", "devops project name")).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsProjectMemberTag}))
ws.Route(ws.PUT("/devops/{devops}/members/{member}").
To(handler.UpdateNamespaceMember).
Doc("Update the role bind of the member.").
Reads(Member{}).
Returns(http.StatusOK, api.StatusOK, Member{}).
Param(ws.PathParameter("devops", "devops project name")).
Param(ws.PathParameter("member", "devops project member's username")).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsProjectMemberTag}))
ws.Route(ws.DELETE("/devops/{devops}/members/{member}").
To(handler.RemoveNamespaceMember).
Doc("Delete a member from the DevOps project.").
Param(ws.PathParameter("devops", "devops project name")).
Param(ws.PathParameter("member", "devops project member's username")).
Returns(http.StatusOK, api.StatusOK, errors.None).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsProjectMemberTag}))
// globalroles
ws.Route(ws.POST("/globalroles").
To(handler.CreateGlobalRole).
Doc("Create global role.").
Deprecate().
Reads(iamv1alpha2.GlobalRole{}).
Returns(http.StatusOK, api.StatusOK, iamv1alpha2.GlobalRole{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.GlobalRoleTag}))
ws.Route(ws.DELETE("/globalroles/{globalrole}").
To(handler.DeleteGlobalRole).
Doc("Delete global role.").
Deprecate().
Param(ws.PathParameter("globalrole", "global role name")).
Returns(http.StatusOK, api.StatusOK, errors.None).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.GlobalRoleTag}))
ws.Route(ws.PUT("/globalroles/{globalrole}").
To(handler.UpdateGlobalRole).
Doc("Update global role.").
Deprecate().
Param(ws.PathParameter("globalrole", "global role name")).
Reads(iamv1alpha2.GlobalRole{}).
Returns(http.StatusOK, api.StatusOK, iamv1alpha2.GlobalRole{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.GlobalRoleTag}))
ws.Route(ws.PATCH("/globalroles/{globalrole}").
To(handler.PatchGlobalRole).
Deprecate().
Doc("Patch global role.").
Param(ws.PathParameter("globalrole", "global role name")).
Reads(iamv1alpha2.GlobalRole{}).
Returns(http.StatusOK, api.StatusOK, iamv1alpha2.GlobalRole{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.GlobalRoleTag}))
ws.Route(ws.GET("/globalroles").
To(handler.ListGlobalRoles).
Doc("List all global roles.").
Deprecate().
Returns(http.StatusOK, api.StatusOK, api.ListResult{Items: []interface{}{iamv1alpha2.GlobalRole{}}}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.GlobalRoleTag}))
ws.Route(ws.GET("/globalroles/{globalrole}").
To(handler.DescribeGlobalRole).
Param(ws.PathParameter("globalrole", "global role name")).
Doc("Retrieve global role details.").
Deprecate().
Returns(http.StatusOK, api.StatusOK, iamv1alpha2.GlobalRole{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.GlobalRoleTag}))
// clusterroles
ws.Route(ws.POST("/clusterroles").
To(handler.CreateClusterRole).
Doc("Create cluster role.").
Deprecate().
Reads(rbacv1.ClusterRole{}).
Returns(http.StatusOK, api.StatusOK, rbacv1.ClusterRole{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.ClusterRoleTag}))
ws.Route(ws.DELETE("/clusterroles/{clusterrole}").
To(handler.DeleteClusterRole).
Doc("Delete cluster role.").
Deprecate().
Param(ws.PathParameter("clusterrole", "cluster role name")).
Returns(http.StatusOK, api.StatusOK, errors.None).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.ClusterRoleTag}))
ws.Route(ws.PUT("/clusterroles/{clusterrole}").
To(handler.UpdateClusterRole).
Doc("Update cluster role.").
Deprecate().
Param(ws.PathParameter("clusterrole", "cluster role name")).
Reads(rbacv1.ClusterRole{}).
Returns(http.StatusOK, api.StatusOK, rbacv1.ClusterRole{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.ClusterRoleTag}))
ws.Route(ws.PATCH("/clusterroles/{clusterrole}").
To(handler.PatchClusterRole).
Doc("Patch cluster role.").
Deprecate().
Param(ws.PathParameter("clusterrole", "cluster role name")).
Reads(rbacv1.ClusterRole{}).
Returns(http.StatusOK, api.StatusOK, rbacv1.ClusterRole{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.ClusterRoleTag}))
ws.Route(ws.GET("/clusterroles").
To(handler.ListClusterRoles).
Doc("List all cluster roles.").
Deprecate().
Returns(http.StatusOK, api.StatusOK, api.ListResult{Items: []interface{}{rbacv1.ClusterRole{}}}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.ClusterRoleTag}))
ws.Route(ws.GET("/clusterroles/{clusterrole}").
To(handler.DescribeClusterRole).
Param(ws.PathParameter("clusterrole", "cluster role name")).
Doc("Retrieve cluster role details.").
Deprecate().
Returns(http.StatusOK, api.StatusOK, rbacv1.ClusterRole{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.ClusterRoleTag}))
// workspaceroles
ws.Route(ws.POST("/workspaces/{workspace}/workspaceroles").
To(handler.CreateWorkspaceRole).
Doc("Create workspace role.").
Deprecate().
Reads(iamv1alpha2.WorkspaceRole{}).
Returns(http.StatusOK, api.StatusOK, iamv1alpha2.WorkspaceRole{}).
Param(ws.PathParameter("workspace", "workspace name")).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.WorkspaceRoleTag}))
ws.Route(ws.DELETE("/workspaces/{workspace}/workspaceroles/{workspacerole}").
To(handler.DeleteWorkspaceRole).
Doc("Delete workspace role.").
Deprecate().
Param(ws.PathParameter("workspace", "workspace name")).
Param(ws.PathParameter("workspacerole", "workspace role name")).
Returns(http.StatusOK, api.StatusOK, errors.None).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.WorkspaceRoleTag}))
ws.Route(ws.PATCH("/workspaces/{workspace}/workspaceroles/{workspacerole}").
To(handler.PatchWorkspaceRole).
Doc("Patch workspace role.").
Deprecate().
Param(ws.PathParameter("workspace", "workspace name")).
Param(ws.PathParameter("workspacerole", "workspace role name")).
Reads(iamv1alpha2.WorkspaceRole{}).
Returns(http.StatusOK, api.StatusOK, errors.None).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.WorkspaceRoleTag}))
ws.Route(ws.PUT("/workspaces/{workspace}/workspaceroles/{workspacerole}").
To(handler.UpdateWorkspaceRole).
Doc("Update workspace role.").
Deprecate().
Param(ws.PathParameter("workspace", "workspace name")).
Param(ws.PathParameter("workspacerole", "workspace role name")).
Reads(iamv1alpha2.WorkspaceRole{}).
Returns(http.StatusOK, api.StatusOK, iamv1alpha2.WorkspaceRole{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.WorkspaceRoleTag}))
ws.Route(ws.GET("/workspaces/{workspace}/workspaceroles").
To(handler.ListWorkspaceRoles).
Doc("List all workspace roles.").
Deprecate().
Param(ws.PathParameter("workspace", "workspace name")).
Returns(http.StatusOK, api.StatusOK, api.ListResult{Items: []interface{}{iamv1alpha2.WorkspaceRole{}}}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.WorkspaceRoleTag}))
ws.Route(ws.GET("/workspaces/{workspace}/workspaceroles/{workspacerole}").
To(handler.DescribeWorkspaceRole).
Doc("Retrieve workspace role details.").
Deprecate().
Param(ws.PathParameter("workspace", "workspace name")).
Param(ws.PathParameter("workspacerole", "workspace role name")).
Returns(http.StatusOK, api.StatusOK, iamv1alpha2.WorkspaceRole{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.WorkspaceRoleTag}))
// roles
ws.Route(ws.POST("/namespaces/{namespace}/roles").
To(handler.CreateNamespaceRole).
Doc("Create role in the specified namespace.").
Deprecate().
Reads(rbacv1.Role{}).
Param(ws.PathParameter("namespace", "namespace")).
Returns(http.StatusOK, api.StatusOK, rbacv1.Role{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.NamespaceRoleTag}))
ws.Route(ws.DELETE("/namespaces/{namespace}/roles/{role}").
To(handler.DeleteNamespaceRole).
Doc("Delete role in the specified namespace.").
Deprecate().
Param(ws.PathParameter("namespace", "namespace")).
Param(ws.PathParameter("role", "role name")).
Returns(http.StatusOK, api.StatusOK, errors.None).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.NamespaceRoleTag}))
ws.Route(ws.PUT("/namespaces/{namespace}/roles/{role}").
To(handler.UpdateNamespaceRole).
Doc("Update namespace role.").
Deprecate().
Param(ws.PathParameter("namespace", "namespace")).
Param(ws.PathParameter("role", "role name")).
Reads(rbacv1.Role{}).
Returns(http.StatusOK, api.StatusOK, rbacv1.Role{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.NamespaceRoleTag}))
ws.Route(ws.PATCH("/namespaces/{namespace}/roles/{role}").
To(handler.PatchNamespaceRole).
Doc("Patch namespace role.").
Deprecate().
Param(ws.PathParameter("namespace", "namespace")).
Param(ws.PathParameter("role", "role name")).
Reads(rbacv1.Role{}).
Returns(http.StatusOK, api.StatusOK, rbacv1.Role{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.NamespaceRoleTag}))
ws.Route(ws.GET("/namespaces/{namespace}/roles").
To(handler.ListRoles).
Doc("List all roles in the specified namespace.").
Deprecate().
Param(ws.PathParameter("namespace", "namespace")).
Returns(http.StatusOK, api.StatusOK, api.ListResult{Items: []interface{}{rbacv1.Role{}}}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.NamespaceRoleTag}))
ws.Route(ws.GET("/namespaces/{namespace}/roles/{role}").
To(handler.DescribeNamespaceRole).
Doc("Retrieve role details.").
Deprecate().
Param(ws.PathParameter("namespace", "namespace")).
Param(ws.PathParameter("role", "role name")).
Returns(http.StatusOK, api.StatusOK, rbacv1.Role{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.NamespaceRoleTag}))
// roles
ws.Route(ws.POST("/devops/{devops}/roles").
To(handler.CreateNamespaceRole).
Doc("Create role in the specified devops project.").
Deprecate().
Reads(rbacv1.Role{}).
Param(ws.PathParameter("devops", "devops project name")).
Returns(http.StatusOK, api.StatusOK, rbacv1.Role{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsProjectRoleTag}))
ws.Route(ws.DELETE("/devops/{devops}/roles/{role}").
To(handler.DeleteNamespaceRole).
Doc("Delete role in the specified devops project.").
Deprecate().
Param(ws.PathParameter("devops", "devops project name")).
Param(ws.PathParameter("role", "role name")).
Returns(http.StatusOK, api.StatusOK, errors.None).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsProjectRoleTag}))
ws.Route(ws.PUT("/devops/{devops}/roles/{role}").
To(handler.UpdateNamespaceRole).
Doc("Update devops project role.").
Deprecate().
Param(ws.PathParameter("devops", "devops project name")).
Param(ws.PathParameter("role", "role name")).
Reads(rbacv1.Role{}).
Returns(http.StatusOK, api.StatusOK, rbacv1.Role{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsProjectRoleTag}))
ws.Route(ws.PATCH("/devops/{devops}/roles/{role}").
To(handler.PatchNamespaceRole).
Doc("Patch devops project role.").
Deprecate().
Param(ws.PathParameter("devops", "devops project name")).
Param(ws.PathParameter("role", "role name")).
Reads(rbacv1.Role{}).
Returns(http.StatusOK, api.StatusOK, rbacv1.Role{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsProjectRoleTag}))
ws.Route(ws.GET("/devops/{devops}/roles").
To(handler.ListRoles).
Doc("List all roles in the specified devops project.").
Deprecate().
Param(ws.PathParameter("devops", "devops project name")).
Returns(http.StatusOK, api.StatusOK, api.ListResult{Items: []interface{}{rbacv1.Role{}}}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsProjectRoleTag}))
ws.Route(ws.GET("/devops/{devops}/roles/{role}").
To(handler.DescribeNamespaceRole).
Doc("Retrieve devops project role details.").
Deprecate().
Param(ws.PathParameter("devops", "devops project name")).
Param(ws.PathParameter("role", "role name")).
Returns(http.StatusOK, api.StatusOK, rbacv1.Role{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsProjectRoleTag}))
ws.Route(ws.GET("/users/{user}/globalroles").
To(handler.RetrieveMemberRoleTemplates).
Doc("Retrieve user's global role templates.").
Param(ws.PathParameter("user", "username")).
Returns(http.StatusOK, api.StatusOK, api.ListResult{Items: []interface{}{iamv1alpha2.GlobalRole{}}}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.GlobalRoleTag}))
ws.Route(ws.GET("/clustermembers/{clustermember}/clusterroles").
To(handler.RetrieveMemberRoleTemplates).
Doc("Retrieve user's role templates in cluster.").
Param(ws.PathParameter("clustermember", "cluster member's username")).
Returns(http.StatusOK, api.StatusOK, api.ListResult{Items: []interface{}{rbacv1.ClusterRole{}}}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.ClusterRoleTag}))
ws.Route(ws.GET("/workspaces/{workspace}/workspacemembers/{workspacemember}/workspaceroles").
To(handler.RetrieveMemberRoleTemplates).
Doc("Retrieve member's role templates in workspace.").
Param(ws.PathParameter("workspace", "workspace")).
Param(ws.PathParameter("workspacemember", "workspace member's username")).
Returns(http.StatusOK, api.StatusOK, api.ListResult{Items: []interface{}{iamv1alpha2.WorkspaceRole{}}}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.WorkspaceRoleTag}))
ws.Route(ws.GET("/namespaces/{namespace}/members/{member}/roles").
To(handler.RetrieveMemberRoleTemplates).
Doc("Retrieve member's role templates in namespace.").
Param(ws.PathParameter("namespace", "namespace")).
Param(ws.PathParameter("member", "namespace member's username")).
Returns(http.StatusOK, api.StatusOK, api.ListResult{Items: []interface{}{rbacv1.Role{}}}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.NamespaceRoleTag}))
ws.Route(ws.GET("/devops/{devops}/members/{member}/roles").
To(handler.RetrieveMemberRoleTemplates).
Doc("Retrieve member's role templates in devops project.").
Param(ws.PathParameter("devops", "devops project name")).
Param(ws.PathParameter("member", "devops project member's username")).
Returns(http.StatusOK, api.StatusOK, api.ListResult{Items: []interface{}{rbacv1.Role{}}}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsProjectRoleTag}))
ws.Route(ws.GET("/workspaces/{workspace}/groups").
To(handler.ListWorkspaceGroups).
Param(ws.PathParameter("workspace", "workspace name")).
Returns(http.StatusOK, api.StatusOK, api.ListResult{}).
Doc("List groups of the specified workspace.").
Metadata(restfulspec.KeyOpenAPITags, []string{constants.GroupTag}))
ws.Route(ws.GET("/workspaces/{workspace}/groups/{group}").
To(handler.DescribeGroup).
Param(ws.PathParameter("workspace", "workspace name")).
Param(ws.PathParameter("group", "group name")).
Doc("Retrieve group details.").
Returns(http.StatusOK, api.StatusOK, iamv1alpha2.Group{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.GroupTag}))
ws.Route(ws.DELETE("/workspaces/{workspace}/groups/{group}").
To(handler.DeleteGroup).
Param(ws.PathParameter("workspace", "workspace name")).
Param(ws.PathParameter("group", "group name")).
Doc("Delete group.").
Returns(http.StatusOK, api.StatusOK, errors.None).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.GroupTag}))
ws.Route(ws.POST("/workspaces/{workspace}/groups").
To(handler.CreateGroup).
Param(ws.PathParameter("workspace", "workspace name")).
Doc("Create Group").
Reads(iamv1alpha2.Group{}).
Returns(http.StatusOK, api.StatusOK, iamv1alpha2.Group{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.GroupTag}))
ws.Route(ws.PUT("/workspaces/{workspace}/groups/{group}/").
To(handler.UpdateGroup).
Param(ws.PathParameter("workspace", "workspace name")).
Param(ws.PathParameter("group", "group name")).
Doc("Update Group").
Reads(iamv1alpha2.Group{}).
Returns(http.StatusOK, api.StatusOK, iamv1alpha2.Group{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.GroupTag}))
ws.Route(ws.PATCH("/workspaces/{workspace}/groups/{group}/").
To(handler.PatchGroup).
Param(ws.PathParameter("workspace", "workspace name")).
Doc("Patch Group").
Reads(iamv1alpha2.Group{}).
Returns(http.StatusOK, api.StatusOK, iamv1alpha2.Group{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.GroupTag}))
ws.Route(ws.GET("/workspaces/{workspace}/groupbindings").
To(handler.ListGroupBindings).
Param(ws.PathParameter("workspace", "workspace name")).
Param(ws.PathParameter("group", "group name")).
Doc("Retrieve group's members in the workspace.").
Returns(http.StatusOK, api.StatusOK, api.ListResult{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.GroupTag}))
ws.Route(ws.GET("/workspaces/{workspace}/rolebindings").
To(handler.ListGroupRoleBindings).
Param(ws.PathParameter("workspace", "workspace name")).
Param(ws.PathParameter("group", "group name")).
Doc("Retrieve group's rolebindings of all projects in the workspace.").
Returns(http.StatusOK, api.StatusOK, api.ListResult{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.GroupTag}))
ws.Route(ws.GET("/workspaces/{workspace}/workspacerolebindings").
To(handler.ListGroupWorkspaceRoleBindings).
Param(ws.PathParameter("workspace", "workspace name")).
Param(ws.PathParameter("group", "group name")).
Doc("Retrieve group's workspacerolebindings of the workspace.").
Returns(http.StatusOK, api.StatusOK, api.ListResult{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.GroupTag}))
ws.Route(ws.DELETE("/workspaces/{workspace}/groupbindings/{groupbinding}").
To(handler.DeleteGroupBinding).
Param(ws.PathParameter("workspace", "workspace name")).
Param(ws.PathParameter("groupbinding", "groupbinding name")).
Doc("Delete GroupBinding to remove user from the group.").
Returns(http.StatusOK, api.StatusOK, errors.None).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.GroupTag}))
ws.Route(ws.POST("/workspaces/{workspace}/groupbindings").
To(handler.CreateGroupBinding).
Param(ws.PathParameter("workspace", "workspace name")).
Doc("Create GroupBinding to add a user to the group").
Reads([]GroupMember{}).
Returns(http.StatusOK, api.StatusOK, iamv1alpha2.GroupBinding{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.GroupTag}))
// namespace rolebinding
ws.Route(ws.POST("/namespaces/{namespace}/rolebindings").
To(handler.CreateRoleBinding).
Doc("Create rolebinding in the specified namespace.").
Deprecate().
Reads([]v1.RoleBinding{}).
Param(ws.PathParameter("namespace", "namespace")).
Returns(http.StatusOK, api.StatusOK, []v1.RoleBinding{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.NamespaceRoleTag}))
ws.Route(ws.DELETE("/namespaces/{namespace}/rolebindings/{rolebinding}").
To(handler.DeleteRoleBinding).
Param(ws.PathParameter("workspace", "workspace name")).
Param(ws.PathParameter("namespace", "groupbinding name")).
Param(ws.PathParameter("rolebinding", "groupbinding name")).
Doc("Delete rolebinding under namespace.").
Deprecate().
Returns(http.StatusOK, api.StatusOK, errors.None).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.GroupTag}))
// workspace rolebinding
ws.Route(ws.POST("/workspaces/{workspace}/workspacerolebindings").
To(handler.CreateWorkspaceRoleBinding).
Param(ws.PathParameter("workspace", "workspace name")).
Reads([]iamv1alpha2.WorkspaceRoleBinding{}).
Doc("Create group's workspacerolebindings of the workspace.").
Deprecate().
Returns(http.StatusOK, api.StatusOK, []iamv1alpha2.WorkspaceRoleBinding{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.GroupTag}))
ws.Route(ws.DELETE("/workspaces/{workspace}/workspacerolebindings/{rolebinding}").
To(handler.DeleteWorkspaceRoleBinding).
Param(ws.PathParameter("workspace", "workspace name")).
Param(ws.PathParameter("rolebinding", "groupbinding name")).
Doc("Delete workspacerolebinding.").
Deprecate().
Returns(http.StatusOK, api.StatusOK, errors.None).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.GroupTag}))
container.Add(ws)
return nil
}

View File

@@ -0,0 +1,870 @@
/*
* Please refer to the LICENSE file in the root directory of the project.
* https://github.com/kubesphere/kubesphere/blob/master/LICENSE
*/
package v1beta1
import (
"fmt"
"sync"
"kubesphere.io/kubesphere/pkg/apiserver/authorization/rbac"
"kubesphere.io/kubesphere/pkg/apiserver/rest"
"github.com/emicklei/go-restful/v3"
rbacv1 "k8s.io/api/rbac/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
utilerrors "k8s.io/apimachinery/pkg/util/errors"
authuser "k8s.io/apiserver/pkg/authentication/user"
iamv1beta1 "kubesphere.io/api/iam/v1beta1"
"kubesphere.io/kubesphere/pkg/api"
"kubesphere.io/kubesphere/pkg/apiserver/authorization/authorizer"
"kubesphere.io/kubesphere/pkg/apiserver/query"
apirequest "kubesphere.io/kubesphere/pkg/apiserver/request"
"kubesphere.io/kubesphere/pkg/models/auth"
"kubesphere.io/kubesphere/pkg/models/iam/am"
"kubesphere.io/kubesphere/pkg/models/iam/im"
resv1beta1 "kubesphere.io/kubesphere/pkg/models/resources/v1beta1"
servererr "kubesphere.io/kubesphere/pkg/server/errors"
)
type Member struct {
Username string `json:"username"`
RoleRef string `json:"roleRef"`
}
type GroupMember struct {
UserName string `json:"userName"`
GroupName string `json:"groupName"`
}
type PasswordReset struct {
CurrentPassword string `json:"currentPassword"`
Password string `json:"password"`
}
type TOTOAuthKeyBind struct {
AuthKey string `json:"authKey"`
OTP string `json:"otp"`
}
type TOTPAuthKey struct {
AuthKey string `json:"authKey"`
}
type handler struct {
im im.IdentityManagementInterface
am am.AccessManagementInterface
authorizer authorizer.Authorizer
}
func NewHandler(im im.IdentityManagementInterface, am am.AccessManagementInterface) rest.Handler {
return &handler{im: im, am: am, authorizer: rbac.NewRBACAuthorizer(am)}
}
func NewFakeHandler() rest.Handler {
return &handler{}
}
func (h *handler) DescribeUser(request *restful.Request, response *restful.Response) {
username := request.PathParameter("user")
user, err := h.im.DescribeUser(username)
if err != nil {
if errors.IsNotFound(err) {
api.HandleNotFound(response, request, err)
return
}
api.HandleInternalError(response, request, err)
return
}
response.WriteEntity(user)
}
func (h *handler) ListUsers(request *restful.Request, response *restful.Response) {
globalRole := request.QueryParameter("globalrole")
if globalRole != "" {
result, err := h.listUserByGlobalRole(globalRole)
if err != nil {
api.HandleInternalError(response, request, err)
return
}
response.WriteEntity(result)
return
}
queryParam := query.ParseQueryParameter(request)
result, err := h.im.ListUsers(queryParam)
if err != nil {
api.HandleInternalError(response, request, err)
return
}
response.WriteEntity(result)
}
func (h *handler) listUserByGlobalRole(roleName string) (*api.ListResult, error) {
result := &api.ListResult{Items: make([]runtime.Object, 0)}
bindings, err := h.am.ListGlobalRoleBindings("", roleName)
if err != nil {
return nil, err
}
for _, binding := range bindings {
for _, subject := range binding.Subjects {
if subject.Kind == rbacv1.UserKind {
user, err := h.im.DescribeUser(subject.Name)
if err != nil {
return nil, err
}
result.Items = append(result.Items, user)
result.TotalItems += 1
}
}
}
return result, nil
}
func (h *handler) CreateUser(req *restful.Request, resp *restful.Response) {
var user iamv1beta1.User
err := req.ReadEntity(&user)
if err != nil {
api.HandleBadRequest(resp, req, err)
return
}
operator, ok := apirequest.UserFrom(req.Request.Context())
if ok && operator.GetName() == iamv1beta1.PreRegistrationUser {
extra := operator.GetExtra()
// The token used for registration must contain additional information
if len(extra[iamv1beta1.ExtraIdentityProvider]) != 1 || len(extra[iamv1beta1.ExtraUID]) != 1 {
err = errors.NewBadRequest("invalid registration token")
api.HandleBadRequest(resp, req, err)
return
}
if user.Labels == nil {
user.Labels = make(map[string]string)
}
user.Labels[iamv1beta1.IdentifyProviderLabel] = extra[iamv1beta1.ExtraIdentityProvider][0]
user.Labels[iamv1beta1.OriginUIDLabel] = extra[iamv1beta1.ExtraUID][0]
delete(user.Annotations, iamv1beta1.GlobalRoleAnnotation)
}
globalRole := user.Annotations[iamv1beta1.GlobalRoleAnnotation]
if globalRole != "" {
if _, err = h.am.GetGlobalRole(globalRole); err != nil {
api.HandleError(resp, req, err)
return
}
}
created, err := h.im.CreateUser(&user)
if err != nil {
api.HandleError(resp, req, err)
return
}
if globalRole != "" {
if err := h.am.CreateOrUpdateGlobalRoleBinding(user.Name, globalRole); err != nil {
api.HandleError(resp, req, err)
return
}
}
// ensure encrypted password will not be output
created.Spec.EncryptedPassword = ""
resp.WriteEntity(created)
}
func (h *handler) UpdateUser(request *restful.Request, response *restful.Response) {
username := request.PathParameter("user")
var user iamv1beta1.User
err := request.ReadEntity(&user)
if err != nil {
api.HandleBadRequest(response, request, err)
return
}
if username != user.Name {
err := fmt.Errorf("the name of the object (%s) does not match the name on the URL (%s)", user.Name, username)
api.HandleBadRequest(response, request, err)
return
}
globalRole := user.Annotations[iamv1beta1.GlobalRoleAnnotation]
updated, err := h.im.UpdateUser(&user)
if err != nil {
api.HandleError(response, request, err)
return
}
operator, ok := apirequest.UserFrom(request.Request.Context())
if globalRole != "" && ok {
if err = h.updateGlobalRoleBinding(operator, updated, globalRole); err != nil {
api.HandleError(response, request, err)
return
}
}
response.WriteEntity(updated)
}
func (h *handler) updateGlobalRoleBinding(operator authuser.Info, user *iamv1beta1.User, globalRole string) error {
oldGlobalRole, err := h.am.GetGlobalRoleOfUser(user.Name)
if err != nil && !errors.IsNotFound(err) {
return err
}
if oldGlobalRole != nil && oldGlobalRole.Name == globalRole {
return nil
}
userManagement := authorizer.AttributesRecord{
Resource: iamv1beta1.ResourcesPluralUser,
Verb: "update",
ResourceScope: apirequest.GlobalScope,
ResourceRequest: true,
User: operator,
}
decision, _, err := h.authorizer.Authorize(userManagement)
if err != nil {
return err
}
if decision != authorizer.DecisionAllow {
return errors.NewForbidden(iamv1beta1.Resource(iamv1beta1.ResourcesSingularUser),
user.Name, fmt.Errorf("update global role binding is not allowed"))
}
if err := h.am.CreateOrUpdateGlobalRoleBinding(user.Name, globalRole); err != nil {
return err
}
return nil
}
func (h *handler) ModifyPassword(request *restful.Request, response *restful.Response) {
username := request.PathParameter("user")
var passwordReset PasswordReset
err := request.ReadEntity(&passwordReset)
if err != nil {
api.HandleBadRequest(response, request, err)
return
}
operator, ok := apirequest.UserFrom(request.Request.Context())
if !ok {
err = errors.NewInternalError(fmt.Errorf("cannot obtain user info"))
api.HandleInternalError(response, request, err)
return
}
userManagement := authorizer.AttributesRecord{
Resource: "users/password",
Verb: "update",
ResourceScope: apirequest.GlobalScope,
ResourceRequest: true,
User: operator,
}
decision, _, err := h.authorizer.Authorize(userManagement)
if err != nil {
api.HandleInternalError(response, request, err)
return
}
// only the user manager can modify the password without verifying the old password
// if old password is defined must be verified
if decision != authorizer.DecisionAllow || passwordReset.CurrentPassword != "" {
if err = h.im.PasswordVerify(username, passwordReset.CurrentPassword); err != nil {
if err == auth.IncorrectPasswordError {
err = errors.NewBadRequest("incorrect old password")
}
api.HandleError(response, request, err)
return
}
}
err = h.im.ModifyPassword(username, passwordReset.Password)
if err != nil {
api.HandleError(response, request, err)
return
}
response.WriteEntity(servererr.None)
}
func (h *handler) DeleteUser(request *restful.Request, response *restful.Response) {
username := request.PathParameter("user")
err := h.im.DeleteUser(username)
if err != nil {
api.HandleError(response, request, err)
return
}
response.WriteEntity(servererr.None)
}
func (h *handler) ListUserLoginRecords(request *restful.Request, response *restful.Response) {
username := request.PathParameter("user")
queryParam := query.ParseQueryParameter(request)
result, err := h.im.ListLoginRecords(username, queryParam)
if err != nil {
api.HandleError(response, request, err)
return
}
response.WriteEntity(result)
}
func (h *handler) ListClusterMembers(request *restful.Request, response *restful.Response) {
roleName := request.QueryParameter("clusterrole")
result := &api.ListResult{Items: make([]runtime.Object, 0)}
bindings, err := h.am.ListClusterRoleBindings("", roleName)
if err != nil {
api.HandleError(response, request, err)
return
}
filtered := []runtime.Object{}
for _, binding := range bindings {
for _, subject := range binding.Subjects {
if subject.Kind == rbacv1.UserKind {
filtered = append(filtered, &iamv1beta1.User{
ObjectMeta: metav1.ObjectMeta{
Name: subject.Name,
Annotations: map[string]string{
iamv1beta1.ClusterRoleAnnotation: binding.RoleRef.Name,
},
},
})
break
}
}
}
list, _, totalCount := resv1beta1.DefaultList(filtered, query.ParseQueryParameter(request), resv1beta1.DefaultCompare, resv1beta1.DefaultFilter)
result.Items = list
result.TotalItems = totalCount
_ = response.WriteEntity(result)
}
func (h *handler) ListWorkspaceMembers(request *restful.Request, response *restful.Response) {
workspace := request.PathParameter("workspace")
roleName := request.QueryParameter("workspacerole")
bindings, err := h.am.ListWorkspaceRoleBindings("", roleName, nil, workspace)
result := &api.ListResult{Items: make([]runtime.Object, 0)}
if err != nil {
api.HandleError(response, request, err)
return
}
filtered := []runtime.Object{}
for _, binding := range bindings {
for _, subject := range binding.Subjects {
if subject.Kind == rbacv1.UserKind {
filtered = append(filtered, &iamv1beta1.User{
ObjectMeta: metav1.ObjectMeta{
Name: subject.Name,
Annotations: map[string]string{
iamv1beta1.WorkspaceRoleAnnotation: binding.RoleRef.Name,
},
},
})
break
}
}
}
list, _, totalCount := resv1beta1.DefaultList(filtered, query.ParseQueryParameter(request), resv1beta1.DefaultCompare, resv1beta1.DefaultFilter)
result.Items = list
result.TotalItems = totalCount
_ = response.WriteEntity(result)
}
func (h *handler) ListNamespaceMembers(request *restful.Request, response *restful.Response) {
namespace := request.PathParameter("namespace")
roleName := request.QueryParameter("role")
bindings, err := h.am.ListRoleBindings("", roleName, nil, namespace)
if err != nil {
api.HandleError(response, request, err)
return
}
result := &api.ListResult{Items: make([]runtime.Object, 0)}
filtered := []runtime.Object{}
for _, binding := range bindings {
for _, subject := range binding.Subjects {
if subject.Kind == rbacv1.UserKind {
filtered = append(filtered, &iamv1beta1.User{
ObjectMeta: metav1.ObjectMeta{
Name: subject.Name,
Annotations: map[string]string{
iamv1beta1.RoleAnnotation: binding.RoleRef.Name,
},
},
})
break
}
}
}
list, _, totalCount := resv1beta1.DefaultList(filtered, query.ParseQueryParameter(request), resv1beta1.DefaultCompare, resv1beta1.DefaultFilter)
result.Items = list
result.TotalItems = totalCount
_ = response.WriteEntity(result)
}
func (h *handler) CreateClusterMembers(request *restful.Request, response *restful.Response) {
var members []Member
err := request.ReadEntity(&members)
if err != nil {
api.HandleBadRequest(response, request, err)
return
}
for _, member := range members {
err := h.am.CreateOrUpdateClusterRoleBinding(member.Username, member.RoleRef)
if err != nil {
api.HandleError(response, request, err)
return
}
}
response.WriteEntity(members)
}
func (h *handler) RemoveClusterMember(request *restful.Request, response *restful.Response) {
username := request.PathParameter("clustermember")
err := h.am.RemoveUserFromCluster(username)
if err != nil {
api.HandleError(response, request, err)
return
}
response.WriteEntity(servererr.None)
}
func (h *handler) CreateNamespaceMembers(request *restful.Request, response *restful.Response) {
namespace := request.PathParameter("namespace")
var members []Member
err := request.ReadEntity(&members)
if err != nil {
api.HandleBadRequest(response, request, err)
return
}
for _, member := range members {
err := h.am.CreateOrUpdateNamespaceRoleBinding(member.Username, namespace, member.RoleRef)
if err != nil {
api.HandleError(response, request, err)
return
}
}
response.WriteEntity(members)
}
func (h *handler) RemoveNamespaceMember(request *restful.Request, response *restful.Response) {
username := request.PathParameter("member")
namespace := request.PathParameter("namespace")
err := h.am.RemoveUserFromNamespace(username, namespace)
if err != nil {
api.HandleError(response, request, err)
return
}
response.WriteEntity(servererr.None)
}
func (h *handler) ListRoleTemplateOfUser(request *restful.Request, response *restful.Response) {
username := request.PathParameter("username")
scope := request.QueryParameter("scope")
namespace := request.QueryParameter("namespace")
workspace := request.QueryParameter("workspace")
userInfo, exist := apirequest.UserFrom(request.Request.Context())
if !exist {
err := errors.NewInternalError(fmt.Errorf("cannot obtain user info"))
api.HandleInternalError(response, request, err)
return
}
if userInfo.GetName() != username {
userManagement := authorizer.AttributesRecord{
Resource: "users",
Verb: "get",
ResourceScope: apirequest.GlobalScope,
ResourceRequest: true,
User: userInfo,
}
decision, _, err := h.authorizer.Authorize(userManagement)
if err != nil {
api.HandleInternalError(response, request, err)
return
}
if decision != authorizer.DecisionAllow {
err := errors.NewForbidden(iamv1beta1.Resource("users"), username, fmt.Errorf("not allow to get users"))
api.HandleError(response, request, err)
return
}
}
result := &api.ListResult{Items: make([]runtime.Object, 0)}
var roleTemplateNames []string
switch scope {
case iamv1beta1.ScopeGlobal:
globalRole, err := h.am.GetGlobalRoleOfUser(username)
if err != nil && !errors.IsNotFound(err) {
api.HandleError(response, request, err)
return
}
if globalRole != nil && globalRole.AggregationRoleTemplates != nil {
roleTemplateNames = globalRole.AggregationRoleTemplates.TemplateNames
}
case iamv1beta1.ScopeWorkspace:
workspaceRoles, err := h.am.GetWorkspaceRoleOfUser(username, nil, workspace)
if err != nil {
api.HandleError(response, request, err)
return
}
for _, workspaceRole := range workspaceRoles {
if workspaceRole.AggregationRoleTemplates != nil {
roleTemplateNames = append(roleTemplateNames, workspaceRole.AggregationRoleTemplates.TemplateNames...)
}
}
case iamv1beta1.ScopeCluster:
clusterRole, err := h.am.GetClusterRoleOfUser(username)
if err != nil && !errors.IsNotFound(err) {
api.HandleError(response, request, err)
return
}
if clusterRole != nil && clusterRole.AggregationRoleTemplates != nil {
roleTemplateNames = clusterRole.AggregationRoleTemplates.TemplateNames
}
case iamv1beta1.ScopeNamespace:
roles, err := h.am.GetNamespaceRoleOfUser(username, nil, namespace)
if err != nil {
api.HandleError(response, request, err)
return
}
for _, role := range roles {
if role.AggregationRoleTemplates != nil {
roleTemplateNames = append(roleTemplateNames, role.AggregationRoleTemplates.TemplateNames...)
}
}
}
for _, name := range roleTemplateNames {
template, err := h.am.GetRoleTemplate(name)
if err != nil {
if errors.IsNotFound(err) {
continue
}
api.HandleError(response, request, err)
return
}
result.Items = append(result.Items, template)
result.TotalItems += 1
}
_ = response.WriteEntity(result)
}
func (h *handler) CreateSubjectAccessReview(request *restful.Request, response *restful.Response) {
data := make(map[string]interface{})
err := request.ReadEntity(&data)
if err != nil {
api.HandleBadRequest(response, request, err)
return
}
uns := unstructured.Unstructured{Object: data}
unsConverter := runtime.DefaultUnstructuredConverter
if uns.IsList() {
list := &iamv1beta1.SubjectAccessReviewList{}
err := unsConverter.FromUnstructured(uns.Object, list)
if err != nil {
api.HandleBadRequest(response, request, err)
return
}
result, err := h.handleList(*list)
if err != nil {
api.HandleBadRequest(response, request, err)
return
}
_ = response.WriteEntity(result)
return
}
review := &iamv1beta1.SubjectAccessReview{}
err = unsConverter.FromUnstructured(uns.Object, review)
if err != nil {
api.HandleBadRequest(response, request, err)
return
}
result, err := h.handleSingle(*review)
if err != nil {
api.HandleBadRequest(response, request, err)
return
}
_ = response.WriteEntity(result)
}
func (h *handler) handleList(list iamv1beta1.SubjectAccessReviewList) (iamv1beta1.SubjectAccessReviewList, error) {
var (
wg sync.WaitGroup
errList []error
newItems []iamv1beta1.SubjectAccessReview
accessReviewMux sync.Mutex
errMux sync.Mutex
)
for _, review := range list.Items {
wg.Add(1)
go func(review iamv1beta1.SubjectAccessReview) {
defer wg.Done()
accessReview, err := h.handleSingle(review)
if err != nil {
errMux.Lock()
errList = append(errList, err)
errMux.Unlock()
return
}
accessReviewMux.Lock()
newItems = append(newItems, accessReview)
accessReviewMux.Unlock()
}(review)
}
wg.Wait()
list.Items = newItems
return list, utilerrors.NewAggregate(errList)
}
func (h *handler) handleSingle(review iamv1beta1.SubjectAccessReview) (iamv1beta1.SubjectAccessReview, error) {
decision, reason, err := h.authorizer.Authorize(prepareAttribute(review))
if err != nil {
return iamv1beta1.SubjectAccessReview{}, err
}
review.Status = iamv1beta1.SubjectAccessReviewStatus{
Allowed: decision == authorizer.DecisionAllow,
Denied: decision == authorizer.DecisionDeny,
Reason: reason,
}
return review, nil
}
func prepareAttribute(subjectAccessReview iamv1beta1.SubjectAccessReview) authorizer.AttributesRecord {
attr := authorizer.AttributesRecord{
User: &iamv1beta1.DefaultInfo{
Name: subjectAccessReview.Spec.User,
UID: subjectAccessReview.Spec.UID,
Groups: subjectAccessReview.Spec.Groups,
},
}
if subjectAccessReview.Spec.ResourceAttributes != nil {
attr.ResourceRequest = true
attr.Verb = subjectAccessReview.Spec.ResourceAttributes.Verb
attr.APIGroup = subjectAccessReview.Spec.ResourceAttributes.Group
attr.APIVersion = subjectAccessReview.Spec.ResourceAttributes.Version
attr.Workspace = subjectAccessReview.Spec.ResourceAttributes.Workspace
attr.Namespace = subjectAccessReview.Spec.ResourceAttributes.Namespace
attr.Resource = subjectAccessReview.Spec.ResourceAttributes.Resource
attr.Name = subjectAccessReview.Spec.ResourceAttributes.Name
attr.Subresource = subjectAccessReview.Spec.ResourceAttributes.Subresource
if attr.Namespace != "" {
attr.ResourceScope = apirequest.NamespaceScope
} else if attr.Workspace != "" {
attr.ResourceScope = apirequest.WorkspaceScope
} else {
attr.ResourceScope = subjectAccessReview.Spec.ResourceAttributes.ResourceScope
}
} else if subjectAccessReview.Spec.NonResourceAttributes != nil {
attr.ResourceRequest = false
attr.Verb = subjectAccessReview.Spec.NonResourceAttributes.Verb
attr.Path = subjectAccessReview.Spec.NonResourceAttributes.Path
}
return attr
}
func (h *handler) CreateWorkspaceMembers(request *restful.Request, response *restful.Response) {
workspace := request.PathParameter("workspace")
var members []Member
err := request.ReadEntity(&members)
if err != nil {
api.HandleBadRequest(response, request, err)
return
}
for _, member := range members {
err := h.am.CreateOrUpdateUserWorkspaceRoleBinding(member.Username, workspace, member.RoleRef)
if err != nil {
api.HandleError(response, request, err)
return
}
}
response.WriteEntity(members)
}
func (h *handler) RemoveWorkspaceMember(request *restful.Request, response *restful.Response) {
workspace := request.PathParameter("workspace")
username := request.PathParameter("workspacemember")
err := h.am.RemoveUserFromWorkspace(username, workspace)
if err != nil {
api.HandleError(response, request, err)
return
}
response.WriteEntity(servererr.None)
}
func (h *handler) DescribeWorkspaceMember(request *restful.Request, response *restful.Response) {
workspace := request.PathParameter("workspace")
memberName := request.PathParameter("workspacemember")
bindings, err := h.am.ListWorkspaceRoleBindings(memberName, "", nil, workspace)
if err != nil {
api.HandleInternalError(response, request, err)
return
}
if len(bindings) == 0 {
api.HandleBadRequest(response, request, NewErrMemberNotExist(memberName))
return
}
user, err := h.im.DescribeUser(memberName)
if err != nil {
api.HandleError(response, request, err)
return
}
user.Annotations[iamv1beta1.WorkspaceRoleAnnotation] = bindings[0].RoleRef.Name
_ = response.WriteEntity(user)
}
func (h *handler) UpdateWorkspaceMember(request *restful.Request, response *restful.Response) {
workspace := request.PathParameter("workspace")
memberName := request.PathParameter("workspacemember")
var member Member
err := request.ReadEntity(&member)
if err != nil {
api.HandleBadRequest(response, request, err)
return
}
if memberName != member.Username {
api.HandleBadRequest(response, request, NewErrIncorrectUsername(memberName))
return
}
bindings, err := h.am.ListWorkspaceRoleBindings(memberName, "", nil, workspace)
if err != nil {
api.HandleInternalError(response, request, err)
return
}
if len(bindings) == 0 {
api.HandleBadRequest(response, request, NewErrMemberNotExist(member.Username))
return
}
err = h.am.CreateOrUpdateUserWorkspaceRoleBinding(member.Username, workspace, member.RoleRef)
if err != nil {
api.HandleError(response, request, err)
return
}
response.WriteEntity(servererr.None)
}
func (h *handler) UpdateClusterMember(request *restful.Request, response *restful.Response) {
memberName := request.PathParameter("clustermember")
var member Member
err := request.ReadEntity(&member)
if err != nil {
api.HandleBadRequest(response, request, err)
return
}
if memberName != member.Username {
api.HandleBadRequest(response, request, NewErrIncorrectUsername(memberName))
return
}
bindings, err := h.am.ListClusterRoleBindings(memberName, "")
if err != nil {
api.HandleInternalError(response, request, err)
return
}
if len(bindings) == 0 {
api.HandleBadRequest(response, request, NewErrMemberNotExist(member.Username))
return
}
err = h.am.CreateOrUpdateClusterRoleBinding(member.Username, member.RoleRef)
if err != nil {
api.HandleError(response, request, err)
return
}
response.WriteEntity(servererr.None)
}
func (h *handler) UpdateNamespaceMember(request *restful.Request, response *restful.Response) {
namespace := request.PathParameter("namespace")
memberName := request.PathParameter("namespacemember")
var member Member
err := request.ReadEntity(&member)
if err != nil {
api.HandleBadRequest(response, request, err)
return
}
if memberName != member.Username {
api.HandleBadRequest(response, request, NewErrIncorrectUsername(memberName))
return
}
bindings, err := h.am.ListRoleBindings(member.Username, "", nil, namespace)
if err != nil {
api.HandleInternalError(response, request, err)
return
}
if len(bindings) == 0 {
api.HandleBadRequest(response, request, NewErrMemberNotExist(member.Username))
return
}
err = h.am.CreateOrUpdateNamespaceRoleBinding(member.Username, namespace, member.RoleRef)
if err != nil {
api.HandleError(response, request, err)
return
}
response.WriteEntity(servererr.None)
}
func NewErrMemberNotExist(username string) error {
return fmt.Errorf("member %s not exist", username)
}
func NewErrIncorrectUsername(username string) error {
return fmt.Errorf("incorrect username %s, the username must equal to the member", username)
}

View File

@@ -0,0 +1,185 @@
/*
* Please refer to the LICENSE file in the root directory of the project.
* https://github.com/kubesphere/kubesphere/blob/master/LICENSE
*/
package v1beta1
import (
"net/http"
restfulspec "github.com/emicklei/go-restful-openapi/v2"
"github.com/emicklei/go-restful/v3"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
iamv1beta1 "kubesphere.io/api/iam/v1beta1"
"kubesphere.io/kubesphere/pkg/api"
apiserverruntime "kubesphere.io/kubesphere/pkg/apiserver/runtime"
"kubesphere.io/kubesphere/pkg/server/errors"
)
var GroupVersion = schema.GroupVersion{Group: "iam.kubesphere.io", Version: "v1beta1"}
func (h *handler) AddToContainer(container *restful.Container) error {
ws := apiserverruntime.NewWebService(GroupVersion)
ws.Route(ws.POST("/users").
To(h.CreateUser).
Doc("Create user").
Metadata(restfulspec.KeyOpenAPITags, []string{api.TagIdentityManagement}).
Returns(http.StatusOK, api.StatusOK, iamv1beta1.User{}).
Reads(iamv1beta1.User{}))
ws.Route(ws.PUT("/users/{user}").
To(h.UpdateUser).
Doc("Update user").
Metadata(restfulspec.KeyOpenAPITags, []string{api.TagIdentityManagement}).
Reads(iamv1beta1.User{}).
Param(ws.PathParameter("user", "username")).
Returns(http.StatusOK, api.StatusOK, iamv1beta1.User{}))
ws.Route(ws.DELETE("/users/{user}").
To(h.DeleteUser).
Doc("Delete user").
Metadata(restfulspec.KeyOpenAPITags, []string{api.TagIdentityManagement}).
Param(ws.PathParameter("user", "username")).
Returns(http.StatusOK, api.StatusOK, errors.None))
ws.Route(ws.PUT("/users/{user}/password").
To(h.ModifyPassword).
Doc("Reset password").
Metadata(restfulspec.KeyOpenAPITags, []string{api.TagIdentityManagement}).
Reads(PasswordReset{}).
Param(ws.PathParameter("user", "username")).
Returns(http.StatusOK, api.StatusOK, errors.None))
ws.Route(ws.GET("/users/{user}").
To(h.DescribeUser).
Doc("Get user").
Notes("Retrieve user details.").
Metadata(restfulspec.KeyOpenAPITags, []string{api.TagIdentityManagement}).
Param(ws.PathParameter("user", "username")).
Returns(http.StatusOK, api.StatusOK, iamv1beta1.User{}))
ws.Route(ws.GET("/users").
To(h.ListUsers).
Doc("List users").
Metadata(restfulspec.KeyOpenAPITags, []string{api.TagIdentityManagement}).
Param(ws.QueryParameter("globalrole", "specific golalrole name")).
Returns(http.StatusOK, api.StatusOK, api.ListResult{Items: []runtime.Object{&iamv1beta1.User{}}}))
ws.Route(ws.GET("/users/{user}/loginrecords").
To(h.ListUserLoginRecords).
Doc("List login records").
Metadata(restfulspec.KeyOpenAPITags, []string{api.TagIdentityManagement}).
Param(ws.PathParameter("user", "username of the user")).
Returns(http.StatusOK, api.StatusOK, api.ListResult{Items: []runtime.Object{&iamv1beta1.LoginRecord{}}}))
// members
ws.Route(ws.GET("/clustermembers").
To(h.ListClusterMembers).
Doc("List all members of cluster").
Metadata(restfulspec.KeyOpenAPITags, []string{api.TagAccessManagement}).
Param(ws.QueryParameter("clusterrole", "specific the cluster role name")).
Returns(http.StatusOK, api.StatusOK, api.ListResult{Items: []runtime.Object{&iamv1beta1.User{}}}))
ws.Route(ws.POST("/clustermembers").
To(h.CreateClusterMembers).
Doc("Add members to cluster").
Metadata(restfulspec.KeyOpenAPITags, []string{api.TagAccessManagement}).
Reads([]Member{}).
Returns(http.StatusOK, api.StatusOK, []Member{}))
ws.Route(ws.DELETE("/clustermembers/{clustermember}").
To(h.RemoveClusterMember).
Doc("Delete member from cluster").
Metadata(restfulspec.KeyOpenAPITags, []string{api.TagAccessManagement}).
Param(ws.PathParameter("clustermember", "cluster member's username")).
Returns(http.StatusOK, api.StatusOK, errors.None))
ws.Route(ws.PUT("/clustermembers/{clustermember}").
To(h.UpdateClusterMember).
Doc("Update member from the cluster").
Metadata(restfulspec.KeyOpenAPITags, []string{api.TagAccessManagement}).
Param(ws.PathParameter("clustermember", "the member name from cluster")).
Reads(Member{}).
Returns(http.StatusOK, api.StatusOK, errors.None))
ws.Route(ws.GET("/workspaces/{workspace}/workspacemembers").
To(h.ListWorkspaceMembers).
Doc("List all members in the specified workspace").
Metadata(restfulspec.KeyOpenAPITags, []string{api.TagAccessManagement}).
Param(ws.PathParameter("workspace", "The specified workspace.")).
Param(ws.QueryParameter("workspacerole", "specific the workspace role name")).
Returns(http.StatusOK, api.StatusOK, api.ListResult{Items: []runtime.Object{&iamv1beta1.User{}}}))
ws.Route(ws.PUT("/workspaces/{workspace}/workspacemembers/{workspacemember}").
To(h.UpdateWorkspaceMember).
Doc("Update member from the workspace").
Metadata(restfulspec.KeyOpenAPITags, []string{api.TagAccessManagement}).
Param(ws.PathParameter("workspace", "The specified workspace.")).
Param(ws.PathParameter("workspacemember", "the member from workspace")).
Reads(Member{}).
Returns(http.StatusOK, api.StatusOK, errors.None))
ws.Route(ws.POST("/workspaces/{workspace}/workspacemembers").
To(h.CreateWorkspaceMembers).
Doc("Add members to the specified workspace").
Metadata(restfulspec.KeyOpenAPITags, []string{api.TagAccessManagement}).
Param(ws.PathParameter("workspace", "The specified workspace.")).
Reads([]Member{}).
Returns(http.StatusOK, api.StatusOK, []Member{}))
ws.Route(ws.DELETE("/workspaces/{workspace}/workspacemembers/{workspacemember}").
To(h.RemoveWorkspaceMember).
Doc("Delete a member from the workspace").
Metadata(restfulspec.KeyOpenAPITags, []string{api.TagAccessManagement}).
Param(ws.PathParameter("workspace", "The specified workspace.")).
Param(ws.PathParameter("workspacemember", "Workspace member's name.")).
Returns(http.StatusOK, api.StatusOK, errors.None))
ws.Route(ws.GET("/workspaces/{workspace}/workspacemembers/{workspacemember}").
To(h.DescribeWorkspaceMember).
Doc("Get workspace member").
Metadata(restfulspec.KeyOpenAPITags, []string{api.TagAccessManagement}).
Param(ws.PathParameter("workspace", "The specified workspace.")).
Param(ws.PathParameter("workspacemember", "Workspace member's name.")).
Returns(http.StatusOK, api.StatusOK, iamv1beta1.User{}))
ws.Route(ws.GET("/namespaces/{namespace}/namespacemembers").
To(h.ListNamespaceMembers).
Doc("List all members in the specified namespace").
Metadata(restfulspec.KeyOpenAPITags, []string{api.TagAccessManagement}).
Param(ws.QueryParameter("role", "specific the role name")).
Param(ws.PathParameter("namespace", "The specified namespace.")).
Returns(http.StatusOK, api.StatusOK, api.ListResult{Items: []runtime.Object{&iamv1beta1.User{}}}))
ws.Route(ws.POST("/namespaces/{namespace}/namespacemembers").
To(h.CreateNamespaceMembers).
Doc("Add members to the namespace in bulk.").
Metadata(restfulspec.KeyOpenAPITags, []string{api.TagAccessManagement}).
Reads([]Member{}).
Returns(http.StatusOK, api.StatusOK, []Member{}).
Param(ws.PathParameter("namespace", "The specified namespace.")))
ws.Route(ws.DELETE("/namespaces/{namespace}/namespacemembers/{member}").
To(h.RemoveNamespaceMember).
Doc("Delete a member from the namespace").
Metadata(restfulspec.KeyOpenAPITags, []string{api.TagAccessManagement}).
Param(ws.PathParameter("namespace", "The specified namespace.")).
Param(ws.PathParameter("member", "namespace member's username")).
Returns(http.StatusOK, api.StatusOK, errors.None))
ws.Route(ws.PUT("/namespaces/{namespace}/namespacemembers/{namespacemember}").
To(h.UpdateNamespaceMember).
Doc("Update member from the namespace").
Metadata(restfulspec.KeyOpenAPITags, []string{api.TagAccessManagement}).
Param(ws.PathParameter("namespace", "The specified namespace.")).
Param(ws.PathParameter("namespacemember", "the member from namespace")).
Reads(Member{}).
Returns(http.StatusOK, api.StatusOK, errors.None))
ws.Route(ws.GET("/users/{username}/roletemplates").
To(h.ListRoleTemplateOfUser).
Doc("List all role templates of the specified user").
Metadata(restfulspec.KeyOpenAPITags, []string{api.TagAccessManagement}).
Param(ws.PathParameter("username", "the name of the specified user")).
Param(ws.QueryParameter("scope", "the scope of role templates")).
Returns(http.StatusOK, api.StatusOK, api.ListResult{Items: []runtime.Object{&iamv1beta1.RoleTemplate{}}}))
ws.Route(ws.POST("/subjectaccessreviews").
To(h.CreateSubjectAccessReview).
Doc("Create subject access review").
Notes("Evaluates all of the request attributes against all policies and allows or denies the request.").
Metadata(restfulspec.KeyOpenAPITags, []string{api.TagAccessManagement}).
Reads(iamv1beta1.SubjectAccessReview{}).
Returns(http.StatusOK, api.StatusOK, iamv1beta1.SubjectAccessReview{}))
container.Add(ws)
return nil
}