mirror of
https://github.com/goharbor/harbor.git
synced 2025-02-02 04:51:22 +01:00
Apply project level policies to standalone Harbor
The following features are only enabled in integration mode, this commit moves these to standalone Harbor: - Content trust policy: only signed images can be pulled - Vulnerability policy: only images whose severity is below the threshold can be pulled - Automatic scan policy: automatic scan pushed images
This commit is contained in:
parent
a1b8969793
commit
66b2d0d3f3
@ -167,6 +167,38 @@ paths:
|
||||
description: User need to log in first.
|
||||
'500':
|
||||
description: Internal errors.
|
||||
put:
|
||||
summary: Update properties for a selected project.
|
||||
description: |
|
||||
This endpoint is aimed to update the properties of a project.
|
||||
parameters:
|
||||
- name: project_id
|
||||
in: path
|
||||
type: integer
|
||||
format: int64
|
||||
required: true
|
||||
description: Selected project ID.
|
||||
- name: project
|
||||
in: body
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/Project'
|
||||
description: Updates of project.
|
||||
tags:
|
||||
- Products
|
||||
responses:
|
||||
'200':
|
||||
description: Updated project properties successfully.
|
||||
'400':
|
||||
description: Illegal format of provided ID value.
|
||||
'401':
|
||||
description: User need to log in first.
|
||||
'403':
|
||||
description: User does not have permission to the project.
|
||||
'404':
|
||||
description: Project ID does not exist.
|
||||
'500':
|
||||
description: Unexpected internal errors.
|
||||
delete:
|
||||
summary: Delete project by projectID
|
||||
description: |
|
||||
@ -193,39 +225,6 @@ paths:
|
||||
description: 'Project contains policies, can not be deleted.'
|
||||
'500':
|
||||
description: Internal errors.
|
||||
'/projects/{project_id}/publicity':
|
||||
put:
|
||||
summary: Update properties for a selected project.
|
||||
description: |
|
||||
This endpoint is aimed to toggle a project publicity status.
|
||||
parameters:
|
||||
- name: project_id
|
||||
in: path
|
||||
type: integer
|
||||
format: int64
|
||||
required: true
|
||||
description: Selected project ID.
|
||||
- name: project
|
||||
in: body
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/Project'
|
||||
description: Updates of project.
|
||||
tags:
|
||||
- Products
|
||||
responses:
|
||||
'200':
|
||||
description: Updated project publicity status successfully.
|
||||
'400':
|
||||
description: Illegal format of provided ID value.
|
||||
'401':
|
||||
description: User need to log in first.
|
||||
'403':
|
||||
description: User does not have permission to the project.
|
||||
'404':
|
||||
description: Project ID does not exist.
|
||||
'500':
|
||||
description: Unexpected internal errors.
|
||||
'/projects/{project_id}/logs':
|
||||
get:
|
||||
summary: Get access logs accompany with a relevant project.
|
||||
@ -2016,10 +2015,6 @@ definitions:
|
||||
owner_name:
|
||||
type: string
|
||||
description: The owner name of the project.
|
||||
public:
|
||||
type: integer
|
||||
format: int
|
||||
description: The public status of the project.
|
||||
Togglable:
|
||||
type: boolean
|
||||
description: >-
|
||||
@ -2031,6 +2026,18 @@ definitions:
|
||||
repo_count:
|
||||
type: integer
|
||||
description: The number of the repositories under this project.
|
||||
metadata:
|
||||
type: object
|
||||
description: The metadata of the project.
|
||||
items:
|
||||
$ref: '#/definitions/ProjectMetadata'
|
||||
ProjectMetadata:
|
||||
type: object
|
||||
properties:
|
||||
public:
|
||||
type: integer
|
||||
format: int
|
||||
description: The public status of the project.
|
||||
enable_content_trust:
|
||||
type: boolean
|
||||
description: >-
|
||||
|
@ -73,14 +73,13 @@ create table project (
|
||||
creation_time timestamp,
|
||||
update_time timestamp,
|
||||
deleted tinyint (1) DEFAULT 0 NOT NULL,
|
||||
public tinyint (1) DEFAULT 0 NOT NULL,
|
||||
primary key (project_id),
|
||||
FOREIGN KEY (owner_id) REFERENCES user(user_id),
|
||||
UNIQUE (name)
|
||||
);
|
||||
|
||||
insert into project (owner_id, name, creation_time, update_time, public) values
|
||||
(1, 'library', NOW(), NOW(), 1);
|
||||
insert into project (owner_id, name, creation_time, update_time) values
|
||||
(1, 'library', NOW(), NOW());
|
||||
|
||||
create table project_member (
|
||||
project_id int NOT NULL,
|
||||
|
@ -71,13 +71,12 @@ create table project (
|
||||
creation_time timestamp,
|
||||
update_time timestamp,
|
||||
deleted tinyint (1) DEFAULT 0 NOT NULL,
|
||||
public tinyint (1) DEFAULT 0 NOT NULL,
|
||||
FOREIGN KEY (owner_id) REFERENCES user(user_id),
|
||||
UNIQUE (name)
|
||||
);
|
||||
|
||||
insert into project (owner_id, name, creation_time, update_time, public) values
|
||||
(1, 'library', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 1);
|
||||
insert into project (owner_id, name, creation_time, update_time) values
|
||||
(1, 'library', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP);
|
||||
|
||||
create table project_member (
|
||||
project_id int NOT NULL,
|
||||
|
@ -420,19 +420,6 @@ func TestChangeUserPasswordWithIncorrectOldPassword(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestQueryRelevantProjectsWhenNoProjectAdded(t *testing.T) {
|
||||
projects, err := GetHasReadPermProjects(currentUser.Username)
|
||||
if err != nil {
|
||||
t.Errorf("Error occurred in QueryRelevantProjects: %v", err)
|
||||
}
|
||||
if len(projects) != 1 {
|
||||
t.Errorf("Expected only one project in DB, but actual: %d", len(projects))
|
||||
}
|
||||
if projects[0].Name != "library" {
|
||||
t.Errorf("There name of the project does not match, expected: %s, actual: %s", "library", projects[0].Name)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddProject(t *testing.T) {
|
||||
|
||||
project := models.Project{
|
||||
@ -657,43 +644,6 @@ func TestGetUserByProject(t *testing.T) {
|
||||
|
||||
}
|
||||
|
||||
func TestToggleProjectPublicity(t *testing.T) {
|
||||
err := ToggleProjectPublicity(currentProject.ProjectID, publicityOn)
|
||||
if err != nil {
|
||||
t.Errorf("Error occurred in ToggleProjectPublicity: %v", err)
|
||||
}
|
||||
|
||||
currentProject, err = GetProjectByName(projectName)
|
||||
if err != nil {
|
||||
t.Errorf("Error occurred in GetProjectByName: %v", err)
|
||||
}
|
||||
if currentProject.Public != publicityOn {
|
||||
t.Errorf("project, id: %d, its publicity is not on", currentProject.ProjectID)
|
||||
}
|
||||
err = ToggleProjectPublicity(currentProject.ProjectID, publicityOff)
|
||||
if err != nil {
|
||||
t.Errorf("Error occurred in ToggleProjectPublicity: %v", err)
|
||||
}
|
||||
|
||||
currentProject, err = GetProjectByName(projectName)
|
||||
if err != nil {
|
||||
t.Errorf("Error occurred in GetProjectByName: %v", err)
|
||||
}
|
||||
|
||||
if currentProject.Public != publicityOff {
|
||||
t.Errorf("project, id: %d, its publicity is not off", currentProject.ProjectID)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
func TestIsProjectPublic(t *testing.T) {
|
||||
|
||||
if isPublic := IsProjectPublic(projectName); isPublic {
|
||||
t.Errorf("project, id: %d, its publicity is not false after turning off", currentProject.ProjectID)
|
||||
}
|
||||
}
|
||||
*/
|
||||
func TestGetUserProjectRoles(t *testing.T) {
|
||||
r, err := GetUserProjectRoles(currentUser.UserID, currentProject.ProjectID)
|
||||
if err != nil {
|
||||
@ -710,17 +660,6 @@ func TestGetUserProjectRoles(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
func TestProjectPermission(t *testing.T) {
|
||||
roleCode, err := GetPermission(currentUser.Username, currentProject.Name)
|
||||
if err != nil {
|
||||
t.Errorf("Error occurred in GetPermission: %v", err)
|
||||
}
|
||||
if roleCode != "MDRWS" {
|
||||
t.Errorf("The expected role code is MDRWS,but actual: %s", roleCode)
|
||||
}
|
||||
}
|
||||
*/
|
||||
func TestGetTotalOfProjects(t *testing.T) {
|
||||
total, err := GetTotalOfProjects(nil)
|
||||
if err != nil {
|
||||
@ -745,22 +684,6 @@ func TestGetProjects(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetPublicProjects(t *testing.T) {
|
||||
value := true
|
||||
projects, err := GetProjects(&models.ProjectQueryParam{
|
||||
Public: &value,
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("Error occurred in getProjects: %v", err)
|
||||
}
|
||||
if len(projects) != 1 {
|
||||
t.Errorf("Expected length of projects is 1, but actual: %d, the projects: %+v", len(projects), projects)
|
||||
}
|
||||
if projects[0].Name != "library" {
|
||||
t.Errorf("Expected project name in the list: %s, actual: %s", "library", projects[0].Name)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddProjectMember(t *testing.T) {
|
||||
err := AddProjectMember(currentProject.ProjectID, 1, models.DEVELOPER)
|
||||
if err != nil {
|
||||
|
@ -91,3 +91,12 @@ func paramPlaceholder(n int) string {
|
||||
}
|
||||
return strings.Join(placeholders, ",")
|
||||
}
|
||||
|
||||
// ListProjectMetadata ...
|
||||
func ListProjectMetadata(name, value string) ([]*models.ProjectMetadata, error) {
|
||||
sql := `select * from project_metadata
|
||||
where name = ? and value = ? and deleted = 0`
|
||||
metadatas := []*models.ProjectMetadata{}
|
||||
_, err := GetOrmer().Raw(sql, name, value).QueryRows(&metadatas)
|
||||
return metadatas, err
|
||||
}
|
||||
|
@ -64,6 +64,12 @@ func TestProMetaDaoMethods(t *testing.T) {
|
||||
assert.Equal(t, value1, m[name1].Value)
|
||||
assert.Equal(t, value2, m[name2].Value)
|
||||
|
||||
// test list
|
||||
metas, err = ListProjectMetadata(name1, value1)
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, 1, len(metas))
|
||||
assert.Equal(t, int64(1), metas[0].ProjectID)
|
||||
|
||||
// test update
|
||||
newValue1 := "new_value1"
|
||||
meta1.Value = newValue1
|
||||
|
@ -30,13 +30,13 @@ import (
|
||||
func AddProject(project models.Project) (int64, error) {
|
||||
|
||||
o := GetOrmer()
|
||||
p, err := o.Raw("insert into project (owner_id, name, creation_time, update_time, deleted, public) values (?, ?, ?, ?, ?, ?)").Prepare()
|
||||
p, err := o.Raw("insert into project (owner_id, name, creation_time, update_time, deleted) values (?, ?, ?, ?, ?)").Prepare()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
r, err := p.Exec(project.OwnerID, project.Name, now, now, project.Deleted, project.Public)
|
||||
r, err := p.Exec(project.OwnerID, project.Name, now, now, project.Deleted)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@ -50,49 +50,11 @@ func AddProject(project models.Project) (int64, error) {
|
||||
return projectID, err
|
||||
}
|
||||
|
||||
/*
|
||||
// IsProjectPublic ...
|
||||
func IsProjectPublic(projectName string) bool {
|
||||
project, err := GetProjectByName(projectName)
|
||||
if err != nil {
|
||||
log.Errorf("Error occurred in GetProjectByName: %v", err)
|
||||
return false
|
||||
}
|
||||
if project == nil {
|
||||
return false
|
||||
}
|
||||
return project.Public == 1
|
||||
}
|
||||
|
||||
//ProjectExists returns whether the project exists according to its name of ID.
|
||||
func ProjectExists(nameOrID interface{}) (bool, error) {
|
||||
o := GetOrmer()
|
||||
type dummy struct{}
|
||||
sql := `select project_id from project where deleted = 0 and `
|
||||
switch nameOrID.(type) {
|
||||
case int64:
|
||||
sql += `project_id = ?`
|
||||
case string:
|
||||
sql += `name = ?`
|
||||
default:
|
||||
return false, fmt.Errorf("Invalid nameOrId: %v", nameOrID)
|
||||
}
|
||||
|
||||
var d []dummy
|
||||
num, err := o.Raw(sql, nameOrID).QueryRows(&d)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return num > 0, nil
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
// 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, p.public
|
||||
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 user u on p.owner_id = u.user_id where p.deleted = 0 and p.project_id = ?`
|
||||
queryParam := make([]interface{}, 1)
|
||||
queryParam = append(queryParam, id)
|
||||
@ -127,94 +89,18 @@ func GetProjectByName(name string) (*models.Project, error) {
|
||||
return &p[0], nil
|
||||
}
|
||||
|
||||
/*
|
||||
// GetPermission gets roles that the user has according to the project.
|
||||
func GetPermission(username, projectName string) (string, error) {
|
||||
o := GetOrmer()
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
if n == 0 {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
return r[0].RoleCode, nil
|
||||
}
|
||||
*/
|
||||
|
||||
// ToggleProjectPublicity toggles the publicity of the project.
|
||||
func ToggleProjectPublicity(projectID int64, publicity int) error {
|
||||
o := GetOrmer()
|
||||
sql := "update project set public = ? where project_id = ?"
|
||||
_, err := o.Raw(sql, publicity, projectID).Exec()
|
||||
return err
|
||||
}
|
||||
|
||||
// GetHasReadPermProjects returns a project list,
|
||||
// which satisfies the following conditions:
|
||||
// 1. the project is not deleted
|
||||
// 2. the prject is public or the user is a member of the project
|
||||
func GetHasReadPermProjects(username string) ([]*models.Project, error) {
|
||||
user, err := GetUser(models.User{
|
||||
Username: username,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
o := GetOrmer()
|
||||
|
||||
sql :=
|
||||
`select distinct p.project_id, p.name, p.public,
|
||||
p.owner_id, p.creation_time, p.update_time
|
||||
from project p
|
||||
left join project_member pm
|
||||
on p.project_id = pm.project_id
|
||||
where (pm.user_id = ? or p.public = 1)
|
||||
and p.deleted = 0 `
|
||||
|
||||
var projects []*models.Project
|
||||
|
||||
if _, err := o.Raw(sql, user.UserID).QueryRows(&projects); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return projects, nil
|
||||
}
|
||||
|
||||
// GetTotalOfProjects returns the total count of projects
|
||||
// according to the query conditions
|
||||
func GetTotalOfProjects(query *models.ProjectQueryParam, base ...*models.BaseProjectCollection) (int64, error) {
|
||||
|
||||
var (
|
||||
owner string
|
||||
name string
|
||||
public *bool
|
||||
member string
|
||||
role int
|
||||
)
|
||||
|
||||
func GetTotalOfProjects(query *models.ProjectQueryParam) (int64, error) {
|
||||
var pagination *models.Pagination
|
||||
if query != nil {
|
||||
owner = query.Owner
|
||||
name = query.Name
|
||||
public = query.Public
|
||||
if query.Member != nil {
|
||||
member = query.Member.Name
|
||||
role = query.Member.Role
|
||||
}
|
||||
pagination = query.Pagination
|
||||
query.Pagination = nil
|
||||
}
|
||||
sql, params := projectQueryConditions(query)
|
||||
if query != nil {
|
||||
query.Pagination = pagination
|
||||
}
|
||||
|
||||
sql, params := projectQueryConditions(owner, name, public, member, role, base...)
|
||||
|
||||
sql = `select count(*) ` + sql
|
||||
|
||||
@ -224,86 +110,39 @@ func GetTotalOfProjects(query *models.ProjectQueryParam, base ...*models.BasePro
|
||||
}
|
||||
|
||||
// GetProjects returns a project list according to the query conditions
|
||||
func GetProjects(query *models.ProjectQueryParam, base ...*models.BaseProjectCollection) ([]*models.Project, error) {
|
||||
func GetProjects(query *models.ProjectQueryParam) ([]*models.Project, error) {
|
||||
sql, params := projectQueryConditions(query)
|
||||
|
||||
var (
|
||||
owner string
|
||||
name string
|
||||
public *bool
|
||||
member string
|
||||
role int
|
||||
page int64
|
||||
size int64
|
||||
)
|
||||
|
||||
if query != nil {
|
||||
owner = query.Owner
|
||||
name = query.Name
|
||||
public = query.Public
|
||||
if query.Member != nil {
|
||||
member = query.Member.Name
|
||||
role = query.Member.Role
|
||||
}
|
||||
if query.Pagination != nil {
|
||||
page = query.Pagination.Page
|
||||
size = query.Pagination.Size
|
||||
}
|
||||
}
|
||||
|
||||
sql, params := projectQueryConditions(owner, name, public, member, role, base...)
|
||||
|
||||
sql = `select distinct p.project_id, p.name, p.public, p.owner_id,
|
||||
sql = `select distinct p.project_id, p.name, p.owner_id,
|
||||
p.creation_time, p.update_time ` + sql
|
||||
if size > 0 {
|
||||
sql += ` limit ?`
|
||||
params = append(params, size)
|
||||
|
||||
if page > 0 {
|
||||
sql += ` offset ?`
|
||||
params = append(params, (page-1)*size)
|
||||
}
|
||||
}
|
||||
|
||||
var projects []*models.Project
|
||||
_, err := GetOrmer().Raw(sql, params).QueryRows(&projects)
|
||||
return projects, err
|
||||
}
|
||||
|
||||
func projectQueryConditions(owner, name string, public *bool, member string,
|
||||
role int, base ...*models.BaseProjectCollection) (string, []interface{}) {
|
||||
func projectQueryConditions(query *models.ProjectQueryParam) (string, []interface{}) {
|
||||
params := []interface{}{}
|
||||
|
||||
// the base project collections:
|
||||
// 1. all projects
|
||||
// 2. public projects
|
||||
// 3. public projects and projects which the user is a member of
|
||||
collection := `project `
|
||||
if len(base) != 0 && base[0] != nil {
|
||||
if len(base[0].Member) == 0 && base[0].Public {
|
||||
collection = `(select * from project pr
|
||||
where pr.public=1) `
|
||||
}
|
||||
if len(base[0].Member) > 0 && base[0].Public {
|
||||
collection = `(select pr.project_id, pr.owner_id, pr.name, pr.
|
||||
creation_time, pr.update_time, pr.deleted, pr.public
|
||||
from project pr
|
||||
join project_member prm
|
||||
on pr.project_id = prm.project_id
|
||||
join user ur
|
||||
on prm.user_id=ur.user_id
|
||||
where ur.username=? or pr.public=1 )`
|
||||
params = append(params, base[0].Member)
|
||||
}
|
||||
sql := ` from project as p`
|
||||
|
||||
if query == nil {
|
||||
sql += ` where p.deleted=0 order by p.name`
|
||||
return sql, params
|
||||
}
|
||||
|
||||
sql := ` from ` + collection + ` as p`
|
||||
// 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(owner) != 0 {
|
||||
if len(query.Owner) != 0 {
|
||||
sql += ` join user u1
|
||||
on p.owner_id = u1.user_id`
|
||||
}
|
||||
|
||||
if len(member) != 0 {
|
||||
if query.Member != nil && len(query.Member.Name) != 0 {
|
||||
sql += ` join project_member pm
|
||||
on p.project_id = pm.project_id
|
||||
join user u2
|
||||
@ -311,33 +150,24 @@ func projectQueryConditions(owner, name string, public *bool, member string,
|
||||
}
|
||||
sql += ` where p.deleted=0`
|
||||
|
||||
if len(owner) != 0 {
|
||||
if len(query.Owner) != 0 {
|
||||
sql += ` and u1.username=?`
|
||||
params = append(params, owner)
|
||||
params = append(params, query.Owner)
|
||||
}
|
||||
|
||||
if len(name) != 0 {
|
||||
if len(query.Name) != 0 {
|
||||
sql += ` and p.name like ?`
|
||||
params = append(params, "%"+escape(name)+"%")
|
||||
params = append(params, "%"+escape(query.Name)+"%")
|
||||
}
|
||||
|
||||
if public != nil {
|
||||
sql += ` and p.public = ?`
|
||||
if *public {
|
||||
params = append(params, 1)
|
||||
} else {
|
||||
params = append(params, 0)
|
||||
}
|
||||
}
|
||||
|
||||
if len(member) != 0 {
|
||||
if query.Member != nil && len(query.Member.Name) != 0 {
|
||||
sql += ` and u2.username=?`
|
||||
params = append(params, member)
|
||||
params = append(params, query.Member.Name)
|
||||
|
||||
if role > 0 {
|
||||
if query.Member.Role > 0 {
|
||||
sql += ` and pm.role = ?`
|
||||
roleID := 0
|
||||
switch role {
|
||||
switch query.Member.Role {
|
||||
case common.RoleProjectAdmin:
|
||||
roleID = 1
|
||||
case common.RoleDeveloper:
|
||||
@ -350,8 +180,24 @@ func projectQueryConditions(owner, name string, public *bool, member string,
|
||||
}
|
||||
}
|
||||
|
||||
if len(query.ProjectIDs) > 0 {
|
||||
sql += fmt.Sprintf(` and p.project_id in ( %s )`,
|
||||
paramPlaceholder(len(query.ProjectIDs)))
|
||||
params = append(params, query.ProjectIDs)
|
||||
}
|
||||
|
||||
sql += ` order by p.name`
|
||||
|
||||
if 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
|
||||
}
|
||||
|
||||
|
@ -103,7 +103,6 @@ func TestGetTopRepos(t *testing.T) {
|
||||
project1 := models.Project{
|
||||
OwnerID: 1,
|
||||
Name: "project1",
|
||||
Public: 0,
|
||||
}
|
||||
project1.ProjectID, err = AddProject(project1)
|
||||
require.NoError(err)
|
||||
@ -112,7 +111,6 @@ func TestGetTopRepos(t *testing.T) {
|
||||
project2 := models.Project{
|
||||
OwnerID: 1,
|
||||
Name: "project2",
|
||||
Public: 0,
|
||||
}
|
||||
project2.ProjectID, err = AddProject(project2)
|
||||
require.NoError(err)
|
||||
@ -232,7 +230,6 @@ func TestGetAllRepositories(t *testing.T) {
|
||||
project1 := models.Project{
|
||||
OwnerID: 1,
|
||||
Name: "projectRepo",
|
||||
Public: 0,
|
||||
}
|
||||
var err2 error
|
||||
project1.ProjectID, err2 = AddProject(project1)
|
||||
|
@ -18,13 +18,17 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// keys of project metadata
|
||||
// keys of project metadata and severity values
|
||||
const (
|
||||
ProMetaPublic = "public"
|
||||
ProMetaEnableContentTrust = "enable_content_trust"
|
||||
ProMetaPreventVul = "prevent_vul"
|
||||
ProMetaPreventVul = "prevent_vul" //prevent vulnerable images from being pulled
|
||||
ProMetaSeverity = "severity"
|
||||
ProMetaAutoScan = "auto_scan"
|
||||
SeverityNone = "negligible"
|
||||
SeverityLow = "low"
|
||||
SeverityMedium = "medium"
|
||||
SeverityHigh = "high"
|
||||
)
|
||||
|
||||
// ProjectMetadata holds the metadata of a project.
|
||||
|
@ -15,31 +15,91 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Project holds the details of a project.
|
||||
// TODO remove useless attrs
|
||||
type Project struct {
|
||||
ProjectID int64 `orm:"pk;auto;column(project_id)" json:"project_id"`
|
||||
OwnerID int `orm:"column(owner_id)" json:"owner_id"`
|
||||
Name string `orm:"column(name)" json:"name"`
|
||||
CreationTime time.Time `orm:"column(creation_time)" json:"creation_time"`
|
||||
UpdateTime time.Time `orm:"update_time" json:"update_time"`
|
||||
Deleted int `orm:"column(deleted)" json:"deleted"`
|
||||
CreationTimeStr string `orm:"-" json:"creation_time_str"`
|
||||
OwnerName string `orm:"-" json:"owner_name"`
|
||||
Togglable bool `orm:"-"`
|
||||
Role int `orm:"-" json:"current_user_role_id"`
|
||||
RepoCount int `orm:"-" json:"repo_count"`
|
||||
Metadata map[string]string `orm:"-" json:"metadata"`
|
||||
ProjectID int64 `orm:"pk;auto;column(project_id)" json:"project_id"`
|
||||
OwnerID int `orm:"column(owner_id)" json:"owner_id"`
|
||||
Name string `orm:"column(name)" json:"name"`
|
||||
CreationTime time.Time `orm:"column(creation_time)" json:"creation_time"`
|
||||
UpdateTime time.Time `orm:"update_time" json:"update_time"`
|
||||
Deleted int `orm:"column(deleted)" json:"deleted"`
|
||||
OwnerName string `orm:"-" json:"owner_name"`
|
||||
Togglable bool `orm:"-" json:"togglable"`
|
||||
Role int `orm:"-" json:"current_user_role_id"`
|
||||
RepoCount int `orm:"-" json:"repo_count"`
|
||||
Metadata map[string]string `orm:"-" json:"metadata"`
|
||||
}
|
||||
|
||||
// TODO remove
|
||||
Public int `orm:"column(public)" json:"public"`
|
||||
EnableContentTrust bool `orm:"-" json:"enable_content_trust"`
|
||||
PreventVulnerableImagesFromRunning bool `orm:"-" json:"prevent_vulnerable_images_from_running"`
|
||||
PreventVulnerableImagesFromRunningSeverity string `orm:"-" json:"prevent_vulnerable_images_from_running_severity"`
|
||||
AutomaticallyScanImagesOnPush bool `orm:"-" json:"automatically_scan_images_on_push"`
|
||||
// GetMetadata ...
|
||||
func (p *Project) GetMetadata(key string) (string, bool) {
|
||||
if len(p.Metadata) == 0 {
|
||||
return "", false
|
||||
}
|
||||
value, exist := p.Metadata[key]
|
||||
return value, exist
|
||||
}
|
||||
|
||||
// SetMetadata ...
|
||||
func (p *Project) SetMetadata(key, value string) {
|
||||
if p.Metadata == nil {
|
||||
p.Metadata = map[string]string{}
|
||||
}
|
||||
p.Metadata[key] = value
|
||||
}
|
||||
|
||||
// IsPublic ...
|
||||
func (p *Project) IsPublic() bool {
|
||||
public, exist := p.GetMetadata(ProMetaPublic)
|
||||
if !exist {
|
||||
return false
|
||||
}
|
||||
|
||||
return isTrue(public)
|
||||
}
|
||||
|
||||
// ContentTrustEnabled ...
|
||||
func (p *Project) ContentTrustEnabled() bool {
|
||||
enabled, exist := p.GetMetadata(ProMetaEnableContentTrust)
|
||||
if !exist {
|
||||
return false
|
||||
}
|
||||
return isTrue(enabled)
|
||||
}
|
||||
|
||||
// VulPrevented ...
|
||||
func (p *Project) VulPrevented() bool {
|
||||
prevent, exist := p.GetMetadata(ProMetaPreventVul)
|
||||
if !exist {
|
||||
return false
|
||||
}
|
||||
return isTrue(prevent)
|
||||
}
|
||||
|
||||
// Severity ...
|
||||
func (p *Project) Severity() string {
|
||||
severity, exist := p.GetMetadata(ProMetaSeverity)
|
||||
if !exist {
|
||||
return ""
|
||||
}
|
||||
return severity
|
||||
}
|
||||
|
||||
// AutoScan ...
|
||||
func (p *Project) AutoScan() bool {
|
||||
auto, exist := p.GetMetadata(ProMetaAutoScan)
|
||||
if !exist {
|
||||
return false
|
||||
}
|
||||
return isTrue(auto)
|
||||
}
|
||||
|
||||
func isTrue(value string) bool {
|
||||
return strings.ToLower(value) == "true" ||
|
||||
strings.ToLower(value) == "1"
|
||||
}
|
||||
|
||||
// ProjectSorter holds an array of projects
|
||||
@ -79,6 +139,7 @@ type ProjectQueryParam struct {
|
||||
Public *bool // the project is public or not, can be ture, false and nil
|
||||
Member *MemberQuery // the member of project
|
||||
Pagination *Pagination // pagination information
|
||||
ProjectIDs []int64 // project ID list
|
||||
}
|
||||
|
||||
// MemberQuery fitler by member's username and role
|
||||
@ -103,12 +164,9 @@ type BaseProjectCollection struct {
|
||||
|
||||
// ProjectRequest holds informations that need for creating project API
|
||||
type ProjectRequest struct {
|
||||
Name string `json:"project_name"`
|
||||
Public int `json:"public"`
|
||||
EnableContentTrust bool `json:"enable_content_trust"`
|
||||
PreventVulnerableImagesFromRunning bool `json:"prevent_vulnerable_images_from_running"`
|
||||
PreventVulnerableImagesFromRunningSeverity string `json:"prevent_vulnerable_images_from_running_severity"`
|
||||
AutomaticallyScanImagesOnPush bool `json:"automatically_scan_images_on_push"`
|
||||
Name string `json:"project_name"`
|
||||
Public *int `json:"public"` //deprecated, reserved for project creation in replication
|
||||
Metadata map[string]string `json:"metadata"`
|
||||
}
|
||||
|
||||
// ProjectQueryResult ...
|
||||
|
@ -30,13 +30,13 @@ import (
|
||||
func ParseClairSev(clairSev string) models.Severity {
|
||||
sev := strings.ToLower(clairSev)
|
||||
switch sev {
|
||||
case "negligible":
|
||||
case models.SeverityNone:
|
||||
return models.SevNone
|
||||
case "low":
|
||||
case models.SeverityLow:
|
||||
return models.SevLow
|
||||
case "medium":
|
||||
case models.SeverityMedium:
|
||||
return models.SevMedium
|
||||
case "high":
|
||||
case models.SeverityHigh:
|
||||
return models.SevHigh
|
||||
default:
|
||||
return models.SevUnknown
|
||||
|
@ -22,6 +22,7 @@ import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/distribution"
|
||||
@ -258,13 +259,23 @@ func getProject(name string) (*models.Project, error) {
|
||||
}
|
||||
|
||||
func (c *Checker) createProject(project *models.Project) error {
|
||||
pro := &models.ProjectRequest{
|
||||
Name: project.Name,
|
||||
Public: project.Public,
|
||||
EnableContentTrust: project.EnableContentTrust,
|
||||
PreventVulnerableImagesFromRunning: project.PreventVulnerableImagesFromRunning,
|
||||
PreventVulnerableImagesFromRunningSeverity: project.PreventVulnerableImagesFromRunningSeverity,
|
||||
AutomaticallyScanImagesOnPush: project.AutomaticallyScanImagesOnPush,
|
||||
// only replicate the public property of project
|
||||
pro := struct {
|
||||
models.ProjectRequest
|
||||
Public int `json:"public"`
|
||||
}{
|
||||
ProjectRequest: models.ProjectRequest{
|
||||
Name: project.Name,
|
||||
Metadata: map[string]string{
|
||||
models.ProMetaPublic: strconv.FormatBool(project.IsPublic()),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// put "public" property in both metadata and public field to keep compatibility
|
||||
// with old version API(<=1.2.0)
|
||||
if project.IsPublic() {
|
||||
pro.Public = 1
|
||||
}
|
||||
|
||||
data, err := json.Marshal(pro)
|
||||
|
@ -93,12 +93,11 @@ func init() {
|
||||
|
||||
beego.Router("/api/search/", &SearchAPI{})
|
||||
beego.Router("/api/projects/", &ProjectAPI{}, "get:List;post:Post;head:Head")
|
||||
beego.Router("/api/projects/:id", &ProjectAPI{}, "delete:Delete;get:Get")
|
||||
beego.Router("/api/projects/:id", &ProjectAPI{}, "delete:Delete;get:Get;put:Put")
|
||||
beego.Router("/api/users/:id", &UserAPI{}, "get:Get")
|
||||
beego.Router("/api/users", &UserAPI{}, "get:List;post:Post;delete:Delete;put:Put")
|
||||
beego.Router("/api/users/:id([0-9]+)/password", &UserAPI{}, "put:ChangePassword")
|
||||
beego.Router("/api/users/:id/sysadmin", &UserAPI{}, "put:ToggleUserAdminRole")
|
||||
beego.Router("/api/projects/:id/publicity", &ProjectAPI{}, "put:ToggleProjectPublic")
|
||||
beego.Router("/api/projects/:id([0-9]+)/logs", &ProjectAPI{}, "get:Logs")
|
||||
beego.Router("/api/projects/:id([0-9]+)/_deletable", &ProjectAPI{}, "get:Deletable")
|
||||
beego.Router("/api/projects/:pid([0-9]+)/members/?:mid", &ProjectMemberAPI{}, "get:Get;post:Post;delete:Delete;put:Put")
|
||||
@ -359,18 +358,10 @@ func (a testapi) ProjectsGet(query *apilib.ProjectQuery, authInfo ...usrInfo) (i
|
||||
}
|
||||
|
||||
//Update properties for a selected project.
|
||||
func (a testapi) ToggleProjectPublicity(prjUsr usrInfo, projectID string, ispublic int32) (int, error) {
|
||||
// create path and map variables
|
||||
path := "/api/projects/" + projectID + "/publicity/"
|
||||
_sling := sling.New().Put(a.basePath)
|
||||
|
||||
_sling = _sling.Path(path)
|
||||
|
||||
type QueryParams struct {
|
||||
Public int32 `json:"public,omitempty"`
|
||||
}
|
||||
|
||||
_sling = _sling.BodyJSON(&QueryParams{Public: ispublic})
|
||||
func (a testapi) ProjectsPut(prjUsr usrInfo, projectID string,
|
||||
project *models.Project) (int, error) {
|
||||
path := "/api/projects/" + projectID
|
||||
_sling := sling.New().Put(a.basePath).Path(path).BodyJSON(project)
|
||||
|
||||
httpStatusCode, _, err := request(_sling, jsonAcceptHeader, prjUsr)
|
||||
return httpStatusCode, err
|
||||
|
@ -19,6 +19,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/vmware/harbor/src/common/models"
|
||||
"github.com/vmware/harbor/tests/apitests/apilib"
|
||||
)
|
||||
|
||||
@ -38,7 +39,7 @@ func TestLogGet(t *testing.T) {
|
||||
fmt.Println("add the project first.")
|
||||
project := apilib.ProjectReq{
|
||||
ProjectName: "project_for_test_log",
|
||||
Public: 1,
|
||||
Metadata: map[string]string{models.ProMetaPublic: "true"},
|
||||
}
|
||||
|
||||
reply, err := apiTest.ProjectsPost(*testUser, project)
|
||||
|
@ -18,6 +18,7 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/vmware/harbor/src/common"
|
||||
"github.com/vmware/harbor/src/common/dao"
|
||||
@ -120,14 +121,23 @@ func (p *ProjectAPI) Post() {
|
||||
return
|
||||
}
|
||||
|
||||
if pro.Metadata == nil {
|
||||
pro.Metadata = map[string]string{}
|
||||
}
|
||||
// accept the "public" property to make replication work well with old versions(<=1.2.0)
|
||||
if pro.Public != nil && len(pro.Metadata[models.ProMetaPublic]) == 0 {
|
||||
pro.Metadata[models.ProMetaPublic] = strconv.FormatBool(*pro.Public == 1)
|
||||
}
|
||||
|
||||
// populate public metadata as false if it isn't set
|
||||
if _, ok := pro.Metadata[models.ProMetaPublic]; !ok {
|
||||
pro.Metadata[models.ProMetaPublic] = strconv.FormatBool(false)
|
||||
}
|
||||
|
||||
projectID, err := p.ProjectMgr.Create(&models.Project{
|
||||
Name: pro.Name,
|
||||
Public: pro.Public,
|
||||
OwnerName: p.SecurityCtx.GetUsername(),
|
||||
EnableContentTrust: pro.EnableContentTrust,
|
||||
PreventVulnerableImagesFromRunning: pro.PreventVulnerableImagesFromRunning,
|
||||
PreventVulnerableImagesFromRunningSeverity: pro.PreventVulnerableImagesFromRunningSeverity,
|
||||
AutomaticallyScanImagesOnPush: pro.AutomaticallyScanImagesOnPush,
|
||||
Name: pro.Name,
|
||||
OwnerName: p.SecurityCtx.GetUsername(),
|
||||
Metadata: pro.Metadata,
|
||||
})
|
||||
if err != nil {
|
||||
if err == errutil.ErrDupProject {
|
||||
@ -178,7 +188,7 @@ func (p *ProjectAPI) Head() {
|
||||
|
||||
// Get ...
|
||||
func (p *ProjectAPI) Get() {
|
||||
if p.project.Public == 0 {
|
||||
if !p.project.IsPublic() {
|
||||
if !p.SecurityCtx.IsAuthenticated() {
|
||||
p.HandleUnauthorized()
|
||||
return
|
||||
@ -311,21 +321,52 @@ func (p *ProjectAPI) List() {
|
||||
query.Public = &pub
|
||||
}
|
||||
|
||||
// base project collection from which filter is done
|
||||
base := &models.BaseProjectCollection{}
|
||||
if !p.SecurityCtx.IsAuthenticated() {
|
||||
// not login, only get public projects
|
||||
base.Public = true
|
||||
} else {
|
||||
if !(p.SecurityCtx.IsSysAdmin() || p.SecurityCtx.IsSolutionUser()) {
|
||||
// login, but not system admin, get public projects and
|
||||
// projects that the user is member of
|
||||
base.Member = p.SecurityCtx.GetUsername()
|
||||
base.Public = true
|
||||
// standalone, filter projects according to the privilleges of the user first
|
||||
if !config.WithAdmiral() {
|
||||
var projects []*models.Project
|
||||
if !p.SecurityCtx.IsAuthenticated() {
|
||||
// not login, only get public projects
|
||||
pros, err := p.ProjectMgr.GetPublic()
|
||||
if err != nil {
|
||||
p.HandleInternalServerError(fmt.Sprintf("failed to get public projects: %v", err))
|
||||
return
|
||||
}
|
||||
projects = []*models.Project{}
|
||||
projects = append(projects, pros...)
|
||||
} else {
|
||||
if !(p.SecurityCtx.IsSysAdmin() || p.SecurityCtx.IsSolutionUser()) {
|
||||
projects = []*models.Project{}
|
||||
// login, but not system admin or solution user, get public projects and
|
||||
// projects that the user is member of
|
||||
pros, err := p.ProjectMgr.GetPublic()
|
||||
if err != nil {
|
||||
p.HandleInternalServerError(fmt.Sprintf("failed to get public projects: %v", err))
|
||||
return
|
||||
}
|
||||
projects = append(projects, pros...)
|
||||
|
||||
mps, err := p.ProjectMgr.List(&models.ProjectQueryParam{
|
||||
Member: &models.MemberQuery{
|
||||
Name: p.SecurityCtx.GetUsername(),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
p.HandleInternalServerError(fmt.Sprintf("failed to list projects: %v", err))
|
||||
return
|
||||
}
|
||||
projects = append(projects, mps.Projects...)
|
||||
}
|
||||
}
|
||||
if projects != nil {
|
||||
projectIDs := []int64{}
|
||||
for _, project := range projects {
|
||||
projectIDs = append(projectIDs, project.ProjectID)
|
||||
}
|
||||
query.ProjectIDs = projectIDs
|
||||
}
|
||||
}
|
||||
|
||||
result, err := p.ProjectMgr.List(query, base)
|
||||
result, err := p.ProjectMgr.List(query)
|
||||
if err != nil {
|
||||
p.ParseAndHandleError("failed to list projects", err)
|
||||
return
|
||||
@ -358,8 +399,8 @@ func (p *ProjectAPI) List() {
|
||||
p.ServeJSON()
|
||||
}
|
||||
|
||||
// ToggleProjectPublic ...
|
||||
func (p *ProjectAPI) ToggleProjectPublic() {
|
||||
// Put ...
|
||||
func (p *ProjectAPI) Put() {
|
||||
if !p.SecurityCtx.IsAuthenticated() {
|
||||
p.HandleUnauthorized()
|
||||
return
|
||||
@ -372,16 +413,10 @@ func (p *ProjectAPI) ToggleProjectPublic() {
|
||||
|
||||
var req *models.ProjectRequest
|
||||
p.DecodeJSONReq(&req)
|
||||
if req.Public != 0 && req.Public != 1 {
|
||||
p.HandleBadRequest("public should be 0 or 1")
|
||||
return
|
||||
}
|
||||
|
||||
if err := p.ProjectMgr.Update(p.project.ProjectID,
|
||||
&models.Project{
|
||||
Metadata: map[string]string{
|
||||
models.ProMetaPublic: strconv.Itoa(req.Public),
|
||||
},
|
||||
Metadata: req.Metadata,
|
||||
}); err != nil {
|
||||
p.ParseAndHandleError(fmt.Sprintf("failed to update project %d",
|
||||
p.project.ProjectID), err)
|
||||
@ -464,5 +499,39 @@ func validateProjectReq(req *models.ProjectRequest) error {
|
||||
if !legal {
|
||||
return fmt.Errorf("project name is not in lower case or contains illegal characters")
|
||||
}
|
||||
|
||||
if req.Metadata != nil {
|
||||
metas := req.Metadata
|
||||
req.Metadata = map[string]string{}
|
||||
|
||||
boolMetas := []string{
|
||||
models.ProMetaPublic,
|
||||
models.ProMetaEnableContentTrust,
|
||||
models.ProMetaPreventVul,
|
||||
models.ProMetaAutoScan}
|
||||
|
||||
for _, boolMeta := range boolMetas {
|
||||
value, exist := metas[boolMeta]
|
||||
if exist {
|
||||
b, err := strconv.ParseBool(value)
|
||||
if err != nil {
|
||||
log.Errorf("failed to parse %s to bool: %v", value, err)
|
||||
b = false
|
||||
}
|
||||
req.Metadata[boolMeta] = strconv.FormatBool(b)
|
||||
}
|
||||
}
|
||||
|
||||
value, exist := metas[models.ProMetaSeverity]
|
||||
if exist {
|
||||
switch strings.ToLower(value) {
|
||||
case models.SeverityHigh, models.SeverityMedium, models.SeverityLow, models.SeverityNone:
|
||||
req.Metadata[models.ProMetaSeverity] = strings.ToLower(value)
|
||||
default:
|
||||
return fmt.Errorf("invalid severity %s", value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ var addProject *apilib.ProjectReq
|
||||
var addPID int
|
||||
|
||||
func InitAddPro() {
|
||||
addProject = &apilib.ProjectReq{"add_project", 1}
|
||||
addProject = &apilib.ProjectReq{"add_project", map[string]string{models.ProMetaPublic: "true"}}
|
||||
}
|
||||
|
||||
func TestAddProject(t *testing.T) {
|
||||
@ -82,7 +82,7 @@ func TestAddProject(t *testing.T) {
|
||||
//case 4: reponse code = 400 : Project name is illegal in length
|
||||
fmt.Println("case 4 : reponse code = 400 : Project name is illegal in length ")
|
||||
|
||||
result, err = apiTest.ProjectsPost(*admin, apilib.ProjectReq{"t", 1})
|
||||
result, err = apiTest.ProjectsPost(*admin, apilib.ProjectReq{"t", map[string]string{models.ProMetaPublic: "true"}})
|
||||
if err != nil {
|
||||
t.Error("Error while creat project", err.Error())
|
||||
t.Log(err)
|
||||
@ -112,7 +112,7 @@ func TestListProjects(t *testing.T) {
|
||||
assert.Nil(err)
|
||||
assert.Equal(int(200), httpStatusCode, "httpStatusCode should be 200")
|
||||
assert.Equal(addProject.ProjectName, result[0].ProjectName, "Project name is wrong")
|
||||
assert.Equal(int32(1), result[0].Public, "Public is wrong")
|
||||
assert.Equal("true", result[0].Metadata[models.ProMetaPublic], "Public is wrong")
|
||||
|
||||
//find add projectID
|
||||
addPID = int(result[0].ProjectId)
|
||||
@ -130,7 +130,7 @@ func TestListProjects(t *testing.T) {
|
||||
} else {
|
||||
assert.Equal(int(200), httpStatusCode, "httpStatusCode should be 200")
|
||||
assert.Equal(addProject.ProjectName, result[0].ProjectName, "Project name is wrong")
|
||||
assert.Equal(int32(1), result[0].Public, "Public is wrong")
|
||||
assert.Equal("true", result[0].Metadata[models.ProMetaPublic], "Public is wrong")
|
||||
assert.Equal(int32(1), result[0].CurrentUserRoleId, "User project role is wrong")
|
||||
}
|
||||
|
||||
@ -155,7 +155,7 @@ func TestListProjects(t *testing.T) {
|
||||
} else {
|
||||
assert.Equal(int(200), httpStatusCode, "httpStatusCode should be 200")
|
||||
assert.Equal(addProject.ProjectName, result[0].ProjectName, "Project name is wrong")
|
||||
assert.Equal(int32(1), result[0].Public, "Public is wrong")
|
||||
assert.Equal("true", result[0].Metadata[models.ProMetaPublic], "Public is wrong")
|
||||
assert.Equal(int32(2), result[0].CurrentUserRoleId, "User project role is wrong")
|
||||
}
|
||||
id := strconv.Itoa(CommonGetUserID())
|
||||
@ -187,7 +187,7 @@ func TestProGetByID(t *testing.T) {
|
||||
} else {
|
||||
assert.Equal(int(200), httpStatusCode, "httpStatusCode should be 200")
|
||||
assert.Equal(addProject.ProjectName, result.ProjectName, "ProjectName is wrong")
|
||||
assert.Equal(int32(1), result.Public, "Public is wrong")
|
||||
assert.Equal("true", result.Metadata[models.ProMetaPublic], "Public is wrong")
|
||||
}
|
||||
fmt.Printf("\n")
|
||||
}
|
||||
@ -273,48 +273,37 @@ func TestProHead(t *testing.T) {
|
||||
fmt.Printf("\n")
|
||||
}
|
||||
|
||||
func TestToggleProjectPublicity(t *testing.T) {
|
||||
func TestPut(t *testing.T) {
|
||||
fmt.Println("\nTest for Project PUT API: Update properties for a selected project")
|
||||
assert := assert.New(t)
|
||||
|
||||
apiTest := newHarborAPI()
|
||||
|
||||
//-------------------case1: Response Code=200------------------------------//
|
||||
project := &models.Project{
|
||||
Metadata: map[string]string{
|
||||
models.ProMetaPublic: "true",
|
||||
},
|
||||
}
|
||||
|
||||
fmt.Println("case 1: respose code:200")
|
||||
httpStatusCode, err := apiTest.ToggleProjectPublicity(*admin, "1", 1)
|
||||
if err != nil {
|
||||
t.Error("Error while search project by proId", err.Error())
|
||||
t.Log(err)
|
||||
} else {
|
||||
assert.Equal(int(200), httpStatusCode, "httpStatusCode should be 200")
|
||||
}
|
||||
//-------------------case2: Response Code=401 User need to log in first. ------------------------------//
|
||||
code, err := apiTest.ProjectsPut(*admin, "1", project)
|
||||
require.Nil(t, err)
|
||||
assert.Equal(int(200), code)
|
||||
|
||||
fmt.Println("case 2: respose code:401, User need to log in first.")
|
||||
httpStatusCode, err = apiTest.ToggleProjectPublicity(*unknownUsr, "1", 1)
|
||||
if err != nil {
|
||||
t.Error("Error while search project by proId", err.Error())
|
||||
t.Log(err)
|
||||
} else {
|
||||
assert.Equal(int(401), httpStatusCode, "httpStatusCode should be 401")
|
||||
}
|
||||
//-------------------case3: Response Code=400 Invalid project id------------------------------//
|
||||
code, err = apiTest.ProjectsPut(*unknownUsr, "1", project)
|
||||
require.Nil(t, err)
|
||||
assert.Equal(int(401), code)
|
||||
|
||||
fmt.Println("case 3: respose code:400, Invalid project id")
|
||||
httpStatusCode, err = apiTest.ToggleProjectPublicity(*admin, "cc", 1)
|
||||
if err != nil {
|
||||
t.Error("Error while search project by proId", err.Error())
|
||||
t.Log(err)
|
||||
} else {
|
||||
assert.Equal(int(400), httpStatusCode, "httpStatusCode should be 400")
|
||||
}
|
||||
//-------------------case4: Response Code=404 Not found the project------------------------------//
|
||||
code, err = apiTest.ProjectsPut(*admin, "cc", project)
|
||||
require.Nil(t, err)
|
||||
assert.Equal(int(400), code)
|
||||
|
||||
fmt.Println("case 4: respose code:404, Not found the project")
|
||||
httpStatusCode, err = apiTest.ToggleProjectPublicity(*admin, "1234", 1)
|
||||
if err != nil {
|
||||
t.Error("Error while search project by proId", err.Error())
|
||||
t.Log(err)
|
||||
} else {
|
||||
assert.Equal(int(404), httpStatusCode, "httpStatusCode should be 404")
|
||||
}
|
||||
code, err = apiTest.ProjectsPut(*admin, "1234", project)
|
||||
require.Nil(t, err)
|
||||
assert.Equal(int(404), code)
|
||||
|
||||
fmt.Printf("\n")
|
||||
}
|
||||
|
@ -147,7 +147,7 @@ func filterRepositories(projects []*models.Project, keyword string) (
|
||||
entry["repository_name"] = r.Name
|
||||
entry["project_name"] = projects[j].Name
|
||||
entry["project_id"] = projects[j].ProjectID
|
||||
entry["project_public"] = projects[j].Public
|
||||
entry["project_public"] = projects[j].IsPublic()
|
||||
entry["pull_count"] = r.PullCount
|
||||
|
||||
tags, err := getTags(r.Name)
|
||||
|
@ -37,7 +37,7 @@ func TestSearch(t *testing.T) {
|
||||
assert.Equal(int(200), httpStatusCode, "httpStatusCode should be 200")
|
||||
assert.Equal(int64(1), result.Projects[0].ProjectID, "Project id should be equal")
|
||||
assert.Equal("library", result.Projects[0].Name, "Project name should be library")
|
||||
assert.Equal(1, result.Projects[0].Public, "Project public status should be 1 (true)")
|
||||
assert.True(result.Projects[0].IsPublic(), "Project public status should be 1 (true)")
|
||||
}
|
||||
|
||||
//--------case 2 : Response Code = 200, sysAdmin and search repo--------//
|
||||
@ -49,7 +49,7 @@ func TestSearch(t *testing.T) {
|
||||
assert.Equal(int(200), httpStatusCode, "httpStatusCode should be 200")
|
||||
assert.Equal("library", result.Repositories[0].ProjectName, "Project name should be library")
|
||||
assert.Equal("library/docker", result.Repositories[0].RepositoryName, "Repository name should be library/docker")
|
||||
assert.Equal(int32(1), result.Repositories[0].ProjectPublic, "Project public status should be 1 (true)")
|
||||
assert.True(result.Repositories[0].ProjectPublic, "Project public status should be 1 (true)")
|
||||
}
|
||||
|
||||
//--------case 3 : Response Code = 200, normal user and search repo--------//
|
||||
@ -61,7 +61,7 @@ func TestSearch(t *testing.T) {
|
||||
assert.Equal(int(200), httpStatusCode, "httpStatusCode should be 200")
|
||||
assert.Equal("library", result.Repositories[0].ProjectName, "Project name should be library")
|
||||
assert.Equal("library/docker", result.Repositories[0].RepositoryName, "Repository name should be library/docker")
|
||||
assert.Equal(int32(1), result.Repositories[0].ProjectPublic, "Project public status should be 1 (true)")
|
||||
assert.True(result.Repositories[0].ProjectPublic, "Project public status should be 1 (true)")
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -15,7 +15,7 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
//"crypto/tls"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
@ -108,38 +108,32 @@ func initSecretStore() {
|
||||
func initProjectManager() {
|
||||
var driver pmsdriver.PMSDriver
|
||||
if WithAdmiral() {
|
||||
// TODO add support for admiral
|
||||
/*
|
||||
// integration with admiral
|
||||
log.Info("initializing the project manager based on PMS...")
|
||||
// TODO read ca/cert file and pass it to the TLS config
|
||||
AdmiralClient = &http.Client{
|
||||
Transport: &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
},
|
||||
// integration with admiral
|
||||
log.Info("initializing the project manager based on PMS...")
|
||||
// TODO read ca/cert file and pass it to the TLS config
|
||||
AdmiralClient = &http.Client{
|
||||
Transport: &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
path := os.Getenv("SERVICE_TOKEN_FILE_PATH")
|
||||
if len(path) == 0 {
|
||||
path = defaultTokenFilePath
|
||||
}
|
||||
log.Infof("service token file path: %s", path)
|
||||
TokenReader = &admiral.FileTokenReader{
|
||||
Path: path,
|
||||
}
|
||||
GlobalProjectMgr = admiral.NewProjectManager(AdmiralClient,
|
||||
AdmiralEndpoint(), TokenReader)
|
||||
*/
|
||||
GlobalProjectMgr = nil
|
||||
path := os.Getenv("SERVICE_TOKEN_FILE_PATH")
|
||||
if len(path) == 0 {
|
||||
path = defaultTokenFilePath
|
||||
}
|
||||
log.Infof("service token file path: %s", path)
|
||||
TokenReader = &admiral.FileTokenReader{
|
||||
Path: path,
|
||||
}
|
||||
driver = admiral.NewDriver(AdmiralClient, AdmiralEndpoint(), TokenReader)
|
||||
} else {
|
||||
// standalone
|
||||
log.Info("initializing the project manager based on local database...")
|
||||
driver = local.NewDriver()
|
||||
// TODO move the statement out of the else block when admiral driver is completed
|
||||
GlobalProjectMgr = promgr.NewDefaultProjectManager(driver, true)
|
||||
}
|
||||
GlobalProjectMgr = promgr.NewDefaultProjectManager(driver, true)
|
||||
|
||||
}
|
||||
|
||||
|
@ -33,7 +33,7 @@ import (
|
||||
"github.com/vmware/harbor/src/ui/auth"
|
||||
"github.com/vmware/harbor/src/ui/config"
|
||||
"github.com/vmware/harbor/src/ui/promgr"
|
||||
//"github.com/vmware/harbor/src/ui/promgr/pmsdriver/admiral"
|
||||
"github.com/vmware/harbor/src/ui/promgr/pmsdriver/admiral"
|
||||
)
|
||||
|
||||
type key string
|
||||
@ -264,15 +264,13 @@ func (t *tokenReqCtxModifier) Modify(ctx *beegoctx.Context) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
/*
|
||||
log.Debug("creating PMS project manager...")
|
||||
pm := admiral.NewProjectManager(config.AdmiralClient,
|
||||
config.AdmiralEndpoint(), &admiral.RawTokenReader{
|
||||
Token: token,
|
||||
})
|
||||
*/
|
||||
// TODO create the DefaultProjectManager with the real admiral PMSDriver
|
||||
pm := promgr.NewDefaultProjectManager(nil, false)
|
||||
log.Debug("creating PMS project manager...")
|
||||
driver := admiral.NewDriver(config.AdmiralClient,
|
||||
config.AdmiralEndpoint(), &admiral.RawTokenReader{
|
||||
Token: token,
|
||||
})
|
||||
|
||||
pm := promgr.NewDefaultProjectManager(driver, false)
|
||||
|
||||
log.Debug("creating admiral security context...")
|
||||
securCtx := admr.NewSecurityContext(authContext, pm)
|
||||
@ -291,13 +289,10 @@ func (u *unauthorizedReqCtxModifier) Modify(ctx *beegoctx.Context) bool {
|
||||
var pm promgr.ProjectManager
|
||||
if config.WithAdmiral() {
|
||||
// integration with admiral
|
||||
/*
|
||||
log.Debug("creating PMS project manager...")
|
||||
pm = admiral.NewProjectManager(config.AdmiralClient,
|
||||
config.AdmiralEndpoint(), nil)
|
||||
*/
|
||||
// TODO create the DefaultProjectManager with the real admiral PMSDriver
|
||||
pm = promgr.NewDefaultProjectManager(nil, false)
|
||||
log.Debug("creating PMS project manager...")
|
||||
driver := admiral.NewDriver(config.AdmiralClient,
|
||||
config.AdmiralEndpoint(), nil)
|
||||
pm = promgr.NewDefaultProjectManager(driver, false)
|
||||
log.Debug("creating admiral security context...")
|
||||
securCtx = admr.NewSecurityContext(nil, pm)
|
||||
} else {
|
||||
|
@ -15,15 +15,13 @@
|
||||
package metamgr
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/vmware/harbor/src/common/dao"
|
||||
"github.com/vmware/harbor/src/common/models"
|
||||
)
|
||||
|
||||
// ProjectMetadataManaegr defines the operations that a project metadata manager should
|
||||
// ProjectMetadataManager defines the operations that a project metadata manager should
|
||||
// implement
|
||||
type ProjectMetadataManaegr interface {
|
||||
type ProjectMetadataManager interface {
|
||||
// Add metadatas for project specified by projectID
|
||||
Add(projectID int64, meta map[string]string) error
|
||||
// Delete metadatas whose keys are specified in parameter meta, if it
|
||||
@ -34,16 +32,18 @@ type ProjectMetadataManaegr interface {
|
||||
// Get metadatas whose keys are specified in parameter meta, if it is
|
||||
// absent, get all
|
||||
Get(projectID int64, meta ...string) (map[string]string, error)
|
||||
// List metadata according to the name and value
|
||||
List(name, value string) ([]*models.ProjectMetadata, error)
|
||||
}
|
||||
|
||||
type defaultProjectMetadataManaegr struct{}
|
||||
type defaultProjectMetadataManager struct{}
|
||||
|
||||
// NewDefaultProjectMetadataManager ...
|
||||
func NewDefaultProjectMetadataManager() ProjectMetadataManaegr {
|
||||
return &defaultProjectMetadataManaegr{}
|
||||
func NewDefaultProjectMetadataManager() ProjectMetadataManager {
|
||||
return &defaultProjectMetadataManager{}
|
||||
}
|
||||
|
||||
func (d *defaultProjectMetadataManaegr) Add(projectID int64, meta map[string]string) error {
|
||||
func (d *defaultProjectMetadataManager) Add(projectID int64, meta map[string]string) error {
|
||||
for k, v := range meta {
|
||||
proMeta := &models.ProjectMetadata{
|
||||
ProjectID: projectID,
|
||||
@ -57,11 +57,11 @@ func (d *defaultProjectMetadataManaegr) Add(projectID int64, meta map[string]str
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *defaultProjectMetadataManaegr) Delete(projectID int64, meta ...string) error {
|
||||
func (d *defaultProjectMetadataManager) Delete(projectID int64, meta ...string) error {
|
||||
return dao.DeleteProjectMetadata(projectID, meta...)
|
||||
}
|
||||
|
||||
func (d *defaultProjectMetadataManaegr) Update(projectID int64, meta map[string]string) error {
|
||||
func (d *defaultProjectMetadataManager) Update(projectID int64, meta map[string]string) error {
|
||||
for k, v := range meta {
|
||||
if err := dao.UpdateProjectMetadata(&models.ProjectMetadata{
|
||||
ProjectID: projectID,
|
||||
@ -72,20 +72,10 @@ func (d *defaultProjectMetadataManaegr) Update(projectID int64, meta map[string]
|
||||
}
|
||||
}
|
||||
|
||||
// TODO remove the logic
|
||||
public, ok := meta[models.ProMetaPublic]
|
||||
if ok {
|
||||
i, err := strconv.Atoi(public)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return dao.ToggleProjectPublicity(projectID, i)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *defaultProjectMetadataManaegr) Get(projectID int64, meta ...string) (map[string]string, error) {
|
||||
func (d *defaultProjectMetadataManager) Get(projectID int64, meta ...string) (map[string]string, error) {
|
||||
proMetas, err := dao.GetProjectMetadata(projectID, meta...)
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
@ -98,3 +88,14 @@ func (d *defaultProjectMetadataManaegr) Get(projectID int64, meta ...string) (ma
|
||||
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (d *defaultProjectMetadataManager) List(name, value string) ([]*models.ProjectMetadata, error) {
|
||||
metas := []*models.ProjectMetadata{}
|
||||
mds, err := dao.ListProjectMetadata(name, value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
metas = append(metas, mds...)
|
||||
return metas, nil
|
||||
}
|
||||
|
@ -54,6 +54,12 @@ func TestMetaMgrMethods(t *testing.T) {
|
||||
assert.Equal(t, 1, len(m))
|
||||
assert.Equal(t, value, m[key])
|
||||
|
||||
// test list
|
||||
metas, err := mgr.List(key, value)
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, 1, len(metas))
|
||||
assert.Equal(t, int64(1), metas[0].ProjectID)
|
||||
|
||||
// test update
|
||||
require.Nil(t, mgr.Update(1, map[string]string{
|
||||
key: newValue,
|
||||
|
@ -30,13 +30,12 @@ import (
|
||||
"github.com/vmware/harbor/src/common/utils"
|
||||
er "github.com/vmware/harbor/src/common/utils/error"
|
||||
"github.com/vmware/harbor/src/common/utils/log"
|
||||
"github.com/vmware/harbor/src/ui/promgr/pmsdriver"
|
||||
)
|
||||
|
||||
const dupProjectPattern = `Project name '\w+' is already used`
|
||||
|
||||
// ProjectManager implements projectmanager.ProjecdtManager interface
|
||||
// base on project management service
|
||||
type ProjectManager struct {
|
||||
type driver struct {
|
||||
client *http.Client
|
||||
endpoint string
|
||||
tokenReader TokenReader
|
||||
@ -57,10 +56,10 @@ type project struct {
|
||||
Guests []*user `json:"viewers"`
|
||||
}
|
||||
|
||||
// NewProjectManager returns an instance of ProjectManager
|
||||
func NewProjectManager(client *http.Client, endpoint string,
|
||||
tokenReader TokenReader) *ProjectManager {
|
||||
return &ProjectManager{
|
||||
// NewDriver returns an instance of driver
|
||||
func NewDriver(client *http.Client, endpoint string,
|
||||
tokenReader TokenReader) pmsdriver.PMSDriver {
|
||||
return &driver{
|
||||
client: client,
|
||||
endpoint: strings.TrimRight(endpoint, "/"),
|
||||
tokenReader: tokenReader,
|
||||
@ -68,8 +67,8 @@ func NewProjectManager(client *http.Client, endpoint string,
|
||||
}
|
||||
|
||||
// Get ...
|
||||
func (p *ProjectManager) Get(projectIDOrName interface{}) (*models.Project, error) {
|
||||
project, err := p.get(projectIDOrName)
|
||||
func (d *driver) Get(projectIDOrName interface{}) (*models.Project, error) {
|
||||
project, err := d.get(projectIDOrName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -77,10 +76,10 @@ func (p *ProjectManager) Get(projectIDOrName interface{}) (*models.Project, erro
|
||||
}
|
||||
|
||||
// get Admiral project with Harbor project ID or name
|
||||
func (p *ProjectManager) get(projectIDOrName interface{}) (*project, error) {
|
||||
func (d *driver) get(projectIDOrName interface{}) (*project, error) {
|
||||
// if token is provided, search project from my projects list first
|
||||
if len(p.getToken()) != 0 {
|
||||
project, err := p.getFromMy(projectIDOrName)
|
||||
if len(d.getToken()) != 0 {
|
||||
project, err := d.getFromMy(projectIDOrName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -90,18 +89,18 @@ func (p *ProjectManager) get(projectIDOrName interface{}) (*project, error) {
|
||||
}
|
||||
|
||||
// try to get project from public projects list
|
||||
return p.getFromPublic(projectIDOrName)
|
||||
return d.getFromPublic(projectIDOrName)
|
||||
}
|
||||
|
||||
// call GET /projects?$filter=xxx eq xxx, the API can only filter projects
|
||||
// which the user is a member of
|
||||
func (p *ProjectManager) getFromMy(projectIDOrName interface{}) (*project, error) {
|
||||
return p.getAdmiralProject(projectIDOrName, false)
|
||||
func (d *driver) getFromMy(projectIDOrName interface{}) (*project, error) {
|
||||
return d.getAdmiralProject(projectIDOrName, false)
|
||||
}
|
||||
|
||||
// call GET /projects?public=true&$filter=xxx eq xxx
|
||||
func (p *ProjectManager) getFromPublic(projectIDOrName interface{}) (*project, error) {
|
||||
project, err := p.getAdmiralProject(projectIDOrName, true)
|
||||
func (d *driver) getFromPublic(projectIDOrName interface{}) (*project, error) {
|
||||
project, err := d.getAdmiralProject(projectIDOrName, true)
|
||||
if project != nil {
|
||||
// the projects returned by GET /projects?public=true&xxx have no
|
||||
// "public" property, populate it here
|
||||
@ -110,7 +109,7 @@ func (p *ProjectManager) getFromPublic(projectIDOrName interface{}) (*project, e
|
||||
return project, err
|
||||
}
|
||||
|
||||
func (p *ProjectManager) getAdmiralProject(projectIDOrName interface{}, public bool) (*project, error) {
|
||||
func (d *driver) getAdmiralProject(projectIDOrName interface{}, public bool) (*project, error) {
|
||||
m := map[string]string{}
|
||||
|
||||
id, name, err := utils.ParseProjectIDOrName(projectIDOrName)
|
||||
@ -126,7 +125,7 @@ func (p *ProjectManager) getAdmiralProject(projectIDOrName interface{}, public b
|
||||
m["public"] = "true"
|
||||
}
|
||||
|
||||
projects, err := p.filter(m)
|
||||
projects, err := d.filter(m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -145,7 +144,7 @@ func (p *ProjectManager) getAdmiralProject(projectIDOrName interface{}, public b
|
||||
return projects[0], nil
|
||||
}
|
||||
|
||||
func (p *ProjectManager) filter(m map[string]string) ([]*project, error) {
|
||||
func (d *driver) filter(m map[string]string) ([]*project, error) {
|
||||
query := ""
|
||||
for k, v := range m {
|
||||
if len(query) == 0 {
|
||||
@ -165,7 +164,7 @@ func (p *ProjectManager) filter(m map[string]string) ([]*project, error) {
|
||||
}
|
||||
|
||||
path := "/projects" + query
|
||||
data, err := p.send(http.MethodGet, path, nil)
|
||||
data, err := d.send(http.MethodGet, path, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -201,7 +200,7 @@ func convert(p *project) (*models.Project, error) {
|
||||
Name: p.Name,
|
||||
}
|
||||
if p.Public {
|
||||
project.Public = 1
|
||||
project.SetMetadata(models.ProMetaPublic, "true")
|
||||
}
|
||||
|
||||
value := p.CustomProperties["__projectIndex"]
|
||||
@ -221,7 +220,7 @@ func convert(p *project) (*models.Project, error) {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse __enableContentTrust %s to bool: %v", value, err)
|
||||
}
|
||||
project.EnableContentTrust = enable
|
||||
project.SetMetadata(models.ProMetaEnableContentTrust, strconv.FormatBool(enable))
|
||||
}
|
||||
|
||||
value = p.CustomProperties["__preventVulnerableImagesFromRunning"]
|
||||
@ -230,12 +229,12 @@ func convert(p *project) (*models.Project, error) {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse __preventVulnerableImagesFromRunning %s to bool: %v", value, err)
|
||||
}
|
||||
project.PreventVulnerableImagesFromRunning = prevent
|
||||
project.SetMetadata(models.ProMetaPreventVul, strconv.FormatBool(prevent))
|
||||
}
|
||||
|
||||
value = p.CustomProperties["__preventVulnerableImagesFromRunningSeverity"]
|
||||
if len(value) != 0 {
|
||||
project.PreventVulnerableImagesFromRunningSeverity = value
|
||||
project.SetMetadata(models.ProMetaSeverity, value)
|
||||
}
|
||||
|
||||
value = p.CustomProperties["__automaticallyScanImagesOnPush"]
|
||||
@ -244,93 +243,14 @@ func convert(p *project) (*models.Project, error) {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse __automaticallyScanImagesOnPush %s to bool: %v", value, err)
|
||||
}
|
||||
project.AutomaticallyScanImagesOnPush = scan
|
||||
project.SetMetadata(models.ProMetaAutoScan, strconv.FormatBool(scan))
|
||||
}
|
||||
|
||||
return project, nil
|
||||
}
|
||||
|
||||
// IsPublic ...
|
||||
func (p *ProjectManager) IsPublic(projectIDOrName interface{}) (bool, error) {
|
||||
project, err := p.get(projectIDOrName)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if project == nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return project.Public, nil
|
||||
}
|
||||
|
||||
// Exist ...
|
||||
func (p *ProjectManager) Exist(projectIDOrName interface{}) (bool, error) {
|
||||
project, err := p.get(projectIDOrName)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return project != nil, nil
|
||||
}
|
||||
|
||||
/*
|
||||
// GetRoles gets roles that the user has to the project
|
||||
// This method is used in GET /projects API.
|
||||
// Jobservice calls GET /projects API to get information of source
|
||||
// project when trying to replicate the project. There is no auth
|
||||
// context in this use case, so the method is needed.
|
||||
func (p *ProjectManager) GetRoles(username string, projectIDOrName interface{}) ([]int, error) {
|
||||
if len(username) == 0 || projectIDOrName == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
id, err := p.getIDbyHarborIDOrName(projectIDOrName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// get expanded project which contains role info by GET /projects/id?expand=true
|
||||
path := fmt.Sprintf("/projects/%s?expand=true", id)
|
||||
data, err := p.send(http.MethodGet, path, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pro := &project{}
|
||||
if err = json.Unmarshal(data, pro); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
roles := []int{}
|
||||
|
||||
for _, user := range pro.Administrators {
|
||||
if user.Email == username {
|
||||
roles = append(roles, common.RoleProjectAdmin)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
for _, user := range pro.Developers {
|
||||
if user.Email == username {
|
||||
roles = append(roles, common.RoleDeveloper)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
for _, user := range pro.Guests {
|
||||
if user.Email == username {
|
||||
roles = append(roles, common.RoleGuest)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return roles, nil
|
||||
}
|
||||
*/
|
||||
|
||||
func (p *ProjectManager) getIDbyHarborIDOrName(projectIDOrName interface{}) (string, error) {
|
||||
pro, err := p.get(projectIDOrName)
|
||||
func (d *driver) getIDbyHarborIDOrName(projectIDOrName interface{}) (string, error) {
|
||||
pro, err := d.get(projectIDOrName)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@ -342,32 +262,24 @@ func (p *ProjectManager) getIDbyHarborIDOrName(projectIDOrName interface{}) (str
|
||||
return pro.ID, nil
|
||||
}
|
||||
|
||||
// GetPublic ...
|
||||
func (p *ProjectManager) GetPublic() ([]*models.Project, error) {
|
||||
t := true
|
||||
return p.GetAll(&models.ProjectQueryParam{
|
||||
Public: &t,
|
||||
})
|
||||
}
|
||||
|
||||
// Create ...
|
||||
func (p *ProjectManager) Create(pro *models.Project) (int64, error) {
|
||||
func (d *driver) Create(pro *models.Project) (int64, error) {
|
||||
proj := &project{
|
||||
CustomProperties: make(map[string]string),
|
||||
}
|
||||
proj.Name = pro.Name
|
||||
proj.Public = pro.Public == 1
|
||||
proj.CustomProperties["__enableContentTrust"] = strconv.FormatBool(pro.EnableContentTrust)
|
||||
proj.CustomProperties["__preventVulnerableImagesFromRunning"] = strconv.FormatBool(pro.PreventVulnerableImagesFromRunning)
|
||||
proj.CustomProperties["__preventVulnerableImagesFromRunningSeverity"] = pro.PreventVulnerableImagesFromRunningSeverity
|
||||
proj.CustomProperties["__automaticallyScanImagesOnPush"] = strconv.FormatBool(pro.AutomaticallyScanImagesOnPush)
|
||||
proj.Public = pro.IsPublic()
|
||||
proj.CustomProperties["__enableContentTrust"] = strconv.FormatBool(pro.ContentTrustEnabled())
|
||||
proj.CustomProperties["__preventVulnerableImagesFromRunning"] = strconv.FormatBool(pro.VulPrevented())
|
||||
proj.CustomProperties["__preventVulnerableImagesFromRunningSeverity"] = pro.Severity()
|
||||
proj.CustomProperties["__automaticallyScanImagesOnPush"] = strconv.FormatBool(pro.AutoScan())
|
||||
|
||||
data, err := json.Marshal(proj)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
b, err := p.send(http.MethodPost, "/projects", bytes.NewBuffer(data))
|
||||
b, err := d.send(http.MethodPost, "/projects", bytes.NewBuffer(data))
|
||||
if err != nil {
|
||||
// when creating a project with a duplicate name in Admiral, a 500 error
|
||||
// with a specific message will be returned for now.
|
||||
@ -413,23 +325,23 @@ func (p *ProjectManager) Create(pro *models.Project) (int64, error) {
|
||||
}
|
||||
|
||||
// Delete ...
|
||||
func (p *ProjectManager) Delete(projectIDOrName interface{}) error {
|
||||
id, err := p.getIDbyHarborIDOrName(projectIDOrName)
|
||||
func (d *driver) Delete(projectIDOrName interface{}) error {
|
||||
id, err := d.getIDbyHarborIDOrName(projectIDOrName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = p.send(http.MethodDelete, fmt.Sprintf("/projects/%s", id), nil)
|
||||
_, err = d.send(http.MethodDelete, fmt.Sprintf("/projects/%s", id), nil)
|
||||
return err
|
||||
}
|
||||
|
||||
// Update ...
|
||||
func (p *ProjectManager) Update(projectIDOrName interface{}, project *models.Project) error {
|
||||
func (d *driver) Update(projectIDOrName interface{}, project *models.Project) error {
|
||||
return errors.New("project update is unsupported")
|
||||
}
|
||||
|
||||
// GetAll ...
|
||||
func (p *ProjectManager) GetAll(query *models.ProjectQueryParam, base ...*models.BaseProjectCollection) ([]*models.Project, error) {
|
||||
// List ...
|
||||
func (d *driver) List(query *models.ProjectQueryParam) (*models.ProjectQueryResult, error) {
|
||||
m := map[string]string{}
|
||||
if query != nil {
|
||||
if len(query.Name) > 0 {
|
||||
@ -440,7 +352,7 @@ func (p *ProjectManager) GetAll(query *models.ProjectQueryParam, base ...*models
|
||||
}
|
||||
}
|
||||
|
||||
projects, err := p.filter(m)
|
||||
projects, err := d.filter(m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -454,27 +366,24 @@ func (p *ProjectManager) GetAll(query *models.ProjectQueryParam, base ...*models
|
||||
list = append(list, project)
|
||||
}
|
||||
|
||||
return list, nil
|
||||
return &models.ProjectQueryResult{
|
||||
Total: int64(len(list)),
|
||||
Projects: list,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetTotal ...
|
||||
func (p *ProjectManager) GetTotal(query *models.ProjectQueryParam, base ...*models.BaseProjectCollection) (int64, error) {
|
||||
projects, err := p.GetAll(query)
|
||||
return int64(len(projects)), err
|
||||
}
|
||||
|
||||
func (p *ProjectManager) send(method, path string, body io.Reader) ([]byte, error) {
|
||||
req, err := http.NewRequest(method, p.endpoint+path, body)
|
||||
func (d *driver) send(method, path string, body io.Reader) ([]byte, error) {
|
||||
req, err := http.NewRequest(method, d.endpoint+path, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req.Header.Add("x-xenon-auth-token", p.getToken())
|
||||
req.Header.Add("x-xenon-auth-token", d.getToken())
|
||||
|
||||
url := req.URL.String()
|
||||
|
||||
req.URL.RawQuery = req.URL.Query().Encode()
|
||||
resp, err := p.client.Do(req)
|
||||
resp, err := d.client.Do(req)
|
||||
if err != nil {
|
||||
log.Debugf("\"%s %s\" failed", req.Method, url)
|
||||
return nil, err
|
||||
@ -497,12 +406,12 @@ func (p *ProjectManager) send(method, path string, body io.Reader) ([]byte, erro
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (p *ProjectManager) getToken() string {
|
||||
if p.tokenReader == nil {
|
||||
func (d *driver) getToken() string {
|
||||
if d.tokenReader == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
token, err := p.tokenReader.ReadToken()
|
||||
token, err := d.tokenReader.ReadToken()
|
||||
if err != nil {
|
||||
token = ""
|
||||
log.Errorf("failed to read token: %v", err)
|
||||
|
@ -101,12 +101,12 @@ func TestConvert(t *testing.T) {
|
||||
assert.Nil(t, err)
|
||||
assert.NotNil(t, pro)
|
||||
assert.Equal(t, "test", pro.Name)
|
||||
assert.Equal(t, 1, pro.Public)
|
||||
assert.True(t, pro.IsPublic())
|
||||
assert.Equal(t, int64(1), pro.ProjectID)
|
||||
assert.True(t, pro.EnableContentTrust)
|
||||
assert.True(t, pro.PreventVulnerableImagesFromRunning)
|
||||
assert.Equal(t, "medium", pro.PreventVulnerableImagesFromRunningSeverity)
|
||||
assert.True(t, pro.AutomaticallyScanImagesOnPush)
|
||||
assert.True(t, pro.ContentTrustEnabled())
|
||||
assert.True(t, pro.VulPrevented())
|
||||
assert.Equal(t, "medium", pro.Severity())
|
||||
assert.True(t, pro.AutoScan())
|
||||
}
|
||||
|
||||
func TestParse(t *testing.T) {
|
||||
@ -182,233 +182,130 @@ func TestParse(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGet(t *testing.T) {
|
||||
pm := NewProjectManager(client, endpoint, tokenReader)
|
||||
d := NewDriver(client, endpoint, tokenReader)
|
||||
name := "project_for_test_get"
|
||||
id, err := pm.Create(&models.Project{
|
||||
id, err := d.Create(&models.Project{
|
||||
Name: name,
|
||||
})
|
||||
require.Nil(t, err)
|
||||
defer delete(t, id)
|
||||
|
||||
// get by invalid input type
|
||||
_, err = pm.Get([]string{})
|
||||
_, err = d.Get([]string{})
|
||||
assert.NotNil(t, err)
|
||||
|
||||
// get by invalid ID
|
||||
project, err := pm.Get(int64(0))
|
||||
project, err := d.Get(int64(0))
|
||||
assert.Nil(t, err)
|
||||
assert.Nil(t, project)
|
||||
|
||||
// get by invalid name
|
||||
project, err = pm.Get("invalid_name")
|
||||
project, err = d.Get("invalid_name")
|
||||
assert.Nil(t, err)
|
||||
assert.Nil(t, project)
|
||||
|
||||
// get by valid ID
|
||||
project, err = pm.Get(id)
|
||||
project, err = d.Get(id)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, id, project.ProjectID)
|
||||
|
||||
// get by valid name
|
||||
project, err = pm.Get(name)
|
||||
project, err = d.Get(name)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, id, project.ProjectID)
|
||||
}
|
||||
|
||||
func TestIsPublic(t *testing.T) {
|
||||
pm := NewProjectManager(client, endpoint, tokenReader)
|
||||
|
||||
// invalid input type
|
||||
public, err := pm.IsPublic([]string{})
|
||||
assert.NotNil(t, err)
|
||||
assert.False(t, public)
|
||||
|
||||
// non-exist project
|
||||
public, err = pm.IsPublic(int64(2))
|
||||
assert.Nil(t, err)
|
||||
assert.False(t, public)
|
||||
|
||||
// public project
|
||||
name := "project_for_pm_based_on_pms_public"
|
||||
id, err := pm.Create(&models.Project{
|
||||
Name: name,
|
||||
Public: 1,
|
||||
})
|
||||
require.Nil(t, err)
|
||||
defer delete(t, id)
|
||||
|
||||
public, err = pm.IsPublic(id)
|
||||
assert.Nil(t, err)
|
||||
assert.True(t, public)
|
||||
|
||||
public, err = pm.IsPublic(name)
|
||||
assert.Nil(t, err)
|
||||
assert.True(t, public)
|
||||
|
||||
// private project
|
||||
name = "project_for_pm_based_on_pms_private"
|
||||
id, err = pm.Create(&models.Project{
|
||||
Name: name,
|
||||
Public: 0,
|
||||
})
|
||||
require.Nil(t, err)
|
||||
defer delete(t, id)
|
||||
|
||||
public, err = pm.IsPublic(id)
|
||||
assert.Nil(t, err)
|
||||
assert.False(t, public)
|
||||
|
||||
public, err = pm.IsPublic(name)
|
||||
assert.Nil(t, err)
|
||||
assert.False(t, public)
|
||||
}
|
||||
|
||||
func TestExist(t *testing.T) {
|
||||
pm := NewProjectManager(client, endpoint, tokenReader)
|
||||
|
||||
// invalid input type
|
||||
exist, err := pm.Exist([]string{})
|
||||
assert.NotNil(t, err)
|
||||
assert.False(t, exist)
|
||||
|
||||
// non-exist project
|
||||
exist, err = pm.Exist(int64(2))
|
||||
assert.Nil(t, err)
|
||||
assert.False(t, exist)
|
||||
|
||||
// exist project
|
||||
name := "project_for_test_exist"
|
||||
id, err := pm.Create(&models.Project{
|
||||
Name: name,
|
||||
})
|
||||
require.Nil(t, err)
|
||||
defer delete(t, id)
|
||||
|
||||
exist, err = pm.Exist(id)
|
||||
assert.Nil(t, err)
|
||||
assert.True(t, exist)
|
||||
|
||||
exist, err = pm.Exist(name)
|
||||
assert.Nil(t, err)
|
||||
assert.True(t, exist)
|
||||
}
|
||||
|
||||
func TestGetPublic(t *testing.T) {
|
||||
pm := NewProjectManager(client, endpoint, tokenReader)
|
||||
|
||||
projects, err := pm.GetPublic()
|
||||
assert.Nil(t, nil)
|
||||
size := len(projects)
|
||||
|
||||
name := "project_for_test_get_public"
|
||||
id, err := pm.Create(&models.Project{
|
||||
Name: name,
|
||||
Public: 1,
|
||||
})
|
||||
require.Nil(t, err)
|
||||
defer delete(t, id)
|
||||
|
||||
projects, err = pm.GetPublic()
|
||||
assert.Nil(t, nil)
|
||||
assert.Equal(t, size+1, len(projects))
|
||||
|
||||
found := false
|
||||
for _, project := range projects {
|
||||
if project.ProjectID == id {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
assert.True(t, found)
|
||||
}
|
||||
|
||||
func TestCreate(t *testing.T) {
|
||||
pm := NewProjectManager(client, endpoint, tokenReader)
|
||||
d := NewDriver(client, endpoint, tokenReader)
|
||||
|
||||
name := "project_for_test_create"
|
||||
id, err := pm.Create(&models.Project{
|
||||
Name: name,
|
||||
Public: 1,
|
||||
EnableContentTrust: true,
|
||||
PreventVulnerableImagesFromRunning: true,
|
||||
PreventVulnerableImagesFromRunningSeverity: "medium",
|
||||
AutomaticallyScanImagesOnPush: true,
|
||||
id, err := d.Create(&models.Project{
|
||||
Name: name,
|
||||
Metadata: map[string]string{
|
||||
models.ProMetaPublic: "true",
|
||||
models.ProMetaEnableContentTrust: "true",
|
||||
models.ProMetaPreventVul: "true",
|
||||
models.ProMetaSeverity: "medium",
|
||||
models.ProMetaAutoScan: "true",
|
||||
},
|
||||
})
|
||||
require.Nil(t, err)
|
||||
defer delete(t, id)
|
||||
|
||||
project, err := pm.Get(id)
|
||||
project, err := d.Get(id)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, name, project.Name)
|
||||
assert.Equal(t, 1, project.Public)
|
||||
assert.True(t, project.EnableContentTrust)
|
||||
assert.True(t, project.PreventVulnerableImagesFromRunning)
|
||||
assert.Equal(t, "medium", project.PreventVulnerableImagesFromRunningSeverity)
|
||||
assert.True(t, project.AutomaticallyScanImagesOnPush)
|
||||
assert.True(t, project.IsPublic())
|
||||
assert.True(t, project.ContentTrustEnabled())
|
||||
assert.True(t, project.VulPrevented())
|
||||
assert.Equal(t, "medium", project.Severity())
|
||||
assert.True(t, project.AutoScan())
|
||||
|
||||
// duplicate project name
|
||||
_, err = pm.Create(&models.Project{
|
||||
_, err = d.Create(&models.Project{
|
||||
Name: name,
|
||||
})
|
||||
assert.Equal(t, errutil.ErrDupProject, err)
|
||||
}
|
||||
|
||||
func TestDelete(t *testing.T) {
|
||||
pm := NewProjectManager(client, endpoint, tokenReader)
|
||||
d := NewDriver(client, endpoint, tokenReader)
|
||||
|
||||
// non-exist project
|
||||
err := pm.Delete(int64(0))
|
||||
err := d.Delete(int64(0))
|
||||
assert.NotNil(t, err)
|
||||
|
||||
// delete by ID
|
||||
name := "project_for_pm_based_on_pms_id"
|
||||
id, err := pm.Create(&models.Project{
|
||||
id, err := d.Create(&models.Project{
|
||||
Name: name,
|
||||
})
|
||||
require.Nil(t, err)
|
||||
err = pm.Delete(id)
|
||||
err = d.Delete(id)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// delete by name
|
||||
name = "project_for_pm_based_on_pms_name"
|
||||
id, err = pm.Create(&models.Project{
|
||||
id, err = d.Create(&models.Project{
|
||||
Name: name,
|
||||
})
|
||||
require.Nil(t, err)
|
||||
err = pm.Delete(name)
|
||||
err = d.Delete(name)
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
|
||||
func TestUpdate(t *testing.T) {
|
||||
pm := NewProjectManager(client, endpoint, tokenReader)
|
||||
err := pm.Update(nil, nil)
|
||||
d := NewDriver(client, endpoint, tokenReader)
|
||||
err := d.Update(nil, nil)
|
||||
assert.NotNil(t, err)
|
||||
}
|
||||
|
||||
func TestGetAll(t *testing.T) {
|
||||
pm := NewProjectManager(client, endpoint, tokenReader)
|
||||
func TestList(t *testing.T) {
|
||||
d := NewDriver(client, endpoint, tokenReader)
|
||||
|
||||
name1 := "project_for_test_get_all_01"
|
||||
id1, err := pm.Create(&models.Project{
|
||||
id1, err := d.Create(&models.Project{
|
||||
Name: name1,
|
||||
})
|
||||
require.Nil(t, err)
|
||||
defer delete(t, id1)
|
||||
|
||||
name2 := "project_for_test_get_all_02"
|
||||
id2, err := pm.Create(&models.Project{
|
||||
Name: name2,
|
||||
Public: 1,
|
||||
id2, err := d.Create(&models.Project{
|
||||
Name: name2,
|
||||
Metadata: map[string]string{
|
||||
models.ProMetaPublic: "true",
|
||||
},
|
||||
})
|
||||
require.Nil(t, err)
|
||||
defer delete(t, id2)
|
||||
|
||||
// no filter
|
||||
projects, err := pm.GetAll(nil)
|
||||
result, err := d.List(nil)
|
||||
require.Nil(t, err)
|
||||
found1 := false
|
||||
found2 := false
|
||||
for _, project := range projects {
|
||||
for _, project := range result.Projects {
|
||||
if project.ProjectID == id1 {
|
||||
found1 = true
|
||||
}
|
||||
@ -420,12 +317,12 @@ func TestGetAll(t *testing.T) {
|
||||
assert.True(t, found2)
|
||||
|
||||
// filter by name
|
||||
projects, err = pm.GetAll(&models.ProjectQueryParam{
|
||||
result, err = d.List(&models.ProjectQueryParam{
|
||||
Name: name1,
|
||||
})
|
||||
require.Nil(t, err)
|
||||
found1 = false
|
||||
for _, project := range projects {
|
||||
for _, project := range result.Projects {
|
||||
if project.ProjectID == id1 {
|
||||
found1 = true
|
||||
break
|
||||
@ -435,12 +332,12 @@ func TestGetAll(t *testing.T) {
|
||||
|
||||
// filter by public
|
||||
value := true
|
||||
projects, err = pm.GetAll(&models.ProjectQueryParam{
|
||||
result, err = d.List(&models.ProjectQueryParam{
|
||||
Public: &value,
|
||||
})
|
||||
require.Nil(t, err)
|
||||
found2 = false
|
||||
for _, project := range projects {
|
||||
for _, project := range result.Projects {
|
||||
if project.ProjectID == id2 {
|
||||
found2 = true
|
||||
break
|
||||
@ -449,27 +346,9 @@ func TestGetAll(t *testing.T) {
|
||||
assert.True(t, found2)
|
||||
}
|
||||
|
||||
func TestGetTotal(t *testing.T) {
|
||||
pm := NewProjectManager(client, endpoint, tokenReader)
|
||||
|
||||
total1, err := pm.GetTotal(nil)
|
||||
require.Nil(t, err)
|
||||
|
||||
name := "project_for_test_get_total"
|
||||
id, err := pm.Create(&models.Project{
|
||||
Name: name,
|
||||
})
|
||||
require.Nil(t, err)
|
||||
defer delete(t, id)
|
||||
|
||||
total2, err := pm.GetTotal(nil)
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, total1+1, total2)
|
||||
}
|
||||
|
||||
func delete(t *testing.T, id int64) {
|
||||
pm := NewProjectManager(client, endpoint, tokenReader)
|
||||
if err := pm.Delete(id); err != nil {
|
||||
d := NewDriver(client, endpoint, tokenReader)
|
||||
if err := d.Delete(id); err != nil {
|
||||
t.Logf("failed to delete project %d: %v", id, err)
|
||||
}
|
||||
}
|
||||
|
@ -30,7 +30,5 @@ type PMSDriver interface {
|
||||
// Update the properties of a project
|
||||
Update(projectIDOrName interface{}, project *models.Project) error
|
||||
// List lists projects according to the query conditions
|
||||
// TODO remove base
|
||||
List(query *models.ProjectQueryParam,
|
||||
base ...*models.BaseProjectCollection) (*models.ProjectQueryResult, error)
|
||||
List(query *models.ProjectQueryParam) (*models.ProjectQueryResult, error)
|
||||
}
|
||||
|
@ -82,7 +82,6 @@ func (d *driver) Create(project *models.Project) (int64, error) {
|
||||
t := time.Now()
|
||||
pro := &models.Project{
|
||||
Name: project.Name,
|
||||
Public: project.Public,
|
||||
OwnerID: project.OwnerID,
|
||||
CreationTime: t,
|
||||
UpdateTime: t,
|
||||
@ -129,16 +128,15 @@ func (d *driver) Update(projectIDOrName interface{},
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO remove base
|
||||
// List returns a project list according to the query parameters
|
||||
func (d *driver) List(query *models.ProjectQueryParam,
|
||||
base ...*models.BaseProjectCollection) (
|
||||
func (d *driver) List(query *models.ProjectQueryParam) (
|
||||
*models.ProjectQueryResult, error) {
|
||||
total, err := dao.GetTotalOfProjects(query, base...)
|
||||
total, err := dao.GetTotalOfProjects(query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
projects, err := dao.GetProjects(query, base...)
|
||||
|
||||
projects, err := dao.GetProjects(query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -156,7 +156,9 @@ func TestList(t *testing.T) {
|
||||
id, err := pm.Create(&models.Project{
|
||||
Name: "get_all_test",
|
||||
OwnerID: 1,
|
||||
Public: 1,
|
||||
Metadata: map[string]string{
|
||||
models.ProMetaPublic: "true",
|
||||
},
|
||||
})
|
||||
assert.Nil(t, err)
|
||||
defer pm.Delete(id)
|
||||
|
@ -16,6 +16,7 @@ package promgr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/vmware/harbor/src/common/models"
|
||||
"github.com/vmware/harbor/src/common/utils/log"
|
||||
@ -30,9 +31,7 @@ type ProjectManager interface {
|
||||
Create(*models.Project) (int64, error)
|
||||
Delete(projectIDOrName interface{}) error
|
||||
Update(projectIDOrName interface{}, project *models.Project) error
|
||||
// TODO remove base
|
||||
List(query *models.ProjectQueryParam,
|
||||
base ...*models.BaseProjectCollection) (*models.ProjectQueryResult, error)
|
||||
List(query *models.ProjectQueryParam) (*models.ProjectQueryResult, error)
|
||||
IsPublic(projectIDOrName interface{}) (bool, error)
|
||||
Exists(projectIDOrName interface{}) (bool, error)
|
||||
// get all public project
|
||||
@ -42,7 +41,7 @@ type ProjectManager interface {
|
||||
type defaultProjectManager struct {
|
||||
pmsDriver pmsdriver.PMSDriver
|
||||
metaMgrEnabled bool // if metaMgrEnabled is enabled, metaMgr will be used to CURD metadata
|
||||
metaMgr metamgr.ProjectMetadataManaegr
|
||||
metaMgr metamgr.ProjectMetadataManager
|
||||
}
|
||||
|
||||
// NewDefaultProjectManager returns an instance of defaultProjectManager,
|
||||
@ -117,7 +116,25 @@ func (d *defaultProjectManager) Update(projectIDOrName interface{}, project *mod
|
||||
if pro == nil {
|
||||
return fmt.Errorf("project %v not found", projectIDOrName)
|
||||
}
|
||||
if err = d.metaMgr.Update(pro.ProjectID, project.Metadata); err != nil {
|
||||
|
||||
// TODO transaction?
|
||||
metaNeedUpdated := map[string]string{}
|
||||
metaNeedCreated := map[string]string{}
|
||||
if pro.Metadata == nil {
|
||||
pro.Metadata = map[string]string{}
|
||||
}
|
||||
for key, value := range project.Metadata {
|
||||
_, exist := pro.Metadata[key]
|
||||
if exist {
|
||||
metaNeedUpdated[key] = value
|
||||
} else {
|
||||
metaNeedCreated[key] = value
|
||||
}
|
||||
}
|
||||
if err = d.metaMgr.Add(pro.ProjectID, metaNeedCreated); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = d.metaMgr.Update(pro.ProjectID, metaNeedUpdated); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@ -125,13 +142,32 @@ func (d *defaultProjectManager) Update(projectIDOrName interface{}, project *mod
|
||||
return d.pmsDriver.Update(projectIDOrName, project)
|
||||
}
|
||||
|
||||
// TODO remove base
|
||||
func (d *defaultProjectManager) List(query *models.ProjectQueryParam,
|
||||
base ...*models.BaseProjectCollection) (*models.ProjectQueryResult, error) {
|
||||
result, err := d.pmsDriver.List(query, base...)
|
||||
func (d *defaultProjectManager) List(query *models.ProjectQueryParam) (*models.ProjectQueryResult, error) {
|
||||
// query by public/private property with ProjectMetadataManager first
|
||||
if d.metaMgrEnabled && query != nil && query.Public != nil {
|
||||
projectIDs, err := d.filterByPublic(*query.Public)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(projectIDs) == 0 {
|
||||
return &models.ProjectQueryResult{}, nil
|
||||
}
|
||||
|
||||
if query.ProjectIDs == nil {
|
||||
query.ProjectIDs = projectIDs
|
||||
} else {
|
||||
query.ProjectIDs = findInBoth(query.ProjectIDs, projectIDs)
|
||||
}
|
||||
}
|
||||
|
||||
// query by other properties
|
||||
result, err := d.pmsDriver.List(query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// populate metadata
|
||||
if d.metaMgrEnabled {
|
||||
for _, project := range result.Projects {
|
||||
meta, err := d.metaMgr.Get(project.ProjectID)
|
||||
@ -144,6 +180,35 @@ func (d *defaultProjectManager) List(query *models.ProjectQueryParam,
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (d *defaultProjectManager) filterByPublic(public bool) ([]int64, error) {
|
||||
metas, err := d.metaMgr.List(models.ProMetaPublic, strconv.FormatBool(public))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
projectIDs := []int64{}
|
||||
for _, meta := range metas {
|
||||
projectIDs = append(projectIDs, meta.ProjectID)
|
||||
}
|
||||
return projectIDs, nil
|
||||
}
|
||||
|
||||
func findInBoth(ids1 []int64, ids2 []int64) []int64 {
|
||||
m := map[int64]struct{}{}
|
||||
for _, id := range ids1 {
|
||||
m[id] = struct{}{}
|
||||
}
|
||||
|
||||
ids := []int64{}
|
||||
for _, id := range ids2 {
|
||||
if _, exist := m[id]; exist {
|
||||
ids = append(ids, id)
|
||||
}
|
||||
}
|
||||
|
||||
return ids
|
||||
}
|
||||
|
||||
func (d *defaultProjectManager) IsPublic(projectIDOrName interface{}) (bool, error) {
|
||||
project, err := d.Get(projectIDOrName)
|
||||
if err != nil {
|
||||
@ -152,7 +217,7 @@ func (d *defaultProjectManager) IsPublic(projectIDOrName interface{}) (bool, err
|
||||
if project == nil {
|
||||
return false, nil
|
||||
}
|
||||
return project.Public == 1, nil
|
||||
return project.IsPublic(), nil
|
||||
}
|
||||
|
||||
func (d *defaultProjectManager) Exists(projectIDOrName interface{}) (bool, error) {
|
||||
|
@ -32,7 +32,9 @@ func newFakePMSDriver() pmsdriver.PMSDriver {
|
||||
project: &models.Project{
|
||||
ProjectID: 1,
|
||||
Name: "library",
|
||||
Public: 1,
|
||||
Metadata: map[string]string{
|
||||
models.ProMetaPublic: "true",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -53,8 +55,7 @@ func (f *fakePMSDriver) Update(projectIDOrName interface{}, project *models.Proj
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *fakePMSDriver) List(query *models.ProjectQueryParam,
|
||||
base ...*models.BaseProjectCollection) (*models.ProjectQueryResult, error) {
|
||||
func (f *fakePMSDriver) List(query *models.ProjectQueryParam) (*models.ProjectQueryResult, error) {
|
||||
return &models.ProjectQueryResult{
|
||||
Total: 1,
|
||||
Projects: []*models.Project{f.project},
|
||||
@ -116,5 +117,5 @@ func TestGetPublic(t *testing.T) {
|
||||
projects, err := proMgr.GetPublic()
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, 1, len(projects))
|
||||
assert.Equal(t, 1, projects[0].Public)
|
||||
assert.True(t, projects[0].IsPublic())
|
||||
}
|
||||
|
@ -2,14 +2,14 @@ package proxy
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
//"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/vmware/harbor/src/adminserver/client"
|
||||
"github.com/vmware/harbor/src/common"
|
||||
"github.com/vmware/harbor/src/common/dao"
|
||||
"github.com/vmware/harbor/src/common/models"
|
||||
notarytest "github.com/vmware/harbor/src/common/utils/notary/test"
|
||||
utilstest "github.com/vmware/harbor/src/common/utils/test"
|
||||
"github.com/vmware/harbor/src/ui/config"
|
||||
//"github.com/vmware/harbor/src/ui/promgr/pmsdriver/admiral"
|
||||
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
@ -110,33 +110,19 @@ func TestMatchListRepos(t *testing.T) {
|
||||
|
||||
}
|
||||
|
||||
func TestEnvPolicyChecker(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
if err := os.Setenv("PROJECT_CONTENT_TRUST", "1"); err != nil {
|
||||
t.Fatalf("Failed to set env variable: %v", err)
|
||||
}
|
||||
if err2 := os.Setenv("PROJECT_VULNERABLE", "1"); err2 != nil {
|
||||
t.Fatalf("Failed to set env variable: %v", err2)
|
||||
}
|
||||
if err3 := os.Setenv("PROJECT_SEVERITY", "negligible"); err3 != nil {
|
||||
t.Fatalf("Failed to set env variable: %v", err3)
|
||||
}
|
||||
contentTrustFlag := getPolicyChecker().contentTrustEnabled("whatever")
|
||||
vulFlag, sev := getPolicyChecker().vulnerablePolicy("whatever")
|
||||
assert.True(contentTrustFlag)
|
||||
assert.True(vulFlag)
|
||||
assert.Equal(sev, models.SevNone)
|
||||
}
|
||||
|
||||
// TODO uncheck after admiral pms driver is implemented
|
||||
/*
|
||||
func TestPMSPolicyChecker(t *testing.T) {
|
||||
var defaultConfigAdmiral = map[string]interface{}{
|
||||
common.ExtEndpoint: "https://" + endpoint,
|
||||
common.WithNotary: true,
|
||||
common.CfgExpiration: 5,
|
||||
common.AdmiralEndpoint: admiralEndpoint,
|
||||
common.TokenExpiration: 30,
|
||||
common.DatabaseType: "mysql",
|
||||
common.MySQLHost: "127.0.0.1",
|
||||
common.MySQLPort: 3306,
|
||||
common.MySQLUsername: "root",
|
||||
common.MySQLPassword: "root123",
|
||||
common.MySQLDatabase: "registry",
|
||||
common.SQLiteFile: "/tmp/registry.db",
|
||||
}
|
||||
adminServer, err := utilstest.NewAdminserver(defaultConfigAdmiral)
|
||||
if err != nil {
|
||||
@ -149,34 +135,38 @@ func TestPMSPolicyChecker(t *testing.T) {
|
||||
if err := config.Init(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
pm := admiral.NewProjectManager(http.DefaultClient,
|
||||
admiralEndpoint, &admiral.RawTokenReader{
|
||||
Token: "token",
|
||||
})
|
||||
database, err := config.Database()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := dao.InitDatabase(database); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
name := "project_for_test_get_sev_low"
|
||||
id, err := pm.Create(&models.Project{
|
||||
Name: name,
|
||||
EnableContentTrust: true,
|
||||
PreventVulnerableImagesFromRunning: false,
|
||||
PreventVulnerableImagesFromRunningSeverity: "low",
|
||||
id, err := config.GlobalProjectMgr.Create(&models.Project{
|
||||
Name: name,
|
||||
OwnerID: 1,
|
||||
Metadata: map[string]string{
|
||||
models.ProMetaEnableContentTrust: "true",
|
||||
models.ProMetaPreventVul: "true",
|
||||
models.ProMetaSeverity: "low",
|
||||
},
|
||||
})
|
||||
require.Nil(t, err)
|
||||
defer func(id int64) {
|
||||
if err := pm.Delete(id); err != nil {
|
||||
if err := config.GlobalProjectMgr.Delete(id); err != nil {
|
||||
t.Logf("failed to delete project %d: %v", id, err)
|
||||
}
|
||||
}(id)
|
||||
project, err := pm.Get(id)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, id, project.ProjectID)
|
||||
|
||||
contentTrustFlag := getPolicyChecker().contentTrustEnabled("project_for_test_get_sev_low")
|
||||
assert.True(t, contentTrustFlag)
|
||||
projectVulnerableEnabled, projectVulnerableSeverity := getPolicyChecker().vulnerablePolicy("project_for_test_get_sev_low")
|
||||
assert.False(t, projectVulnerableEnabled)
|
||||
assert.True(t, projectVulnerableEnabled)
|
||||
assert.Equal(t, projectVulnerableSeverity, models.SevLow)
|
||||
}
|
||||
*/
|
||||
|
||||
func TestMatchNotaryDigest(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
//The data from common/utils/notary/helper_test.go
|
||||
|
@ -16,7 +16,6 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
@ -38,9 +37,6 @@ var rec *httptest.ResponseRecorder
|
||||
// NotaryEndpoint , exported for testing.
|
||||
var NotaryEndpoint = config.InternalNotaryEndpoint()
|
||||
|
||||
// EnvChecker is the instance of envPolicyChecker
|
||||
var EnvChecker = envPolicyChecker{}
|
||||
|
||||
// MatchPullManifest checks if the request looks like a request to pull manifest. If it is returns the image and tag/sha256 digest as 2nd and 3rd return values
|
||||
func MatchPullManifest(req *http.Request) (bool, string, string) {
|
||||
//TODO: add user agent check.
|
||||
@ -77,16 +73,6 @@ type policyChecker interface {
|
||||
vulnerablePolicy(name string) (bool, models.Severity)
|
||||
}
|
||||
|
||||
//For testing
|
||||
type envPolicyChecker struct{}
|
||||
|
||||
func (ec envPolicyChecker) contentTrustEnabled(name string) bool {
|
||||
return os.Getenv("PROJECT_CONTENT_TRUST") == "1"
|
||||
}
|
||||
func (ec envPolicyChecker) vulnerablePolicy(name string) (bool, models.Severity) {
|
||||
return os.Getenv("PROJECT_VULNERABLE") == "1", clair.ParseClairSev(os.Getenv("PROJECT_SEVERITY"))
|
||||
}
|
||||
|
||||
type pmsPolicyChecker struct {
|
||||
pm promgr.ProjectManager
|
||||
}
|
||||
@ -97,7 +83,7 @@ func (pc pmsPolicyChecker) contentTrustEnabled(name string) bool {
|
||||
log.Errorf("Unexpected error when getting the project, error: %v", err)
|
||||
return true
|
||||
}
|
||||
return project.EnableContentTrust
|
||||
return project.ContentTrustEnabled()
|
||||
}
|
||||
func (pc pmsPolicyChecker) vulnerablePolicy(name string) (bool, models.Severity) {
|
||||
project, err := pc.pm.Get(name)
|
||||
@ -105,7 +91,7 @@ func (pc pmsPolicyChecker) vulnerablePolicy(name string) (bool, models.Severity)
|
||||
log.Errorf("Unexpected error when getting the project, error: %v", err)
|
||||
return true, models.SevUnknown
|
||||
}
|
||||
return project.PreventVulnerableImagesFromRunning, clair.ParseClairSev(project.PreventVulnerableImagesFromRunningSeverity)
|
||||
return project.VulPrevented(), clair.ParseClairSev(project.Severity())
|
||||
}
|
||||
|
||||
// newPMSPolicyChecker returns an instance of an pmsPolicyChecker
|
||||
@ -116,10 +102,7 @@ func newPMSPolicyChecker(pm promgr.ProjectManager) policyChecker {
|
||||
}
|
||||
|
||||
func getPolicyChecker() policyChecker {
|
||||
if config.WithAdmiral() {
|
||||
return newPMSPolicyChecker(config.GlobalProjectMgr)
|
||||
}
|
||||
return EnvChecker
|
||||
return newPMSPolicyChecker(config.GlobalProjectMgr)
|
||||
}
|
||||
|
||||
type imageInfo struct {
|
||||
|
@ -70,7 +70,6 @@ func initRouters() {
|
||||
beego.Router("/api/projects/:pid([0-9]+)/members/?:mid", &api.ProjectMemberAPI{})
|
||||
beego.Router("/api/projects/", &api.ProjectAPI{}, "head:Head")
|
||||
beego.Router("/api/projects/:id([0-9]+)", &api.ProjectAPI{})
|
||||
beego.Router("/api/projects/:id([0-9]+)/publicity", &api.ProjectAPI{}, "put:ToggleProjectPublic")
|
||||
|
||||
beego.Router("/api/users/:id", &api.UserAPI{}, "get:Get;delete:Delete;put:Put")
|
||||
beego.Router("/api/users", &api.UserAPI{}, "get:List;post:Post")
|
||||
|
@ -16,7 +16,6 @@ package registry
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
@ -170,10 +169,8 @@ func autoScanEnabled(project *models.Project) bool {
|
||||
log.Debugf("Auto Scan disabled because Harbor is not deployed with Clair")
|
||||
return false
|
||||
}
|
||||
if config.WithAdmiral() {
|
||||
return project.AutomaticallyScanImagesOnPush
|
||||
}
|
||||
return os.Getenv("ENABLE_HARBOR_SCAN_ON_PUSH") == "1"
|
||||
|
||||
return project.AutoScan()
|
||||
}
|
||||
|
||||
// Render returns nil as it won't render any template.
|
||||
|
@ -23,7 +23,7 @@
|
||||
<div class="form-group" style="padding-left: 135px;">
|
||||
<label class="col-md-4 form-group-label-override">{{'PROJECT.ACCESS_LEVEL' | translate}}</label>
|
||||
<div class="checkbox-inline">
|
||||
<input type="checkbox" id="create_project_public" [(ngModel)]="project.public" name="public">
|
||||
<input type="checkbox" id="create_project_public" [(ngModel)]="project.metadata.public" name="public">
|
||||
<label for="create_project_public"></label>
|
||||
<span class="access-level-label">{{ accessLevelDisplayText | translate}}</span>
|
||||
<a href="javascript:void(0)" role="tooltip" aria-haspopup="true" class="tooltip tooltip-md tooltip-bottom-right" style="top:-8px; left:-8px;">
|
||||
|
@ -73,7 +73,7 @@ export class CreateProjectComponent implements AfterViewChecked, OnInit, OnDestr
|
||||
private messageHandlerService: MessageHandlerService) { }
|
||||
|
||||
public get accessLevelDisplayText(): string {
|
||||
return this.project.public ? 'PROJECT.PUBLIC' : 'PROJECT.PRIVATE';
|
||||
return this.project.metadata.public ? 'PROJECT.PUBLIC' : 'PROJECT.PRIVATE';
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
@ -115,7 +115,7 @@ export class CreateProjectComponent implements AfterViewChecked, OnInit, OnDestr
|
||||
|
||||
this.isSubmitOnGoing=true;
|
||||
this.projectService
|
||||
.createProject(this.project.name, this.project.public ? 1 : 0)
|
||||
.createProject(this.project.name, this.project.metadata)
|
||||
.subscribe(
|
||||
status => {
|
||||
this.isSubmitOnGoing=false;
|
||||
|
@ -7,11 +7,11 @@
|
||||
<clr-dg-row *ngFor="let p of projects">
|
||||
<clr-dg-action-overflow [hidden]="!(p.current_user_role_id === 1 || isSystemAdmin)">
|
||||
<button class="action-item" (click)="newReplicationRule(p)" [hidden]="!isSystemAdmin">{{'PROJECT.REPLICATION_RULE' | translate}}</button>
|
||||
<button class="action-item" (click)="toggleProject(p)">{{'PROJECT.MAKE' | translate}} {{(p.public === 0 ? 'PROJECT.PUBLIC' : 'PROJECT.PRIVATE') | translate}} </button>
|
||||
<button class="action-item" (click)="toggleProject(p)">{{'PROJECT.MAKE' | translate}} {{(p.metadata.public === 'false' ? 'PROJECT.PUBLIC' : 'PROJECT.PRIVATE') | translate}} </button>
|
||||
<button class="action-item" (click)="deleteProject(p)">{{'PROJECT.DELETE' | translate}}</button>
|
||||
</clr-dg-action-overflow>
|
||||
<clr-dg-cell><a href="javascript:void(0)" (click)="goToLink(p.project_id)">{{p.name}}</a></clr-dg-cell>
|
||||
<clr-dg-cell>{{ (p.public === 1 ? 'PROJECT.PUBLIC' : 'PROJECT.PRIVATE') | translate}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{ (p.metadata.public === 'true' ? 'PROJECT.PUBLIC' : 'PROJECT.PRIVATE') | translate}}</clr-dg-cell>
|
||||
<clr-dg-cell *ngIf="showRoleInfo">{{roleInfo[p.current_user_role_id] | translate}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{p.repo_count}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{p.creation_time | date: 'short'}}</clr-dg-cell>
|
||||
|
@ -173,15 +173,15 @@ export class ListProjectComponent implements OnDestroy {
|
||||
|
||||
toggleProject(p: Project) {
|
||||
if (p) {
|
||||
p.public === 0 ? p.public = 1 : p.public = 0;
|
||||
p.metadata.public === 'true' ? p.metadata.public = 'false' : p.metadata.public = 'true';
|
||||
this.proService
|
||||
.toggleProjectPublic(p.project_id, p.public)
|
||||
.toggleProjectPublic(p.project_id, p.metadata.public)
|
||||
.subscribe(
|
||||
response => {
|
||||
this.msgHandler.showSuccess('PROJECT.TOGGLED_SUCCESS');
|
||||
let pp: Project = this.projects.find((item: Project) => item.project_id === p.project_id);
|
||||
if (pp) {
|
||||
pp.public = p.public;
|
||||
pp.metadata.public = p.metadata.public;
|
||||
this.statisticHandler.refresh();
|
||||
}
|
||||
},
|
||||
@ -263,4 +263,4 @@ export class ListProjectComponent implements OnDestroy {
|
||||
return st;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -58,20 +58,22 @@ export class ProjectService {
|
||||
.catch(error=>Observable.throw(error));
|
||||
}
|
||||
|
||||
createProject(name: string, isPublic: number): Observable<any> {
|
||||
createProject(name: string, metadata: any): Observable<any> {
|
||||
return this.http
|
||||
.post(`/api/projects`,
|
||||
JSON.stringify({'project_name': name, 'public': isPublic})
|
||||
JSON.stringify({'project_name': name, 'metadata': {
|
||||
public: metadata.public ? 'true' : 'false',
|
||||
}})
|
||||
, this.options)
|
||||
.map(response=>response.status)
|
||||
.catch(error=>Observable.throw(error));
|
||||
}
|
||||
|
||||
toggleProjectPublic(projectId: number, isPublic: number): Observable<any> {
|
||||
return this.http
|
||||
.put(`/api/projects/${projectId}/publicity`, { 'public': isPublic }, this.options)
|
||||
.map(response=>response.status)
|
||||
.catch(error=>Observable.throw(error));
|
||||
toggleProjectPublic(projectId: number, isPublic: string): Observable<any> {
|
||||
return this.http
|
||||
.put(`/api/projects/${projectId}`, { 'metadata': {'public': isPublic} }, this.options)
|
||||
.map(response => response.status)
|
||||
.catch(error => Observable.throw(error));
|
||||
}
|
||||
|
||||
deleteProject(projectId: number): Observable<any> {
|
||||
|
@ -22,14 +22,14 @@
|
||||
"deleted": 0,
|
||||
"owner_name": "",
|
||||
"public": 1,
|
||||
"Togglable": true,
|
||||
"togglable": true,
|
||||
"update_time": "2017-02-10T07:57:56Z",
|
||||
"current_user_role_id": 1,
|
||||
"repo_count": 0
|
||||
}
|
||||
]
|
||||
*/
|
||||
export class Project {
|
||||
export class Project {
|
||||
project_id: number;
|
||||
owner_id: number;
|
||||
name: string;
|
||||
@ -37,12 +37,22 @@ export class Project {
|
||||
creation_time_str: string;
|
||||
deleted: number;
|
||||
owner_name: string;
|
||||
public: number;
|
||||
Togglable: boolean;
|
||||
togglable: boolean;
|
||||
update_time: Date;
|
||||
current_user_role_id: number;
|
||||
repo_count: number;
|
||||
has_project_admin_role: boolean;
|
||||
is_member: boolean;
|
||||
role_name: string;
|
||||
}
|
||||
metadata: {
|
||||
public: string | boolean;
|
||||
enable_content_trust: string | boolean;
|
||||
prevent_vul: string | boolean;
|
||||
severity: string;
|
||||
auto_scan: string | boolean;
|
||||
};
|
||||
constructor () {
|
||||
this.metadata = <any>{};
|
||||
this.metadata.public = false;
|
||||
}
|
||||
}
|
||||
|
@ -45,11 +45,11 @@ type Project struct {
|
||||
// The owner name of the project.
|
||||
OwnerName string `json:"owner_name,omitempty"`
|
||||
|
||||
// The public status of the project.
|
||||
Public int32 `json:"public,omitempty"`
|
||||
// The metadata of the project.
|
||||
Metadata map[string]string `json:"metadata,omitempty"`
|
||||
|
||||
// Correspond to the UI about whether the project's publicity is updatable (for UI)
|
||||
Togglable bool `json:"Togglable,omitempty"`
|
||||
Togglable bool `json:"togglable,omitempty"`
|
||||
|
||||
// The role ID of the current user who triggered the API (for UI)
|
||||
CurrentUserRoleId int32 `json:"current_user_role_id,omitempty"`
|
||||
|
@ -23,10 +23,8 @@
|
||||
package apilib
|
||||
|
||||
type ProjectReq struct {
|
||||
|
||||
// The name of the project.
|
||||
ProjectName string `json:"project_name,omitempty"`
|
||||
|
||||
// The public status of the project.
|
||||
Public int32 `json:"public,omitempty"`
|
||||
// The metadata of the project.
|
||||
Metadata map[string]string `json:"metadata,omitempty"`
|
||||
}
|
||||
|
@ -30,8 +30,8 @@ type SearchRepository struct {
|
||||
// The name of the project that the repository belongs to
|
||||
ProjectName string `json:"project_name,omitempty"`
|
||||
|
||||
// The flag to indicate the publicity of the project that the repository belongs to (1 is public, 0 is not)
|
||||
ProjectPublic int32 `json:"project_public,omitempty"`
|
||||
// The flag to indicate the publicity of the project that the repository belongs to
|
||||
ProjectPublic bool `json:"project_public,omitempty"`
|
||||
|
||||
// The name of the repository
|
||||
RepositoryName string `json:"repository_name,omitempty"`
|
||||
|
@ -53,4 +53,5 @@ Changelog for harbor database schema
|
||||
## 1.3.0
|
||||
|
||||
- create table `project_metadata`
|
||||
- insert data into table `project_metadata`
|
||||
- insert data into table `project_metadata`
|
||||
- delete column `public` from table `project`
|
Loading…
Reference in New Issue
Block a user