Skip to content

Origin/18124 custom role feature#22815

Open
maxgraustenzel-create wants to merge 39 commits into
goharbor:mainfrom
maxgraustenzel-create:origin/18124-custom-role-feature
Open

Origin/18124 custom role feature#22815
maxgraustenzel-create wants to merge 39 commits into
goharbor:mainfrom
maxgraustenzel-create:origin/18124-custom-role-feature

Conversation

@maxgraustenzel-create

@maxgraustenzel-create maxgraustenzel-create commented Feb 4, 2026

Copy link
Copy Markdown

This change adds custom roles to harbor.

Directly Solved Issues:

#18124 RBAC - be able to create a custom role
#18143 Create custom project roles
#21222 Add role for security auditor
#15711 Add a readonly admin role in Harbor
#12062 Make permission system more fine-grained
#21306 - Read-only auditor role (December 2024 - very recent!)
#8632 - Limited guest role
#1486 - Guest shouldn't see all members (from 2016!)
...

Partially Solved Issues:

#10159 - Repository-level permissions
...

Design changes :

  • Store permissions in role_permission and permission_policy table (from rbac_role.go, migration with init_permissions.sql)
  • Permission management functionality for the system administrator (swagger + ui)
  • Dynamic permission selection when adding members
  • Caching of role permissions for the user and session in rbac_user.go (contains now the roles object as part of session with the associated permissions)

@chlins

chlins commented Feb 5, 2026

Copy link
Copy Markdown
Member

@maxgraustenzel-create Please first submit a proposal in the community repository for discussion.

@github-actions

github-actions Bot commented Apr 6, 2026

Copy link
Copy Markdown

This PR is being marked stale due to a period of inactivty. If this PR is still relevant, please comment or remove the stale label. Otherwise, this PR will close in 30 days.

@github-actions github-actions Bot added the Stale label Apr 6, 2026
@Max-303

Max-303 commented Apr 10, 2026

Copy link
Copy Markdown

@maxgraustenzel-create Please first submit a proposal in the community repository for discussion.

This is the proposal:
goharbor/community#276

@github-actions github-actions Bot removed the Stale label Apr 11, 2026
@pknomad

pknomad commented May 14, 2026

Copy link
Copy Markdown

Tagging issue #18308 to this since it's also related to some of the issues opened.

@maxgraustenzel-create
maxgraustenzel-create force-pushed the origin/18124-custom-role-feature branch from 1a2ee15 to 236bb14 Compare May 20, 2026 14:44
maxgraustenzel-create and others added 16 commits June 2, 2026 10:59
- Portal: replace hardcoded built-in role buttons in project member
  list, add-member, and add-group dialogs with dynamic role list loaded
  from API; built-in roles show translated names + blue badge; custom
  roles listed after a divider; scroll when list overflows
- Portal: role column in member datagrid now resolves role name via API
  (translated for built-ins, raw name for custom roles)
- Backend: fire audit log events for role create / update / delete;
  add UpdateRoleEvent topic and metadata; subscribe all three topics
  in handler init so events actually reach the audit log handler
- Backend: fix typo in DeleteRoleEvent audit message ("robot" → "role")
- DB: add 0190 migration schema placeholder

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Backend: add checkNoEscalation to member handler — CreateProjectMember
and UpdateProjectMember now verify that every permission in the target
role is held by the calling user before accepting the request. Sysadmins
pass through unconditionally via the RBAC layer. Returns 403 Forbidden
on escalation attempt, closing the TODO in member controller isValidRole.

Frontend: compute assignableRoleIds from the current user's own role
permissions (loaded via forkJoin with the member list). Built-in roles
below projectAdmin level and custom roles that are a strict subset of
the caller's permissions remain selectable; others are disabled and
visually dimmed in the action dropdown, add-member, and add-group dialogs.
Default selection in add-member/add-group modals snaps to the first
assignable role so the form is always valid on open.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
sysadmin_flag lives on SessionUserBackend; the resolved SessionUser type
only exposes has_admin_role — fixes TS2339 compilation error.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Migration 0040 renames master → maintainer before 0190 runs,
so the UPDATE was silently skipping the maintainer role.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Role permission cache (L1 sync.Map + L2 Redis) in controller/role/controller.go
  eliminates per-request role_permission DB queries after first warm-up
- Restore isValidRole() validation in member controller
- Fix permissions endpoint: open to all authenticated users (used by project UI)
- Remove orm.Debug=true and leftover debug log.Debug("*** ...") statements
- Uncomment and fix RBAC evaluator tests for fork's 3-arg NewBuilderForUser
- Remove TODO placeholder comments from production code
- Add [TIMING:*] instrumentation to measure permission evaluation breakdown:
  Can(), project_get, project_assemble, list_roles_sql, builder, role_get
- Extended timing to project controller and DAO for detailed DB breakdown

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ckstop

The L1+L2 role-permission cache had two correctness gaps:
- L2 had no TTL (cache.Default() Expiration=0), so out-of-band SQL changes to
  role_permission were never reflected until a core restart.
- invalidateRole() cleared only the local L1; Get() reads L1 first, so other
  nodes served stale permissions until their process restarted.

Fix:
- Global Redis "version token" (role:version) bumped on every in-app write;
  nodes poll it throttled to ~1s and bump a local generation that invalidates
  all L1 entries → cross-node propagation within ~1s. Lock-free (atomics+CAS).
- 30-min TTL on both L1 (per-entry expiresAt) and L2 (Save expiration) as a
  self-healing backstop for out-of-band changes.
- Read order: version-refresh -> L1 (gen+TTL) -> L2 -> DB (populates both).
  Write order: bump version -> delete L2 -> delete L1.
- Documented the bounded cache-aside repopulation race.

Also fixes a pre-existing test bug: TestUpdateCustomRole never mocked
roleMgr.Update (failed on the committed baseline too).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Brings in upstream through eb944bb (incl. Angular 21 / Clarity 18 / Node 22
portal upgrade and go.mod OSS bumps). Backend auto-merged and builds clean.

Conflicts resolved (portal): kept the custom-roles feature logic while adopting
upstream's Angular-21 control-flow syntax:
- member.component.html: @if blocks + getMemberRoleDisplayName()
- add-group/add-group.component.html: @for over dynamic roles with builtin badges
- package-lock.json: took upstream's Angular-21 lockfile (fork adds no npm deps)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Angular 19+ makes components standalone by default. The fork's RolesComponent,
AddRoleComponent and RolePermissionsPanelComponent are declared in roles.module.ts,
so they must opt out of standalone (NG6008). Portal now builds green under
Angular 21 / Clarity 18 / Node 22.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds a runtime toggle so Get() with permissions can skip L1/L2 entirely and read
the role + role_permission from the DB on every Can() call. Replaces the fragile
build-time source-stripping used for the no-cache perf baseline (which broke after
the version-key refactor) with a same-binary env switch. Off by default.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Prepare the custom-roles branch for a clean merge request:
- Remove all [TIMING:*] perf-logging (Can/builder/evaluator/project_ctrl/
  list_roles_sql/security_ctx) and the timing-only changes in dao.go,
  session.go and project/controller.go (reverted to upstream).
- Drop the per-role permission lookup's timing scaffolding in
  rbac/project/evaluator.go, keeping the feature (ctl_r.Get) and adding
  proper error handling; collapse a redundant nested if-err.
- Remove the "TODO MGS" scratch comment and the commented-out dead code
  (convertScope/toScope/SetProject/CreateSec/IsValidSec) plus the now-unused
  regex vars and regexp import in controller/role/controller.go.

No functional change to the custom-roles feature or the permission cache.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
End-to-end checks for the custom-roles feature: role CRUD + built-in
immutability, built-in role permissions via ListRole, project/group/robot
member anti-escalation, sysadmin bypass, is_builtin display flags, and edge
cases (zero-permission role, empty-roles fallback).

Run: python3 tests/integration/test_custom_roles.py --url <harbor> --user admin --password <pw>

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add clrDgSelectionType="multi" to the roles datagrid so Clarity 18
renders the selection checkbox column (binding [(clrDgSelected)] to an
array alone is no longer sufficient), and migrate the row iteration from
*ngFor to @for to match every other datagrid in the portal. Without the
checkboxes admins could not select a role to enable the Edit/Delete
actions in the action bar.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@maxgraustenzel-create
maxgraustenzel-create force-pushed the origin/18124-custom-role-feature branch from 850fcfc to 2af19e6 Compare June 17, 2026 14:56
maxgraustenzel-create and others added 2 commits June 23, 2026 16:29
Extract the permission cache out of the role controller into a
cachingController decorator (cache.go). The inner controller is now
cache-agnostic, pure DB logic.

Drop the Redis "version token" cross-node generation machinery in favor
of a simpler staleness-bounded design: L1 memory (1s) -> L2 Redis (30m)
-> DB. Writes delete the L2 key and drop L1; other nodes refresh once
their L1 window expires, bounding cross-node staleness to the L1 TTL
without extra coordination. Either layer disables via env TTL <= 0
(ROLE_CACHE_L1_MEMORY_TTL, ROLE_CACHE_L2_REDIS_TTL).

Add cache_test.go covering L1 hit/expiry, L2 promotion, write
invalidation, per-layer disable, and env parsing. Update the
key-changes doc to describe the decorator design.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…L2_REDIS_TTL)

Keep RBAC off the per-request Redis path unless an operator opts in;
L1 in-memory TTL bounds staleness without Redis (cf. goharbor#23335).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
maxgraustenzel-create and others added 14 commits July 8, 2026 15:53
…-role-feature

# Conflicts:
#	make/migrations/postgresql/0190_2.16.0_schema.up.sql
Replace the hand-rolled Redis Fetch/Save in cachingController.Get with the
shared lib/cache.FetchOrSave helper, which coalesces concurrent L1-miss builds
via singleflight (one DB build + one Redis populate) and persists with a
non-cancelable context. The L1 memory tier is unchanged, so the per-request
RBAC hot path stays Redis-free.

- errRoleNotFound sentinel keeps a missing role out of the cache.
- On any L2/Redis error, fall back to a direct DB read so a Redis hiccup never
  fails an authz check.
- Update(): log instead of silently skipping invalidation when role is nil.
- gitignore compiled photon component binaries.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Not part of the custom-roles feature; committed by accident in early commits.

- delete init_permissions.sql:Zone.Identifier (WSL download marker, orphaned)
- delete 'make/harbor.yml copy.tmpl' (file-manager duplicate)
- delete make/photon/registry/config.yml (build-generated file)
- revert make/harbor.yml.tmpl (hostname localhost -> upstream default)
- revert make/prepare (drop 'set -x' debug tracing)
- delete docs/custom-roles-{key-changes,cache-comparison}.md (internal dev notes)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- casbin_adapter: restore policyLinesOfRole() call (drop inlined duplicate +
  commented-out line; also restores the empty-role-name guard)
- evaluator: drop stray blank line
- role controller/model tests: remove dead copied-from-robot /* */ blocks;
  add TestCreateCustomRole, TestSetLevel, TestSetEditable
- portal: internationalize the Roles nav link (ROLE.ROLE_NAV) + add ROLE_NAV
  to tr/pt-br/zh-cn/zh-tw lang files; remove stray TODO comments

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- tests/apitests base.py: fix duplicated 'robot' dict key -> 'role' (RoleApi
  was overwriting the robot client)
- permissions.go: remove dead isProjectAdmin block (computed via a DB call then
  discarded) + now-unused mc field and member/local imports
- role.go: drop vague '// more validation' comment
- portal: remove console.log debug in add-robot; drop '// MGS TODO' in
  base.module; tidy stray blank lines/whitespace in add-member template
- gitignore build-portal.sh and install*.log

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add TestCreateRole_NonSysAdminForbidden: a non-sysadmin creating a role with
broad permissions is rejected with 403 at the checkSysAdmin gate before any
role is created, so there is no privilege-escalation path through role
create/update.

Given that, the isValidRolePermissionScope subset-check is not wired into this
handler: role mutation is sysadmin-only (a sysadmin already holds every
permission, and has no enumerable role-permission list to subset against), and
escalation is already enforced on the non-admin assignment paths
(validateNoEscalation). Replace the stale '//TODO check escalation' with a doc
comment documenting this security model.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add TestUpdateProjectMember_EscalationBlocked: a caller allowed to update
members but lacking a permission the target role grants is rejected with 403 at
the handler, and the member controller's UpdateRole is never reached.

checkNoEscalation was wired into both CreateProjectMember and UpdateProjectMember
and unit-tested as a function, but no handler-level test covered the update
(modification) path -- so a regression dropping the check from UpdateProjectMember
would have gone unnoticed. This closes that gap.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- cache: role-not-found is not cached (returns nil); Redis-error on the L2 path
  falls back to the DB so a Redis hiccup never fails an authz check
- member: CreateProjectMember escalation blocked at the assign path (mirror of
  the update-path test) -> both member escalation entry points now handler-tested
- role handler: DeleteRole success, UpdateRole sysadmin-gate wiring, UpdateRole
  legacy/immutable-role denied

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…t is lazy

Explain that redisCache is the dependency-injection point for the L2 backend
(tests inject an in-memory or erroring fake) and that cache.Default() is resolved
lazily in redis() because role.Ctl is built at import time while the shared cache
is not initialized until startup.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
RolePrefix()/RoleNamePrefix was read but never registered as a config item, so
it always resolved to empty and prepended nothing. It was also applied at read
time (not stored), which would have caused retroactive display changes and
per-node divergence if ever wired. Remove the function, the constant, and the
prepending in populate() and the role event resolvers.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ents

- common/models/base.go: remove the commented-out new(Role) registration (Role
  now self-registers in pkg/role/model)
- controller/role/cache.go: tighten the redisCache/redis() comments

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Fill the ROLE i18n section to key-parity (95 keys) in every language file:
ru-ru and ko-kr had no ROLE section; the rest were missing ~20 keys each.
Most values reuse each file's own existing translations of the same terms;
role-specific strings are translated per language. Remove the stray 'Rolle'
key from de-de.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- roles.component / system-robot-accounts.component: drop debug console.log in
  the (empty) ngAfterViewInit hooks and a stray '//TODO create page size' comment
- delete src/testing/controller/role/controller.go: an unreferenced, mislabeled
  mock (it is a project-controller mock under package 'project', never used)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The ROLE i18n section was cloned from ROBOT and carried 22 keys no component
references (TOKEN/Secret, TOKEN_EXPIRATION, NAME_PREFIX, expiry/secret strings,
'Filter Robot Accounts', etc.), while 6 keys the role UI DOES reference were
absent and rendered as raw keys (ADD_ROLE, DELETE_ROLE, UPDATE_ROLE,
UPDATE_ROLE_SUCCESSFULLY, NAME_TOOLTIP, PUSH_PERMISSION_TOOLTIP).

Remove the 22 dead keys and add the 6 missing ones (translated) across all 11
language files, so ROLE now contains exactly the 79 keys the code uses.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@Vad1mo
Vad1mo requested a review from Copilot July 10, 2026 15:37

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds database-backed custom project roles across Harbor’s RBAC API, authorization engine, and portal.

Changes:

  • Adds role persistence, permission caching, APIs, audit events, and authorization evaluation.
  • Adds role management and dynamic member/robot permission selection to the portal.
  • Adds migration, localization, mocks, and tests.

Reviewed changes

Copilot reviewed 86 out of 87 changed files in this pull request and generated 24 comments.

Show a summary per file
File Description
.gitignore Ignores local build artifacts
tests/integration/.gitignore Ignores Python test artifacts
tests/apitests/python/library/base.py Registers role API client
api/v2.0/swagger.yaml Defines role and effective-permission APIs
make/migrations/postgresql/0190_2.16.0_schema.up.sql Migrates roles and permissions
src/testing/pkg/role/manager.go Adds role manager mock
src/testing/pkg/role/dao/dao.go Adds role DAO mock
src/server/v2.0/handler/role.go Implements role endpoints
src/server/v2.0/handler/role_test.go Tests role handlers
src/server/v2.0/handler/robot.go Prevents robot permission escalation
src/server/v2.0/handler/robot_test.go Tests escalation checks
src/server/v2.0/handler/project.go Exposes effective permissions
src/server/v2.0/handler/permissions.go Extends permission catalog
src/server/v2.0/handler/model/role.go Converts role API models
src/server/v2.0/handler/member.go Validates role assignment scope
src/server/v2.0/handler/handler.go Registers role handlers
src/portal/src/i18n/lang/zh-tw-lang.json Adds Traditional Chinese role text
src/portal/src/i18n/lang/zh-cn-lang.json Adds Simplified Chinese role text
src/portal/src/i18n/lang/tr-tr-lang.json Adds Turkish role text
src/portal/src/i18n/lang/ru-ru-lang.json Adds Russian role text
src/portal/src/i18n/lang/pt-br-lang.json Adds Portuguese role text
src/portal/src/i18n/lang/ko-kr-lang.json Adds Korean role text
src/portal/src/i18n/lang/fr-fr-lang.json Adds French role text
src/portal/src/i18n/lang/es-es-lang.json Adds Spanish role text
src/portal/src/i18n/lang/en-us-lang.json Adds English role text
src/portal/src/i18n/lang/de-de-lang.json Adds German role text
src/portal/src/app/shared/entities/shared.const.ts Adds role confirmation targets
src/portal/src/app/shared/components/role-permissions-panel/role-permissions-panel.component.ts Implements role permission selector
src/portal/src/app/shared/components/role-permissions-panel/role-permissions-panel.component.spec.ts Tests permission selector
src/portal/src/app/shared/components/role-permissions-panel/role-permissions-panel.component.scss Styles permission selector
src/portal/src/app/shared/components/role-permissions-panel/role-permissions-panel.component.html Renders permission selector
src/portal/src/app/shared/components/robot-permissions-panel/robot-permissions-panel.component.ts Normalizes empty resources
src/portal/src/app/base/project/robot-account/robot-account.component.ts Loads effective permissions
src/portal/src/app/base/project/robot-account/robot-account.component.html Passes effective permissions
src/portal/src/app/base/project/robot-account/add-robot/add-robot.component.ts Filters robot permissions
src/portal/src/app/base/project/robot-account/add-robot/add-robot.component.html Uses filtered permissions
src/portal/src/app/base/project/member/member.component.ts Loads and filters roles
src/portal/src/app/base/project/member/member.component.scss Styles role lists
src/portal/src/app/base/project/member/member.component.html Adds dynamic role menus
src/portal/src/app/base/project/member/add-member/add-member.component.ts Loads selectable roles
src/portal/src/app/base/project/member/add-member/add-member.component.scss Styles member role list
src/portal/src/app/base/project/member/add-member/add-member.component.html Renders member roles
src/portal/src/app/base/project/member/add-group/add-group.component.ts Loads group roles
src/portal/src/app/base/project/member/add-group/add-group.component.scss Styles group role list
src/portal/src/app/base/project/member/add-group/add-group.component.html Renders group roles
src/portal/src/app/base/left-side-nav/system-robot-accounts/system-robot-util.ts Normalizes role resources
src/portal/src/app/base/left-side-nav/system-robot-accounts/system-robot-accounts.component.ts Minor formatting change
src/portal/src/app/base/left-side-nav/roles/roles.module.ts Defines roles module
src/portal/src/app/base/left-side-nav/roles/roles.component.ts Implements role management
src/portal/src/app/base/left-side-nav/roles/roles.component.spec.ts Tests role actions
src/portal/src/app/base/left-side-nav/roles/roles.component.scss Styles role management
src/portal/src/app/base/left-side-nav/roles/roles.component.html Renders role management
src/portal/src/app/base/left-side-nav/roles/roles-util.ts Adds role UI utilities
src/portal/src/app/base/left-side-nav/roles/add-role/add-role.component.ts Implements role editor
src/portal/src/app/base/left-side-nav/roles/add-role/add-role.component.spec.ts Tests role editor creation
src/portal/src/app/base/left-side-nav/roles/add-role/add-role.component.scss Styles role editor
src/portal/src/app/base/left-side-nav/roles/add-role/add-role.component.html Renders role wizard
src/portal/src/app/base/left-side-nav/projects/list-project/list-project.component.html Displays custom role IDs
src/portal/src/app/base/harbor-shell/harbor-shell.component.html Adds roles navigation
src/portal/src/app/base/base.module.ts Registers roles route
src/pkg/role/model/model.go Defines persistent role model
src/pkg/role/manager.go Adds role manager
src/pkg/role/manager_test.go Tests role manager
src/pkg/role/dao/dao.go Implements role persistence
src/pkg/role/dao/dao_test.go Tests role persistence
src/controller/role/model.go Defines controller role model
src/controller/role/model_test.go Tests role model helpers
src/controller/role/controller.go Implements role lifecycle
src/controller/role/controller_test.go Tests role lifecycle
src/controller/role/cache.go Caches role permissions
src/controller/member/controller.go Accepts custom role IDs
src/controller/event/topic.go Adds role audit events
src/controller/event/metadata/role.go Resolves role event metadata
src/controller/event/handler/init.go Registers role audit handlers
src/common/security/local/context.go Injects role controller
src/common/rbac/project/rbac_user.go Carries populated roles
src/common/rbac/project/evaluator.go Loads dynamic role permissions
src/common/rbac/project/evaluator_test.go Tests dynamic evaluation
src/common/rbac/const.go Adds role permission catalog
src/common/models/role.go Removes legacy role model
src/common/models/base.go Removes legacy registration

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/controller/role/cache.go Outdated
if c.l1Enabled() {
if v, ok := c.local.Load(id); ok {
if e := v.(*l1Entry); time.Now().Before(e.expiresAt) {
return e.role, nil
log.Warningf("role cache: Update succeeded with a nil role; cannot invalidate, permissions may be stale")
return nil
}
c.invalidate(ctx, r.ID)
Comment on lines +134 to +138
if err := d.roleMgr.Delete(ctx, id); err != nil {
return err
}
if err := d.rbacMgr.DeletePermissionsByRole(ctx, ROLETYPE, id); err != nil {
return err
Comment on lines +85 to +89
(ResourceMetadata, ActionCreate, PROJECT_ADMIN),
(ResourceMetadata, ActionRead, PROJECT_ADMIN),
(ResourceMetadata, ActionUpdate, PROJECT_ADMIN),
(ResourceMetadata, ActionDelete, PROJECT_ADMIN),

Comment on lines +275 to +278
// validateName validates the role name, especially '+' cannot be a valid character
func validateRoleName(name string) error {
return nil
}
Comment on lines +430 to +433
"ACCOUNT_EXISTING": "Conta de automação já existe.",
"CREATED_SUCCESS": "'{{param}}' criado com sucesso.",
"DELETION_TITLE": "Confirmar a remoção da conta",
"DELETION_SUMMARY": "Você quer remover a regra {{param}}?",
Comment on lines +22 to +25
"ACCOUNT_EXISTING": "Роль уже существует.",
"CREATED_SUCCESS": "Успешно создан '{{param}}'.",
"DELETION_TITLE": "Подтвердить удаление роботов",
"DELETION_SUMMARY": "Вы хотите удалить роботов {{param}}?",
Comment on lines +431 to +434
"ACCOUNT_EXISTING": "Robot Hesabı zaten var.",
"CREATED_SUCCESS": "Başarıyla '{{param}}' oluşturuldu.",
"DELETION_TITLE": "Robot hesaplarının kaldırılmasını onaylayın",
"DELETION_SUMMARY": "Robot hesaplarını silmek istiyor musunuz {{param}}?",
Comment on lines +431 to +434
"ACCOUNT_EXISTING": "机器人账户已经存在。",
"CREATED_SUCCESS": "创建账户 '{{param}}' 成功。",
"DELETION_TITLE": "删除账户确认",
"DELETION_SUMMARY": "您确认删除机器人账户 {{param}}?",
Comment on lines +430 to +433
"ACCOUNT_EXISTING": "機器人帳號已存在。",
"CREATED_SUCCESS": "已成功建立 '{{param}}'。",
"DELETION_TITLE": "確認移除機器人帳號",
"DELETION_SUMMARY": "您是否要刪除機器人帳號 {{param}}?",
maxgraustenzel-create and others added 6 commits July 13, 2026 09:49
- govet: drop no-op append (rbac/const.go)
- revive: rename ctl_r -> ctlR; implement validateRoleName ('+' rejected,
  matching the frontend tooltip) instead of an unused param
- whitespace/gofmt: remove leading blank lines after '{'; goimports grouping

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Auto-fixed formatting across the role feature components (prettier/prettier)
and stylelint issues in the role/member SCSS; the PR's portal files were not
lint-clean and failed the UI_UT job.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The PR added MEMBER.BUILT_IN only to en-us. Add it to the other 9 languages
(ru-ru placed in its effective MEMBER block, which is duplicated in that file).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- role-permissions-panel spec: host template used <robot-permissions-panel>
  while the ViewChild queried RolePermissionsPanelComponent (undefined) -> the
  spec (and the karma job) failed. Point the template at role-permissions-panel.
- i18n: ROLE.DELETION_SUMMARY/TITLE said 'robot accounts'; the role delete
  confirmation now refers to roles (across all 10 languages).
- add-robot: distinguish a loaded-but-empty effective-permissions result (offer
  no candidates) from not-yet-loaded (show catalog) via effectivePermissionsLoaded,
  so a user with no project permissions is not offered the whole catalog.
- role controller Update: populate modified/modified_by (+ modified_at) so the
  audit metadata the UI shows is accurate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- cache Get returns a deep copy (clone) so request code (e.g. update handler)
  cannot mutate cached authorization state
- role delete rejects when the role is still assigned to project members
  (project_member.role has no FK -> would orphan memberships); adds
  member.GetTotalOfProjectMembersByRole
- role Update populates modified/modified_by/modified_at (audit accuracy)
- migration 0190: restore metadata:list for project-admin (built-in grants it),
  widen role.name to varchar(255) to match the API/UI, add a unique index on name
- validateRoleName enforces one contract: non-empty, <=255, no '+'
- GetEffectivePermissions resolves the project once (by id) instead of per
  candidate permission, and fails on lookup/check errors instead of suppressing

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
member/add-member/add-group loaded only the first 100 roles. Add loadAllRoles()
(paginates via x-total-count) so roles beyond the first page remain selectable
and assignable.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants