From 6dc7f1cdca48047646c7d84ab845359bc3fb4eb2 Mon Sep 17 00:00:00 2001 From: Greg Mondello Date: Mon, 12 Oct 2026 12:12:37 -0500 Subject: [PATCH 1/5] Auto-generate custom role permissions reference from IAM metadata Adds a generator + workflow that rebuild the custom-role permissions reference from custom-role-metadata.yaml in docker/iam-services (the source of truth), replacing the manual updates done by hand. - scripts/generate-custom-role-permissions.mjs rewrites the tables between generation markers; frontmatter, intro, and "Next steps" are hand-maintained. - .github/workflows/sync-custom-role-permissions.yml regenerates the page and opens a PR when triggered by docker/iam-services. The regenerated page adds the Licensing and Cloud Sandboxes sections and the DHI "Manage customizations" row (from iam-services#1172 and #1173), superseding the manual #26324. Co-Authored-By: Claude Opus 4.8 --- .../sync-custom-role-permissions.yml | 74 ++++++++ .../custom-roles/permissions-reference.md | 33 +++- scripts/generate-custom-role-permissions.mjs | 158 ++++++++++++++++++ 3 files changed, 257 insertions(+), 8 deletions(-) create mode 100644 .github/workflows/sync-custom-role-permissions.yml create mode 100644 scripts/generate-custom-role-permissions.mjs diff --git a/.github/workflows/sync-custom-role-permissions.yml b/.github/workflows/sync-custom-role-permissions.yml new file mode 100644 index 000000000000..50ac63d3e14a --- /dev/null +++ b/.github/workflows/sync-custom-role-permissions.yml @@ -0,0 +1,74 @@ +name: Sync custom role permissions + +# Regenerates the custom-role permissions reference from the IAM source of truth +# (custom-role-metadata.yaml in docker/iam-services) and opens a PR for review. +# Triggered by the "Sync docs permissions" workflow in docker/iam-services when +# that file changes on main; can also be run manually. + +on: + workflow_dispatch: + inputs: + ref: + description: 'The git ref in docker/iam-services to read custom-role-metadata.yaml from.' + type: string + required: false + default: 'main' + +concurrency: + group: sync-custom-role-permissions + cancel-in-progress: true + +jobs: + sync: + name: Sync custom role permissions reference + runs-on: ubuntu-latest + steps: + - id: setup + uses: docker/actions/setup@707e5d98a430d92e8e6c2d34faa5bfb2a9149e9b + with: + app_id: ${{ secrets.HUB_PLATFORM_APP_ID }} + app_private_key: ${{ secrets.HUB_PLATFORM_APP_PRIVATE_KEY }} + + - name: Checkout docs + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd + with: + token: ${{ steps.setup.outputs.token }} + show-progress: false + + - name: Fetch custom-role-metadata.yaml from iam-services + env: + GH_TOKEN: ${{ steps.setup.outputs.token }} + REF: ${{ inputs.ref }} + run: | + gh api \ + "repos/docker/iam-services/contents/internal/services/access_control/config/custom-role-metadata.yaml?ref=${REF}" \ + -H "Accept: application/vnd.github.raw+json" \ + > /tmp/custom-role-metadata.yaml + + - name: Generate permissions reference + run: | + node scripts/generate-custom-role-permissions.mjs \ + --metadata /tmp/custom-role-metadata.yaml \ + --doc content/manuals/security/roles-and-permissions/custom-roles/permissions-reference.md + + - name: Create PR + uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 + with: + token: ${{ steps.setup.outputs.token }} + title: '⚙️ Sync custom role permissions reference' + body: | + Automatically generated from + [`custom-role-metadata.yaml`](https://github.com/docker/iam-services/blob/main/internal/services/access_control/config/custom-role-metadata.yaml) + in `docker/iam-services` by the **Sync custom role permissions** + workflow, triggered by a change to that file. + + Only the content between the generation markers in + `permissions-reference.md` is updated. If this change is unexpected + or shouldn't ship yet, close this PR — the workflow will re-open it + the next time the source changes. + commit-message: 'Sync custom role permissions reference' + sign-commits: true + add-paths: | + content/manuals/security/roles-and-permissions/custom-roles/permissions-reference.md + branch: chore/sync-custom-role-permissions + delete-branch: true diff --git a/content/manuals/security/roles-and-permissions/custom-roles/permissions-reference.md b/content/manuals/security/roles-and-permissions/custom-roles/permissions-reference.md index 34f707e08b28..cc0e66f1dd07 100644 --- a/content/manuals/security/roles-and-permissions/custom-roles/permissions-reference.md +++ b/content/manuals/security/roles-and-permissions/custom-roles/permissions-reference.md @@ -20,6 +20,8 @@ Custom roles use permissions from organization management, Docker Hub, billing, AI Governance, Docker Hardened Images, and Docker Offload. Use the following tables to [create or edit a custom role](manage.md). + + ## Organization management | Permission | Description | @@ -45,11 +47,11 @@ the following tables to [create or edit a custom role](manage.md). ## Docker Hub -| Permission | Description | -| :--------------------------------- | :------------------------------------------------------------------------------------------- | -| View repositories | View repository details and contents | -| Manage repositories | Full repository management including settings, webhooks, privacy, Dockerfile, immutable tags | -| Manage repository team permissions | Add and remove teams from a repository, and manage team access level | +| Permission | Description | +| :--------------------------------- | :-------------------------------------------------------------------------------------------- | +| View repositories | View repository details and contents | +| Manage repositories | Full repository management including settings, webhooks, privacy, Dockerfile, immutable tags. | +| Manage repository team permissions | Add and remove teams from a repository, and manage team access level | ## Billing @@ -58,6 +60,12 @@ the following tables to [create or edit a custom role](manage.md). | View billing | View organization billing information | | Manage billing | Complete access to managing organization billing | +## Licensing + +| Permission | Description | +| :-------------- | :----------------------------------------------------------------------------------------------------------------------------------- | +| Manage licenses | Assign and revoke licenses for members and manage auto-assign rules. Does not include purchasing or changing the number of licenses. | + ## AI Governance | Permission | Description | @@ -69,9 +77,10 @@ the following tables to [create or edit a custom role](manage.md). ## DHI (Docker Hardened Images) -| Permission | Description | -| :----------------- | :----------------------------------------------- | -| Create DHI mirrors | Create Docker Hardened Image mirror repositories | +| Permission | Description | +| :-------------------- | :-------------------------------------------------------------- | +| Create DHI mirrors | Create Docker Hardened Image mirror repositories | +| Manage customizations | Create, update, and delete Docker Hardened Image customizations | ## Docker Offload @@ -79,6 +88,14 @@ the following tables to [create or edit a custom role](manage.md). | :---------------- | :--------------------------------------------- | | Offload Read-Only | View Offload account status, leases, and zones | +## Cloud Sandboxes + +| Permission | Description | +| :------------------ | :------------------------------------ | +| Use Cloud Sandboxes | Create and use Docker Cloud Sandboxes | + + + ## Next steps - [Manage custom roles](manage.md): Create, assign, and delete custom diff --git a/scripts/generate-custom-role-permissions.mjs b/scripts/generate-custom-role-permissions.mjs new file mode 100644 index 000000000000..39daac91bf7a --- /dev/null +++ b/scripts/generate-custom-role-permissions.mjs @@ -0,0 +1,158 @@ +// Regenerates the permission tables in the custom-role permissions reference +// from the IAM source of truth (`custom-role-metadata.yaml` in +// docker/iam-services). Run by the "Sync custom role permissions" workflow when +// that file changes; the result is opened as a PR for review. +// +// Only the region between the generation markers is rewritten — the frontmatter, +// intro, and "Next steps" are hand-maintained. Table columns are left +// unpadded here; prettier aligns them afterwards. +// +// Usage: +// node scripts/generate-custom-role-permissions.mjs \ +// --metadata path/to/custom-role-metadata.yaml \ +// --doc content/manuals/security/roles-and-permissions/custom-roles/permissions-reference.md +// add --check to exit non-zero (without writing) when the doc is out of date. + +import { readFileSync, writeFileSync } from 'node:fs'; + +const BEGIN = ''; +const END = ''; + +function parseArgs(argv) { + const args = {}; + for (let i = 2; i < argv.length; i += 1) { + const arg = argv[i]; + if (arg === '--check') args.check = true; + else if (arg === '--metadata') args.metadata = argv[(i += 1)]; + else if (arg === '--doc') args.doc = argv[(i += 1)]; + } + if (!args.metadata || !args.doc) { + throw new Error('Usage: --metadata --doc [--check]'); + } + return args; +} + +// Minimal parser for the fixed shape of custom-role-metadata.yaml: +// groups: +// - name: +// relations: +// - name: +// description: +// relation: +// Commented (`#`) and blank lines are ignored. Values after the first ": " are +// taken verbatim (descriptions may contain colons and commas) with surrounding +// quotes stripped. A dedicated parser avoids adding a YAML dependency to the +// docs repo for this single, controlled file. +function parseMetadata(yaml) { + const groups = []; + let group = null; + let relation = null; + + for (const rawLine of yaml.split('\n')) { + const line = rawLine.replace(/\s+$/, ''); + const trimmed = line.trim(); + if (trimmed === '' || trimmed.startsWith('#')) continue; + + const indent = line.length - line.trimStart().length; + const field = (key) => { + const prefix = `${key}:`; + const idx = trimmed.indexOf(prefix); + if (idx !== 0) return null; + let value = trimmed.slice(prefix.length).trim(); + if ( + (value.startsWith('"') && value.endsWith('"')) || + (value.startsWith("'") && value.endsWith("'")) + ) { + value = value.slice(1, -1); + } + return value; + }; + + // A group starts at ` - name:` (indent 2); a relation at ` - name:` + // (indent 6). Distinguish by indentation. + if (trimmed.startsWith('- name:')) { + const name = field.call(null, 'name') ?? trimmed.slice('- name:'.length).trim(); + if (indent <= 2) { + group = { name, relations: [] }; + groups.push(group); + relation = null; + } else { + relation = { name }; + if (group) group.relations.push(relation); + } + continue; + } + + const description = field('description'); + if (description !== null && relation) { + relation.description = description; + continue; + } + } + + return groups; +} + +// Render a GFM table with left-aligned, space-padded columns to match the +// hand-formatted style already in the docs (markdown is excluded from the +// repo's prettier run, so there is no formatter to align it afterwards). +function renderTable(rows) { + const headers = ['Permission', 'Description']; + const widths = headers.map((header, col) => + Math.max(header.length, ...rows.map((row) => row[col].length)), + ); + const line = (cells) => + `| ${cells.map((cell, col) => cell.padEnd(widths[col])).join(' | ')} |`; + const separator = `| ${widths + .map((width) => `:${'-'.repeat(Math.max(width - 1, 2))}`) + .join(' | ')} |`; + return [line(headers), separator, ...rows.map(line)].join('\n'); +} + +function renderTables(groups) { + const sections = groups + .filter((group) => group.relations.length > 0) + .map((group) => { + const rows = group.relations.map((rel) => [ + rel.name, + rel.description ?? '', + ]); + return `## ${group.name}\n\n${renderTable(rows)}`; + }); + return sections.join('\n\n'); +} + +function main() { + const args = parseArgs(process.argv); + const groups = parseMetadata(readFileSync(args.metadata, 'utf8')); + const doc = readFileSync(args.doc, 'utf8'); + + const beginIdx = doc.indexOf(BEGIN); + const endIdx = doc.indexOf(END); + if (beginIdx === -1 || endIdx === -1 || endIdx < beginIdx) { + throw new Error( + `Generation markers not found in ${args.doc}. Expected:\n${BEGIN}\n...\n${END}`, + ); + } + + const before = doc.slice(0, beginIdx + BEGIN.length); + const after = doc.slice(endIdx); + const next = `${before}\n\n${renderTables(groups)}\n\n${after}`; + + if (next === doc) { + console.log('Custom role permissions reference is up to date.'); + return; + } + + if (args.check) { + console.error( + 'Custom role permissions reference is out of date. Run the generator to update it.', + ); + process.exit(1); + } + + writeFileSync(args.doc, next); + console.log(`Updated ${args.doc}.`); +} + +main(); From d9699ad6d274914530778378c88aff680013458d Mon Sep 17 00:00:00 2001 From: Greg Mondello <72952982+gmondello@users.noreply.github.com> Date: Fri, 23 Oct 2026 14:47:30 -0500 Subject: [PATCH 2/5] Render custom role permissions from a Hugo data file Replace the Node generator and generation markers with a data file and a custom-role-permissions shortcode, following the existing hugo.Data approach. The sync workflow now only vendors custom-role-metadata.yaml into data/custom_role_permissions.yaml. The shortcode renders the tables for both HTML and Markdown output and fails the build on missing names or descriptions. Exclude the Markdown-emitting shortcode from Prettier. Co-Authored-By: Claude Sonnet 5.5 --- .../sync-custom-role-permissions.yml | 34 ++-- .prettierignore | 2 + .../custom-roles/permissions-reference.md | 76 +-------- data/custom_role_permissions.yaml | 151 +++++++++++++++++ .../_shortcodes/custom-role-permissions.html | 28 ++++ .../custom-role-permissions.markdown.md | 28 ++++ scripts/generate-custom-role-permissions.mjs | 158 ------------------ 7 files changed, 226 insertions(+), 251 deletions(-) create mode 100644 data/custom_role_permissions.yaml create mode 100644 layouts/_shortcodes/custom-role-permissions.html create mode 100644 layouts/_shortcodes/custom-role-permissions.markdown.md delete mode 100644 scripts/generate-custom-role-permissions.mjs diff --git a/.github/workflows/sync-custom-role-permissions.yml b/.github/workflows/sync-custom-role-permissions.yml index 50ac63d3e14a..5de232c6feea 100644 --- a/.github/workflows/sync-custom-role-permissions.yml +++ b/.github/workflows/sync-custom-role-permissions.yml @@ -1,7 +1,10 @@ name: Sync custom role permissions -# Regenerates the custom-role permissions reference from the IAM source of truth -# (custom-role-metadata.yaml in docker/iam-services) and opens a PR for review. +# Vendors the IAM source of truth (custom-role-metadata.yaml in +# docker/iam-services) into data/custom_role_permissions.yaml and opens a PR for +# review. The permissions reference page renders that file with the +# custom-role-permissions shortcode, so the site build never needs access to the +# private upstream repository. # Triggered by the "Sync docs permissions" workflow in docker/iam-services when # that file changes on main; can also be run manually. @@ -9,10 +12,10 @@ on: workflow_dispatch: inputs: ref: - description: 'The git ref in docker/iam-services to read custom-role-metadata.yaml from.' + description: "The git ref in docker/iam-services to read custom-role-metadata.yaml from." type: string required: false - default: 'main' + default: "main" concurrency: group: sync-custom-role-permissions @@ -20,7 +23,7 @@ concurrency: jobs: sync: - name: Sync custom role permissions reference + name: Sync custom role permissions data runs-on: ubuntu-latest steps: - id: setup @@ -35,7 +38,7 @@ jobs: token: ${{ steps.setup.outputs.token }} show-progress: false - - name: Fetch custom-role-metadata.yaml from iam-services + - name: Vendor custom-role-metadata.yaml from iam-services env: GH_TOKEN: ${{ steps.setup.outputs.token }} REF: ${{ inputs.ref }} @@ -43,32 +46,27 @@ jobs: gh api \ "repos/docker/iam-services/contents/internal/services/access_control/config/custom-role-metadata.yaml?ref=${REF}" \ -H "Accept: application/vnd.github.raw+json" \ - > /tmp/custom-role-metadata.yaml - - - name: Generate permissions reference - run: | - node scripts/generate-custom-role-permissions.mjs \ - --metadata /tmp/custom-role-metadata.yaml \ - --doc content/manuals/security/roles-and-permissions/custom-roles/permissions-reference.md + > data/custom_role_permissions.yaml - name: Create PR uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 with: token: ${{ steps.setup.outputs.token }} - title: '⚙️ Sync custom role permissions reference' + title: "⚙️ Sync custom role permissions data" body: | Automatically generated from [`custom-role-metadata.yaml`](https://github.com/docker/iam-services/blob/main/internal/services/access_control/config/custom-role-metadata.yaml) in `docker/iam-services` by the **Sync custom role permissions** workflow, triggered by a change to that file. - Only the content between the generation markers in - `permissions-reference.md` is updated. If this change is unexpected + Only `data/custom_role_permissions.yaml` is updated. The + permissions reference page renders it with the + `custom-role-permissions` shortcode. If this change is unexpected or shouldn't ship yet, close this PR — the workflow will re-open it the next time the source changes. - commit-message: 'Sync custom role permissions reference' + commit-message: "Sync custom role permissions data" sign-commits: true add-paths: | - content/manuals/security/roles-and-permissions/custom-roles/permissions-reference.md + data/custom_role_permissions.yaml branch: chore/sync-custom-role-permissions delete-branch: true diff --git a/.prettierignore b/.prettierignore index 74756670b804..07c537e77bd2 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,2 +1,4 @@ **/*.md /data/api-reference.json +# Emits Markdown (called with {{% %}}); Prettier would reflow the table. +/layouts/_shortcodes/custom-role-permissions.html diff --git a/content/manuals/security/roles-and-permissions/custom-roles/permissions-reference.md b/content/manuals/security/roles-and-permissions/custom-roles/permissions-reference.md index cc0e66f1dd07..b610ebff989f 100644 --- a/content/manuals/security/roles-and-permissions/custom-roles/permissions-reference.md +++ b/content/manuals/security/roles-and-permissions/custom-roles/permissions-reference.md @@ -20,81 +20,7 @@ Custom roles use permissions from organization management, Docker Hub, billing, AI Governance, Docker Hardened Images, and Docker Offload. Use the following tables to [create or edit a custom role](manage.md). - - -## Organization management - -| Permission | Description | -| :-------------------------------- | :---------------------------------------------------------------------------------------------- | -| View teams | View teams and team members | -| Manage teams | Create, update, and delete teams and team members | -| Manage registry access | Control which registries members can access | -| Manage image access | Set policies for which images members can pull and use | -| Update organization information | Update organization information such as name and location | -| Member management | Manage organization members, invites, and roles | -| View custom roles | View existing custom roles and their permissions | -| Manage custom roles | Full access to custom role management and assignment | -| Manage organization access tokens | Create, update, and delete repositories in this org. Push/pull or registry actions not included | -| View activity logs | Access organization audit logs and activity history | -| View usage reports | Download organization usage reports (pulls, storage) | -| View domains | View domains and domain audit settings | -| Manage domains | Manage verified domains and domain audit settings | -| View SSO and SCIM | View single sign-on and user provisioning configurations | -| Manage SSO and SCIM | Full access to SSO and SCIM management | -| Manage Desktop settings | Configure Docker Desktop settings policies and view usage reports | -| View OIDC connections | View OIDC connections and their configuration | -| Manage OIDC connections | View, create, edit, and delete OIDC connections | - -## Docker Hub - -| Permission | Description | -| :--------------------------------- | :-------------------------------------------------------------------------------------------- | -| View repositories | View repository details and contents | -| Manage repositories | Full repository management including settings, webhooks, privacy, Dockerfile, immutable tags. | -| Manage repository team permissions | Add and remove teams from a repository, and manage team access level | - -## Billing - -| Permission | Description | -| :------------- | :----------------------------------------------- | -| View billing | View organization billing information | -| Manage billing | Complete access to managing organization billing | - -## Licensing - -| Permission | Description | -| :-------------- | :----------------------------------------------------------------------------------------------------------------------------------- | -| Manage licenses | Assign and revoke licenses for members and manage auto-assign rules. Does not include purchasing or changing the number of licenses. | - -## AI Governance - -| Permission | Description | -| :-------------------- | :--------------------------------------------------- | -| View policies | View existing AI Governance policies and their rules | -| Manage policies | Full access to AI Governance policy management | -| View audit logs | View audit events for the organization | -| Manage audit settings | Update audit configuration for the organization | - -## DHI (Docker Hardened Images) - -| Permission | Description | -| :-------------------- | :-------------------------------------------------------------- | -| Create DHI mirrors | Create Docker Hardened Image mirror repositories | -| Manage customizations | Create, update, and delete Docker Hardened Image customizations | - -## Docker Offload - -| Permission | Description | -| :---------------- | :--------------------------------------------- | -| Offload Read-Only | View Offload account status, leases, and zones | - -## Cloud Sandboxes - -| Permission | Description | -| :------------------ | :------------------------------------ | -| Use Cloud Sandboxes | Create and use Docker Cloud Sandboxes | - - +{{% custom-role-permissions %}} ## Next steps diff --git a/data/custom_role_permissions.yaml b/data/custom_role_permissions.yaml new file mode 100644 index 000000000000..707bc461228e --- /dev/null +++ b/data/custom_role_permissions.yaml @@ -0,0 +1,151 @@ +# File contains metadata that is used to drive the roles UI screen. +# Custom role permissions are grouped together by domain model (note these grouping have no logical impact on access control service). +# +# Only permissions defined below will show up in the UI. +groups: + - name: Organization management + relations: + - name: View teams + description: View teams and team members + relation: permset-group-read + - name: Manage teams + description: Create, update, and delete teams and team members + relation: permset-group-write + - name: Manage registry access + description: Control which registries members can access + relation: permset-registry-access-management-write + - name: Manage image access + description: Set policies for which images members can pull and use + relation: permset-image-access-management-write + - name: Update organization information + description: Update organization information such as name and location + relation: permset-organization-info-write + - name: Member management + description: Manage organization members, invites, and roles + relation: permset-member-write + - name: View custom roles + description: View existing custom roles and their permissions + relation: permset-custom-role-read + - name: Manage custom roles + description: Full access to custom role management and assignment + relation: permset-custom-role-write + - name: Manage organization access tokens + description: Create, update, and delete organization access tokens + relation: permset-access-token-write + - name: View activity logs + description: Access organization audit logs and activity history + relation: permset-activity-log-read + - name: View usage reports + description: Download organization usage reports (pulls, storage) + relation: permset-report-read + - name: View domains + description: View domains and domain audit settings + relation: permset-domain-read + - name: Manage domains + description: Manage verified domains and domain audit settings + relation: permset-domain-write + - name: View SSO and SCIM + description: View single sign-on and user provisioning configurations + relation: permset-sso-read + - name: Manage SSO and SCIM + description: Full access to SSO and SCIM management + relation: permset-sso-write + - name: Manage Desktop settings + description: Configure Docker Desktop settings policies and view usage reports + relation: permset-desktop-settings-write + - name: View OIDC connections + description: View OIDC connections and their configuration + relation: permset-oidc-read + - name: Manage OIDC connections + description: View, create, edit, and delete OIDC connections + relation: permset-oidc-write + + - name: Docker Hub + relations: + - name: View repositories + description: View repository details and contents + relation: permset-repo-read + - name: Manage repositories + description: Full repository management including settings, webhooks, privacy, Dockerfile, immutable tags. + relation: permset-repo-write + - name: Manage repository team permissions + description: Add and remove teams from a repository, and manage team access level + relation: permset-repo-group-write + # - name: Pull Image + # description: Pull Docker images from repositories + # relation: permset-repo-image-pull + # - name: Push Image + # description: Push Docker images from repositories + # relation: permset-repo-image-push + # - name: Manage stars + # description: Star and unstar repositories + # relation: permset-repo-star-write + # - name: View tags + # description: View image tags in repositories + # relation: permset-repo-tag-read + # - name: Manage tags + # description: Delete image tags in repositories + # relation: permset-repo-tag-write + # - name: View images + # description: View image list and reclaimable size + # relation: permset-repo-image-read + # - name: Manage images + # description: Create, update and delete images + # relation: permset-repo-image-write + # - name: View teams + # description: View repository team details + # relation: permset-repo-team-read + # - name: Manage teams + # description: Create, update, and delete repository teams + # relation: permset-repo-team-write + + - name: Billing + relations: + - name: View billing + description: View organization billing information + relation: permset-billing-read + - name: Manage billing + description: Complete access to managing organization billing + relation: permset-billing-manage + + - name: Licensing + relations: + - name: Manage licenses + description: Assign and revoke licenses for members and manage auto-assign rules. Does not include purchasing or changing the number of licenses. + relation: permset-license-manage + + - name: AI Governance + relations: + - name: View policies + description: View existing AI Governance policies and their rules + relation: permset-governance-policy-read + - name: Manage policies + description: Full access to AI Governance policy management + relation: permset-governance-policy-write + - name: View audit logs + description: View audit events for the organization + relation: permset-audit_events-read + - name: Manage audit settings + description: Update audit configuration for the organization + relation: permset-audit_settings-write + + - name: DHI (Docker Hardened Images) + relations: + - name: Create DHI mirrors + description: Create Docker Hardened Image mirror repositories + relation: permset-dhi-mirror-write + - name: Manage customizations + description: Create, update, and delete Docker Hardened Image customizations + relation: permset-dhi-customization-write + + - name: Docker Offload + relations: + - name: Offload Read-Only + description: View Offload account status, leases, and zones + relation: permset-offload-read + + - name: Cloud Sandboxes + relations: + - name: Use Cloud Sandboxes + description: Create and use Docker Cloud Sandboxes + relation: permset-sandbox-use diff --git a/layouts/_shortcodes/custom-role-permissions.html b/layouts/_shortcodes/custom-role-permissions.html new file mode 100644 index 000000000000..6862912d5de5 --- /dev/null +++ b/layouts/_shortcodes/custom-role-permissions.html @@ -0,0 +1,28 @@ +{{- /* + Renders the custom role permission tables from data/custom_role_permissions.yaml, + which is synced from docker/iam-services by the "Sync custom role permissions" + workflow. Call it with {{% custom-role-permissions %}} so the headings are + parsed as Markdown and appear in the page's table of contents. +*/ -}} +{{- $data := hugo.Data.custom_role_permissions -}} +{{- if not $data.groups -}} + {{- errorf "custom-role-permissions: no groups found in data/custom_role_permissions.yaml: %s" .Position -}} +{{- end -}} +{{- range $group := $data.groups -}} + {{- if not $group.name -}} + {{- errorf "custom-role-permissions: group without a name: %s" $.Position -}} + {{- end -}} + {{- if $group.relations }} + +## {{ $group.name }} + +| Permission | Description | +| :--------- | :---------- | +{{ range $group.relations -}} + {{- if or (not .name) (not .description) -}} + {{- errorf "custom-role-permissions: relation %q in group %q needs a name and description: %s" .name $group.name $.Position -}} + {{- end -}} +| {{ replace .name "|" "\\|" }} | {{ replace .description "|" "\\|" }} | +{{ end -}} + {{- end -}} +{{- end }} diff --git a/layouts/_shortcodes/custom-role-permissions.markdown.md b/layouts/_shortcodes/custom-role-permissions.markdown.md new file mode 100644 index 000000000000..6862912d5de5 --- /dev/null +++ b/layouts/_shortcodes/custom-role-permissions.markdown.md @@ -0,0 +1,28 @@ +{{- /* + Renders the custom role permission tables from data/custom_role_permissions.yaml, + which is synced from docker/iam-services by the "Sync custom role permissions" + workflow. Call it with {{% custom-role-permissions %}} so the headings are + parsed as Markdown and appear in the page's table of contents. +*/ -}} +{{- $data := hugo.Data.custom_role_permissions -}} +{{- if not $data.groups -}} + {{- errorf "custom-role-permissions: no groups found in data/custom_role_permissions.yaml: %s" .Position -}} +{{- end -}} +{{- range $group := $data.groups -}} + {{- if not $group.name -}} + {{- errorf "custom-role-permissions: group without a name: %s" $.Position -}} + {{- end -}} + {{- if $group.relations }} + +## {{ $group.name }} + +| Permission | Description | +| :--------- | :---------- | +{{ range $group.relations -}} + {{- if or (not .name) (not .description) -}} + {{- errorf "custom-role-permissions: relation %q in group %q needs a name and description: %s" .name $group.name $.Position -}} + {{- end -}} +| {{ replace .name "|" "\\|" }} | {{ replace .description "|" "\\|" }} | +{{ end -}} + {{- end -}} +{{- end }} diff --git a/scripts/generate-custom-role-permissions.mjs b/scripts/generate-custom-role-permissions.mjs deleted file mode 100644 index 39daac91bf7a..000000000000 --- a/scripts/generate-custom-role-permissions.mjs +++ /dev/null @@ -1,158 +0,0 @@ -// Regenerates the permission tables in the custom-role permissions reference -// from the IAM source of truth (`custom-role-metadata.yaml` in -// docker/iam-services). Run by the "Sync custom role permissions" workflow when -// that file changes; the result is opened as a PR for review. -// -// Only the region between the generation markers is rewritten — the frontmatter, -// intro, and "Next steps" are hand-maintained. Table columns are left -// unpadded here; prettier aligns them afterwards. -// -// Usage: -// node scripts/generate-custom-role-permissions.mjs \ -// --metadata path/to/custom-role-metadata.yaml \ -// --doc content/manuals/security/roles-and-permissions/custom-roles/permissions-reference.md -// add --check to exit non-zero (without writing) when the doc is out of date. - -import { readFileSync, writeFileSync } from 'node:fs'; - -const BEGIN = ''; -const END = ''; - -function parseArgs(argv) { - const args = {}; - for (let i = 2; i < argv.length; i += 1) { - const arg = argv[i]; - if (arg === '--check') args.check = true; - else if (arg === '--metadata') args.metadata = argv[(i += 1)]; - else if (arg === '--doc') args.doc = argv[(i += 1)]; - } - if (!args.metadata || !args.doc) { - throw new Error('Usage: --metadata --doc [--check]'); - } - return args; -} - -// Minimal parser for the fixed shape of custom-role-metadata.yaml: -// groups: -// - name: -// relations: -// - name: -// description: -// relation: -// Commented (`#`) and blank lines are ignored. Values after the first ": " are -// taken verbatim (descriptions may contain colons and commas) with surrounding -// quotes stripped. A dedicated parser avoids adding a YAML dependency to the -// docs repo for this single, controlled file. -function parseMetadata(yaml) { - const groups = []; - let group = null; - let relation = null; - - for (const rawLine of yaml.split('\n')) { - const line = rawLine.replace(/\s+$/, ''); - const trimmed = line.trim(); - if (trimmed === '' || trimmed.startsWith('#')) continue; - - const indent = line.length - line.trimStart().length; - const field = (key) => { - const prefix = `${key}:`; - const idx = trimmed.indexOf(prefix); - if (idx !== 0) return null; - let value = trimmed.slice(prefix.length).trim(); - if ( - (value.startsWith('"') && value.endsWith('"')) || - (value.startsWith("'") && value.endsWith("'")) - ) { - value = value.slice(1, -1); - } - return value; - }; - - // A group starts at ` - name:` (indent 2); a relation at ` - name:` - // (indent 6). Distinguish by indentation. - if (trimmed.startsWith('- name:')) { - const name = field.call(null, 'name') ?? trimmed.slice('- name:'.length).trim(); - if (indent <= 2) { - group = { name, relations: [] }; - groups.push(group); - relation = null; - } else { - relation = { name }; - if (group) group.relations.push(relation); - } - continue; - } - - const description = field('description'); - if (description !== null && relation) { - relation.description = description; - continue; - } - } - - return groups; -} - -// Render a GFM table with left-aligned, space-padded columns to match the -// hand-formatted style already in the docs (markdown is excluded from the -// repo's prettier run, so there is no formatter to align it afterwards). -function renderTable(rows) { - const headers = ['Permission', 'Description']; - const widths = headers.map((header, col) => - Math.max(header.length, ...rows.map((row) => row[col].length)), - ); - const line = (cells) => - `| ${cells.map((cell, col) => cell.padEnd(widths[col])).join(' | ')} |`; - const separator = `| ${widths - .map((width) => `:${'-'.repeat(Math.max(width - 1, 2))}`) - .join(' | ')} |`; - return [line(headers), separator, ...rows.map(line)].join('\n'); -} - -function renderTables(groups) { - const sections = groups - .filter((group) => group.relations.length > 0) - .map((group) => { - const rows = group.relations.map((rel) => [ - rel.name, - rel.description ?? '', - ]); - return `## ${group.name}\n\n${renderTable(rows)}`; - }); - return sections.join('\n\n'); -} - -function main() { - const args = parseArgs(process.argv); - const groups = parseMetadata(readFileSync(args.metadata, 'utf8')); - const doc = readFileSync(args.doc, 'utf8'); - - const beginIdx = doc.indexOf(BEGIN); - const endIdx = doc.indexOf(END); - if (beginIdx === -1 || endIdx === -1 || endIdx < beginIdx) { - throw new Error( - `Generation markers not found in ${args.doc}. Expected:\n${BEGIN}\n...\n${END}`, - ); - } - - const before = doc.slice(0, beginIdx + BEGIN.length); - const after = doc.slice(endIdx); - const next = `${before}\n\n${renderTables(groups)}\n\n${after}`; - - if (next === doc) { - console.log('Custom role permissions reference is up to date.'); - return; - } - - if (args.check) { - console.error( - 'Custom role permissions reference is out of date. Run the generator to update it.', - ); - process.exit(1); - } - - writeFileSync(args.doc, next); - console.log(`Updated ${args.doc}.`); -} - -main(); From dec18ffb809da774313af450d72dda43b6f53722 Mon Sep 17 00:00:00 2001 From: Greg Mondello <72952982+gmondello@users.noreply.github.com> Date: Fri, 23 Oct 2026 14:55:40 -0500 Subject: [PATCH 3/5] Match permissions reference to main before merging Take main's version of the page so the upcoming merge of main is conflict-free. The next commit swaps the tables for the shortcode. Co-Authored-By: Claude Sonnet 5.5 --- .../custom-roles/permissions-reference.md | 85 +++++++++++++++++-- 1 file changed, 78 insertions(+), 7 deletions(-) diff --git a/content/manuals/security/roles-and-permissions/custom-roles/permissions-reference.md b/content/manuals/security/roles-and-permissions/custom-roles/permissions-reference.md index b610ebff989f..d0cafa418164 100644 --- a/content/manuals/security/roles-and-permissions/custom-roles/permissions-reference.md +++ b/content/manuals/security/roles-and-permissions/custom-roles/permissions-reference.md @@ -3,12 +3,12 @@ title: Custom role permissions reference linkTitle: Permissions reference description: >- Permissions available for Docker custom roles across organization management, - Docker Hub, billing, AI Governance, Docker Hardened Images, and Docker - Offload. + Docker Hub, billing, licensing, AI Governance, Docker Hardened Images, + Docker Offload, and Docker Cloud Sandboxes. keywords: >- custom roles, custom role permissions, Docker, Docker Hub, organization - management, billing, AI Governance, access tokens, SSO, SCIM, OIDC, DHI, - Docker Offload, security + management, billing, licensing, licenses, AI Governance, access tokens, SSO, + SCIM, OIDC, DHI, Docker Offload, Cloud Sandboxes, security weight: 20 aliases: - /enterprise/security/roles-and-permissions/custom-roles/permissions-reference/ @@ -17,10 +17,81 @@ aliases: {{< summary-bar feature_name="Custom roles" >}} Custom roles use permissions from organization management, Docker Hub, -billing, AI Governance, Docker Hardened Images, and Docker Offload. Use -the following tables to [create or edit a custom role](manage.md). +billing, licensing, AI Governance, Docker Hardened Images, Docker +Offload, and Docker Cloud Sandboxes. Use the following tables to [create +or edit a custom role](manage.md). -{{% custom-role-permissions %}} +## Organization management + +| Permission | Description | +| :-------------------------------- | :---------------------------------------------------------------------------------------------- | +| View teams | View teams and team members | +| Manage teams | Create, update, and delete teams and team members | +| Manage registry access | Control which registries members can access | +| Manage image access | Set policies for which images members can pull and use | +| Update organization information | Update organization information such as name and location | +| Member management | Manage organization members, invites, and roles | +| View custom roles | View existing custom roles and their permissions | +| Manage custom roles | Full access to custom role management and assignment | +| Manage organization access tokens | Create, update, and delete repositories in this org. Push/pull or registry actions not included | +| View activity logs | Access organization audit logs and activity history | +| View usage reports | Download organization usage reports (pulls, storage) | +| View domains | View domains and domain audit settings | +| Manage domains | Manage verified domains and domain audit settings | +| View SSO and SCIM | View single sign-on and user provisioning configurations | +| Manage SSO and SCIM | Full access to SSO and SCIM management | +| Manage Desktop settings | Configure Docker Desktop settings policies and view usage reports | +| View OIDC connections | View OIDC connections and their configuration | +| Manage OIDC connections | View, create, edit, and delete OIDC connections | + +## Docker Hub + +| Permission | Description | +| :--------------------------------- | :------------------------------------------------------------------------------------------- | +| View repositories | View repository details and contents | +| Manage repositories | Full repository management including settings, webhooks, privacy, Dockerfile, immutable tags | +| Manage repository team permissions | Add and remove teams from a repository, and manage team access level | + +## Billing + +| Permission | Description | +| :------------- | :----------------------------------------------- | +| View billing | View organization billing information | +| Manage billing | Complete access to managing organization billing | + +## Licensing + +| Permission | Description | +| :-------------- | :---------------------------------------------------------------------------------------------------------------------------------- | +| Manage licenses | Assign and revoke licenses for members and manage auto-assign rules. Does not include purchasing or changing the number of licenses | + +## AI Governance + +| Permission | Description | +| :-------------------- | :--------------------------------------------------- | +| View policies | View existing AI Governance policies and their rules | +| Manage policies | Full access to AI Governance policy management | +| View audit logs | View audit events for the organization | +| Manage audit settings | Update audit configuration for the organization | + +## DHI (Docker Hardened Images) + +| Permission | Description | +| :-------------------- | :-------------------------------------------------------------- | +| Create DHI mirrors | Create Docker Hardened Image mirror repositories | +| Manage customizations | Create, update, and delete Docker Hardened Image customizations | + +## Docker Offload + +| Permission | Description | +| :---------------- | :--------------------------------------------- | +| Offload Read-Only | View Offload account status, leases, and zones | + +## Cloud Sandboxes + +| Permission | Description | +| :------------------ | :------------------------------------ | +| Use Cloud Sandboxes | Create and use Docker Cloud Sandboxes | ## Next steps From 47d44c25cdad29ef23969d83cc9fbf4bf89bd7df Mon Sep 17 00:00:00 2001 From: Greg Mondello <72952982+gmondello@users.noreply.github.com> Date: Fri, 23 Oct 2026 14:56:03 -0500 Subject: [PATCH 4/5] Render the permissions reference with the custom-role-permissions shortcode Replace the hand-maintained tables from main with the shortcode call. Keeps main's updated front matter and intro. Co-Authored-By: Claude Sonnet 5.5 --- .../custom-roles/permissions-reference.md | 72 +------------------ 1 file changed, 1 insertion(+), 71 deletions(-) diff --git a/content/manuals/security/roles-and-permissions/custom-roles/permissions-reference.md b/content/manuals/security/roles-and-permissions/custom-roles/permissions-reference.md index d0cafa418164..85a49f7cf4bf 100644 --- a/content/manuals/security/roles-and-permissions/custom-roles/permissions-reference.md +++ b/content/manuals/security/roles-and-permissions/custom-roles/permissions-reference.md @@ -21,77 +21,7 @@ billing, licensing, AI Governance, Docker Hardened Images, Docker Offload, and Docker Cloud Sandboxes. Use the following tables to [create or edit a custom role](manage.md). -## Organization management - -| Permission | Description | -| :-------------------------------- | :---------------------------------------------------------------------------------------------- | -| View teams | View teams and team members | -| Manage teams | Create, update, and delete teams and team members | -| Manage registry access | Control which registries members can access | -| Manage image access | Set policies for which images members can pull and use | -| Update organization information | Update organization information such as name and location | -| Member management | Manage organization members, invites, and roles | -| View custom roles | View existing custom roles and their permissions | -| Manage custom roles | Full access to custom role management and assignment | -| Manage organization access tokens | Create, update, and delete repositories in this org. Push/pull or registry actions not included | -| View activity logs | Access organization audit logs and activity history | -| View usage reports | Download organization usage reports (pulls, storage) | -| View domains | View domains and domain audit settings | -| Manage domains | Manage verified domains and domain audit settings | -| View SSO and SCIM | View single sign-on and user provisioning configurations | -| Manage SSO and SCIM | Full access to SSO and SCIM management | -| Manage Desktop settings | Configure Docker Desktop settings policies and view usage reports | -| View OIDC connections | View OIDC connections and their configuration | -| Manage OIDC connections | View, create, edit, and delete OIDC connections | - -## Docker Hub - -| Permission | Description | -| :--------------------------------- | :------------------------------------------------------------------------------------------- | -| View repositories | View repository details and contents | -| Manage repositories | Full repository management including settings, webhooks, privacy, Dockerfile, immutable tags | -| Manage repository team permissions | Add and remove teams from a repository, and manage team access level | - -## Billing - -| Permission | Description | -| :------------- | :----------------------------------------------- | -| View billing | View organization billing information | -| Manage billing | Complete access to managing organization billing | - -## Licensing - -| Permission | Description | -| :-------------- | :---------------------------------------------------------------------------------------------------------------------------------- | -| Manage licenses | Assign and revoke licenses for members and manage auto-assign rules. Does not include purchasing or changing the number of licenses | - -## AI Governance - -| Permission | Description | -| :-------------------- | :--------------------------------------------------- | -| View policies | View existing AI Governance policies and their rules | -| Manage policies | Full access to AI Governance policy management | -| View audit logs | View audit events for the organization | -| Manage audit settings | Update audit configuration for the organization | - -## DHI (Docker Hardened Images) - -| Permission | Description | -| :-------------------- | :-------------------------------------------------------------- | -| Create DHI mirrors | Create Docker Hardened Image mirror repositories | -| Manage customizations | Create, update, and delete Docker Hardened Image customizations | - -## Docker Offload - -| Permission | Description | -| :---------------- | :--------------------------------------------- | -| Offload Read-Only | View Offload account status, leases, and zones | - -## Cloud Sandboxes - -| Permission | Description | -| :------------------ | :------------------------------------ | -| Use Cloud Sandboxes | Create and use Docker Cloud Sandboxes | +{{% custom-role-permissions %}} ## Next steps From 87f8dee0d9087b5454313f4fa8ad81306a5fc9a4 Mon Sep 17 00:00:00 2001 From: Greg Mondello Date: Wed, 28 Oct 2026 11:09:30 -0500 Subject: [PATCH 5/5] Render permissions reference with an inline shortcode --- .../sync-custom-role-permissions.yml | 13 ++++----- .prettierignore | 2 -- .../custom-roles/permissions-reference.md | 25 ++++++++++++++++- .../_shortcodes/custom-role-permissions.html | 28 ------------------- .../custom-role-permissions.markdown.md | 28 ------------------- 5 files changed, 30 insertions(+), 66 deletions(-) delete mode 100644 layouts/_shortcodes/custom-role-permissions.html delete mode 100644 layouts/_shortcodes/custom-role-permissions.markdown.md diff --git a/.github/workflows/sync-custom-role-permissions.yml b/.github/workflows/sync-custom-role-permissions.yml index 5de232c6feea..d3b50559e778 100644 --- a/.github/workflows/sync-custom-role-permissions.yml +++ b/.github/workflows/sync-custom-role-permissions.yml @@ -2,9 +2,9 @@ name: Sync custom role permissions # Vendors the IAM source of truth (custom-role-metadata.yaml in # docker/iam-services) into data/custom_role_permissions.yaml and opens a PR for -# review. The permissions reference page renders that file with the -# custom-role-permissions shortcode, so the site build never needs access to the -# private upstream repository. +# review. The permissions reference page renders that file with an inline +# shortcode, so the site build never needs access to the private upstream +# repository. # Triggered by the "Sync docs permissions" workflow in docker/iam-services when # that file changes on main; can also be run manually. @@ -60,10 +60,9 @@ jobs: workflow, triggered by a change to that file. Only `data/custom_role_permissions.yaml` is updated. The - permissions reference page renders it with the - `custom-role-permissions` shortcode. If this change is unexpected - or shouldn't ship yet, close this PR — the workflow will re-open it - the next time the source changes. + permissions reference page renders it with an inline shortcode. If + this change is unexpected or shouldn't ship yet, close this PR — + the workflow will re-open it the next time the source changes. commit-message: "Sync custom role permissions data" sign-commits: true add-paths: | diff --git a/.prettierignore b/.prettierignore index 07c537e77bd2..74756670b804 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,4 +1,2 @@ **/*.md /data/api-reference.json -# Emits Markdown (called with {{% %}}); Prettier would reflow the table. -/layouts/_shortcodes/custom-role-permissions.html diff --git a/content/manuals/security/roles-and-permissions/custom-roles/permissions-reference.md b/content/manuals/security/roles-and-permissions/custom-roles/permissions-reference.md index 85a49f7cf4bf..c913b8c5c533 100644 --- a/content/manuals/security/roles-and-permissions/custom-roles/permissions-reference.md +++ b/content/manuals/security/roles-and-permissions/custom-roles/permissions-reference.md @@ -21,7 +21,30 @@ billing, licensing, AI Governance, Docker Hardened Images, Docker Offload, and Docker Cloud Sandboxes. Use the following tables to [create or edit a custom role](manage.md). -{{% custom-role-permissions %}} +{{% custom_role_permissions.inline %}} +{{- $data := hugo.Data.custom_role_permissions -}} +{{- if not $data.groups -}} + {{- errorf "custom-role-permissions: no groups found in data/custom_role_permissions.yaml: %s" .Position -}} +{{- end -}} +{{- range $group := $data.groups -}} + {{- if not $group.name -}} + {{- errorf "custom-role-permissions: group without a name: %s" $.Position -}} + {{- end -}} + {{- if $group.relations }} + +## {{ $group.name }} + +| Permission | Description | +| :--------- | :---------- | +{{ range $group.relations -}} + {{- if or (not .name) (not .description) -}} + {{- errorf "custom-role-permissions: relation %q in group %q needs a name and description: %s" .name $group.name $.Position -}} + {{- end -}} +| {{ replace .name "|" "\\|" }} | {{ replace .description "|" "\\|" }} | +{{ end -}} + {{- end -}} +{{- end }} +{{% /custom_role_permissions.inline %}} ## Next steps diff --git a/layouts/_shortcodes/custom-role-permissions.html b/layouts/_shortcodes/custom-role-permissions.html deleted file mode 100644 index 6862912d5de5..000000000000 --- a/layouts/_shortcodes/custom-role-permissions.html +++ /dev/null @@ -1,28 +0,0 @@ -{{- /* - Renders the custom role permission tables from data/custom_role_permissions.yaml, - which is synced from docker/iam-services by the "Sync custom role permissions" - workflow. Call it with {{% custom-role-permissions %}} so the headings are - parsed as Markdown and appear in the page's table of contents. -*/ -}} -{{- $data := hugo.Data.custom_role_permissions -}} -{{- if not $data.groups -}} - {{- errorf "custom-role-permissions: no groups found in data/custom_role_permissions.yaml: %s" .Position -}} -{{- end -}} -{{- range $group := $data.groups -}} - {{- if not $group.name -}} - {{- errorf "custom-role-permissions: group without a name: %s" $.Position -}} - {{- end -}} - {{- if $group.relations }} - -## {{ $group.name }} - -| Permission | Description | -| :--------- | :---------- | -{{ range $group.relations -}} - {{- if or (not .name) (not .description) -}} - {{- errorf "custom-role-permissions: relation %q in group %q needs a name and description: %s" .name $group.name $.Position -}} - {{- end -}} -| {{ replace .name "|" "\\|" }} | {{ replace .description "|" "\\|" }} | -{{ end -}} - {{- end -}} -{{- end }} diff --git a/layouts/_shortcodes/custom-role-permissions.markdown.md b/layouts/_shortcodes/custom-role-permissions.markdown.md deleted file mode 100644 index 6862912d5de5..000000000000 --- a/layouts/_shortcodes/custom-role-permissions.markdown.md +++ /dev/null @@ -1,28 +0,0 @@ -{{- /* - Renders the custom role permission tables from data/custom_role_permissions.yaml, - which is synced from docker/iam-services by the "Sync custom role permissions" - workflow. Call it with {{% custom-role-permissions %}} so the headings are - parsed as Markdown and appear in the page's table of contents. -*/ -}} -{{- $data := hugo.Data.custom_role_permissions -}} -{{- if not $data.groups -}} - {{- errorf "custom-role-permissions: no groups found in data/custom_role_permissions.yaml: %s" .Position -}} -{{- end -}} -{{- range $group := $data.groups -}} - {{- if not $group.name -}} - {{- errorf "custom-role-permissions: group without a name: %s" $.Position -}} - {{- end -}} - {{- if $group.relations }} - -## {{ $group.name }} - -| Permission | Description | -| :--------- | :---------- | -{{ range $group.relations -}} - {{- if or (not .name) (not .description) -}} - {{- errorf "custom-role-permissions: relation %q in group %q needs a name and description: %s" .name $group.name $.Position -}} - {{- end -}} -| {{ replace .name "|" "\\|" }} | {{ replace .description "|" "\\|" }} | -{{ end -}} - {{- end -}} -{{- end }}