// Copyright (c) 2017 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/src/common" "github.com/vmware/harbor/src/common/models" "github.com/vmware/harbor/src/common/utils/log" "fmt" "time" ) // AddProject adds a project to the database along with project roles information and access log records. func AddProject(project models.Project) (int64, error) { o := GetOrmer() sql := "insert into project (owner_id, name, creation_time, update_time, deleted) values (?, ?, ?, ?, ?) RETURNING project_id" var projectID int64 now := time.Now() err := o.Raw(sql, project.OwnerID, project.Name, now, now, project.Deleted).QueryRow(&projectID) if err != nil { return 0, err } pmID, err := addProjectMember(models.Member{ ProjectID: projectID, EntityID: project.OwnerID, Role: models.PROJECTADMIN, EntityType: common.UserMember, }) if err != nil { return 0, err } if pmID == 0 { return projectID, err } return projectID, nil } func addProjectMember(member models.Member) (int, error) { log.Debugf("Adding project member %+v", member) o := GetOrmer() if member.EntityID <= 0 { return 0, fmt.Errorf("Invalid entity_id, member: %+v", member) } if member.ProjectID <= 0 { return 0, fmt.Errorf("Invalid project_id, member: %+v", member) } var pmID int sql := "insert into project_member (project_id, entity_id , role, entity_type) values (?, ?, ?, ?) RETURNING id" err := o.Raw(sql, member.ProjectID, member.EntityID, member.Role, member.EntityType).QueryRow(&pmID) if err != nil { return 0, err } return pmID, err } // GetProjectByID ... func GetProjectByID(id int64) (*models.Project, error) { o := GetOrmer() sql := `select p.project_id, p.name, u.username as owner_name, p.owner_id, p.creation_time, p.update_time from project p left join harbor_user u on p.owner_id = u.user_id where p.deleted = false and p.project_id = ?` queryParam := make([]interface{}, 1) queryParam = append(queryParam, id) p := []models.Project{} count, err := o.Raw(sql, queryParam).QueryRows(&p) if err != nil { return nil, err } if count == 0 { return nil, nil } return &p[0], nil } // GetProjectByName ... func GetProjectByName(name string) (*models.Project, error) { o := GetOrmer() var p []models.Project n, err := o.Raw(`select * from project where name = ? and deleted = false`, name).QueryRows(&p) if err != nil { return nil, err } if n == 0 { return nil, nil } return &p[0], nil } // GetTotalOfProjects returns the total count of projects // according to the query conditions func GetTotalOfProjects(query *models.ProjectQueryParam) (int64, error) { var pagination *models.Pagination if query != nil { pagination = query.Pagination query.Pagination = nil } sql, params := projectQueryConditions(query) if query != nil { query.Pagination = pagination } sql = `select count(*) ` + sql var total int64 err := GetOrmer().Raw(sql, params).QueryRow(&total) return total, err } // GetProjects returns a project list according to the query conditions func GetProjects(query *models.ProjectQueryParam) ([]*models.Project, error) { sqlStr, queryParam := projectQueryConditions(query) sqlStr = `select distinct p.project_id, p.name, p.owner_id, p.creation_time, p.update_time ` + sqlStr + ` order by p.name` sqlStr, queryParam = CreatePagination(query, sqlStr, queryParam) log.Debugf("sql:=%+v, param= %+v", sqlStr, queryParam) var projects []*models.Project _, err := GetOrmer().Raw(sqlStr, queryParam).QueryRows(&projects) return projects, err } // GetGroupProjects - Get user's all projects, including user is the user member of this project // and the user is in the group which is a group member of this project. func GetGroupProjects(groupDNCondition string, query *models.ProjectQueryParam) ([]*models.Project, error) { sql, params := projectQueryConditions(query) sql = `select distinct p.project_id, p.name, p.owner_id, p.creation_time, p.update_time ` + sql if len(groupDNCondition) > 0 { sql = fmt.Sprintf( `%s union select distinct p.project_id, p.name, p.owner_id, p.creation_time, p.update_time from project p left join project_member pm on p.project_id = pm.project_id left join user_group ug on ug.id = pm.entity_id and pm.entity_type = 'g' and ug.group_type = 1 where ug.ldap_group_dn in ( %s ) order by name`, sql, groupDNCondition) } sqlStr, queryParams := CreatePagination(query, sql, params) log.Debugf("query sql:%v", sql) var projects []*models.Project _, err := GetOrmer().Raw(sqlStr, queryParams).QueryRows(&projects) return projects, err } // GetTotalGroupProjects - Get the total count of projects, including user is the member of this project and the // user is in the group, which is the group member of this project. func GetTotalGroupProjects(groupDNCondition string, query *models.ProjectQueryParam) (int, error) { var sql string sqlCondition, params := projectQueryConditions(query) if len(groupDNCondition) == 0 { sql = `select count(1) ` + sqlCondition } else { sql = fmt.Sprintf( `select count(1) from ( select p.project_id %s union select p.project_id from project p left join project_member pm on p.project_id = pm.project_id left join user_group ug on ug.id = pm.entity_id and pm.entity_type = 'g' and ug.group_type = 1 where ug.ldap_group_dn in ( %s )) t`, sqlCondition, groupDNCondition) } log.Debugf("query sql:%v", sql) var count int if err := GetOrmer().Raw(sql, params).QueryRow(&count); err != nil { return 0, err } return count, nil } func projectQueryConditions(query *models.ProjectQueryParam) (string, []interface{}) { params := []interface{}{} sql := ` from project as p` if query == nil { sql += ` where p.deleted=false` return sql, params } // if query.ProjectIDs is not nil but has no element, the query will returns no rows if query.ProjectIDs != nil && len(query.ProjectIDs) == 0 { sql += ` where 1 = 0` return sql, params } if len(query.Owner) != 0 { sql += ` join harbor_user u1 on p.owner_id = u1.user_id` } if query.Member != nil && len(query.Member.Name) != 0 { sql += ` join project_member pm on p.project_id = pm.project_id and pm.entity_type = 'u' join harbor_user u2 on pm.entity_id=u2.user_id` } sql += ` where p.deleted=false` if len(query.Owner) != 0 { sql += ` and u1.username=?` params = append(params, query.Owner) } if len(query.Name) != 0 { sql += ` and p.name like ?` params = append(params, "%"+Escape(query.Name)+"%") } if query.Member != nil && len(query.Member.Name) != 0 { sql += ` and u2.username=?` params = append(params, query.Member.Name) if query.Member.Role > 0 { sql += ` and pm.role = ?` roleID := 0 switch query.Member.Role { case common.RoleProjectAdmin: roleID = 1 case common.RoleDeveloper: roleID = 2 case common.RoleGuest: roleID = 3 } params = append(params, roleID) } } if len(query.ProjectIDs) > 0 { sql += fmt.Sprintf(` and p.project_id in ( %s )`, paramPlaceholder(len(query.ProjectIDs))) params = append(params, query.ProjectIDs) } return sql, params } // CreatePagination ... func CreatePagination(query *models.ProjectQueryParam, sql string, params []interface{}) (string, []interface{}) { if query != nil && query.Pagination != nil && query.Pagination.Size > 0 { sql += ` limit ?` params = append(params, query.Pagination.Size) if query.Pagination.Page > 0 { sql += ` offset ?` params = append(params, (query.Pagination.Page-1)*query.Pagination.Size) } } return sql, params } // DeleteProject ... func DeleteProject(id int64) error { project, err := GetProjectByID(id) if err != nil { return err } name := fmt.Sprintf("%s#%d", project.Name, project.ProjectID) sql := `update project set deleted = true, name = ? where project_id = ?` _, err = GetOrmer().Raw(sql, name, id).Exec() return err } // GetRolesByLDAPGroup - Get Project roles of the // specified group DN is a member of current project func GetRolesByLDAPGroup(projectID int64, groupDNCondition string) ([]int, error) { var roles []int if len(groupDNCondition) == 0 { return roles, nil } o := GetOrmer() //Because an LDAP user can be memberof multiple groups, //the role is in descent order (1-admin, 2-developer, 3-guest), use min to select the max privilege role. sql := fmt.Sprintf( `select min(pm.role) from project_member pm left join user_group ug on pm.entity_type = 'g' and pm.entity_id = ug.id where ug.ldap_group_dn in ( %s ) and pm.project_id = ? `, groupDNCondition) log.Debugf("sql:%v", sql) if _, err := o.Raw(sql, projectID).QueryRows(&roles); err != nil { log.Warningf("Error in GetRolesByLDAPGroup, error: %v", err) return nil, err } //If there is no row selected, the min returns an empty row, to avoid return 0 as role if len(roles) == 1 && roles[0] == 0 { return []int{}, nil } return roles, nil }