diff --git a/docs/permissions.md b/docs/permissions.md
index 2543ef3c3..b9a7bac3b 100644
--- a/docs/permissions.md
+++ b/docs/permissions.md
@@ -10,44 +10,45 @@ System admin have all permissions for the project.
The following table depicts the various user permission levels in a project.
-| Action | Guest | Developer | Master | Project Admin |
-| --------------------------------------- | ----- | --------- | ------ | ------------- |
-| See the porject configurations | ✓ | ✓ | ✓ | ✓ |
-| Edit the project configurations | | | | ✓ |
-| See a list of project members | ✓ | ✓ | ✓ | ✓ |
-| Create/edit/delete project members | | | | ✓ |
-| See a list of project logs | ✓ | ✓ | ✓ | ✓ |
-| See a list of project replications | | | ✓ | ✓ |
-| See a list of project replication jobs | | | | ✓ |
-| See a list of project labels | | | ✓ | ✓ |
-| Create/edit/delete project lables | | | ✓ | ✓ |
-| See a list of repositories | ✓ | ✓ | ✓ | ✓ |
-| Create repositories | | ✓ | ✓ | ✓ |
-| Edit/delete repositories | | | ✓ | ✓ |
-| See a list of images | ✓ | ✓ | ✓ | ✓ |
-| Retag image | ✓ | ✓ | ✓ | ✓ |
-| Pull image | ✓ | ✓ | ✓ | ✓ |
-| Push image | | ✓ | ✓ | ✓ |
-| Scan/delete image | | | ✓ | ✓ |
-| See a list of image vulnerabilities | ✓ | ✓ | ✓ | ✓ |
-| See image build history | ✓ | ✓ | ✓ | ✓ |
-| Add/Remove labels of image | | ✓ | ✓ | ✓ |
-| See a list of helm charts | ✓ | ✓ | ✓ | ✓ |
-| Download helm charts | ✓ | ✓ | ✓ | ✓ |
-| Upload helm charts | | ✓ | ✓ | ✓ |
-| Delete helm charts | | | ✓ | ✓ |
-| See a list of helm chart versions | ✓ | ✓ | ✓ | ✓ |
-| Download helm chart versions | ✓ | ✓ | ✓ | ✓ |
-| Upload helm chart versions | | ✓ | ✓ | ✓ |
-| Delete helm chart versions | | | ✓ | ✓ |
-| Add/Remove labels of helm chart version | | ✓ | ✓ | ✓ |
-| See a list of project robots | | | ✓ | ✓ |
-| Create/edit/delete project robots | | | | ✓ |
-| See configured CVE whitelist | ✓ | ✓ | ✓ | ✓ |
-| Create/edit/remove CVE whitelist | | | | ✓ |
-| Enable/disable webhooks | | ✓ | ✓ | ✓ |
-| Create/delete tag retention rules | | ✓ | ✓ | ✓ |
-| Enable/disable tag retention rules | | ✓ | ✓ | ✓ |
-| See project quotas | ✓ | ✓ | ✓ | ✓ |
-| Edit project quotas | | | | |
+| Action | Limited Guest | Guest | Developer | Master | Project Admin |
+| --------------------------------------- | ------------- | ----- | --------- | ------ | ------------- |
+| See the porject configurations | ✓ | ✓ | ✓ | ✓ | ✓ |
+| Edit the project configurations | | | | | ✓ |
+| See a list of project members | | ✓ | ✓ | ✓ | ✓ |
+| Create/edit/delete project members | | | | | ✓ |
+| See a list of project logs | | ✓ | ✓ | ✓ | ✓ |
+| See a list of project replications | | | | ✓ | ✓ |
+| See a list of project replication jobs | | | | | ✓ |
+| See a list of project labels | | | | ✓ | ✓ |
+| Create/edit/delete project lables | | | | ✓ | ✓ |
+| See a list of repositories | ✓ | ✓ | ✓ | ✓ | ✓ |
+| Create repositories | | | ✓ | ✓ | ✓ |
+| Edit/delete repositories | | | | ✓ | ✓ |
+| See a list of images | ✓ | ✓ | ✓ | ✓ | ✓ |
+| Retag image | | ✓ | ✓ | ✓ | ✓ |
+| Pull image | ✓ | ✓ | ✓ | ✓ | ✓ |
+| Push image | | | ✓ | ✓ | ✓ |
+| Scan/delete image | | | | ✓ | ✓ |
+| See a list of image vulnerabilities | ✓ | ✓ | ✓ | ✓ | ✓ |
+| See image build history | ✓ | ✓ | ✓ | ✓ | ✓ |
+| Add/Remove labels of image | | | ✓ | ✓ | ✓ |
+| See a list of helm charts | ✓ | ✓ | ✓ | ✓ | ✓ |
+| Download helm charts | ✓ | ✓ | ✓ | ✓ | ✓ |
+| Upload helm charts | | | ✓ | ✓ | ✓ |
+| Delete helm charts | | | | ✓ | ✓ |
+| See a list of helm chart versions | ✓ | ✓ | ✓ | ✓ | ✓ |
+| Download helm chart versions | ✓ | ✓ | ✓ | ✓ | ✓ |
+| Upload helm chart versions | | | ✓ | ✓ | ✓ |
+| Delete helm chart versions | | | | ✓ | ✓ |
+| Add/Remove labels of helm chart version | | | ✓ | ✓ | ✓ |
+| See a list of project robots | | | | ✓ | ✓ |
+| Create/edit/delete project robots | | | | | ✓ |
+| See configured CVE whitelist | ✓ | ✓ | ✓ | ✓ | ✓ |
+| Create/edit/remove CVE whitelist | | | | | ✓ |
+| Enable/disable webhooks | | | ✓ | ✓ | ✓ |
+| Create/delete tag retention rules | | | ✓ | ✓ | ✓ |
+| Enable/disable tag retention rules | | | ✓ | ✓ | ✓ |
+| See project quotas | ✓ | ✓ | ✓ | ✓ | ✓ |
+| Edit project quotas | | | | | |
+
diff --git a/make/migrations/postgresql/0015_1.10.0_schema.up.sql b/make/migrations/postgresql/0015_1.10.0_schema.up.sql
index 6b7e860d9..d4cc01997 100644
--- a/make/migrations/postgresql/0015_1.10.0_schema.up.sql
+++ b/make/migrations/postgresql/0015_1.10.0_schema.up.sql
@@ -57,4 +57,7 @@ DROP TABLE IF EXISTS img_scan_job;
DROP TRIGGER IF EXISTS TRIGGER ON img_scan_overview;
DROP TABLE IF EXISTS img_scan_overview;
-DROP TABLE IF EXISTS clair_vuln_timestamp
\ No newline at end of file
+DROP TABLE IF EXISTS clair_vuln_timestamp;
+
+/* Add limited guest role */
+INSERT INTO role (role_code, name) VALUES ('LRS', 'limitedGuest');
diff --git a/src/common/const.go b/src/common/const.go
index c051762ff..62a577eff 100755
--- a/src/common/const.go
+++ b/src/common/const.go
@@ -33,6 +33,7 @@ const (
RoleDeveloper = 2
RoleGuest = 3
RoleMaster = 4
+ RoleLimitedGuest = 5
LabelLevelSystem = "s"
LabelLevelUser = "u"
diff --git a/src/common/dao/project.go b/src/common/dao/project.go
index e027ec221..764601aa3 100644
--- a/src/common/dao/project.go
+++ b/src/common/dao/project.go
@@ -78,7 +78,7 @@ func addProjectMember(member models.Member) (int, error) {
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
+ sql := `select p.project_id, p.name, u.username as owner_name, p.owner_id, p.creation_time, p.update_time
from project p left join harbor_user u on p.owner_id = u.user_id where p.deleted = false and p.project_id = ?`
queryParam := make([]interface{}, 1)
queryParam = append(queryParam, id)
@@ -142,7 +142,7 @@ func GetTotalOfProjects(query *models.ProjectQueryParam) (int64, error) {
// GetProjects returns a project list according to the query conditions
func GetProjects(query *models.ProjectQueryParam) ([]*models.Project, error) {
sqlStr, queryParam := projectQueryConditions(query)
- sqlStr = `select distinct p.project_id, p.name, p.owner_id,
+ sqlStr = `select distinct p.project_id, p.name, p.owner_id,
p.creation_time, p.update_time ` + sqlStr + ` order by p.name`
sqlStr, queryParam = CreatePagination(query, sqlStr, queryParam)
@@ -158,15 +158,15 @@ func GetProjects(query *models.ProjectQueryParam) ([]*models.Project, error) {
// and the user is in the group which is a group member of this project.
func GetGroupProjects(groupIDs []int, query *models.ProjectQueryParam) ([]*models.Project, error) {
sql, params := projectQueryConditions(query)
- sql = `select distinct p.project_id, p.name, p.owner_id,
+ sql = `select distinct p.project_id, p.name, p.owner_id,
p.creation_time, p.update_time ` + sql
groupIDCondition := JoinNumberConditions(groupIDs)
if len(groupIDs) > 0 {
sql = fmt.Sprintf(
- `%s union select distinct p.project_id, p.name, p.owner_id, p.creation_time, p.update_time
- from project p
+ `%s union select distinct p.project_id, p.name, p.owner_id, p.creation_time, p.update_time
+ from project p
left join project_member pm on p.project_id = pm.project_id
- left join user_group ug on ug.id = pm.entity_id and pm.entity_type = 'g'
+ left join user_group ug on ug.id = pm.entity_id and pm.entity_type = 'g'
where ug.id in ( %s )`,
sql, groupIDCondition)
}
@@ -188,11 +188,11 @@ func GetTotalGroupProjects(groupIDs []int, query *models.ProjectQueryParam) (int
sql = `select count(1) ` + sqlCondition
} else {
sql = fmt.Sprintf(
- `select count(1)
- from ( select p.project_id %s union select p.project_id
- from project p
+ `select count(1)
+ from ( select p.project_id %s union select p.project_id
+ from project p
left join project_member pm on p.project_id = pm.project_id
- left join user_group ug on ug.id = pm.entity_id and pm.entity_type = 'g'
+ left join user_group ug on ug.id = pm.entity_id and pm.entity_type = 'g'
where ug.id in ( %s )) t`,
sqlCondition, groupIDCondition)
}
@@ -254,6 +254,8 @@ func projectQueryConditions(query *models.ProjectQueryParam) (string, []interfac
roleID = 3
case common.RoleMaster:
roleID = 4
+ case common.RoleLimitedGuest:
+ roleID = 5
}
params = append(params, roleID)
}
@@ -287,8 +289,8 @@ func DeleteProject(id int64) error {
return err
}
name := fmt.Sprintf("%s#%d", project.Name, project.ProjectID)
- sql := `update project
- set deleted = true, name = ?
+ sql := `update project
+ set deleted = true, name = ?
where project_id = ?`
_, err = GetOrmer().Raw(sql, name, id).Exec()
return err
@@ -304,8 +306,8 @@ func GetRolesByGroupID(projectID int64, groupIDs []int) ([]int, error) {
groupIDCondition := JoinNumberConditions(groupIDs)
o := GetOrmer()
sql := fmt.Sprintf(
- `select distinct pm.role from project_member pm
- left join user_group ug on pm.entity_type = 'g' and pm.entity_id = ug.id
+ `select distinct pm.role from project_member pm
+ left join user_group ug on pm.entity_type = 'g' and pm.entity_id = ug.id
where ug.id in ( %s ) and pm.project_id = ?`,
groupIDCondition)
log.Debugf("sql for GetRolesByGroupID(project ID: %d, group ids: %v):%v", projectID, groupIDs, sql)
diff --git a/src/common/models/project.go b/src/common/models/project.go
index 1b56284a3..38e1622bb 100644
--- a/src/common/models/project.go
+++ b/src/common/models/project.go
@@ -201,6 +201,7 @@ type ProjectSummary struct {
MasterCount int64 `json:"master_count"`
DeveloperCount int64 `json:"developer_count"`
GuestCount int64 `json:"guest_count"`
+ LimitedGuestCount int64 `json:"limited_guest_count"`
Quota struct {
Hard types.ResourceList `json:"hard"`
diff --git a/src/common/rbac/project/util.go b/src/common/rbac/project/util.go
index 85116fe21..63ed661c8 100644
--- a/src/common/rbac/project/util.go
+++ b/src/common/rbac/project/util.go
@@ -48,124 +48,7 @@ var (
}
// all policies for the projects
- allPolicies = []*rbac.Policy{
- {Resource: rbac.ResourceSelf, Action: rbac.ActionRead},
- {Resource: rbac.ResourceSelf, Action: rbac.ActionUpdate},
- {Resource: rbac.ResourceSelf, Action: rbac.ActionDelete},
-
- {Resource: rbac.ResourceMember, Action: rbac.ActionCreate},
- {Resource: rbac.ResourceMember, Action: rbac.ActionRead},
- {Resource: rbac.ResourceMember, Action: rbac.ActionUpdate},
- {Resource: rbac.ResourceMember, Action: rbac.ActionDelete},
- {Resource: rbac.ResourceMember, Action: rbac.ActionList},
-
- {Resource: rbac.ResourceMetadata, Action: rbac.ActionCreate},
- {Resource: rbac.ResourceMetadata, Action: rbac.ActionRead},
- {Resource: rbac.ResourceMetadata, Action: rbac.ActionUpdate},
- {Resource: rbac.ResourceMetadata, Action: rbac.ActionDelete},
-
- {Resource: rbac.ResourceLog, Action: rbac.ActionList},
-
- {Resource: rbac.ResourceReplication, Action: rbac.ActionList},
- {Resource: rbac.ResourceReplication, Action: rbac.ActionCreate},
- {Resource: rbac.ResourceReplication, Action: rbac.ActionRead},
- {Resource: rbac.ResourceReplication, Action: rbac.ActionUpdate},
- {Resource: rbac.ResourceReplication, Action: rbac.ActionDelete},
-
- {Resource: rbac.ResourceReplicationJob, Action: rbac.ActionCreate},
- {Resource: rbac.ResourceReplicationJob, Action: rbac.ActionRead},
- {Resource: rbac.ResourceReplicationJob, Action: rbac.ActionList},
-
- {Resource: rbac.ResourceReplicationExecution, Action: rbac.ActionRead},
- {Resource: rbac.ResourceReplicationExecution, Action: rbac.ActionList},
- {Resource: rbac.ResourceReplicationExecution, Action: rbac.ActionCreate},
- {Resource: rbac.ResourceReplicationExecution, Action: rbac.ActionUpdate},
- {Resource: rbac.ResourceReplicationExecution, Action: rbac.ActionDelete},
-
- {Resource: rbac.ResourceReplicationTask, Action: rbac.ActionRead},
- {Resource: rbac.ResourceReplicationTask, Action: rbac.ActionList},
- {Resource: rbac.ResourceReplicationTask, Action: rbac.ActionCreate},
- {Resource: rbac.ResourceReplicationTask, Action: rbac.ActionUpdate},
- {Resource: rbac.ResourceReplicationTask, Action: rbac.ActionDelete},
-
- {Resource: rbac.ResourceTagRetention, Action: rbac.ActionCreate},
- {Resource: rbac.ResourceTagRetention, Action: rbac.ActionRead},
- {Resource: rbac.ResourceTagRetention, Action: rbac.ActionUpdate},
- {Resource: rbac.ResourceTagRetention, Action: rbac.ActionDelete},
- {Resource: rbac.ResourceTagRetention, Action: rbac.ActionList},
- {Resource: rbac.ResourceTagRetention, Action: rbac.ActionOperate},
-
- {Resource: rbac.ResourceImmutableTag, Action: rbac.ActionCreate},
- {Resource: rbac.ResourceImmutableTag, Action: rbac.ActionUpdate},
- {Resource: rbac.ResourceImmutableTag, Action: rbac.ActionDelete},
- {Resource: rbac.ResourceImmutableTag, Action: rbac.ActionList},
-
- {Resource: rbac.ResourceLabel, Action: rbac.ActionCreate},
- {Resource: rbac.ResourceLabel, Action: rbac.ActionRead},
- {Resource: rbac.ResourceLabel, Action: rbac.ActionUpdate},
- {Resource: rbac.ResourceLabel, Action: rbac.ActionDelete},
- {Resource: rbac.ResourceLabel, Action: rbac.ActionList},
-
- {Resource: rbac.ResourceLabelResource, Action: rbac.ActionList},
-
- {Resource: rbac.ResourceRepository, Action: rbac.ActionCreate},
- {Resource: rbac.ResourceRepository, Action: rbac.ActionRead},
- {Resource: rbac.ResourceRepository, Action: rbac.ActionUpdate},
- {Resource: rbac.ResourceRepository, Action: rbac.ActionDelete},
- {Resource: rbac.ResourceRepository, Action: rbac.ActionList},
- {Resource: rbac.ResourceRepository, Action: rbac.ActionPull},
- {Resource: rbac.ResourceRepository, Action: rbac.ActionPush},
-
- {Resource: rbac.ResourceRepositoryLabel, Action: rbac.ActionCreate},
- {Resource: rbac.ResourceRepositoryLabel, Action: rbac.ActionDelete},
- {Resource: rbac.ResourceRepositoryLabel, Action: rbac.ActionList},
-
- {Resource: rbac.ResourceRepositoryTag, Action: rbac.ActionRead},
- {Resource: rbac.ResourceRepositoryTag, Action: rbac.ActionDelete},
- {Resource: rbac.ResourceRepositoryTag, Action: rbac.ActionList},
-
- {Resource: rbac.ResourceRepositoryTagScanJob, Action: rbac.ActionCreate},
- {Resource: rbac.ResourceRepositoryTagScanJob, Action: rbac.ActionRead},
-
- {Resource: rbac.ResourceRepositoryTagVulnerability, Action: rbac.ActionList},
-
- {Resource: rbac.ResourceRepositoryTagManifest, Action: rbac.ActionRead},
-
- {Resource: rbac.ResourceRepositoryTagLabel, Action: rbac.ActionCreate},
- {Resource: rbac.ResourceRepositoryTagLabel, Action: rbac.ActionDelete},
- {Resource: rbac.ResourceRepositoryTagLabel, Action: rbac.ActionList},
-
- {Resource: rbac.ResourceHelmChart, Action: rbac.ActionCreate},
- {Resource: rbac.ResourceHelmChart, Action: rbac.ActionRead},
- {Resource: rbac.ResourceHelmChart, Action: rbac.ActionDelete},
- {Resource: rbac.ResourceHelmChart, Action: rbac.ActionList},
-
- {Resource: rbac.ResourceHelmChartVersion, Action: rbac.ActionCreate},
- {Resource: rbac.ResourceHelmChartVersion, Action: rbac.ActionRead},
- {Resource: rbac.ResourceHelmChartVersion, Action: rbac.ActionDelete},
- {Resource: rbac.ResourceHelmChartVersion, Action: rbac.ActionList},
-
- {Resource: rbac.ResourceHelmChartVersionLabel, Action: rbac.ActionCreate},
- {Resource: rbac.ResourceHelmChartVersionLabel, Action: rbac.ActionDelete},
-
- {Resource: rbac.ResourceConfiguration, Action: rbac.ActionRead},
- {Resource: rbac.ResourceConfiguration, Action: rbac.ActionUpdate},
-
- {Resource: rbac.ResourceRobot, Action: rbac.ActionCreate},
- {Resource: rbac.ResourceRobot, Action: rbac.ActionRead},
- {Resource: rbac.ResourceRobot, Action: rbac.ActionUpdate},
- {Resource: rbac.ResourceRobot, Action: rbac.ActionDelete},
- {Resource: rbac.ResourceRobot, Action: rbac.ActionList},
-
- {Resource: rbac.ResourceNotificationPolicy, Action: rbac.ActionCreate},
- {Resource: rbac.ResourceNotificationPolicy, Action: rbac.ActionUpdate},
- {Resource: rbac.ResourceNotificationPolicy, Action: rbac.ActionDelete},
- {Resource: rbac.ResourceNotificationPolicy, Action: rbac.ActionList},
- {Resource: rbac.ResourceNotificationPolicy, Action: rbac.ActionRead},
-
- {Resource: rbac.ResourceScan, Action: rbac.ActionCreate},
- {Resource: rbac.ResourceScan, Action: rbac.ActionRead},
- }
+ allPolicies = computeAllPolicies()
)
// PoliciesForPublicProject ...
@@ -197,3 +80,19 @@ func GetAllPolicies(namespace rbac.Namespace) []*rbac.Policy {
return policies
}
+
+func computeAllPolicies() []*rbac.Policy {
+ var results []*rbac.Policy
+
+ mp := map[string]bool{}
+ for _, policies := range rolePoliciesMap {
+ for _, policy := range policies {
+ if !mp[policy.String()] {
+ results = append(results, policy)
+ mp[policy.String()] = true
+ }
+ }
+ }
+
+ return results
+}
diff --git a/src/common/rbac/project/visitor_role.go b/src/common/rbac/project/visitor_role.go
index d8d594f4f..41ee7205e 100755
--- a/src/common/rbac/project/visitor_role.go
+++ b/src/common/rbac/project/visitor_role.go
@@ -299,6 +299,28 @@ var (
{Resource: rbac.ResourceRobot, Action: rbac.ActionRead},
{Resource: rbac.ResourceRobot, Action: rbac.ActionList},
},
+
+ "limitedGuest": {
+ {Resource: rbac.ResourceSelf, Action: rbac.ActionRead},
+
+ {Resource: rbac.ResourceRepository, Action: rbac.ActionList},
+ {Resource: rbac.ResourceRepository, Action: rbac.ActionPull},
+
+ {Resource: rbac.ResourceRepositoryTag, Action: rbac.ActionRead},
+ {Resource: rbac.ResourceRepositoryTag, Action: rbac.ActionList},
+
+ {Resource: rbac.ResourceRepositoryTagVulnerability, Action: rbac.ActionList},
+
+ {Resource: rbac.ResourceRepositoryTagManifest, Action: rbac.ActionRead},
+
+ {Resource: rbac.ResourceHelmChart, Action: rbac.ActionRead},
+ {Resource: rbac.ResourceHelmChart, Action: rbac.ActionList},
+
+ {Resource: rbac.ResourceHelmChartVersion, Action: rbac.ActionRead},
+ {Resource: rbac.ResourceHelmChartVersion, Action: rbac.ActionList},
+
+ {Resource: rbac.ResourceConfiguration, Action: rbac.ActionRead},
+ },
}
)
@@ -319,6 +341,8 @@ func (role *visitorRole) GetRoleName() string {
return "developer"
case common.RoleGuest:
return "guest"
+ case common.RoleLimitedGuest:
+ return "limitedGuest"
default:
return ""
}
diff --git a/src/common/rbac/project/visitor_role_test.go b/src/common/rbac/project/visitor_role_test.go
index b1f22d24a..e2cc7c2e2 100644
--- a/src/common/rbac/project/visitor_role_test.go
+++ b/src/common/rbac/project/visitor_role_test.go
@@ -35,6 +35,9 @@ func (suite *VisitorRoleTestSuite) TestGetRoleName() {
guest := visitorRole{roleID: common.RoleGuest}
suite.Equal(guest.GetRoleName(), "guest")
+ limitedGuest := visitorRole{roleID: common.RoleLimitedGuest}
+ suite.Equal(limitedGuest.GetRoleName(), "limitedGuest")
+
unknow := visitorRole{roleID: 404}
suite.Equal(unknow.GetRoleName(), "")
}
diff --git a/src/common/security/local/context.go b/src/common/security/local/context.go
index b246ae2e8..a80130da8 100644
--- a/src/common/security/local/context.go
+++ b/src/common/security/local/context.go
@@ -125,6 +125,8 @@ func (s *SecurityContext) GetProjectRoles(projectIDOrName interface{}) []int {
roles = append(roles, common.RoleDeveloper)
case "RS":
roles = append(roles, common.RoleGuest)
+ case "LRS":
+ roles = append(roles, common.RoleLimitedGuest)
}
}
return mergeRoles(roles, s.GetRolesByGroup(projectIDOrName))
diff --git a/src/core/api/project.go b/src/core/api/project.go
index 40559991a..51bcb3d67 100644
--- a/src/core/api/project.go
+++ b/src/core/api/project.go
@@ -685,6 +685,7 @@ func getProjectMemberSummary(projectID int64, summary *models.ProjectSummary) {
{common.RoleMaster, &summary.MasterCount},
{common.RoleDeveloper, &summary.DeveloperCount},
{common.RoleGuest, &summary.GuestCount},
+ {common.RoleLimitedGuest, &summary.LimitedGuestCount},
} {
wg.Add(1)
go func(role int, count *int64) {
diff --git a/src/core/api/projectmember.go b/src/core/api/projectmember.go
index b704ad5c6..54e0707cd 100644
--- a/src/core/api/projectmember.go
+++ b/src/core/api/projectmember.go
@@ -191,7 +191,7 @@ func (pma *ProjectMemberAPI) Put() {
pma.SendBadRequestError(err)
return
}
- if req.Role < 1 || req.Role > 4 {
+ if !isValidRole(req.Role) {
pma.SendBadRequestError(fmt.Errorf("Invalid role id %v", req.Role))
return
}
@@ -284,9 +284,22 @@ func AddProjectMember(projectID int64, request models.MemberReq) (int, error) {
return 0, ErrDuplicateProjectMember
}
- if member.Role < 1 || member.Role > 4 {
+ if !isValidRole(member.Role) {
// Return invalid role error
return 0, ErrInvalidRole
}
return project.AddProjectMember(member)
}
+
+func isValidRole(role int) bool {
+ switch role {
+ case common.RoleProjectAdmin,
+ common.RoleMaster,
+ common.RoleDeveloper,
+ common.RoleGuest,
+ common.RoleLimitedGuest:
+ return true
+ default:
+ return false
+ }
+}
diff --git a/src/core/api/projectmember_test.go b/src/core/api/projectmember_test.go
index 89aab304b..306fe138f 100644
--- a/src/core/api/projectmember_test.go
+++ b/src/core/api/projectmember_test.go
@@ -344,3 +344,28 @@ func TestProjectMemberAPI_PutAndDelete(t *testing.T) {
runCodeCheckingCases(t, cases...)
}
+
+func Test_isValidRole(t *testing.T) {
+ type args struct {
+ role int
+ }
+ tests := []struct {
+ name string
+ args args
+ want bool
+ }{
+ {"project admin", args{1}, true},
+ {"master", args{4}, true},
+ {"developer", args{2}, true},
+ {"guest", args{3}, true},
+ {"limited guest", args{5}, true},
+ {"unknow", args{6}, false},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ if got := isValidRole(tt.args.role); got != tt.want {
+ t.Errorf("isValidRole() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
diff --git a/src/portal/lib/src/shared/shared.const.ts b/src/portal/lib/src/shared/shared.const.ts
index 8e76581d9..4a36ed52c 100644
--- a/src/portal/lib/src/shared/shared.const.ts
+++ b/src/portal/lib/src/shared/shared.const.ts
@@ -145,6 +145,11 @@ export const PROJECT_ROOTS = [
NAME: "guest",
VALUE: 3,
LABEL: "GROUP.GUEST"
+ },
+ {
+ NAME: "limited",
+ VALUE: 5,
+ LABEL: "GROUP.LIMITED_GUEST"
}
];
diff --git a/src/portal/src/app/project/member/add-member/add-member.component.html b/src/portal/src/app/project/member/add-member/add-member.component.html
index ee2f5f1ff..de4028ee1 100644
--- a/src/portal/src/app/project/member/add-member/add-member.component.html
+++ b/src/portal/src/app/project/member/add-member/add-member.component.html
@@ -45,6 +45,10 @@
+