From ac5bbc4657ee8ef14b34506f46f1a50cda7167fb Mon Sep 17 00:00:00 2001 From: Wenkai Yin Date: Mon, 28 Mar 2016 15:34:41 +0800 Subject: [PATCH] db refactor --- api/member.go | 22 ++++-- dao/dao_test.go | 157 ++++++++++++++++++------------------------- dao/itemdetail.go | 11 +-- dao/project.go | 96 +++++++++++--------------- dao/projectmember.go | 94 ++++++++++++++++++++++++++ dao/projectrole.go | 5 ++ dao/register.go | 7 +- dao/role.go | 105 +++++++++++++++++++++++------ dao/user.go | 136 ++++++++++++++++++++----------------- models/project.go | 2 + models/role.go | 13 ++-- models/user.go | 9 ++- 12 files changed, 415 insertions(+), 242 deletions(-) create mode 100644 dao/projectmember.go diff --git a/api/member.go b/api/member.go index 2e9243d12..d7ee03545 100644 --- a/api/member.go +++ b/api/member.go @@ -16,6 +16,7 @@ package api import ( + "fmt" "net/http" "strconv" @@ -23,6 +24,7 @@ import ( "github.com/vmware/harbor/models" "github.com/astaxie/beego" + "github.com/vmware/harbor/utils/log" ) // ProjectMemberAPI handles request to /api/projects/{}/members/{} @@ -148,7 +150,13 @@ func (pma *ProjectMemberAPI) Post() { } for _, rid := range req.Roles { - err = dao.AddUserProjectRole(userID, pid, int(rid)) + role, err := dao.IntToRole(rid) + if err != nil { + log.Error(err) + pma.RenderError(http.StatusBadRequest, fmt.Sprintf("Invalid role: %d", rid)) + } + + err = dao.AddProjectMember(pid, userID, role) if err != nil { beego.Error("Failed to update DB to add project user role, project id:", pid, ", user id:", userID, ", role id:", rid) pma.RenderError(http.StatusInternalServerError, "Failed to update data in database") @@ -182,7 +190,7 @@ func (pma *ProjectMemberAPI) Put() { } //TODO: delete and insert should in one transaction //delete user project role record for the given user - err = dao.DeleteUserProjectRoles(mid, pid) + err = dao.DeleteProjectMember(pid, mid) if err != nil { beego.Error("Failed to delete project roles for user, user id:", mid, ", project id: ", pid, ", error: ", err) pma.RenderError(http.StatusInternalServerError, "Failed to update data in DB") @@ -190,7 +198,13 @@ func (pma *ProjectMemberAPI) Put() { } //insert roles in request for _, rid := range req.Roles { - err = dao.AddUserProjectRole(mid, pid, int(rid)) + role, err := dao.IntToRole(rid) + if err != nil { + log.Error(err) + pma.RenderError(http.StatusBadRequest, fmt.Sprintf("Invalid role: %d", rid)) + } + + err = dao.AddProjectMember(pid, mid, role) if err != nil { beego.Error("Failed to update DB to add project user role, project id:", pid, ", user id:", mid, ", role id:", rid) pma.RenderError(http.StatusInternalServerError, "Failed to update data in database") @@ -210,7 +224,7 @@ func (pma *ProjectMemberAPI) Delete() { pma.RenderError(http.StatusForbidden, "") return } - err = dao.DeleteUserProjectRoles(mid, pid) + err = dao.DeleteProjectMember(pid, mid) if err != nil { beego.Error("Failed to delete project roles for user, user id:", mid, ", project id:", pid, ", error:", err) pma.RenderError(http.StatusInternalServerError, "Failed to update data in DB") diff --git a/dao/dao_test.go b/dao/dao_test.go index 1bc1e1f0f..96ecbc351 100644 --- a/dao/dao_test.go +++ b/dao/dao_test.go @@ -17,11 +17,12 @@ package dao import ( "fmt" - "log" "os" "testing" "time" + "github.com/vmware/harbor/utils/log" + "github.com/vmware/harbor/models" "github.com/astaxie/beego/orm" @@ -41,44 +42,61 @@ func execUpdate(o orm.Ormer, sql string, params interface{}) error { } func clearUp(username string) { + var err error + o := orm.NewOrm() o.Begin() - err := execUpdate(o, `delete upr from user_project_role upr - left join project_role pr on upr.pr_id = pr.pr_id - left join project p on pr.project_id = p.project_id - left join user u on u.user_id = p.owner_id - where u.username = ?`, username) + + err = execUpdate(o, `delete pm + from project_member pm + join user u + on pm.user_id = u.user_id + where u.username = ?`, username) if err != nil { o.Rollback() - log.Println(err) + log.Error(err) } - err = execUpdate(o, `delete pr from project_role pr - left join project p on pr.project_id = p.project_id - left join user u on u.user_id = p.owner_id - where u.username = ?`, username) + + err = execUpdate(o, `delete pm + from project_member pm + join project p + on pm.project_id = p.project_id + where p.name = ?`, projectName) if err != nil { o.Rollback() - log.Println(err) + log.Error(err) } - err = execUpdate(o, `delete a from access_log a - left join user u on a.user_id = u.user_id - where u.username = ?`, username) + + err = execUpdate(o, `delete al + from access_log al + join user u + on al.user_id = u.user_id + where u.username = ?`, username) if err != nil { o.Rollback() - log.Println(err) + log.Error(err) } - err = execUpdate(o, `delete p from project p - left join user u on p.owner_id = u.user_id - where u.username = ?`, username) + + err = execUpdate(o, `delete al + from access_log al + join project p + on al.project_id = p.project_id + where p.name = ?`, projectName) if err != nil { o.Rollback() - log.Println(err) + log.Error(err) } - err = execUpdate(o, `delete u from user u - where u.username = ?`, username) + + err = execUpdate(o, `delete from project where name = ?`, projectName) if err != nil { o.Rollback() - log.Println(err) + log.Error(err) + } + + err = execUpdate(o, `delete from user where username = ?`, username) + if err != nil { + o.Rollback() + log.Error(err) } o.Commit() } @@ -379,32 +397,6 @@ func TestGetProject(t *testing.T) { } } -func getProjectRole(projectID int64) []models.Role { - o := orm.NewOrm() - var r []models.Role - _, err := o.Raw(`select r.role_id, r.name - from project_role pr - left join role r on pr.role_id = r.role_id - where project_id = ?`, projectID).QueryRows(&r) - if err != nil { - log.Printf("Error occurred in querying project_role: %v", err) - } - return r -} - -func TestCheckProjectRoles(t *testing.T) { - r := getProjectRole(currentProject.ProjectID) - if len(r) != 3 { - t.Errorf("The length of project roles is not 3") - } - if r[1].RoleID != 3 { - t.Errorf("The role id does not match, expected: 3, acutal: %d", r[1].RoleID) - } - if r[1].Name != "developer" { - t.Errorf("The name of role id: 3 should be developer, actual:%s", r[1].Name) - } -} - func TestGetAccessLog(t *testing.T) { queryAccessLog := models.AccessLog{ UserID: currentUser.UserID, @@ -546,20 +538,6 @@ func TestQueryProject(t *testing.T) { } } -func getUserProjectRole(projectID int64, userID int) []models.Role { - o := orm.NewOrm() - var r []models.Role - _, err := o.Raw(`select r.role_id, r.name - from user_project_role upr - left join project_role pr on upr.pr_id = pr.pr_id - left join role r on r.role_id = pr.role_id - where pr.project_id = ? and upr.user_id = ?`, projectID, userID).QueryRows(&r) - if err != nil { - log.Fatalf("Error occurred in querying user_project_role: %v", err) - } - return r -} - func TestGetUserProjectRoles(t *testing.T) { user := *currentUser r, err := GetUserProjectRoles(user, currentProject.ProjectID) @@ -575,16 +553,6 @@ func TestGetUserProjectRoles(t *testing.T) { if r[0].Name != "projectAdmin" { t.Errorf("the expected rolename is: projectAdmin, actual: %s", r[0].Name) } - user.RoleID = 1 - - r, err = GetUserProjectRoles(user, currentProject.ProjectID) - if err != nil { - t.Errorf("Error happened in GetUserProjectRole: %v, user: %+v, project Id: %d", err, user, currentProject.ProjectID) - } - //Get the size of current user project role. - if len(r) != 0 { - t.Errorf("The user, id: %d, should not have role id: 1 in project id: %d, actual role list: %v", currentUser.UserID, currentProject.ProjectID, r) - } } func TestProjectPermission(t *testing.T) { @@ -610,34 +578,43 @@ func TestQueryRelevantProjects(t *testing.T) { } } -func TestAssignUserProjectRole(t *testing.T) { - err := AddUserProjectRole(currentUser.UserID, currentProject.ProjectID, developer) +func TestAddProjectMember(t *testing.T) { + err := AddProjectMember(currentProject.ProjectID, 1, Developer) if err != nil { - t.Errorf("Error occurred in AddUserProjectRole: %v", err) + t.Errorf("Error occurred in AddProjectMember: %v", err) } - r := getUserProjectRole(currentProject.ProjectID, currentUser.UserID) - - //Get the size of current user project role info. - if len(r) != 2 { - t.Errorf("Expected length of role list is 2, actual: %d", len(r)) + roles, err := GetUserProjectRoles(models.User{UserID: 1}, currentProject.ProjectID) + if err != nil { + t.Errorf("Error occurred in GetUserProjectRoles: %v", err) } - if r[1].RoleID != 3 { - t.Errorf("Expected role id of the second role in list is 3, actual: %d", r[1].RoleID) + flag := false + for _, role := range roles { + if role.Name == "developer" { + flag = true + break + } + } + + if !flag { + t.Errorf("the user which ID is 1 does not have developer privileges") } } -func TestDeleteUserProjectRole(t *testing.T) { - err := DeleteUserProjectRoles(currentUser.UserID, currentProject.ProjectID) +func TestDeleteProjectMember(t *testing.T) { + err := DeleteProjectMember(currentProject.ProjectID, 1) if err != nil { - t.Errorf("Error occurred in DeleteUserProjectRoles: %v", err) + t.Errorf("Error occurred in DeleteProjectMember: %v", err) } - r := getUserProjectRole(currentProject.ProjectID, currentUser.UserID) - //Get the size of current user project role. - if len(r) != 0 { - t.Errorf("Expected role list length is 0, actual: %d, role list: %+v", len(r), r) + roles, err := GetUserProjectRoles(models.User{UserID: 1}, currentProject.ProjectID) + if err != nil { + t.Errorf("Error occurred in GetUserProjectRoles: %v", err) + } + + if len(roles) != 0 { + t.Errorf("delete record failed from table project_member") } } diff --git a/dao/itemdetail.go b/dao/itemdetail.go index f4f2cb558..e3b0b7ed7 100644 --- a/dao/itemdetail.go +++ b/dao/itemdetail.go @@ -15,23 +15,25 @@ package dao +/* import ( "github.com/vmware/harbor/models" "github.com/astaxie/beego/orm" ) + // GetUserByProject gets all members of the project. func GetUserByProject(projectID int64, queryUser models.User) ([]models.User, error) { o := orm.NewOrm() u := []models.User{} - sql := `select + sql := `select u.user_id, u.username, r.name rolename, r.role_id - from user u left join user_project_role upr + from user u left join user_project_role upr on u.user_id = upr.user_id - left join project_role pr + left join project_role pr on pr.pr_id = upr.pr_id - left join role r + left join role r on r.role_id = pr.role_id where u.deleted = 0 and pr.project_id = ? ` @@ -47,3 +49,4 @@ func GetUserByProject(projectID int64, queryUser models.User) ([]models.User, er _, err := o.Raw(sql, queryParam).QueryRows(&u) return u, err } +*/ diff --git a/dao/project.go b/dao/project.go index 9d4205057..6582d26f2 100644 --- a/dao/project.go +++ b/dao/project.go @@ -40,12 +40,13 @@ func AddProject(project models.Project) error { o := orm.NewOrm() - p, err := o.Raw("insert into project (owner_id, name, creation_time, deleted, public) values (?, ?, now(), ?, ?)").Prepare() + p, err := o.Raw("insert into project (owner_id, name, creation_time, update_time, deleted, public) values (?, ?, ?, ?, ?, ?)").Prepare() if err != nil { return err } - r, err := p.Exec(project.OwnerID, project.Name, project.Deleted, project.Public) + now := time.Now() + r, err := p.Exec(project.OwnerID, project.Name, now, now, project.Deleted, project.Public) if err != nil { return err } @@ -55,27 +56,7 @@ func AddProject(project models.Project) error { return err } - projectAdminRole := models.ProjectRole{ProjectID: projectID, RoleID: models.PROJECTADMIN} - _, err = AddProjectRole(projectAdminRole) - if err != nil { - return err - } - - projectDeveloperRole := models.ProjectRole{ProjectID: projectID, RoleID: models.DEVELOPER} - _, err = AddProjectRole(projectDeveloperRole) - if err != nil { - return err - } - - projectGuestRole := models.ProjectRole{ProjectID: projectID, RoleID: models.GUEST} - _, err = AddProjectRole(projectGuestRole) - if err != nil { - return err - } - - //Add all project roles, after that when assigning a user to a project just update the upr table - err = AddUserProjectRole(project.OwnerID, projectID, models.PROJECTADMIN) - if err != nil { + if err = AddProjectMember(projectID, project.OwnerID, ProjectAdmin); err != nil { return err } @@ -103,11 +84,9 @@ func QueryProject(query models.Project) ([]models.Project, error) { o := orm.NewOrm() sql := `select distinct - p.project_id, p.owner_id, p.name,p.creation_time, p.public + p.project_id, p.owner_id, p.name,p.creation_time, p.update_time, p.public from project p - left join project_role pr on p.project_id = pr.project_id - left join user_project_role upr on upr.pr_id = pr.pr_id - left join user u on u.user_id = upr.user_id + left join project_member pm on p.project_id = pm.project_id where p.deleted = 0 ` queryParam := make([]interface{}, 1) @@ -116,8 +95,7 @@ func QueryProject(query models.Project) ([]models.Project, error) { sql += ` and p.public = ?` queryParam = append(queryParam, query.Public) } else if isAdmin, _ := IsAdminRole(query.UserID); isAdmin == false { - sql += ` and (p.owner_id = ? or u.user_id = ?) ` - queryParam = append(queryParam, query.UserID) + sql += ` and (pm.user_id = ?) ` queryParam = append(queryParam, query.UserID) } @@ -161,60 +139,65 @@ func ProjectExists(nameOrID interface{}) (bool, error) { } // GetProjectByID ... -func GetProjectByID(projectID int64) (*models.Project, error) { +func GetProjectByID(id int64) (*models.Project, error) { o := orm.NewOrm() - sql := `select p.project_id, p.name, u.username as owner_name, p.owner_id, p.creation_time, p.public + sql := `select p.project_id, p.name, u.username as owner_name, p.owner_id, p.creation_time, p.update_time, p.public from project p left join user u on p.owner_id = u.user_id where p.deleted = 0 and p.project_id = ?` queryParam := make([]interface{}, 1) - queryParam = append(queryParam, projectID) + queryParam = append(queryParam, id) p := []models.Project{} count, err := o.Raw(sql, queryParam).QueryRows(&p) if err != nil { return nil, err - } else if count == 0 { - return nil, nil - } else { - return &p[0], nil } + + if count == 0 { + return nil, nil + } + + return &p[0], nil } // GetProjectByName ... -func GetProjectByName(projectName string) (*models.Project, error) { +func GetProjectByName(name string) (*models.Project, error) { o := orm.NewOrm() var p []models.Project - n, err := o.Raw(`select project_id, owner_id, name, deleted, public from project where name = ? and deleted = 0`, projectName).QueryRows(&p) + n, err := o.Raw(`select * from project where name = ? and deleted = 0`, name).QueryRows(&p) if err != nil { return nil, err - } else if n == 0 { - return nil, nil - } else { - return &p[0], nil } + + if n == 0 { + return nil, nil + } + + return &p[0], nil } // GetPermission gets roles that the user has according to the project. func GetPermission(username, projectName string) (string, error) { o := orm.NewOrm() - sql := "select r.role_code from role as r " + - "inner join project_role as pr on r.role_id = pr.role_id " + - "inner join user_project_role as ur on pr.pr_id = ur.pr_id " + - "inner join user as u on u.user_id = ur.user_id " + - "inner join project p on p.project_id = pr.project_id " + - "where u.username = ? and p.name = ? and u.deleted = 0 and p.deleted = 0" + sql := `select r.role_code from role as r + inner join project_member as pm on r.role_id = pm.role + inner join user as u on u.user_id = pm.user_id + inner join project p on p.project_id = pm.project_id + where u.username = ? and p.name = ? and u.deleted = 0 and p.deleted = 0` var r []models.Role n, err := o.Raw(sql, username, projectName).QueryRows(&r) if err != nil { return "", err - } else if n == 0 { - return "", nil - } else { - return r[0].RoleCode, nil } + + if n == 0 { + return "", nil + } + + return r[0].RoleCode, nil } // ToggleProjectPublicity toggles the publicity of the project. @@ -228,10 +211,11 @@ func ToggleProjectPublicity(projectID int64, publicity int) error { // QueryRelevantProjects returns all projects that the user is a member of. func QueryRelevantProjects(userID int) ([]models.Project, error) { o := orm.NewOrm() - sql := `SELECT distinct p.project_id, p.name, p.public FROM registry.project p - left join project_role pr on p.project_id = pr.project_id - left join user_project_role upr on upr.pr_id = pr.pr_id - where upr.user_id = ? or p.public = 1 and p.deleted = 0` + sql := `select distinct p.project_id, p.name, p.public + from project p + left join project_member pm on p.project_id = pm.project_id + left join user u on u.user_id = pm.user_id + where u.user_id = ? or p.public = 1 and p.deleted = 0` var res []models.Project _, err := o.Raw(sql, userID).QueryRows(&res) if err != nil { diff --git a/dao/projectmember.go b/dao/projectmember.go new file mode 100644 index 000000000..fac182444 --- /dev/null +++ b/dao/projectmember.go @@ -0,0 +1,94 @@ +/* + Copyright (c) 2016 VMware, Inc. All Rights Reserved. + 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 dao + +import ( + "github.com/astaxie/beego/orm" + "github.com/vmware/harbor/models" +) + +// AddProjectMember inserts a record to table project_member +func AddProjectMember(projectID int64, userID int, r role) error { + o := orm.NewOrm() + + sql := "insert into project_member (project_id, user_id , role) values (?, ?, ?)" + + rr, err := getRole(r) + if err != nil { + return err + } + + if _, err = o.Raw(sql, projectID, userID, rr.RoleID).Exec(); err != nil { + return err + } + + return nil +} + +// UpdateProjectMember updates the record in table project_member +func UpdateProjectMember(projectID int64, userID int, r role) error { + o := orm.NewOrm() + + sql := "update project_member set role = ? where project_id = ? and user_id = ?" + + rr, err := getRole(r) + if err != nil { + return err + } + + if _, err := o.Raw(sql, rr.RoleID, projectID, userID).Exec(); err != nil { + return err + } + + return nil +} + +// DeleteProjectMember delete the record from table project_member +func DeleteProjectMember(projectID int64, userID int) error { + o := orm.NewOrm() + + sql := "delete from project_member where project_id = ? and user_id = ?" + + if _, err := o.Raw(sql, projectID, userID).Exec(); err != nil { + return err + } + + return nil +} + +// GetUserByProject gets all members of the project. +func GetUserByProject(projectID int64, queryUser models.User) ([]models.User, error) { + o := orm.NewOrm() + u := []models.User{} + sql := `select u.user_id, u.username, r.name rolename, r.role_id + from user u + join project_member pm + on pm.project_id = ? and u.user_id = pm.user_id + join role r + on pm.role = r.role_id + where u.deleted = 0` + + queryParam := make([]interface{}, 1) + queryParam = append(queryParam, projectID) + + if queryUser.Username != "" { + sql += " and u.username like ? " + queryParam = append(queryParam, queryUser.Username) + } + sql += ` order by u.user_id ` + _, err := o.Raw(sql, queryParam).QueryRows(&u) + return u, err +} diff --git a/dao/projectrole.go b/dao/projectrole.go index dbf409fa0..a94b02a09 100644 --- a/dao/projectrole.go +++ b/dao/projectrole.go @@ -15,6 +15,7 @@ package dao +/* import ( "github.com/vmware/harbor/models" @@ -37,6 +38,7 @@ func AddProjectRole(projectRole models.ProjectRole) (int64, error) { return id, err } + // AddUserProjectRole inserts role information to table project_role and user_project_role. func AddUserProjectRole(userID int, projectID int64, roleID int) error { @@ -91,3 +93,6 @@ func DeleteUserProjectRoles(userID int, projectID int64) error { _, err = p.Exec(userID, projectID) return err } + + +*/ diff --git a/dao/register.go b/dao/register.go index d589be4db..f3e66cb1a 100644 --- a/dao/register.go +++ b/dao/register.go @@ -18,6 +18,7 @@ package dao import ( "errors" "regexp" + "time" "github.com/vmware/harbor/models" "github.com/vmware/harbor/utils" @@ -34,7 +35,8 @@ func Register(user models.User) (int64, error) { } o := orm.NewOrm() - p, err := o.Raw("insert into user (username, password, realname, email, comment, salt) values (?, ?, ?, ?, ?, ?)").Prepare() + + p, err := o.Raw("insert into user (username, password, realname, email, comment, salt, sysadmin_flag, creation_time, update_time) values (?, ?, ?, ?, ?, ?, ?, ?, ?)").Prepare() if err != nil { return 0, err } @@ -45,7 +47,8 @@ func Register(user models.User) (int64, error) { return 0, err } - r, err := p.Exec(user.Username, utils.Encrypt(user.Password, salt), user.Realname, user.Email, user.Comment, salt) + now := time.Now() + r, err := p.Exec(user.Username, utils.Encrypt(user.Password, salt), user.Realname, user.Email, user.Comment, salt, user.HasAdminRole, now, now) if err != nil { return 0, err diff --git a/dao/role.go b/dao/role.go index 47b9a9d7d..28d8d3ea7 100644 --- a/dao/role.go +++ b/dao/role.go @@ -16,37 +16,57 @@ package dao import ( + "fmt" + "github.com/vmware/harbor/models" "github.com/astaxie/beego/orm" ) +type role int + +// Start from 2 to guarantee the compatibility with former code +const ( + ProjectAdmin role = 2 + Developer = 3 + Guest = 4 +) + +var roleList = make(map[role]*models.Role) + +// IntToRole is used to convert int to role. +func IntToRole(i int) (r role, err error) { + switch i { + case 2: + r = ProjectAdmin + case 3: + r = Developer + case 4: + r = Guest + default: + err = fmt.Errorf("no role is correspondent with the input: %d", i) + } + return +} + // GetUserProjectRoles returns roles that the user has according to the project. func GetUserProjectRoles(userQuery models.User, projectID int64) ([]models.Role, error) { o := orm.NewOrm() - sql := `select distinct r.role_id, r.role_code, r.name - from role r - left join project_role pr on r.role_id = pr.role_id - left join user_project_role upr on pr.pr_id = upr.pr_id - left join user u on u.user_id = upr.user_id - where u.deleted = 0 - and u.user_id = ? ` + sql := `select * + from role + where role_id = + ( + select role + from project_member + where project_id = ? and user_id = ? + )` queryParam := make([]interface{}, 1) queryParam = append(queryParam, userQuery.UserID) - if projectID > 0 { - sql += ` and pr.project_id = ? ` - queryParam = append(queryParam, projectID) - } - if userQuery.RoleID > 0 { - sql += ` and r.role_id = ? ` - queryParam = append(queryParam, userQuery.RoleID) - } - var roleList []models.Role - _, err := o.Raw(sql, queryParam).QueryRows(&roleList) + _, err := o.Raw(sql, projectID, userQuery.UserID).QueryRows(&roleList) if err != nil { return nil, err @@ -56,11 +76,54 @@ func GetUserProjectRoles(userQuery models.User, projectID int64) ([]models.Role, // IsAdminRole returns whether the user is admin. func IsAdminRole(userID int) (bool, error) { - //role_id == 1 means the user is system admin - userQuery := models.User{UserID: userID, RoleID: models.SYSADMIN} - adminRoleList, err := GetUserProjectRoles(userQuery, 0) + + user, err := GetUser(models.User{UserID: userID}) if err != nil { return false, err } - return len(adminRoleList) > 0, nil + + if user == nil { + return false, nil + } + + return user.HasAdminRole == 1, nil +} + +func getRole(r role) (*models.Role, error) { + if roleList[r] != nil { + return roleList[r], nil + } + + o := orm.NewOrm() + var roles []*models.Role + + sql := "select role_id, role_code, name, role_mask from role" + + _, err := o.Raw(sql).QueryRows(&roles) + if err != nil { + return nil, err + } + + for _, rr := range roles { + if rr.RoleCode == "MDRWS" { + roleList[ProjectAdmin] = rr + continue + } + + if rr.RoleCode == "RWS" { + roleList[Developer] = rr + continue + } + + if rr.RoleCode == "RS" { + roleList[Guest] = rr + continue + } + } + + if roleList[r] == nil { + return nil, fmt.Errorf("unsupported role type: %v", r) + } + + return roleList[r], nil } diff --git a/dao/user.go b/dao/user.go index 10b3dbe15..a8e53b293 100644 --- a/dao/user.go +++ b/dao/user.go @@ -22,8 +22,8 @@ import ( "github.com/vmware/harbor/models" "github.com/vmware/harbor/utils" - "github.com/astaxie/beego" "github.com/astaxie/beego/orm" + "github.com/vmware/harbor/utils/log" ) // GetUser ... @@ -31,12 +31,8 @@ func GetUser(query models.User) (*models.User, error) { o := orm.NewOrm() - sql := `select user_id, username, email, realname, reset_uuid, salt, - ifnull((select pr.role_id - from project_role pr - left join user_project_role upr on upr.pr_id = pr.pr_id - where pr.role_id = 1 - and upr.user_id = u.user_id),0) as has_admin_role + sql := `select user_id, username, email, realname, comment, reset_uuid, salt, + sysadmin_flag, creation_time, update_time from user u where deleted = 0 ` queryParam := make([]interface{}, 1) @@ -60,51 +56,52 @@ func GetUser(query models.User) (*models.User, error) { if err != nil { return nil, err - } else if n == 0 { - return nil, nil - } else { - return &u[0], nil } + if n == 0 { + return nil, nil + } + + return &u[0], nil } // LoginByDb is used for user to login with database auth mode. func LoginByDb(auth models.AuthModel) (*models.User, error) { - - query := models.User{Username: auth.Principal, Email: auth.Principal} - o := orm.NewOrm() - var u []models.User - n, err := o.Raw(`select username from user where (username = ? or email = ?)`, query.Username, query.Email).QueryRows(&u) + + var users []models.User + n, err := o.Raw(`select * from user where (username = ? or email = ?)`, + auth.Principal, auth.Principal).QueryRows(&users) if err != nil { return nil, err - } else if n == 0 { - beego.Warning("User does not exist. Principal:", auth.Principal) + } + if n == 0 { return nil, nil - } else { - u[0].Password = auth.Password - return CheckUserPassword(u[0]) } + user := users[0] + + if user.Password != utils.Encrypt(auth.Password, user.Salt) { + return nil, nil + } + + return &user, nil } // ListUsers lists all users according to different conditions. func ListUsers(query models.User) ([]models.User, error) { o := orm.NewOrm() u := []models.User{} - sql := `select u.user_id, u.username, u.email, ifnull((select pr.role_id - from project_role pr - left join user_project_role upr on upr.pr_id = pr.pr_id - where pr.role_id = 1 - and upr.user_id = u.user_id),0) as has_admin_role - from user u - where u.deleted = 0 and u.user_id != 1 ` + sql := `select user_id, username, email, realname, comment, reset_uuid, salt, + sysadmin_flag, creation_time, update_time + from user u + where u.deleted = 0 and u.user_id != 1 ` queryParam := make([]interface{}, 1) if query.Username != "" { - sql += ` and u.username like ? ` + sql += ` and username like ? ` queryParam = append(queryParam, query.Username) } - sql += ` order by u.user_id desc ` + sql += ` order by user_id desc ` _, err := o.Raw(sql, queryParam).QueryRows(&u) return u, err @@ -112,59 +109,76 @@ func ListUsers(query models.User) ([]models.User, error) { // ToggleUserAdminRole gives a user admim role. func ToggleUserAdminRole(u models.User) error { - - projectRole := models.ProjectRole{PrID: 1} //admin project role - o := orm.NewOrm() - var pr []models.ProjectRole - - n, err := o.Raw(`select user_id from user_project_role where user_id = ? and pr_id = ? `, u.UserID, projectRole.PrID).QueryRows(&pr) + var user models.User + err := o.Raw(`select sysadmin_flag from user where user_id = ?`, u.UserID).QueryRow(&user) if err != nil { return err } - var sql string - if n == 0 { - sql = `insert into user_project_role (user_id, pr_id) values (?, ?)` + var sysAdminFlag int + if user.HasAdminRole == 0 { + sysAdminFlag = 1 } else { - sql = `delete from user_project_role where user_id = ? and pr_id = ?` + sysAdminFlag = 0 } - p, err := o.Raw(sql).Prepare() + sql := `update user set sysadmin_flag = ? where user_id = ?` + + r, err := o.Raw(sql, sysAdminFlag, u.UserID).Exec() if err != nil { return err } - defer p.Close() - _, err = p.Exec(u.UserID, projectRole.PrID) - return err + if _, err := r.RowsAffected(); err != nil { + return err + } + + return nil } // ChangeUserPassword ... -func ChangeUserPassword(u models.User, oldPassword ...string) error { +func ChangeUserPassword(u models.User, oldPassword ...string) (err error) { o := orm.NewOrm() - var err error + var r sql.Result if len(oldPassword) == 0 { //In some cases, it may no need to check old password, just as Linux change password policies. - _, err = o.Raw(`update user set password=?, salt=? where user_id=?`, utils.Encrypt(u.Password, u.Salt), u.Salt, u.UserID).Exec() - } else if len(oldPassword) == 1 { + r, err = o.Raw(`update user set password=?, salt=? where user_id=?`, utils.Encrypt(u.Password, u.Salt), u.Salt, u.UserID).Exec() + if err != nil { + return err + } + + c, err := r.RowsAffected() + if err != nil { + return err + } + + if c == 0 { + return errors.New("No record has been modified, change password failed.") + } + + return nil + } + + if len(oldPassword) == 1 { r, err = o.Raw(`update user set password=?, salt=? where user_id=? and password = ?`, utils.Encrypt(u.Password, u.Salt), u.Salt, u.UserID, utils.Encrypt(oldPassword[0], u.Salt)).Exec() if err != nil { return err } - count, err := r.RowsAffected() + c, err := r.RowsAffected() if err != nil { return err } - if count == 0 { - return errors.New("No record be changed, change password failed.") + if c == 0 { + return errors.New("No record has been modified, change password failed.") } - } else { - return errors.New("Wrong numbers of params.") + + return nil } - return err + + return errors.New("Wrong numbers of params.") } // ResetUserPassword ... @@ -181,7 +195,7 @@ func ResetUserPassword(u models.User) error { if count == 0 { return errors.New("No record be changed, reset password failed.") } - return err + return nil } // UpdateUserResetUUID ... @@ -224,12 +238,14 @@ func CheckUserPassword(query models.User) (*models.User, error) { if err != nil { return nil, err - } else if n == 0 { - beego.Warning("User principal does not match password. Current:", currentUser) - return nil, nil - } else { - return &user[0], nil } + + if n == 0 { + log.Warning("User principal does not match password. Current:", currentUser) + return nil, nil + } + + return &user[0], nil } // DeleteUser ... diff --git a/models/project.go b/models/project.go index 1b3bb92a2..e240c609f 100644 --- a/models/project.go +++ b/models/project.go @@ -32,4 +32,6 @@ type Project struct { Public int `orm:"column(public)"` //This field does not have correspondent column in DB, this is just for UI to disable button Togglable bool + + UpdateTime time.Time `orm:"update_time" json:"update_time"` } diff --git a/models/role.go b/models/role.go index c9a2e625c..169e17ae7 100644 --- a/models/role.go +++ b/models/role.go @@ -15,6 +15,7 @@ package models +/* const ( //SYSADMIN system administrator SYSADMIN = 1 @@ -25,14 +26,17 @@ const ( //GUEST guest GUEST = 4 ) - +*/ // Role holds the details of a role. type Role struct { - RoleID int `json:"role_id" orm:"column(role_id)"` - RoleCode string `json:"role_code" orm:"column(role_code)"` - Name string `json:"role_name" orm:"column(name)"` + RoleID int `orm:"column(role_id)" json:"role_id"` + RoleCode string `orm:"column(role_code)" json:"role_code"` + Name string `orm:"column(name)" json:"role_name"` + + RoleMask int `orm:"role_mask" json:"role_mask"` } +/* // ProjectRole holds information about the relationship of project and role. type ProjectRole struct { PrID int `orm:"column(pr_id)" json:"PrId"` @@ -46,3 +50,4 @@ type UserProjectRole struct { UserID int `orm:"column(user_id)" json:"UserId"` PrID int64 `orm:"column(pr_id)" json:"PrId"` } +*/ diff --git a/models/user.go b/models/user.go index e89d74b4b..82b8bc221 100644 --- a/models/user.go +++ b/models/user.go @@ -15,6 +15,10 @@ package models +import ( + "time" +) + // User holds the details of a user. type User struct { UserID int `orm:"column(user_id)" json:"UserId"` @@ -27,7 +31,10 @@ type User struct { Rolename string RoleID int `json:"RoleId"` RoleList []Role - HasAdminRole int + HasAdminRole int `orm:"column(sysadmin_flag)"` ResetUUID string `orm:"column(reset_uuid)" json:"ResetUuid"` Salt string `orm:"column(salt)"` + + CreationTime time.Time `orm:"creation_time" json:"creation_time"` + UpdateTime time.Time `orm:"update_time" json:"update_time"` }