Harbor 项目创建权限管控:从配置文件到 API 校验的完整实践
【免费下载链接】harborAn open source trusted cloud native registry project that stores, signs, and scans content.项目地址: https://gitcode.com/GitHub_Trending/ha/harbor
导读
Harbor 作为开源的云原生制品仓库,其「项目创建权限」(Project Creation Restriction)开关决定了普通用户能否自助创建项目。本文以 Harbor 官方测试用例 tests/testcases/Group8-Configuration/8-02-Authentication-edit-project-creation-restriction.md 为主线,完整还原"管理员修改项目创建限制 → 普通用户创建项目被拒"的验证流程,并结合仓库源码(配置元数据、参数校验、API 权限拦截)讲解该功能的底层实现。读完本文,你将掌握该开关的两种取值、配置入口、默认行为、后端校验逻辑以及自动化回归验证方法。
一、测试用例背景:验证什么
该测试用例属于Group8-Configuration(配置类测试组),其标题为Test 8-02 - Update-project-restrict,核心目的(Purpose)是:
To verify that an admin user can update project restrict. 验证管理员用户能够更新「项目创建限制」配置。
这是一个黑盒功能验证用例,覆盖的操作链路为:
- 管理员在 UI 配置页将项目创建限制从
everyone改为admin only; - 登出管理员;
- 使用非管理员账号登录;
- 尝试新建项目;
- 断言:非管理员看不到"新建项目"按钮(即创建入口被隐藏,后端同时返回 403)。
从源码角度,这一系列行为背后对应的是 Harbor 的全局配置项project_creation_restriction,它由系统级配置框架统一管理,并最终在项目创建 API 处强制执行。
二、核心配置项:project_creation_restriction
2.1 两种取值与默认值
在 src/common/const.go 中定义了该配置项仅有的两种合法取值:
| 常量 | 取值 | 含义 |
|---|---|---|
ProCrtRestrEveryone | everyone | 任何已登录用户(含普通用户)均可创建项目(默认值) |
ProCrtRestrAdmOnly | adminonly | 仅系统管理员可创建项目 |
配置项的元数据定义在 src/lib/config/metadata/metadatalist.go:
{Name: common.ProjectCreationRestriction, Scope: UserScope, Group: BasicGroup, EnvKey: "PROJECT_CREATION_RESTRICTION", DefaultValue: common.ProCrtRestrEveryone, // 默认 "everyone" ItemType: &ProjectCreationRestrictionType{}, Editable: false, Description: `Indicate who can create projects, it could be ''adminonly'' or ''everyone''.`},要点说明:
- 默认值为
everyone,即默认情况下普通用户登录后即可自助创建项目; - 该项属于
UserScope用户级配置(而非部署期 system 级配置),意味着它不通过harbor.yml或环境变量在部署时固定,而是运行时通过 UI / API 修改并持久化到数据库; Editable: false表示该配置项不能通过 API 随意修改,仅能由管理员通过 UI 或在部署配置中设置(见下文 API 说明)。
2.2 取值合法性校验
配置值的合法性由 src/lib/config/metadata/type.go 中的ProjectCreationRestrictionType.validate保证:
func (t *ProjectCreationRestrictionType) validate(str string) error { if !(str == common.ProCrtRestrAdmOnly || str == common.ProCrtRestrEveryone) { return fmt.Errorf("invalid %s, should be %s or %s", common.ProjectCreationRestriction, common.ProCrtRestrAdmOnly, common.ProCrtRestrEveryone) } return nil }即配置值只接受adminonly或everyone二选一,写入其他值会被配置管理器直接拒绝。这也解释了为什么测试用例与 API 测试中始终使用这两个精确字符串。
2.3 读取逻辑
src/lib/config/userconfig.go 提供了业务侧读取入口:
// OnlyAdminCreateProject returns the flag to restrict that only sys admin can create project func OnlyAdminCreateProject(ctx context.Context) (bool, error) { err := DefaultMgr().Load(ctx) if err != nil { return true, err } return DefaultMgr().Get(ctx, common.ProjectCreationRestriction).GetString() == common.ProCrtRestrAdmOnly, nil }- 每次调用先从配置管理器
Load最新配置(支持内存 / DB / REST 等多种配置驱动,见 src/common/const.go 中DBCfgManager、InMemoryCfgManager、RestCfgManager); - 返回值
true表示"仅管理员可创建项目";加载失败时出于安全考虑默认返回 true(收紧权限),避免配置异常导致权限失控。
三、后端强制校验:CreateProject API 的权限拦截
测试用例期望"非管理员无法创建项目",这一约束不仅仅依赖 UI 隐藏按钮,还在服务端强制实施。项目创建入口在 src/server/v2.0/handler/project.go 的CreateProject中完成两层拦截:
func (a *projectAPI) CreateProject(ctx context.Context, params operation.CreateProjectParams) middleware.Responder { if err := a.RequireAuthenticated(ctx); err != nil { return a.SendError(ctx, err) } onlyAdmin, err := config.OnlyAdminCreateProject(ctx) if err != nil { return a.SendError(ctx, fmt.Errorf("failed to determine whether only admin can create projects: %v", err)) } secCtx, _ := security.FromContext(ctx) if r, ok := secCtx.(*robotSec.SecurityContext); ok && !r.User().IsSysLevel() { log.Errorf("Only system level robot can create project") return a.SendError(ctx, errors.ForbiddenError(nil).WithMessage("Only system level robot can create project")) } if onlyAdmin && !(a.isSysAdmin(ctx, rbac.ActionCreate) || secCtx.IsSolutionUser()) { log.Errorf("Only sys admin can create project") return a.SendError(ctx, errors.ForbiddenError(nil).WithMessage("Only system admin can create project")) } ... }拦截流程可概括为:
- 认证:
RequireAuthenticated确保请求来自已登录用户; - 读取全局配置:调用
config.OnlyAdminCreateProject(ctx)判断当前是否为"仅管理员可建项目"模式; - Robot 账号限制:非系统级的 robot 账号一律禁止创建项目(返回
Only system level robot can create project); - 管理员校验:当
onlyAdmin == true且当前用户既不是系统管理员、也不是 Solution User 时,返回403 Forbidden,错误信息为Only system admin can create project—— 这正是 API 自动化测试断言的服务端错误体。
也就是说,即使绕过 UI 直接调用 REST API,普通用户也无法创建项目,权限边界在服务端是闭环的。
四、UI 侧表现:普通用户看不到"新建项目"按钮
测试用例的期望结果是:
In step5, non-admin user will not see the add project button.
Harbor 前端根据系统配置动态渲染项目列表页。相关逻辑位于 src/portal/src/app/base/left-side-nav/projects/list-project/list-project.component.ts,它读取系统配置中的project_creation_restriction,当其值为adminonly时,仅向系统管理员展示"新建项目"入口;普通用户的项目列表页则不渲染该按钮。配置的获取与传递在 src/portal/src/app/services/app-config.ts 与 src/portal/src/app/base/left-side-nav/config/config.ts 中定义,与后端返回的配置项一一对应。
因此该功能存在双重防线:UI 层面隐藏入口(提升体验),服务端 API 层面强制拒绝(保证安全)。
五、管理员修改配置的两种途径
5.1 通过 UI 配置页
管理员登录后,进入配置管理(Administration)→ 系统设置 / Configuration页面,找到「项目创建限制」(Project Creation Restriction)项,在Everyone与Admin Only之间切换并保存。该页面由 src/portal/src/app/base/left-side-nav/config/system/system-settings.component.ts 实现,其中project_creation_restriction是其处理的配置属性之一。
5.2 通过 REST API
自动化测试使用 Harbor 的 v2.0 配置 API 完成切换。API 客户端封装见 tests/apitests/python/library/configurations.py:
def set_configurations_of_project_creation_restriction(self, project_creation_restriction, expect_status_code = 200, **kwargs): client = self._get_client(**kwargs) config=dict(project_creation_restriction=project_creation_restriction) set_configurations(client, expect_status_code = expect_status_code, **config)对应的 REST 调用为PUT /api/v2.0/configurations,请求体示例:
{ "project_creation_restriction": "adminonly" }读取配置则使用GET /api/v2.0/configurations,返回体中的project_creation_restriction.value即为当前取值(见get_configurations实现)。注意:普通用户无权调用配置 API,该接口仅系统管理员可访问,这也是测试中必须先用ADMIN_CLIENT修改配置的原因。
部署层面的补充:虽然该项为运行时配置,但 Harbor 也提供了
PROJECT_CREATION_RESTRICTION环境变量作为初始化值(见元数据EnvKey),可在首次部署时由harbor.yml/ 环境注入,随后可在 UI 中再调整。
六、完整的端到端回归验证
6.1 UI 手工验证步骤(对应原测试用例)
前置环境:
- 一个运行中的 Harbor 实例;
- 一台装有 Docker CLI(Docker 客户端)的 Linux 主机;
- 拥有系统管理员账号与一个普通用户账号。
执行步骤:
- 以管理员身份登录 Harbor UI;
- 进入配置管理 → 系统设置,将「项目创建限制」从
Everyone改为Admin Only并保存; - 登出管理员账号;
- 以普通用户身份登录;
- 尝试新建项目。
预期结果:
- 步骤 5 中,普通用户页面不会出现"新建项目"按钮;即使手工构造 API 请求,也会收到
403 Forbidden,错误体为Only system admin can create project。
6.2 API 自动化测试:test_edit_project_creation.py
仓库中已提供与手工用例等价的 Python 自动化用例 tests/apitests/python/test_edit_project_creation.py,其核心步骤(与 tests/apitests/python/library/project.py 的项目封装配合使用):
- 管理员创建一个新用户(密码
Aa123456); - 以管理员身份调用配置 API,将
project_creation_restriction设为adminonly; - 用新用户调用创建项目 API,断言返回 403,并精确校验响应体:
self.project.create_project( metadata = {"public": "false"}, expect_status_code = 403, expect_response_body = "{\"errors\":[{\"code\":\"FORBIDDEN\",\"message\":\"Only system admin can create project\"}]}", **TestProjects.USER_edit_project_creation_CLIENT)- 以管理员身份将配置改回
everyone; - 再次用新用户创建项目,断言创建成功(得到项目 ID);
- 清理:删除项目与用户(
tearDown)。
该用例形成完整的"限制开启 → 拒绝 → 限制关闭 → 放行"闭环,既能验证配置写入/读取的正确性,也能验证后端权限拦截的准确性,可作为 CI 中配置类回归测试的参考模板(相关 CI 运行入口见 tests/ci/api_run.sh)。
七、常见问题与注意事项
| 现象 / 疑问 | 说明 |
|---|---|
| 修改后普通用户仍显示"新建项目"按钮 | 检查配置是否保存成功、浏览器缓存;或通过GET /api/v2.0/configurations确认真实值 |
| 直接调用 API 创建项目返回 403 | 属预期行为:adminonly模式下服务端强制拦截(src/server/v2.0/handler/project.go) |
| robot 账号创建项目被拒 | 非系统级 robot 一律禁止创建项目,与project_creation_restriction无关(见同文件 L114-L117) |
| 配置值写不进去 | 该配置项仅接受everyone/adminonly,其他值会被元数据校验拒绝(src/lib/config/metadata/type.go) |
| 配置管理器加载失败 | OnlyAdminCreateProject出于安全默认返回true(收紧为仅管理员),避免误放行(src/lib/config/userconfig.go) |
八、总结
project_creation_restriction是 Harbor 多租户权限模型中一个简单但关键的系统配置:
- 配置层:取值限定
everyone(默认)/adminonly,由元数据框架统一管理、校验与持久化(src/lib/config/metadata/metadatalist.go); - 服务端:项目创建 API 通过
OnlyAdminCreateProject读取配置并强制拦截非管理员请求(src/server/v2.0/handler/project.go); - 客户端:UI 根据配置动态隐藏/显示"新建项目"入口;
- 验证:既有 UI 手工用例 8-02-Authentication-edit-project-creation-restriction.md,也有等价的 API 自动化用例 test_edit_project_creation.py 可长期回归。
对于多团队共享的 Harbor 实例,建议在生产环境将该项设为adminonly,由管理员统一审批项目创建,从而避免命名空间混乱与资源滥用;在开发/测试环境可保持默认的everyone以降低协作门槛。
【免费下载链接】harborAn open source trusted cloud native registry project that stores, signs, and scans content.项目地址: https://gitcode.com/GitHub_Trending/ha/harbor
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考