Merge pull request #65 from vmware/db-role-refactor

Db role refactor
This commit is contained in:
Daniel Jiang 2016-03-30 18:22:47 +08:00
commit 3ebb2dea0f
18 changed files with 368 additions and 448 deletions

View File

@ -10,27 +10,30 @@ create table access (
primary key (access_id)
);
insert into access values
( null, 'A', 'All access for the system'),
( null, 'M', 'Management access for project'),
( null, 'R', 'Read access for project'),
( null, 'W', 'Write access for project'),
( null, 'D', 'Delete access for project'),
( null, 'S', 'Search access for project');
insert into access (access_code, comment) values
('M', 'Management access for project'),
('R', 'Read access for project'),
('W', 'Write access for project'),
('D', 'Delete access for project'),
('S', 'Search access for project');
create table role (
role_id int NOT NULL AUTO_INCREMENT,
role_mask int DEFAULT 0 NOT NULL,
role_code varchar(20),
name varchar (20),
primary key (role_id)
);
/*
role mask is used for future enhancement when a project member can have multi-roles
currently set to 0
*/
insert into role values
( null, 'AMDRWS', 'sysAdmin'),
( null, 'MDRWS', 'projectAdmin'),
( null, 'RWS', 'developer'),
( null, 'RS', 'guest');
insert into role (role_code, name) values
('MDRWS', 'projectAdmin'),
('RWS', 'developer'),
('RS', 'guest');
create table user (
@ -43,20 +46,24 @@ create table user (
deleted tinyint (1) DEFAULT 0 NOT NULL,
reset_uuid varchar(40) DEFAULT NULL,
salt varchar(40) DEFAULT NULL,
sysadmin_flag tinyint (1),
creation_time timestamp,
update_time timestamp,
primary key (user_id),
UNIQUE (username),
UNIQUE (email)
);
insert into user values
(1, 'admin', 'admin@example.com', '', 'system admin', 'admin user',0, null, ''),
(2, 'anonymous', 'anonymous@example.com', '', 'anonymous user', 'anonymous user', 1, null, '');
insert into user (username, email, password, realname, comment, deleted, sysadmin_flag, creation_time, update_time) values
('admin', 'admin@example.com', '', 'system admin', 'admin user',0, 1, NOW(), NOW()),
('anonymous', 'anonymous@example.com', '', 'anonymous user', 'anonymous user', 1, 0, NOW(), NOW());
create table project (
project_id int NOT NULL AUTO_INCREMENT,
owner_id int NOT NULL,
name varchar (30) NOT NULL,
creation_time timestamp,
update_time timestamp,
deleted tinyint (1) DEFAULT 0 NOT NULL,
public tinyint (1) DEFAULT 0 NOT NULL,
primary key (project_id),
@ -64,32 +71,23 @@ create table project (
UNIQUE (name)
);
insert into project values
(null, 1, 'library', NOW(), 0, 1);
insert into project (owner_id, name, creation_time, update_time, public) values
(1, 'library', NOW(), NOW(), 1);
create table project_role (
pr_id int NOT NULL AUTO_INCREMENT,
create table project_member (
project_id int NOT NULL,
role_id int NOT NULL,
primary key (pr_id),
FOREIGN KEY (role_id) REFERENCES role(role_id),
FOREIGN KEY (project_id) REFERENCES project (project_id)
);
insert into project_role values
( 1,1,1 );
create table user_project_role (
upr_id int NOT NULL AUTO_INCREMENT,
user_id int NOT NULL,
pr_id int NOT NULL,
primary key (upr_id),
FOREIGN KEY (user_id) REFERENCES user(user_id),
FOREIGN KEY (pr_id) REFERENCES project_role (pr_id)
role int NOT NULL,
creation_time timestamp,
update_time timestamp,
PRIMARY KEY (project_id, user_id),
FOREIGN KEY (role) REFERENCES role(role_id),
FOREIGN KEY (project_id) REFERENCES project(project_id),
FOREIGN KEY (user_id) REFERENCES user(user_id)
);
insert into user_project_role values
( 1,1,1 );
insert into project_member (project_id, user_id, role, creation_time, update_time) values
(1, 1, 1, NOW(), NOW());
create table access_log (
log_id int NOT NULL AUTO_INCREMENT,

View File

@ -92,7 +92,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 {
log.Errorf("Error occurred in GetUserProjectRoles, error: %v", err)
pma.CustomAbort(http.StatusInternalServerError, "Internal error.")
@ -115,17 +115,26 @@ 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 {
log.Errorf("Error occurred in GetUserProjectRoles, error: %v", err)
pma.CustomAbort(http.StatusInternalServerError, "Internal error.")
}
if len(rolelist) == 0 {
log.Warningf("Current user, id: %d does not have project admin role for project, id: %d", pma.currentUserID, pid)
hasProjectAdminRole := false
for _, role := range rolelist {
if role.RoleID == models.PROJECTADMIN {
hasProjectAdminRole = true
break
}
}
if !hasProjectAdminRole {
log.Warningf("Current user, id: %d does not have project admin role for project, id:", pma.currentUserID, pid)
pma.RenderError(http.StatusForbidden, "")
return
}
var req memberReq
pma.DecodeJSONReq(&req)
username := req.Username
@ -135,7 +144,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 {
log.Errorf("Error occurred in GetUserProjectRoles, error: %v", err)
pma.CustomAbort(http.StatusInternalServerError, "Internal error.")
@ -147,7 +156,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 {
log.Errorf("Failed to update DB to add project user role, project id: %d, user id: %d, role id: %d", pid, userID, rid)
pma.RenderError(http.StatusInternalServerError, "Failed to update data in database")
@ -160,20 +169,28 @@ 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 {
log.Errorf("Error occurred in GetUserProjectRoles, error: %v", 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 {
log.Warningf("Current user, id: %d does not have project admin role for project, id: %d", pma.currentUserID, 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 {
log.Warningf("User is not in project, user id: %d, project id: %d", mid, pid)
pma.RenderError(http.StatusNotFound, "user not exist in project")
@ -181,7 +198,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 {
log.Errorf("Failed to delete project roles for user, user id: %d, project id: %d, error: %v", mid, pid, err)
pma.RenderError(http.StatusInternalServerError, "Failed to update data in DB")
@ -189,7 +206,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 {
log.Errorf("Failed to update DB to add project user role, project id: %d, user id: %d, role id: %d", pid, mid, rid)
pma.RenderError(http.StatusInternalServerError, "Failed to update data in database")
@ -202,14 +219,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 {
log.Warningf("Current user, id: %d does not have project admin role for project, id: %d", pma.currentUserID, pid)
pma.RenderError(http.StatusForbidden, "")
return
}
err = dao.DeleteUserProjectRoles(mid, pid)
err = dao.DeleteProjectMember(pid, mid)
if err != nil {
log.Errorf("Failed to delete project roles for user, user id: %d, project id: %d, error: %v", mid, pid, err)
pma.RenderError(http.StatusInternalServerError, "Failed to update data in DB")

View File

@ -187,13 +187,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 {
log.Errorf("Error occurred in GetUserProjectRoles, returning false, error: %v", err)
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 {

View File

@ -30,7 +30,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 {
log.Errorf("Error occurred in GetUserProjectRoles, error: %v", err)
return false

View File

@ -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
}
}

View File

@ -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)
}
}

View File

@ -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
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
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))
}
if r[1].RoleID != 3 {
t.Errorf("Expected role id of the second role in list is 3, actual: %d", r[1].RoleID)
}
}
func TestDeleteUserProjectRole(t *testing.T) {
err := DeleteUserProjectRoles(currentUser.UserID, currentProject.ProjectID)
roles, err := GetUserProjectRoles(1, currentProject.ProjectID)
if err != nil {
t.Errorf("Error occurred in DeleteUserProjectRoles: %v", err)
t.Errorf("Error occurred in GetUserProjectRoles: %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)
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 TestDeleteProjectMember(t *testing.T) {
err := DeleteProjectMember(currentProject.ProjectID, 1)
if err != nil {
t.Errorf("Error occurred in DeleteProjectMember: %v", err)
}
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")
}
}

View File

@ -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 {

View File

@ -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)

View File

@ -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
}

View File

@ -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

View File

@ -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
@ -55,12 +46,26 @@ 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)
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
}

View File

@ -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
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,60 +111,51 @@ 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()
}
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 ...
func ResetUserPassword(u models.User) error {
@ -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 ...

View File

@ -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"`
}

View File

@ -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"`
}

View File

@ -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"`
}

View File

@ -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,

View File

@ -197,15 +197,15 @@
<label for="txtRole" class="control-label">{{i18n .Lang "role"}}:</label>
<ul class="list-group" id="lstRole">
<li class="list-group-item">
<input type="radio" name="chooseRole" id="chkRole2" value="2">
<input type="radio" name="chooseRole" id="chkRole2" value="1">
<label for="chkRole2" class="control-label">Project Admin</label>
</li>
<li class="list-group-item">
<input type="radio" name="chooseRole" id="chkRole3" value="3">
<input type="radio" name="chooseRole" id="chkRole3" value="2">
<label for="chkRole3" class="control-label">Developer</label>
</li>
<li class="list-group-item">
<input type="radio" name="chooseRole" id="chkRole4" value="4">
<input type="radio" name="chooseRole" id="chkRole4" value="3">
<label for="chkRole4" class="control-label">Guest</label>
</li>
</ul>