mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-09-24 16:03:43 +08:00
Merge pull request #15920 from swordqiu/feature/qj-project-owner
feature: project admin user
This commit is contained in:
@@ -32,6 +32,13 @@ type ProjectDetails struct {
|
||||
|
||||
SProject
|
||||
|
||||
// 项目管理员名称
|
||||
Admin string `json:"admin"`
|
||||
// 项目管理员域ID
|
||||
AdminDomainId string `json:"admin_domain_id"`
|
||||
// 项目管理员域名称
|
||||
AdminDomain string `json:"admin_domain"`
|
||||
|
||||
// 加入项目的用户组数量
|
||||
GroupCount int `json:"group_count"`
|
||||
// 加入项目的用户数量
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/tristate"
|
||||
"yunion.io/x/pkg/util/rbacscope"
|
||||
@@ -32,6 +33,7 @@ import (
|
||||
"yunion.io/x/onecloud/pkg/appsrv"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
"yunion.io/x/onecloud/pkg/keystone/options"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/auth"
|
||||
"yunion.io/x/onecloud/pkg/util/stringutils2"
|
||||
@@ -238,10 +240,17 @@ func (manager *SAssignmentManager) fetchUserProjectIdsQuery(userId string) *sqlc
|
||||
}
|
||||
|
||||
func (manager *SAssignmentManager) fetchProjectUserIdsQuery(projId string) *sqlchemy.SQuery {
|
||||
return manager.fetchProjectRoleUserIdsQuery(projId, "")
|
||||
}
|
||||
|
||||
func (manager *SAssignmentManager) fetchProjectRoleUserIdsQuery(projId, roleId string) *sqlchemy.SQuery {
|
||||
q1 := manager.Query("actor_id")
|
||||
q1 = q1.Equals("type", api.AssignmentUserProject)
|
||||
q1 = q1.Equals("target_id", projId)
|
||||
q1 = q1.IsFalse("inherited")
|
||||
if len(roleId) > 0 {
|
||||
q1 = q1.Equals("role_id", roleId)
|
||||
}
|
||||
|
||||
assigns := AssignmentManager.Query().SubQuery()
|
||||
usergroups := UsergroupManager.Query().SubQuery()
|
||||
@@ -253,6 +262,9 @@ func (manager *SAssignmentManager) fetchProjectUserIdsQuery(projId string) *sqlc
|
||||
q2 = q2.Filter(sqlchemy.Equals(assigns.Field("type"), api.AssignmentGroupProject))
|
||||
q2 = q2.Filter(sqlchemy.Equals(assigns.Field("target_id"), projId))
|
||||
q2 = q2.Filter(sqlchemy.IsFalse(assigns.Field("inherited")))
|
||||
if len(roleId) > 0 {
|
||||
q2 = q2.Equals("role_id", roleId)
|
||||
}
|
||||
|
||||
union := sqlchemy.Union(q1, q2)
|
||||
return union.Query().Distinct()
|
||||
@@ -344,9 +356,33 @@ func (manager *SAssignmentManager) ProjectAddUser(ctx context.Context, userCred
|
||||
}
|
||||
db.OpsLog.LogEvent(user, db.ACT_ATTACH, project.GetShortDesc(ctx), userCred)
|
||||
db.OpsLog.LogEvent(project, db.ACT_ATTACH, user.GetShortDesc(ctx), userCred)
|
||||
if len(project.AdminId) == 0 && role.Name == options.Options.ProjectAdminRole {
|
||||
err := project.resetAdminUser()
|
||||
if err != nil {
|
||||
log.Errorf("rsetAdminUser fail: %s", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (assign *SAssignment) getRole() (*SRole, error) {
|
||||
return RoleManager.FetchRoleById(assign.RoleId)
|
||||
}
|
||||
|
||||
func (assign *SAssignment) getProject() (*SProject, error) {
|
||||
if assign.Type == api.AssignmentUserProject || assign.Type == api.AssignmentGroupProject {
|
||||
return ProjectManager.FetchProjectById(assign.TargetId)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (assign *SAssignment) getDomain() (*SDomain, error) {
|
||||
if assign.Type == api.AssignmentUserDomain || assign.Type == api.AssignmentGroupDomain {
|
||||
return DomainManager.FetchDomainById(assign.TargetId)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (manager *SAssignmentManager) batchRemove(actorId string, typeStrs []string) error {
|
||||
q := manager.Query()
|
||||
q = q.In("type", typeStrs)
|
||||
@@ -365,6 +401,17 @@ func (manager *SAssignmentManager) batchRemove(actorId string, typeStrs []string
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "db.Update")
|
||||
}
|
||||
// clear project admin Id
|
||||
role, _ := assigns[i].getRole()
|
||||
if role.Name == options.Options.ProjectAdminRole {
|
||||
project, _ := assigns[i].getProject()
|
||||
if project != nil && project.AdminId == actorId {
|
||||
err := project.resetAdminUser()
|
||||
if err != nil {
|
||||
log.Errorf("batchRemove project resetAdminUser fail %s", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -419,6 +466,12 @@ func (manager *SAssignmentManager) projectRemoveUser(ctx context.Context, userCr
|
||||
}
|
||||
db.OpsLog.LogEvent(user, db.ACT_DETACH, project.GetShortDesc(ctx), userCred)
|
||||
db.OpsLog.LogEvent(project, db.ACT_DETACH, user.GetShortDesc(ctx), userCred)
|
||||
if project.AdminId == user.Id && role.Name == options.Options.ProjectAdminRole {
|
||||
err := project.resetAdminUser()
|
||||
if err != nil {
|
||||
log.Errorf("resetAdminUser fail %s", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -452,6 +505,12 @@ func (manager *SAssignmentManager) projectAddGroup(ctx context.Context, userCred
|
||||
}
|
||||
db.OpsLog.LogEvent(group, db.ACT_ATTACH, project.GetShortDesc(ctx), userCred)
|
||||
db.OpsLog.LogEvent(project, db.ACT_ATTACH, group.GetShortDesc(ctx), userCred)
|
||||
if len(project.AdminId) == 0 && role.Name == options.Options.ProjectAdminRole {
|
||||
err := project.resetAdminUser()
|
||||
if err != nil {
|
||||
log.Errorf("rsetAdminUser fail: %s", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -474,6 +533,12 @@ func (manager *SAssignmentManager) projectRemoveGroup(ctx context.Context, userC
|
||||
}
|
||||
db.OpsLog.LogEvent(group, db.ACT_DETACH, project.GetShortDesc(ctx), userCred)
|
||||
db.OpsLog.LogEvent(project, db.ACT_DETACH, group.GetShortDesc(ctx), userCred)
|
||||
if len(project.AdminId) > 0 && role.Name == options.Options.ProjectAdminRole {
|
||||
err := project.resetAdminUser()
|
||||
if err != nil {
|
||||
log.Errorf("rsetAdminUser fail: %s", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -71,6 +71,8 @@ type SDomain struct {
|
||||
|
||||
DomainId string `width:"64" charset:"ascii" default:"default" nullable:"false" index:"true"`
|
||||
ParentId string `width:"64" charset:"ascii"`
|
||||
|
||||
AdminId string `width:"64" charset:"ascii" nullable:"true"`
|
||||
}
|
||||
|
||||
func (manager *SDomainManager) InitializeData() error {
|
||||
@@ -110,9 +112,29 @@ func (manager *SDomainManager) InitializeData() error {
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
/*err = manager.initAdminUsers(context.TODO())
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "initAdminUsers")
|
||||
}*/
|
||||
return nil
|
||||
}
|
||||
|
||||
/*func (manager *SDomainManager) initAdminUsers(ctx context.Context) error {
|
||||
q := manager.Query().IsNullOrEmpty("admin_id")
|
||||
domains := make([]SDomain, 0)
|
||||
err := db.FetchModelObjects(manager, q, &domains)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "FetchModelObjects")
|
||||
}
|
||||
for i := range domains {
|
||||
err := domains[i].initAdminUser(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "domains initAdmin")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}*/
|
||||
|
||||
func (manager *SDomainManager) Query(fields ...string) *sqlchemy.SQuery {
|
||||
return manager.SStandaloneResourceBaseManager.Query(fields...).IsTrue("is_domain")
|
||||
}
|
||||
|
||||
@@ -82,6 +82,8 @@ type SProject struct {
|
||||
|
||||
// 该项目是否为域(domain)
|
||||
IsDomain tristate.TriState `default:"false"`
|
||||
|
||||
AdminId string `width:"64" charset:"ascii" nullable:"true" list:"domain"`
|
||||
}
|
||||
|
||||
func (manager *SProjectManager) GetContextManagers() [][]db.IModelManager {
|
||||
@@ -92,7 +94,16 @@ func (manager *SProjectManager) GetContextManagers() [][]db.IModelManager {
|
||||
}
|
||||
|
||||
func (manager *SProjectManager) InitializeData() error {
|
||||
return manager.initSysProject(context.TODO())
|
||||
ctx := context.TODO()
|
||||
err := manager.initSysProject(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "initSysProject")
|
||||
}
|
||||
err = manager.initAdminUsers(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "initAdminUsers")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (manager *SProjectManager) initSysProject(ctx context.Context) error {
|
||||
@@ -126,6 +137,45 @@ func (manager *SProjectManager) initSysProject(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (manager *SProjectManager) initAdminUsers(ctx context.Context) error {
|
||||
q := manager.Query().IsNullOrEmpty("admin_id")
|
||||
projects := make([]SProject, 0)
|
||||
err := db.FetchModelObjects(manager, q, &projects)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "FetchModelObjects")
|
||||
}
|
||||
for i := range projects {
|
||||
err := projects[i].resetAdminUser()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "projects initAdmin")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (project *SProject) resetAdminUser() error {
|
||||
role, err := RoleManager.FetchRoleByName(options.Options.ProjectAdminRole, "", "")
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "FetchRoleByName %s", options.Options.ProjectAdminRole)
|
||||
}
|
||||
q := AssignmentManager.fetchProjectRoleUserIdsQuery(project.Id, role.Id)
|
||||
userId := struct {
|
||||
ActorId string
|
||||
}{}
|
||||
err = q.First(&userId)
|
||||
if err != nil && errors.Cause(err) != sql.ErrNoRows {
|
||||
return errors.Wrap(err, "query")
|
||||
}
|
||||
_, err = db.Update(project, func() error {
|
||||
project.AdminId = userId.ActorId
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "update")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (manager *SProjectManager) Query(fields ...string) *sqlchemy.SQuery {
|
||||
return manager.SIdentityBaseResourceManager.Query(fields...).IsFalse("is_domain")
|
||||
}
|
||||
@@ -384,12 +434,16 @@ func (manager *SProjectManager) FetchCustomizeColumns(
|
||||
|
||||
identRows := manager.SIdentityBaseResourceManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
|
||||
projIds := make([]string, len(objs))
|
||||
adminUserIds := make([]string, 0)
|
||||
for i := range rows {
|
||||
rows[i] = api.ProjectDetails{
|
||||
IdentityBaseResourceDetails: identRows[i],
|
||||
}
|
||||
proj := objs[i].(*SProject)
|
||||
projIds[i] = proj.Id
|
||||
if len(proj.AdminId) > 0 {
|
||||
adminUserIds = append(adminUserIds, proj.AdminId)
|
||||
}
|
||||
}
|
||||
|
||||
extResource, extLastUpdate, err := ScopeResourceManager.FetchProjectsScopeResources(projIds)
|
||||
@@ -401,6 +455,13 @@ func (manager *SProjectManager) FetchCustomizeColumns(
|
||||
if err != nil {
|
||||
return rows
|
||||
}
|
||||
|
||||
userMaps := make(map[string]SUser)
|
||||
err = db.FetchModelObjectsByIds(UserManager, "id", adminUserIds, &userMaps)
|
||||
if err != nil {
|
||||
log.Errorf("FetchModelObjectsByIds fail %s", err)
|
||||
}
|
||||
|
||||
for i := range rows {
|
||||
groups, _ := groupCnt[projIds[i]]
|
||||
users, _ := userCnt[projIds[i]]
|
||||
@@ -416,7 +477,14 @@ func (manager *SProjectManager) FetchCustomizeColumns(
|
||||
nextUpdate := rows[i].ExtResourcesLastUpdate.Add(time.Duration(options.Options.FetchScopeResourceCountIntervalSeconds) * time.Second)
|
||||
rows[i].ExtResourcesNextUpdate = nextUpdate
|
||||
}
|
||||
|
||||
proj := objs[i].(*SProject)
|
||||
if len(proj.AdminId) > 0 {
|
||||
if user, ok := userMaps[proj.AdminId]; ok {
|
||||
rows[i].Admin = user.Name
|
||||
rows[i].AdminDomain = user.GetDomain().Name
|
||||
rows[i].AdminDomainId = user.DomainId
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rows
|
||||
|
||||
@@ -67,6 +67,8 @@ type SKeystoneOptions struct {
|
||||
NoPolicyViolationCheck bool `help:"do not check policy violation when modify or assign policy" default:"false"`
|
||||
|
||||
LdapSearchPageSize uint32 `help:"pagination size for LDAP search" default:"100"`
|
||||
|
||||
ProjectAdminRole string `help:"name of role to be saved as admin user of project" default:"project_owner"`
|
||||
}
|
||||
|
||||
var (
|
||||
|
||||
Reference in New Issue
Block a user