diff --git a/cmd/climc/shell/groups.go b/cmd/climc/shell/groups.go index 0900bffedc..1dd45f3c0f 100644 --- a/cmd/climc/shell/groups.go +++ b/cmd/climc/shell/groups.go @@ -17,6 +17,7 @@ package shell import ( "yunion.io/x/jsonutils" + api "yunion.io/x/onecloud/pkg/apis/identity" "yunion.io/x/onecloud/pkg/mcclient" "yunion.io/x/onecloud/pkg/mcclient/modules" "yunion.io/x/onecloud/pkg/mcclient/options" @@ -133,4 +134,61 @@ func init() { return nil }) + R(&GroupShowOptions{}, "group-project-list", "List projects of group", func(s *mcclient.ClientSession, args *GroupShowOptions) error { + query := jsonutils.NewDict() + if len(args.Domain) > 0 { + domainId, err := modules.Domains.GetId(s, args.Domain, nil) + if err != nil { + return err + } + query.Add(jsonutils.NewString(domainId), "domain_id") + } + uid, err := modules.Groups.GetId(s, args.ID, query) + if err != nil { + return err + } + projects, e := modules.Groups.GetProjects(s, uid) + if e != nil { + return e + } + printList(projects, modules.Projects.GetColumns(s)) + return nil + }) + + type GroupJoinProjectOptions struct { + Group string `help:"Group Id or name" optional:"false" positional:"true"` + Project []string `help:"Projects to join" nargs:"+"` + Role []string `help:"User join project with roles" nargs:"+"` + } + R(&GroupJoinProjectOptions{}, "group-join-project", "Group join projects with roles", func(s *mcclient.ClientSession, args *GroupJoinProjectOptions) error { + input := api.SJoinProjectsInput{} + input.Projects = args.Project + input.Roles = args.Role + result, err := modules.Groups.PerformAction(s, args.Group, "join", jsonutils.Marshal(input)) + if err != nil { + return err + } + printObject(result) + return nil + }) + + type GroupLeaveProjectsOptions struct { + Group string `help:"group id or name" optional:"false" positional:"true"` + Project string `help:"project id or name" optional:"false" positional:"true"` + Role []string `help:"roles to remove" nargs:"+"` + } + R(&GroupLeaveProjectsOptions{}, "group-leave-project", "Leave a group from projects", func(s *mcclient.ClientSession, args *GroupLeaveProjectsOptions) error { + input := api.SLeaveProjectsInput{} + input.ProjectRoles = make([]api.SProjectRole, len(args.Role)) + for i := range args.Role { + input.ProjectRoles[i].Project = args.Project + input.ProjectRoles[i].Role = args.Role[i] + } + result, err := modules.Groups.PerformAction(s, args.Group, "leave", jsonutils.Marshal(input)) + if err != nil { + return err + } + printObject(result) + return nil + }) } diff --git a/cmd/climc/shell/projects.go b/cmd/climc/shell/projects.go index d3cd234043..ab54db2721 100644 --- a/cmd/climc/shell/projects.go +++ b/cmd/climc/shell/projects.go @@ -17,6 +17,7 @@ package shell import ( "yunion.io/x/jsonutils" + api "yunion.io/x/onecloud/pkg/apis/identity" "yunion.io/x/onecloud/pkg/mcclient" "yunion.io/x/onecloud/pkg/mcclient/modules" "yunion.io/x/onecloud/pkg/mcclient/options" @@ -364,6 +365,7 @@ func init() { return nil })*/ + // Deprecated type ProjectBatchJoinOptions struct { Ids []string `help:"user ids or group ids"` Resource string `help:"resource type" choices:"users|groups"` @@ -378,4 +380,55 @@ func init() { } return nil }) + + type ProjectAddUserGroupOptions struct { + Project string `help:"ID or name of project to add users/groups" positional:"true" optional:"false"` + User []string `help:"ID of user to add"` + Group []string `help:"ID of group to add"` + Role []string `help:"ID of role to add"` + } + R(&ProjectAddUserGroupOptions{}, "project-add-user-group", "Batch add users/groups to project", func(s *mcclient.ClientSession, args *ProjectAddUserGroupOptions) error { + input := api.SProjectAddUserGroupInput{} + input.Users = args.User + input.Groups = args.Group + input.Roles = args.Role + err := input.Validate() + if err != nil { + return err + } + result, err := modules.Projects.PerformAction(s, args.Project, "join", jsonutils.Marshal(input)) + if err != nil { + return err + } + printObject(result) + return nil + }) + + type ProjectRemoveUserGroup struct { + Project string `help:"ID or name of project to remove user/group" optional:"false" positional:"true"` + User string `help:"user to remove"` + Group string `help:"group to remove"` + Role []string `help:"roles to remove"` + } + R(&ProjectRemoveUserGroup{}, "project-remove-user-group", "Remove users/groups from project", func(s *mcclient.ClientSession, args *ProjectRemoveUserGroup) error { + input := api.SProjectRemoveUserGroupInput{} + input.UserRoles = make([]api.SUserRole, len(args.Role)) + input.GroupRoles = make([]api.SGroupRole, len(args.Role)) + for i := range args.Role { + input.UserRoles[i].User = args.User + input.UserRoles[i].Role = args.Role[i] + input.GroupRoles[i].Group = args.Group + input.GroupRoles[i].Role = args.Role[i] + } + err := input.Validate() + if err != nil { + return err + } + result, err := modules.Projects.PerformAction(s, args.Project, "leave", jsonutils.Marshal(input)) + if err != nil { + return err + } + printObject(result) + return nil + }) } diff --git a/cmd/climc/shell/users.go b/cmd/climc/shell/users.go index cea0daa146..b0feccfcb0 100644 --- a/cmd/climc/shell/users.go +++ b/cmd/climc/shell/users.go @@ -17,6 +17,7 @@ package shell import ( "yunion.io/x/jsonutils" + api "yunion.io/x/onecloud/pkg/apis/identity" "yunion.io/x/onecloud/pkg/mcclient" "yunion.io/x/onecloud/pkg/mcclient/modules" "yunion.io/x/onecloud/pkg/mcclient/options" @@ -361,4 +362,41 @@ func init() { return nil }) + type UserJoinProjectOptions struct { + User string `help:"User Id or name" optional:"false" positional:"true"` + Project []string `help:"Projects to join" nargs:"+"` + Role []string `help:"User join project with roles" nargs:"+"` + } + R(&UserJoinProjectOptions{}, "user-join-project", "User join projects with roles", func(s *mcclient.ClientSession, args *UserJoinProjectOptions) error { + input := api.SJoinProjectsInput{} + input.Projects = args.Project + input.Roles = args.Role + result, err := modules.UsersV3.PerformAction(s, args.User, "join", jsonutils.Marshal(input)) + if err != nil { + return err + } + printObject(result) + return nil + }) + + type UserLeaveProjectsOptions struct { + User string `help:"user id or name" optional:"false" positional:"true"` + Project string `help:"project id or name" optional:"false" positional:"true"` + Role []string `help:"roles to remove" nargs:"+"` + } + R(&UserLeaveProjectsOptions{}, "user-leave-project", "Leave a user from projects", func(s *mcclient.ClientSession, args *UserLeaveProjectsOptions) error { + input := api.SLeaveProjectsInput{} + input.ProjectRoles = make([]api.SProjectRole, len(args.Role)) + for i := range args.Role { + input.ProjectRoles[i].Project = args.Project + input.ProjectRoles[i].Role = args.Role[i] + } + result, err := modules.UsersV3.PerformAction(s, args.User, "leave", jsonutils.Marshal(input)) + if err != nil { + return err + } + printObject(result) + return nil + }) + } diff --git a/docs/identity/group_join.yaml b/docs/identity/group_join.yaml new file mode 100644 index 0000000000..7370da0654 --- /dev/null +++ b/docs/identity/group_join.yaml @@ -0,0 +1,16 @@ +post: + summary: 将组以多个角色加入多个项目 + parameters: + - $ref: '../parameters/group.yaml#/group_id' + - name: group + in: body + required: true + schema: + $ref: "../schemas/project.yaml#/ProjectJoinRequestInput" + responses: + 200: + description: 组信息 + schema: + $ref: "../schemas/group.yaml#/GroupGetResponse" + tags: + - groups diff --git a/docs/identity/group_leave.yaml b/docs/identity/group_leave.yaml new file mode 100644 index 0000000000..8830b4d4d8 --- /dev/null +++ b/docs/identity/group_leave.yaml @@ -0,0 +1,16 @@ +post: + summary: 将组的多个角色从多个项目中删除 + parameters: + - $ref: '../parameters/group.yaml#/group_id' + - name: group + in: body + required: true + schema: + $ref: "../schemas/project.yaml#/ProjectLeaveRequestInput" + responses: + 200: + description: 组信息 + schema: + $ref: "../schemas/group.yaml#/GroupGetResponse" + tags: + - groups diff --git a/docs/identity/project_join.yaml b/docs/identity/project_join.yaml new file mode 100644 index 0000000000..ed03bb1fe4 --- /dev/null +++ b/docs/identity/project_join.yaml @@ -0,0 +1,16 @@ +post: + summary: 将多个用户和组以多个角色加入指定项目 + parameters: + - $ref: '../parameters/project.yaml#/project_id' + - name: project + in: body + required: true + schema: + $ref: "../schemas/project.yaml#/ProjectJoinUserGroupRequestInput" + responses: + 200: + description: 项目信息 + schema: + $ref: "../schemas/project.yaml#/ProjectGetResponse" + tags: + - projects diff --git a/docs/identity/project_leave.yaml b/docs/identity/project_leave.yaml new file mode 100644 index 0000000000..32671e0093 --- /dev/null +++ b/docs/identity/project_leave.yaml @@ -0,0 +1,16 @@ +post: + summary: 将多个用户和组以多个角色从指定项目删除 + parameters: + - $ref: '../parameters/project.yaml#/project_id' + - name: project + in: body + required: true + schema: + $ref: "../schemas/project.yaml#/ProjectRemoveUserGroupRequestInput" + responses: + 200: + description: 项目信息 + schema: + $ref: "../schemas/project.yaml#/ProjectGetResponse" + tags: + - projects diff --git a/docs/identity/projectgrouprole.yaml b/docs/identity/projectgrouprole.yaml index 15ec0bd3b3..ffa9d0cb81 100644 --- a/docs/identity/projectgrouprole.yaml +++ b/docs/identity/projectgrouprole.yaml @@ -1,4 +1,5 @@ put: + deprecated: true summary: 将组以指定角色加入项目 parameters: - $ref: '../parameters/identity.yaml#/project_id' @@ -11,6 +12,7 @@ put: - roles delete: + deprecated: true summary: 将组在项目中的角色删除 parameters: - $ref: '../parameters/identity.yaml#/project_id' diff --git a/docs/identity/projectuserrole.yaml b/docs/identity/projectuserrole.yaml index ce3deb51ed..da7bf71e61 100644 --- a/docs/identity/projectuserrole.yaml +++ b/docs/identity/projectuserrole.yaml @@ -1,4 +1,5 @@ put: + deprecated: true summary: 将用户以指定角色加入项目 parameters: - $ref: '../parameters/identity.yaml#/project_id' @@ -11,6 +12,7 @@ put: - roles delete: + deprecated: true summary: 将用户在项目中的角色删除 parameters: - $ref: '../parameters/identity.yaml#/project_id' diff --git a/docs/identity/user_join.yaml b/docs/identity/user_join.yaml new file mode 100644 index 0000000000..7703625e6a --- /dev/null +++ b/docs/identity/user_join.yaml @@ -0,0 +1,16 @@ +post: + summary: 将用户以多个角色加入多个项目 + parameters: + - $ref: '../parameters/user.yaml#/user_id' + - name: user + in: body + required: true + schema: + $ref: "../schemas/project.yaml#/ProjectJoinRequestInput" + responses: + 200: + description: 用户信息 + schema: + $ref: "../schemas/user.yaml#/UserGetResponse" + tags: + - users diff --git a/docs/identity/user_leave.yaml b/docs/identity/user_leave.yaml new file mode 100644 index 0000000000..a989ef8496 --- /dev/null +++ b/docs/identity/user_leave.yaml @@ -0,0 +1,16 @@ +post: + summary: 将用户的多个角色从多个项目中删除 + parameters: + - $ref: '../parameters/user.yaml#/user_id' + - name: user + in: body + required: true + schema: + $ref: "../schemas/project.yaml#/ProjectLeaveRequestInput" + responses: + 200: + description: 用户信息 + schema: + $ref: "../schemas/user.yaml#/UserGetResponse" + tags: + - users diff --git a/docs/index.yaml b/docs/index.yaml index 1e481f2b41..d711d8392d 100644 --- a/docs/index.yaml +++ b/docs/index.yaml @@ -312,11 +312,19 @@ paths: $ref: "./identity/users.yaml" /users/{user_id}: $ref: "./identity/user.yaml" + /users/{user_id}/join: + $ref: "./identity/user_join.yaml" + /users/{user_id}/leave: + $ref: "./identity/user_leave.yaml" /groups: $ref: "./identity/groups.yaml" /groups/{group_id}: $ref: "./identity/group.yaml" + /groups/{group_id}/join: + $ref: "./identity/group_join.yaml" + /groups/{group_id}/leave: + $ref: "./identity/group_leave.yaml" /groups/{group_id}/users/{user_id}: $ref: "./identity/groupuser.yaml" @@ -324,6 +332,10 @@ paths: $ref: "./identity/projects.yaml" /projects/{project_id}: $ref: "./identity/project.yaml" + /projects/{project_id}/join: + $ref: "./identity/project_join.yaml" + /projects/{project_id}/leave: + $ref: "./identity/project_leave.yaml" /roles: $ref: "./identity/roles.yaml" diff --git a/docs/schemas/project.yaml b/docs/schemas/project.yaml index 1438a0b4ed..629b6d66d7 100644 --- a/docs/schemas/project.yaml +++ b/docs/schemas/project.yaml @@ -57,3 +57,84 @@ Project: can_delete: type: boolean description: 是否可以删除 + +ProjectJoinRequestInput: + type: object + description: 用户或组批量加入项目的请求body + properties: + projects: + type: array + description: 项目列表 + items: + type: string + roles: + type: array + description: 角色列表 + items: + type: string + +ProjectLeaveRequestInput: + type: object + description: 用户或组批量移除项目的请求body + properties: + project_roles: + type: array + description: 移除的项目和角色列表 + items: + type: object + properties: + project: + type: string + description: 项目ID或名称 + role: + type: string + description: 角色ID或名称 + +ProjectJoinUserGroupRequestInput: + type: object + description: 将多个用户或组批量加入指定项目的请求body + properties: + users: + type: array + description: 加入的用户列表 + items: + type: string + groups: + type: array + description: 加入的组列表 + items: + type: string + roles: + type: array + description: 加入的角色列表 + items: + type: string + +ProjectRemoveUserGroupRequestInput: + type: object + description: 将多个用户或组从指定项目移除的请求body + properties: + user_roles: + type: array + description: 用户和角色列表 + items: + type: object + properties: + user: + type: string + description: 用户ID或名称 + role: + type: string + description: 角色ID或名称 + group_roles: + type: array + description: 组和角色列表 + items: + type: object + properties: + group: + type: string + description: 组ID或名称 + role: + type: string + description: 角色ID或名称 diff --git a/pkg/apis/identity/input.go b/pkg/apis/identity/input.go new file mode 100644 index 0000000000..00efbe887e --- /dev/null +++ b/pkg/apis/identity/input.go @@ -0,0 +1,110 @@ +// Copyright 2019 Yunion +// +// 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 identity + +import ( + "yunion.io/x/pkg/errors" +) + +type SJoinProjectsInput struct { + Projects []string + Roles []string +} + +func (input SJoinProjectsInput) Validate() error { + if len(input.Projects) == 0 { + return errors.Error("empty projects") + } + if len(input.Roles) == 0 { + return errors.Error("empty roles") + } + return nil +} + +type SProjectRole struct { + Project string + Role string +} +type SLeaveProjectsInput struct { + ProjectRoles []SProjectRole +} + +func (input SLeaveProjectsInput) Validate() error { + if len(input.ProjectRoles) == 0 { + return errors.Error("empty project_roles") + } + for i := range input.ProjectRoles { + if len(input.ProjectRoles[i].Project) == 0 { + return errors.Error("no project in project_roles") + } + if len(input.ProjectRoles[i].Role) == 0 { + return errors.Error("no role in project_roles") + } + } + return nil +} + +type SProjectAddUserGroupInput struct { + Users []string + Groups []string + Roles []string +} + +func (input SProjectAddUserGroupInput) Validate() error { + if len(input.Users) == 0 || len(input.Groups) == 0 { + return errors.Error("empty user and group") + } + if len(input.Roles) == 0 { + return errors.Error("invalid roles") + } + return nil +} + +type SUserRole struct { + User string + Role string +} +type SGroupRole struct { + Group string + Role string +} + +type SProjectRemoveUserGroupInput struct { + UserRoles []SUserRole + GroupRoles []SGroupRole +} + +func (input SProjectRemoveUserGroupInput) Validate() error { + if len(input.UserRoles) == 0 && len(input.GroupRoles) == 0 { + return errors.Error("empty input") + } + for i := range input.UserRoles { + if len(input.UserRoles[i].User) == 0 { + return errors.Error("empty user") + } + if len(input.UserRoles[i].Role) == 0 { + return errors.Error("empty role") + } + } + for i := range input.GroupRoles { + if len(input.GroupRoles[i].Group) == 0 { + return errors.Error("empty group") + } + if len(input.GroupRoles[i].Role) == 0 { + return errors.Error("empty role") + } + } + return nil +} diff --git a/pkg/cloudcommon/db/db_dispatcher.go b/pkg/cloudcommon/db/db_dispatcher.go index e16fe9e0e8..1cb18cbb85 100644 --- a/pkg/cloudcommon/db/db_dispatcher.go +++ b/pkg/cloudcommon/db/db_dispatcher.go @@ -417,7 +417,7 @@ func fetchContextObject(manager IModelManager, ctx context.Context, userCred mcc return nil, httperrors.NewInternalServerError("No context manager") } for i := 0; i < len(ctxMans); i += 1 { - for j := 0; j < len(ctxMans); j += 1 { + for j := 0; j < len(ctxMans[i]); j += 1 { if ctxMans[i][j].KeywordPlural() == ctxId.Type { ctxObj, err := fetchItem(ctxMans[i][j], ctx, userCred, ctxId.Id, nil) if err != nil { diff --git a/pkg/compute/tasks/disk_clean_overdued_snapshots.go b/pkg/compute/tasks/disk_clean_overdued_snapshots.go index a56ab73a64..087451bfff 100644 --- a/pkg/compute/tasks/disk_clean_overdued_snapshots.go +++ b/pkg/compute/tasks/disk_clean_overdued_snapshots.go @@ -1,3 +1,17 @@ +// Copyright 2019 Yunion +// +// 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 tasks import ( diff --git a/pkg/compute/tasks/snapshot_delete_task.go b/pkg/compute/tasks/snapshot_delete_task.go index 29a65a2c9a..62298ab5ee 100644 --- a/pkg/compute/tasks/snapshot_delete_task.go +++ b/pkg/compute/tasks/snapshot_delete_task.go @@ -1,3 +1,17 @@ +// Copyright 2019 Yunion +// +// 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 tasks import ( diff --git a/pkg/keystone/models/assignments.go b/pkg/keystone/models/assignments.go index 41329e7df5..be46695079 100644 --- a/pkg/keystone/models/assignments.go +++ b/pkg/keystone/models/assignments.go @@ -259,11 +259,12 @@ func (manager *SAssignmentManager) projectAddUser(ctx context.Context, userCred } } err = manager.add(api.AssignmentUserProject, user.Id, project.Id, role.Id) - if err == nil { - db.OpsLog.LogEvent(user, db.ACT_ATTACH, project.GetShortDesc(ctx), userCred) - db.OpsLog.LogEvent(project, db.ACT_ATTACH, user.GetShortDesc(ctx), userCred) + if err != nil { + return errors.Wrap(err, "manager.add") } - return err + db.OpsLog.LogEvent(user, db.ACT_ATTACH, project.GetShortDesc(ctx), userCred) + db.OpsLog.LogEvent(project, db.ACT_ATTACH, user.GetShortDesc(ctx), userCred) + return nil } func (manager *SAssignmentManager) batchRemove(actorId string, typeStrs []string) error { @@ -333,11 +334,12 @@ func (manager *SAssignmentManager) projectRemoveUser(ctx context.Context, userCr } } err := manager.remove(api.AssignmentUserProject, user.Id, project.Id, role.Id) - if err == nil { - db.OpsLog.LogEvent(user, db.ACT_DETACH, project.GetShortDesc(ctx), userCred) - db.OpsLog.LogEvent(project, db.ACT_DETACH, user.GetShortDesc(ctx), userCred) + if err != nil { + return errors.Wrap(err, "manager.remove") } - return err + db.OpsLog.LogEvent(user, db.ACT_DETACH, project.GetShortDesc(ctx), userCred) + db.OpsLog.LogEvent(project, db.ACT_DETACH, user.GetShortDesc(ctx), userCred) + return nil } func (manager *SAssignmentManager) projectAddGroup(ctx context.Context, userCred mcclient.TokenCredential, project *SProject, group *SGroup, role *SRole) error { @@ -357,11 +359,12 @@ func (manager *SAssignmentManager) projectAddGroup(ctx context.Context, userCred } } err = manager.add(api.AssignmentGroupProject, group.Id, project.Id, role.Id) - if err == nil { - db.OpsLog.LogEvent(group, db.ACT_ATTACH, project.GetShortDesc(ctx), userCred) - db.OpsLog.LogEvent(project, db.ACT_ATTACH, group.GetShortDesc(ctx), userCred) + if err != nil { + return errors.Wrap(err, "manager.add") } - return err + db.OpsLog.LogEvent(group, db.ACT_ATTACH, project.GetShortDesc(ctx), userCred) + db.OpsLog.LogEvent(project, db.ACT_ATTACH, group.GetShortDesc(ctx), userCred) + return nil } func (manager *SAssignmentManager) projectRemoveGroup(ctx context.Context, userCred mcclient.TokenCredential, project *SProject, group *SGroup, role *SRole) error { @@ -378,11 +381,12 @@ func (manager *SAssignmentManager) projectRemoveGroup(ctx context.Context, userC } } err := manager.remove(api.AssignmentGroupProject, group.Id, project.Id, role.Id) - if err == nil { - db.OpsLog.LogEvent(group, db.ACT_DETACH, project.GetShortDesc(ctx), userCred) - db.OpsLog.LogEvent(project, db.ACT_DETACH, group.GetShortDesc(ctx), userCred) + if err != nil { + return errors.Wrap(err, "manager.remove") } - return err + db.OpsLog.LogEvent(group, db.ACT_DETACH, project.GetShortDesc(ctx), userCred) + db.OpsLog.LogEvent(project, db.ACT_DETACH, group.GetShortDesc(ctx), userCred) + return nil } func (manager *SAssignmentManager) remove(typeStr, actorId, projectId, roleId string) error { @@ -412,7 +416,11 @@ func (manager *SAssignmentManager) add(typeStr, actorId, projectId, roleId strin Inherited: tristate.False, } assign.SetModelManager(manager, &assign) - return manager.TableSpec().InsertOrUpdate(&assign) + err := manager.TableSpec().InsertOrUpdate(&assign) + if err != nil { + return errors.Wrap(err, "InsertOrUpdate") + } + return nil } func AddAdhocHandlers(version string, app *appsrv.Application) { diff --git a/pkg/keystone/models/groups.go b/pkg/keystone/models/groups.go index 088a6af088..74b812ca6c 100644 --- a/pkg/keystone/models/groups.go +++ b/pkg/keystone/models/groups.go @@ -278,3 +278,45 @@ func (manager *SGroupManager) FetchGroupsInDomain(domainId string, excludes []st func (group *SGroup) UnlinkIdp(idpId string) error { return IdmappingManager.deleteAny(idpId, api.IdMappingEntityGroup, group.Id) } + +func (group *SGroup) AllowPerformJoin(ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + data jsonutils.JSONObject, +) bool { + return db.IsAdminAllowPerform(userCred, group, "join") +} + +func (group *SGroup) PerformJoin( + ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + data jsonutils.JSONObject, +) (jsonutils.JSONObject, error) { + err := joinProjects(group, false, ctx, userCred, data) + if err != nil { + return nil, err + } + return nil, nil +} + +func (group *SGroup) AllowPerformLeave(ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + data jsonutils.JSONObject, +) bool { + return db.IsAdminAllowPerform(userCred, group, "leave") +} + +func (group *SGroup) PerformLeave( + ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + data jsonutils.JSONObject, +) (jsonutils.JSONObject, error) { + err := leaveProjects(group, false, ctx, userCred, data) + if err != nil { + return nil, err + } + return nil, nil +} diff --git a/pkg/keystone/models/identitybase.go b/pkg/keystone/models/identitybase.go index 53e18bfa87..0ff11018d1 100644 --- a/pkg/keystone/models/identitybase.go +++ b/pkg/keystone/models/identitybase.go @@ -89,9 +89,9 @@ func (model *SIdentityBaseResource) GetIIdentityModel() IIdentityModel { return model.GetVirtualObject().(IIdentityModel) } -func (model *SIdentityBaseResource) IsOwner(userCred mcclient.TokenCredential) bool { - return userCred.GetProjectDomainId() == model.DomainId -} +// func (model *SIdentityBaseResource) IsOwner(userCred mcclient.TokenCredential) bool { +// return userCred.GetProjectDomainId() == model.DomainId +// } func (model *SIdentityBaseResource) GetDomain() *SDomain { if len(model.DomainId) > 0 && model.DomainId != api.KeystoneDomainRoot { @@ -109,11 +109,11 @@ func (manager *SIdentityBaseResourceManager) GetIIdentityModelManager() IIdentit } func (manager *SIdentityBaseResourceManager) FetchByName(userCred mcclient.IIdentityProvider, idStr string) (db.IModel, error) { - return db.FetchByName(manager, userCred, idStr) + return db.FetchByName(manager.GetIIdentityModelManager(), userCred, idStr) } func (manager *SIdentityBaseResourceManager) FetchByIdOrName(userCred mcclient.IIdentityProvider, idStr string) (db.IModel, error) { - return db.FetchByIdOrName(manager, userCred, idStr) + return db.FetchByIdOrName(manager.GetIIdentityModelManager(), userCred, idStr) } func (manager *SIdentityBaseResourceManager) ListItemFilter(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*sqlchemy.SQuery, error) { diff --git a/pkg/keystone/models/projects.go b/pkg/keystone/models/projects.go index be35427978..a1bee11259 100644 --- a/pkg/keystone/models/projects.go +++ b/pkg/keystone/models/projects.go @@ -361,3 +361,154 @@ func (manager *SProjectManager) ValidateCreateData(ctx context.Context, userCred } return manager.SIdentityBaseResourceManager.ValidateCreateData(ctx, userCred, ownerId, query, data) } + +func (project *SProject) AllowPerformJoin(ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + data jsonutils.JSONObject, +) bool { + return db.IsAdminAllowPerform(userCred, project, "join") +} + +func (project *SProject) PerformJoin( + ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + data jsonutils.JSONObject, +) (jsonutils.JSONObject, error) { + input := api.SProjectAddUserGroupInput{} + err := data.Unmarshal(&input) + if err != nil { + return nil, httperrors.NewInputParameterError("unmarshal project add user group input error %s", err) + } + err = input.Validate() + if err != nil { + return nil, httperrors.NewInputParameterError(err.Error()) + } + roles := make([]*SRole, 0) + for i := range input.Roles { + obj, err := RoleManager.FetchByIdOrName(userCred, input.Roles[i]) + if err != nil { + if err == sql.ErrNoRows { + return nil, httperrors.NewResourceNotFoundError2(RoleManager.Keyword(), input.Roles[i]) + } else { + return nil, httperrors.NewGeneralError(err) + } + } + roles = append(roles, obj.(*SRole)) + } + users := make([]*SUser, 0) + for i := range input.Users { + obj, err := UserManager.FetchByIdOrName(userCred, input.Users[i]) + if err != nil { + if err == sql.ErrNoRows { + return nil, httperrors.NewResourceNotFoundError2(UserManager.Keyword(), input.Users[i]) + } else { + return nil, httperrors.NewGeneralError(err) + } + } + users = append(users, obj.(*SUser)) + } + groups := make([]*SGroup, 0) + for i := range input.Groups { + obj, err := GroupManager.FetchByIdOrName(userCred, input.Groups[i]) + if err != nil { + if err == sql.ErrNoRows { + return nil, httperrors.NewResourceNotFoundError2(GroupManager.Keyword(), input.Groups[i]) + } else { + return nil, httperrors.NewGeneralError(err) + } + } + groups = append(groups, obj.(*SGroup)) + } + + for i := range users { + for j := range roles { + err = AssignmentManager.projectAddUser(ctx, userCred, project, users[i], roles[j]) + if err != nil { + return nil, httperrors.NewGeneralError(err) + } + } + } + for i := range groups { + for j := range roles { + err = AssignmentManager.projectAddGroup(ctx, userCred, project, groups[i], roles[j]) + if err != nil { + return nil, httperrors.NewGeneralError(err) + } + } + } + + return nil, nil +} + +func (project *SProject) AllowPerformLeave(ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + data jsonutils.JSONObject, +) bool { + return db.IsAdminAllowPerform(userCred, project, "leave") +} + +func (project *SProject) PerformLeave( + ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + data jsonutils.JSONObject, +) (jsonutils.JSONObject, error) { + input := api.SProjectRemoveUserGroupInput{} + err := data.Unmarshal(&input) + if err != nil { + return nil, httperrors.NewInputParameterError("unmarshal project remove usergroup input error %s", err) + } + err = input.Validate() + if err != nil { + return nil, httperrors.NewInputParameterError(err.Error()) + } + + for i := range input.UserRoles { + userObj, err := UserManager.FetchByIdOrName(userCred, input.UserRoles[i].User) + if err != nil { + if err == sql.ErrNoRows { + return nil, httperrors.NewResourceNotFoundError2(UserManager.Keyword(), input.UserRoles[i].User) + } else { + return nil, httperrors.NewGeneralError(err) + } + } + roleObj, err := RoleManager.FetchByIdOrName(userCred, input.UserRoles[i].Role) + if err != nil { + if err == sql.ErrNoRows { + return nil, httperrors.NewResourceNotFoundError2(RoleManager.Keyword(), input.UserRoles[i].Role) + } else { + return nil, httperrors.NewGeneralError(err) + } + } + err = AssignmentManager.projectRemoveUser(ctx, userCred, project, userObj.(*SUser), roleObj.(*SRole)) + if err != nil { + return nil, httperrors.NewGeneralError(err) + } + } + for i := range input.GroupRoles { + groupObj, err := GroupManager.FetchByIdOrName(userCred, input.GroupRoles[i].Group) + if err != nil { + if err == sql.ErrNoRows { + return nil, httperrors.NewResourceNotFoundError2(GroupManager.Keyword(), input.GroupRoles[i].Group) + } else { + return nil, httperrors.NewGeneralError(err) + } + } + roleObj, err := RoleManager.FetchByIdOrName(userCred, input.GroupRoles[i].Role) + if err != nil { + if err == sql.ErrNoRows { + return nil, httperrors.NewResourceNotFoundError2(RoleManager.Keyword(), input.GroupRoles[i].Role) + } else { + return nil, httperrors.NewGeneralError(err) + } + } + err = AssignmentManager.projectRemoveGroup(ctx, userCred, project, groupObj.(*SGroup), roleObj.(*SRole)) + if err != nil { + return nil, httperrors.NewGeneralError(err) + } + } + return nil, nil +} diff --git a/pkg/keystone/models/users.go b/pkg/keystone/models/users.go index 2c3f5ff003..25f9c66cc3 100644 --- a/pkg/keystone/models/users.go +++ b/pkg/keystone/models/users.go @@ -682,3 +682,134 @@ func (manager *SUserManager) FetchUsersInDomain(domainId string, excludes []stri func (user *SUser) UnlinkIdp(idpId string) error { return IdmappingManager.deleteAny(idpId, api.IdMappingEntityUser, user.Id) } + +func (user *SUser) AllowPerformJoin(ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + data jsonutils.JSONObject, +) bool { + return db.IsAdminAllowPerform(userCred, user, "join") +} + +func (user *SUser) PerformJoin( + ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + data jsonutils.JSONObject, +) (jsonutils.JSONObject, error) { + err := joinProjects(user, true, ctx, userCred, data) + if err != nil { + return nil, err + } + return nil, nil +} + +func joinProjects(ident db.IModel, isUser bool, ctx context.Context, userCred mcclient.TokenCredential, data jsonutils.JSONObject) error { + input := api.SJoinProjectsInput{} + err := data.Unmarshal(&input) + if err != nil { + return httperrors.NewInputParameterError("unmarshal input error %s", err) + } + + err = input.Validate() + if err != nil { + return httperrors.NewInputParameterError(err.Error()) + } + + projects := make([]*SProject, 0) + roles := make([]*SRole, 0) + + for i := range input.Projects { + obj, err := ProjectManager.FetchByIdOrName(userCred, input.Projects[i]) + if err != nil { + if err == sql.ErrNoRows { + return httperrors.NewResourceNotFoundError2(ProjectManager.Keyword(), input.Projects[i]) + } else { + return httperrors.NewGeneralError(err) + } + } + projects = append(projects, obj.(*SProject)) + } + for i := range input.Roles { + obj, err := RoleManager.FetchByIdOrName(userCred, input.Roles[i]) + if err != nil { + if err == sql.ErrNoRows { + return httperrors.NewResourceNotFoundError2(RoleManager.Keyword(), input.Roles[i]) + } else { + return httperrors.NewGeneralError(err) + } + } + roles = append(roles, obj.(*SRole)) + } + + for i := range projects { + for j := range roles { + if isUser { + err = AssignmentManager.projectAddUser(ctx, userCred, projects[i], ident.(*SUser), roles[j]) + } else { + err = AssignmentManager.projectAddGroup(ctx, userCred, projects[i], ident.(*SGroup), roles[j]) + } + if err != nil { + return httperrors.NewGeneralError(err) + } + } + } + + return nil +} + +func (user *SUser) AllowPerformLeave(ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + data jsonutils.JSONObject, +) bool { + return db.IsAdminAllowPerform(userCred, user, "leave") +} + +func (user *SUser) PerformLeave( + ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + data jsonutils.JSONObject, +) (jsonutils.JSONObject, error) { + err := leaveProjects(user, true, ctx, userCred, data) + if err != nil { + return nil, err + } + return nil, nil +} + +func leaveProjects(ident db.IModel, isUser bool, ctx context.Context, userCred mcclient.TokenCredential, data jsonutils.JSONObject) error { + input := api.SLeaveProjectsInput{} + err := data.Unmarshal(&input) + if err != nil { + return httperrors.NewInputParameterError("unmarshal leave porject input error: %s", err) + } + for i := range input.ProjectRoles { + projObj, err := ProjectManager.FetchByIdOrName(userCred, input.ProjectRoles[i].Project) + if err != nil { + if err == sql.ErrNoRows { + return httperrors.NewResourceNotFoundError2(ProjectManager.Keyword(), input.ProjectRoles[i].Project) + } else { + return httperrors.NewGeneralError(err) + } + } + roleObj, err := RoleManager.FetchByIdOrName(userCred, input.ProjectRoles[i].Role) + if err != nil { + if err == sql.ErrNoRows { + return httperrors.NewResourceNotFoundError2(RoleManager.Keyword(), input.ProjectRoles[i].Role) + } else { + return httperrors.NewGeneralError(err) + } + } + if isUser { + err = AssignmentManager.projectRemoveUser(ctx, userCred, projObj.(*SProject), ident.(*SUser), roleObj.(*SRole)) + } else { + err = AssignmentManager.projectRemoveGroup(ctx, userCred, projObj.(*SProject), ident.(*SGroup), roleObj.(*SRole)) + } + if err != nil { + return httperrors.NewGeneralError(err) + } + } + return nil +} diff --git a/pkg/mcclient/modules/mod_groups.go b/pkg/mcclient/modules/mod_groups.go index c87e03484e..2bdd1b973a 100644 --- a/pkg/mcclient/modules/mod_groups.go +++ b/pkg/mcclient/modules/mod_groups.go @@ -33,6 +33,11 @@ var ( Groups GroupManager ) +func (this *GroupManager) GetProjects(session *mcclient.ClientSession, uid string) (*ListResult, error) { + url := fmt.Sprintf("/groups/%s/projects?admin=true", uid) + return this._list(session, url, "projects") +} + func init() { Groups = GroupManager{NewIdentityV3Manager("group", "groups", []string{},