How to Manage Projects and Access Control in Harbor: Complete RBAC Implementation
Harbor isolates container repositories into projects protected by role-based access control (RBAC), enforcing permissions through policy maps defined in src/common/rbac/project/rbac_role.go that grant specific actions to roles like projectAdmin, maintainer, and developer.
Harbor organizes registries into logical units called projects. According to the goharbor/harbor source code, these projects function as isolated namespaces where access control policies determine exactly which users, groups, or robot accounts can perform operations on repositories, tags, and member configurations.
Harbor Project Architecture and Data Model
Core Project Structure
The fundamental project definition resides in src/pkg/project/models/project.go. This struct contains the primary fields that define a project:
ProjectID– The primary key for database operationsOwnerID– References the user who owns the project, typically a system administratorName– The unique, lowercase project identifier validated byproject/manager.gousing the regular expression^[a-z0-9]+(?:[._-][a-z0-9]+)*$Metadata– A key-value map storing configuration flags includingpublic,enable_content_trust, andproxy_speed_kbRegistryID– Non-zero values indicate proxy-cache projects that mirror external registries
Project Lifecycle Management
All project CRUD operations route through the Project controller at src/server/v2.0/handler/project.go.
The CreateProject handler validates incoming requests against the name regex, checks the system-level configuration OnlyAdminCreateProject to determine if creation is restricted to administrators, injects default storage quota policies, and finally delegates persistence to project.Ctl.Create.
For deletion, the DeleteProject handler first validates the requester's permissions through the RBAC layer before invoking project.Ctl.Delete to remove the project and associated resources.
RBAC Roles and Permission Policies
Role Definitions and Policy Maps
Harbor's RBAC engine lives in src/common/rbac/project/rbac_role.go, which defines the rolePoliciesMap variable mapping role names to allowed actions:
var rolePoliciesMap = map[string][]*types.Policy{
"projectAdmin": {
{Resource: rbac.ResourceSelf, Action: rbac.ActionRead},
{Resource: rbac.ResourceMember, Action: rbac.ActionCreate},
// ... full permissions for project administration
},
"maintainer": {
{Resource: rbac.ResourceSelf, Action: rbac.ActionRead},
{Resource: rbac.ResourceMember, Action: rbac.ActionRead},
// ... repository and artifact management
},
"developer": {
// ... push and pull permissions
},
"guest": {
// ... read-only access
},
"limitedGuest": {
// ... limited read access only
},
}
Each role grants specific permissions on resources such as self, member, metadata, repository, tag, and scanner.
Role Evaluation and Enforcement
When API requests reach Harbor handlers, the RequireProjectAccess method in the BaseAPI extracts the current security context and queries the RBAC evaluator to determine if the user's role list contains the required permission for the requested action on the target resource.
The system calculates role priority through the highestRole function, which assigns numeric scores: projectAdmin (50), maintainer (40), developer (30), guest (20), and limitedGuest (10). This ranking exposes current_user_role_id in API responses for client-side permission hints.
Managing Project Members
Member Data Access Layer
Project membership data persists in the project_member table and is accessed through src/pkg/member/dao/dao.go. The AddProjectMember function implements upsert logic by first deleting existing records for the same (project_id, entity_id, entity_type) tuple before inserting the new role assignment.
Querying and Counting Members
The GetProjectMember function constructs a UNION SQL query that retrieves both individual user members and group members associated with a project. For role-based analytics, GetTotalOfProjectMembers counts members filtered by specific roles:
total, err := a.memberMgr.GetTotalOfProjectMembers(orm.Clone(ctx), p.ProjectID, nil, role)
This function powers the projectAPI.getProjectMemberSummary endpoint to populate the ProjectSummary structure with accurate member statistics.
Practical Implementation Examples
Creating Projects via REST API
The following curl command creates a private project with content trust enabled:
curl -u admin:Harbor12345 -X POST "https://harbor.mycorp.com/api/v2.0/projects" \
-H "Content-Type: application/json" \
-d '{
"project_name": "myteam",
"metadata": {
"public": "false",
"enable_content_trust": "true"
}
}'
The server validates the project name against the regex pattern, enforces admin-only creation if configured, and stores metadata in the project_metadata table.
Adding Members with Role Assignments
To assign the developer role (ID 3) to a user:
curl -u admin:Harbor12345 -X POST "https://harbor.mycorp.com/api/v2.0/projects/myteam/members" \
-H "Content-Type: application/json" \
-d '{
"role_id": 3,
"member_user": { "username": "jdoe" }
}'
Internally, this calls memberMgr.AddProjectMember, which executes the DAO logic in src/pkg/member/dao/dao.go to persist the membership record.
Programmatic Member Management in Go
List all members of a project using the internal SDK:
import (
"context"
"github.com/goharbor/harbor/src/pkg/member"
"github.com/goharbor/harbor/src/pkg/project"
)
func listMembers(ctx context.Context, projectName string) ([]*member.Models.Member, error) {
// Resolve project ID from name
p, err := project.Ctl.GetByName(ctx, projectName)
if err != nil {
return nil, err
}
// Query members without filters
members, err := member.Mgr.GetProjectMember(ctx, member.Models.Member{
ProjectID: p.ProjectID,
}, nil)
return members, err
}
To determine a user's highest role for permission checking:
import (
"github.com/goharbor/harbor/src/common"
"github.com/goharbor/harbor/src/pkg/member"
)
func highestUserRole(ctx context.Context, projID int64, userID int) (int, error) {
roles, err := member.Mgr.ListRoles(ctx, &member.Models.User{UserID: userID}, projID)
if err != nil {
return 0, err
}
// Match Harbor's internal role ranking
rank := map[int]int{
common.RoleProjectAdmin: 50,
common.RoleMaintainer: 40,
common.RoleDeveloper: 30,
common.RoleGuest: 20,
common.RoleLimitedGuest: 10,
}
var bestRole, bestScore int
for _, r := range roles {
if s := rank[r]; s > bestScore {
bestRole, bestScore = r, s
}
}
return bestRole, nil
}
Summary
- Projects in Harbor are isolated namespaces defined in
src/pkg/project/models/project.go, with unique lowercase names validated by^[a-z0-9]+(?:[._-][a-z0-9]+)*$ - RBAC enforcement relies on
src/common/rbac/project/rbac_role.go, which maps five distinct roles (projectAdmin, maintainer, developer, guest, limitedGuest) to specific resource actions - Member management occurs through
src/pkg/member/dao/dao.go, supporting both individual users and groups with role-based counting and upsert operations - API handlers in
src/server/v2.0/handler/project.gocoordinate creation, deletion, and permission checks through theRequireProjectAccesssecurity context - Role priorities follow a numeric scale (50 to 10) that determines the effective permission level when users hold multiple roles
Frequently Asked Questions
What are the default RBAC roles available in Harbor projects?
Harbor defines five standard roles in src/common/rbac/project/rbac_role.go: projectAdmin (full control, priority 50), maintainer (repository management, priority 40), developer (push and pull, priority 30), guest (read-only, priority 20), and limitedGuest (restricted read access, priority 10). Each role maps to a specific set of allowed actions on resources like repositories, tags, and project metadata.
How does Harbor validate project names during creation?
Project names must match the regular expression ^[a-z0-9]+(?:[._-][a-z0-9]+)*$ enforced in src/pkg/project/manager.go. This validation ensures names contain only lowercase alphanumeric characters with optional periods, underscores, or hyphens as internal separators. The CreateProject handler in src/server/v2.0/handler/project.go applies this validation before persisting to the database.
Where does Harbor store project membership and role assignments?
Membership data resides in the project_member database table, accessed through the DAO implementations in src/pkg/member/dao/dao.go. The AddProjectMember function handles role assignments by first removing existing entries for the same entity, then inserting the new role record. The GetProjectMember function retrieves memberships through a UNION query that combines individual users and group entries.
Can system administrators restrict project creation privileges?
Yes. Harbor checks the OnlyAdminCreateProject configuration flag in the CreateProject handler at src/server/v2.0/handler/project.go. When enabled, only users with system administrator privileges can successfully create new projects through the API. Standard users receive a permission denied response when attempting to create projects under this configuration.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →