diff --git a/api/member.go b/api/member.go index 2e9243d12..2d32d7318 100644 --- a/api/member.go +++ b/api/member.go @@ -93,7 +93,7 @@ func (pma *ProjectMemberAPI) Get() { } pma.Data["json"] = userList } else { //return detail of a member - roleList, err := dao.GetUserProjectRoles(models.User{UserID: pma.memberID}, pid) + roleList, err := dao.GetUserProjectRoles(pma.memberID, pid) if err != nil { beego.Error("Error occurred in GetUserProjectRoles:", err) pma.CustomAbort(http.StatusInternalServerError, "Internal error.") @@ -116,17 +116,27 @@ func (pma *ProjectMemberAPI) Get() { // Post ... func (pma *ProjectMemberAPI) Post() { pid := pma.project.ProjectID - userQuery := models.User{UserID: pma.currentUserID, RoleID: models.PROJECTADMIN} - rolelist, err := dao.GetUserProjectRoles(userQuery, pid) + + //userQuery := models.User{UserID: pma.currentUserID, RoleID: models.PROJECTADMIN} + rolelist, err := dao.GetUserProjectRoles(pma.currentUserID, pid) if err != nil { beego.Error("Error occurred in GetUserProjectRoles:", err) pma.CustomAbort(http.StatusInternalServerError, "Internal error.") } - if len(rolelist) == 0 { + + hasProjectAdminRole := false + for _, role := range rolelist { + if role.RoleID == models.PROJECTADMIN { + hasProjectAdminRole = true + break + } + } + if !hasProjectAdminRole { beego.Warning("Current user, id:", pma.currentUserID, "does not have project admin role for project, id:", pid) pma.RenderError(http.StatusForbidden, "") return } + var req memberReq pma.DecodeJSONReq(&req) username := req.Username @@ -136,7 +146,7 @@ func (pma *ProjectMemberAPI) Post() { pma.RenderError(http.StatusNotFound, "User does not exist") return } - rolelist, err = dao.GetUserProjectRoles(models.User{UserID: userID}, pid) + rolelist, err = dao.GetUserProjectRoles(userID, pid) if err != nil { beego.Error("Error occurred in GetUserProjectRoles:", err) pma.CustomAbort(http.StatusInternalServerError, "Internal error.") @@ -148,7 +158,7 @@ func (pma *ProjectMemberAPI) Post() { } for _, rid := range req.Roles { - err = dao.AddUserProjectRole(userID, pid, int(rid)) + err = dao.AddProjectMember(pid, userID, int(rid)) 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") @@ -161,20 +171,29 @@ func (pma *ProjectMemberAPI) Post() { func (pma *ProjectMemberAPI) Put() { pid := pma.project.ProjectID mid := pma.memberID - userQuery := models.User{UserID: pma.currentUserID, RoleID: models.PROJECTADMIN} - rolelist, err := dao.GetUserProjectRoles(userQuery, pid) + + rolelist, err := dao.GetUserProjectRoles(pma.currentUserID, pid) if err != nil { beego.Error("Error occurred in GetUserProjectRoles:", err) pma.CustomAbort(http.StatusInternalServerError, "Internal error.") } - if len(rolelist) == 0 { + + hasProjectAdminRole := false + for _, role := range rolelist { + if role.RoleID == models.PROJECTADMIN { + hasProjectAdminRole = true + break + } + } + + if !hasProjectAdminRole { beego.Warning("Current user, id:", pma.currentUserID, ", does not have project admin role for project, id:", pid) pma.RenderError(http.StatusForbidden, "") return } var req memberReq pma.DecodeJSONReq(&req) - roleList, err := dao.GetUserProjectRoles(models.User{UserID: mid}, pid) + roleList, err := dao.GetUserProjectRoles(mid, pid) if len(roleList) == 0 { beego.Warning("User is not in project, user id:", mid, ", project id:", pid) pma.RenderError(http.StatusNotFound, "user not exist in project") @@ -182,7 +201,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 +209,7 @@ func (pma *ProjectMemberAPI) Put() { } //insert roles in request for _, rid := range req.Roles { - err = dao.AddUserProjectRole(mid, pid, int(rid)) + err = dao.AddProjectMember(pid, mid, int(rid)) 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") @@ -203,14 +222,22 @@ func (pma *ProjectMemberAPI) Put() { func (pma *ProjectMemberAPI) Delete() { pid := pma.project.ProjectID mid := pma.memberID - userQuery := models.User{UserID: pma.currentUserID, RoleID: models.PROJECTADMIN} - rolelist, err := dao.GetUserProjectRoles(userQuery, pid) - if len(rolelist) == 0 { + + rolelist, err := dao.GetUserProjectRoles(pma.currentUserID, pid) + hasProjectAdminRole := false + for _, role := range rolelist { + if role.RoleID == models.PROJECTADMIN { + hasProjectAdminRole = true + break + } + } + + if !hasProjectAdminRole { beego.Warning("Current user, id:", pma.currentUserID, ", does not have project admin role for project, id:", pid) 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/api/project.go b/api/project.go index fc8c86f33..2b8656e0c 100644 --- a/api/project.go +++ b/api/project.go @@ -189,13 +189,21 @@ func (p *ProjectAPI) FilterAccessLog() { } func isProjectAdmin(userID int, pid int64) bool { - userQuery := models.User{UserID: userID, RoleID: models.PROJECTADMIN} - rolelist, err := dao.GetUserProjectRoles(userQuery, pid) + rolelist, err := dao.GetUserProjectRoles(userID, pid) if err != nil { beego.Error("Error occurred in GetUserProjectRoles:", err, ", returning false") return false } - return len(rolelist) > 0 + + hasProjectAdminRole := false + for _, role := range rolelist { + if role.RoleID == models.PROJECTADMIN { + hasProjectAdminRole = true + break + } + } + + return hasProjectAdminRole } func validateProjectReq(req projectReq) error { diff --git a/api/utils.go b/api/utils.go index 700bf9fb9..84ef6327c 100644 --- a/api/utils.go +++ b/api/utils.go @@ -31,7 +31,7 @@ func checkProjectPermission(userID int, projectID int64) bool { if exist { return true } - roleList, err := dao.GetUserProjectRoles(models.User{UserID: userID}, projectID) + roleList, err := dao.GetUserProjectRoles(userID, projectID) if err != nil { beego.Error("Error occurred in GetUserProjectRoles:", err) return false diff --git a/controllers/itemdetail.go b/controllers/itemdetail.go index 95d393fb9..e13fe146f 100644 --- a/controllers/itemdetail.go +++ b/controllers/itemdetail.go @@ -21,7 +21,6 @@ import ( "os" "github.com/vmware/harbor/dao" - "github.com/vmware/harbor/models" "github.com/astaxie/beego" ) @@ -69,7 +68,7 @@ func (idc *ItemDetailController) Get() { idc.Data["Username"] = idc.GetSession("username") idc.Data["UserId"] = userID - roleList, err := dao.GetUserProjectRoles(models.User{UserID: userID}, projectID) + roleList, err := dao.GetUserProjectRoles(userID, projectID) if err != nil { beego.Error("Error occurred in GetUserProjectRoles:", err) idc.CustomAbort(http.StatusInternalServerError, "Internal error.") @@ -86,9 +85,7 @@ func (idc *ItemDetailController) Get() { return } - if isAdmin { - idc.Data["RoleId"] = models.SYSADMIN - } else if len(roleList) > 0 { + if len(roleList) > 0 { idc.Data["RoleId"] = roleList[0].RoleID } } diff --git a/dao/base.go b/dao/base.go index dee02370a..3afb633cf 100644 --- a/dao/base.go +++ b/dao/base.go @@ -16,9 +16,10 @@ package dao import ( - "log" "net" + "github.com/vmware/harbor/utils/log" + "os" "strings" "time" @@ -88,7 +89,7 @@ func InitDB() { c.Close() ch <- 1 } else { - log.Printf("failed to connect to db, retry after 2 seconds...") + log.Info("failed to connect to db, retry after 2 seconds...") time.Sleep(2 * time.Second) } } diff --git a/dao/dao_test.go b/dao/dao_test.go index 1bc1e1f0f..7e9e1e0d2 100644 --- a/dao/dao_test.go +++ b/dao/dao_test.go @@ -16,12 +16,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 +41,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() } @@ -109,7 +126,7 @@ func TestMain(m *testing.M) { } dbPassword := os.Getenv("DB_PWD") - fmt.Printf("DB_HOST: %s, DB_USR: %s, DB_PORT: %s, DB_PWD: %s\n", dbHost, dbUser, dbPort, dbPassword) + log.Infof("DB_HOST: %s, DB_USR: %s, DB_PORT: %s, DB_PWD: %s\n", dbHost, dbUser, dbPort, dbPassword) os.Setenv("MYSQL_PORT_3306_TCP_ADDR", dbHost) os.Setenv("MYSQL_PORT_3306_TCP_PORT", dbPort) @@ -379,32 +396,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,25 +537,10 @@ 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) + r, err := GetUserProjectRoles(currentUser.UserID, currentProject.ProjectID) if err != nil { - t.Errorf("Error happened in GetUserProjectRole: %v, user: %+v, project Id: %d", err, user, currentProject.ProjectID) + t.Errorf("Error happened in GetUserProjectRole: %v, userID: %+v, project Id: %d", err, currentUser.UserID, currentProject.ProjectID) } //Get the size of current user project role. @@ -575,16 +551,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 +576,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, models.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(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(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/project.go b/dao/project.go index 9d4205057..d2a611e9f 100644 --- a/dao/project.go +++ b/dao/project.go @@ -22,8 +22,8 @@ import ( "fmt" "time" - "github.com/astaxie/beego" "github.com/astaxie/beego/orm" + "github.com/vmware/harbor/utils/log" ) //TODO:transaction, return err @@ -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, models.PROJECTADMIN); err != nil { return err } @@ -89,7 +70,7 @@ func AddProject(project models.Project) error { func IsProjectPublic(projectName string) bool { project, err := GetProjectByName(projectName) if err != nil { - beego.Error("Error occurred in GetProjectByName:", err) + log.Errorf("Error occurred in GetProjectByName: %v", err) return false } if project == nil { @@ -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/itemdetail.go b/dao/projectmember.go similarity index 50% rename from dao/itemdetail.go rename to dao/projectmember.go index f4f2cb558..d994afb43 100644 --- a/dao/itemdetail.go +++ b/dao/projectmember.go @@ -16,25 +16,56 @@ package dao import ( - "github.com/vmware/harbor/models" - "github.com/astaxie/beego/orm" + "github.com/vmware/harbor/models" ) +// AddProjectMember inserts a record to table project_member +func AddProjectMember(projectID int64, userID int, role int) error { + o := orm.NewOrm() + + sql := "insert into project_member (project_id, user_id , role) values (?, ?, ?)" + + _, err := o.Raw(sql, projectID, userID, role).Exec() + + return err +} + +// UpdateProjectMember updates the record in table project_member +func UpdateProjectMember(projectID int64, userID int, role int) error { + o := orm.NewOrm() + + sql := "update project_member set role = ? where project_id = ? and user_id = ?" + + _, err := o.Raw(sql, role, projectID, userID).Exec() + + return err +} + +// 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 left join user_project_role upr - on u.user_id = upr.user_id - left join project_role pr - on pr.pr_id = upr.pr_id - left join role r - on r.role_id = pr.role_id - where u.deleted = 0 - and pr.project_id = ? ` + 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) diff --git a/dao/projectrole.go b/dao/projectrole.go deleted file mode 100644 index dbf409fa0..000000000 --- a/dao/projectrole.go +++ /dev/null @@ -1,93 +0,0 @@ -/* - 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/vmware/harbor/models" - - "github.com/astaxie/beego/orm" -) - -// AddProjectRole ... -func AddProjectRole(projectRole models.ProjectRole) (int64, error) { - o := orm.NewOrm() - p, err := o.Raw("insert into project_role (project_id, role_id) values (?, ?)").Prepare() - if err != nil { - return 0, err - } - defer p.Close() - r, err := p.Exec(projectRole.ProjectID, projectRole.RoleID) - if err != nil { - return 0, err - } - id, err := r.LastInsertId() - return id, err -} - -// AddUserProjectRole inserts role information to table project_role and user_project_role. -func AddUserProjectRole(userID int, projectID int64, roleID int) error { - - o := orm.NewOrm() - - var pr []models.ProjectRole - - var prID int - - sql := `select pr.pr_id, pr.project_id, pr.role_id from project_role pr where pr.project_id = ? and pr.role_id = ?` - n, err := o.Raw(sql, projectID, roleID).QueryRows(&pr) - if err != nil { - return err - } - - if n == 0 { //project role not found, insert a pr record - p, err := o.Raw("insert into project_role (project_id, role_id) values (?, ?)").Prepare() - if err != nil { - return err - } - defer p.Close() - r, err := p.Exec(projectID, roleID) - if err != nil { - return err - } - id, err := r.LastInsertId() - if err != nil { - return err - } - prID = int(id) - } else if n > 0 { - prID = pr[0].PrID - } - p, err := o.Raw("insert into user_project_role (user_id, pr_id) values (?, ?)").Prepare() - if err != nil { - return err - } - defer p.Close() - _, err = p.Exec(userID, prID) - return err -} - -// DeleteUserProjectRoles ... -func DeleteUserProjectRoles(userID int, projectID int64) error { - o := orm.NewOrm() - sql := `delete from user_project_role where user_id = ? and pr_id in - (select pr_id from project_role where project_id = ?)` - p, err := o.Raw(sql).Prepare() - if err != nil { - return err - } - _, 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..43062a1fc 100644 --- a/dao/role.go +++ b/dao/role.go @@ -16,37 +16,28 @@ package dao import ( - "github.com/vmware/harbor/models" + "fmt" "github.com/astaxie/beego/orm" + "github.com/vmware/harbor/models" ) // GetUserProjectRoles returns roles that the user has according to the project. -func GetUserProjectRoles(userQuery models.User, projectID int64) ([]models.Role, error) { +func GetUserProjectRoles(userID int, 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 = ? ` - 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) - } + sql := `select * + from role + where role_id = + ( + select role + from project_member + where project_id = ? and user_id = ? + )` var roleList []models.Role - _, err := o.Raw(sql, queryParam).QueryRows(&roleList) + _, err := o.Raw(sql, projectID, userID).QueryRows(&roleList) if err != nil { return nil, err @@ -54,13 +45,27 @@ func GetUserProjectRoles(userQuery models.User, projectID int64) ([]models.Role, return roleList, nil } -// 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) +// IsAdminRole returns whether the user is admin. +func IsAdminRole(userIDOrUsername interface{}) (bool, error) { + u := models.User{} + + switch v := userIDOrUsername.(type) { + case int: + u.UserID = v + case string: + u.Username = v + default: + return false, fmt.Errorf("invalid parameter, only int and string are supported: %v", userIDOrUsername) + } + + user, err := GetUser(u) if err != nil { return false, err } - return len(adminRoleList) > 0, nil + + if user == nil { + return false, nil + } + + return user.HasAdminRole == 1, nil } diff --git a/dao/user.go b/dao/user.go index 10b3dbe15..655e69d21 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,54 @@ 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 = ?) and deleted = 0`, + 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 + } + + user.Password = "" //do not return the password + + 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 +111,50 @@ 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 + sql := `update user set sysadmin_flag =not sysadmin_flag where user_id = ?` - n, err := o.Raw(`select user_id from user_project_role where user_id = ? and pr_id = ? `, u.UserID, projectRole.PrID).QueryRows(&pr) + r, err := o.Raw(sql, u.UserID).Exec() if err != nil { return err } - var sql string - if n == 0 { - sql = `insert into user_project_role (user_id, pr_id) values (?, ?)` - } else { - sql = `delete from user_project_role where user_id = ? and pr_id = ?` - } - - p, err := o.Raw(sql).Prepare() - if err != nil { + if _, err := r.RowsAffected(); err != nil { return err } - defer p.Close() - _, err = p.Exec(u.UserID, projectRole.PrID) - return err + return nil } // ChangeUserPassword ... -func ChangeUserPassword(u models.User, oldPassword ...string) error { +func ChangeUserPassword(u models.User, oldPassword ...string) (err error) { + if len(oldPassword) > 1 { + return errors.New("Wrong numbers of params.") + } + 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=? 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() - if err != nil { - return err - } - if count == 0 { - return errors.New("No record be changed, change password failed.") - } + r, err = o.Raw(`update user set password=?, salt=? where user_id=?`, utils.Encrypt(u.Password, u.Salt), u.Salt, u.UserID).Exec() } else { - return errors.New("Wrong numbers of params.") + 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() } - return err + + 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 } // ResetUserPassword ... @@ -181,7 +171,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 +214,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..c55b29441 100644 --- a/models/role.go +++ b/models/role.go @@ -16,33 +16,19 @@ package models const ( - //SYSADMIN system administrator - SYSADMIN = 1 //PROJECTADMIN project administrator - PROJECTADMIN = 2 + PROJECTADMIN = 1 //DEVELOPER developer - DEVELOPER = 3 + DEVELOPER = 2 //GUEST guest - GUEST = 4 + GUEST = 3 ) // 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"` -// ProjectRole holds information about the relationship of project and role. -type ProjectRole struct { - PrID int `orm:"column(pr_id)" json:"PrId"` - ProjectID int64 `orm:"column(project_id)" json:"ProjectId"` - RoleID int `orm:"column(role_id)" json:"RoleId"` -} - -// UserProjectRole holds information about relationship of user, project and role. -type UserProjectRole struct { - UprID int `orm:"column(upr_id)" json:"UprId"` - UserID int `orm:"column(user_id)" json:"UserId"` - PrID int64 `orm:"column(pr_id)" json:"PrId"` + RoleMask int `orm:"role_mask" json:"role_mask"` } 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"` } diff --git a/static/resources/js/item-detail.js b/static/resources/js/item-detail.js index 4b4477cd7..e13075ecf 100644 --- a/static/resources/js/item-detail.js +++ b/static/resources/js/item-detail.js @@ -332,7 +332,6 @@ jQuery(function(){ getUserRoleCallback(userId); }); $("#tblUser .glyphicon-trash").on("click", function(){ - var roleId = $(this).attr("roleid"); var userId = $(this).attr("userid"); new AjaxUtil({ url: "/api/projects/" + $("#projectId").val() + "/members/" + userId, diff --git a/views/item-detail.tpl b/views/item-detail.tpl index daa038c07..a1ce83e96 100644 --- a/views/item-detail.tpl +++ b/views/item-detail.tpl @@ -197,15 +197,15 @@