Merge pull request #15920 from swordqiu/feature/qj-project-owner

feature: project admin user
This commit is contained in:
Zexi Li
2023-02-13 10:01:31 +08:00
committed by GitHub
5 changed files with 166 additions and 2 deletions
+7
View File
@@ -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"`
// 加入项目的用户数量
+65
View File
@@ -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
}
+22
View File
@@ -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")
}
+70 -2
View File
@@ -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
+2
View File
@@ -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 (