From 95822d2aff128c98319c39f6cc0b6e07330cc0f8 Mon Sep 17 00:00:00 2001 From: Aamod007 Date: Sun, 14 Jun 2026 16:42:35 +0530 Subject: [PATCH 1/3] docs: clarify token privilege scoping Signed-off-by: Aamod007 --- content/en/cloud/concepts/identity-and-security/tokens.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/content/en/cloud/concepts/identity-and-security/tokens.md b/content/en/cloud/concepts/identity-and-security/tokens.md index 4e39d0c946f..a54c6c8ea5b 100644 --- a/content/en/cloud/concepts/identity-and-security/tokens.md +++ b/content/en/cloud/concepts/identity-and-security/tokens.md @@ -22,6 +22,12 @@ Access tokens are opaque tokens that conform to the OAuth 2.0 framework. They co Layer5 Cloud API tokens are scoped to your user account, not to a specific organization. This means a single API token provides access to all organizations you are a member of, similar to how [GitHub Personal Access Tokens](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens) work. For users who belong to multiple organizations, see [Specifying Organization Context]({{< ref "cloud/reference/api-reference/_index.md#specifying-organization-context" >}}) in the REST API documentation to learn how to control which organization your API requests operate on. {{< /alert >}} +## Privilege Scoping + +API tokens in Layer5 are **identity-scoped**. This means they inherently carry the exact same privileges, roles, and permissions as your user account. + +Layer5 does not currently support fine-grained, token-specific privilege scopes (for example, generating a strictly "read-only" token if you have "read-write" permissions). The token grants the bearer the same level of access you have across all environments and organizations you are a member of. + ## Creating tokens You can create a token for your user account at any time. Tokens never expire, but can be revoked. You can also give the token a descriptive label. This label will be shown in the list of tokens on your user account's security tokens page. From 5e671ae01cd0d177c9ebe8ae65156f5e0865bfbc Mon Sep 17 00:00:00 2001 From: Aamod007 Date: Tue, 21 Jul 2026 23:11:16 +0530 Subject: [PATCH 2/3] docs: clarify token privilege scoping and secure preview workflows Signed-off-by: Aamod007 --- .github/workflows/build-and-preview-docs.yml | 93 +++++++------------ .github/workflows/build-pr-preview.yml | 68 ++++++++++++++ .../concepts/identity-and-security/tokens.md | 4 +- 3 files changed, 105 insertions(+), 60 deletions(-) create mode 100644 .github/workflows/build-pr-preview.yml diff --git a/.github/workflows/build-and-preview-docs.yml b/.github/workflows/build-and-preview-docs.yml index ddf08ce9223..bf62e093eae 100644 --- a/.github/workflows/build-and-preview-docs.yml +++ b/.github/workflows/build-and-preview-docs.yml @@ -1,16 +1,19 @@ name: Build and Preview Docs on: + workflow_run: + workflows: ["Build PR Preview"] + types: [completed] pull_request_target: branches: [master] - types: [opened, synchronize, reopened, closed] + types: [closed] permissions: contents: write pull-requests: write concurrency: - group: preview-${{ github.event.pull_request.number || github.run_id }} + group: preview-${{ github.event.workflow_run.pull_requests[0].number || github.event.pull_request.number || github.run_id }} cancel-in-progress: true defaults: @@ -18,70 +21,43 @@ defaults: shell: bash jobs: - build-and-deploy-preview: + deploy-preview: runs-on: ubuntu-24.04 - outputs: - removed_prs_json: ${{ steps.prune-previews.outputs.removed_prs_json }} + if: > + (github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'success') || + (github.event_name == 'pull_request_target' && github.event.action == 'closed') env: - HUGO_VERSION: 0.158.0 PREVIEW_RETENTION_LIMIT: 6 steps: - - name: Checkout PR code - if: github.event.action != 'closed' - uses: actions/checkout@v6 + - name: Get PR number + id: pr-info + run: | + if [ "${{ github.event_name }}" = "workflow_run" ]; then + PR_NUM="${{ github.event.workflow_run.pull_requests[0].number }}" + else + PR_NUM="${{ github.event.pull_request.number }}" + fi + echo "pr_number=$PR_NUM" >> "$GITHUB_OUTPUT" + + - name: Download preview artifact + if: github.event_name == 'workflow_run' + uses: actions/download-artifact@v4 with: - repository: ${{ github.event.pull_request.head.repo.full_name }} - ref: ${{ github.event.pull_request.head.sha }} - persist-credentials: false - submodules: recursive - fetch-depth: 0 + name: pr-preview-site-${{ steps.pr-info.outputs.pr_number }} + path: ./public + github-token: ${{ secrets.GITHUB_TOKEN }} + run-id: ${{ github.event.workflow_run.id }} - name: Checkout for cleanup - if: github.event.action == 'closed' + if: github.event_name == 'pull_request_target' && github.event.action == 'closed' uses: actions/checkout@v6 with: ref: gh-pages fetch-depth: 0 - - name: Install Hugo CLI - if: github.event.action != 'closed' - run: | - wget -O ${{ runner.temp }}/hugo.deb https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.deb \ - && sudo dpkg -i ${{ runner.temp }}/hugo.deb - - - name: Install Dart Sass (npm) - if: github.event.action != 'closed' - run: | - npm i -g sass - sass --version - - - name: Setup Node - if: github.event.action != 'closed' - uses: actions/setup-node@v4 - with: - node-version: '20' - - - name: Install dependencies - if: github.event.action != 'closed' - run: - '[[ -f package-lock.json || -f npm-shrinkwrap.json ]] && npm ci || - true' - - - name: Build PR preview - if: github.event.action != 'closed' - env: - BASE_URL: /pr-preview/pr-${{ github.event.pull_request.number }}/ - HUGO_PREVIEW: 'true' - run: | - npm run build:preview - cat > public/robots.txt <<'EOF' - User-agent: * - Disallow: / - EOF - - name: Deploy PR preview - if: github.event.action != 'closed' + if: github.event_name == 'workflow_run' uses: rossjrw/pr-preview-action@v1.6.3 with: source-dir: ./public @@ -91,7 +67,7 @@ jobs: comment: false - name: Checkout gh-pages for preview retention - if: github.event.action != 'closed' + if: github.event_name == 'workflow_run' uses: actions/checkout@v6 with: ref: gh-pages @@ -100,7 +76,7 @@ jobs: - name: Prune old PR previews id: prune-previews - if: github.event.action != 'closed' + if: github.event_name == 'workflow_run' run: | cd gh-pages-maintenance mkdir -p pr-preview @@ -141,16 +117,17 @@ jobs: echo "removed_prs_json=$(printf '%s\n' "${removed_prs[@]}" | jq -R . | jq -sc .)" >> "$GITHUB_OUTPUT" - name: Comment PR with Preview URL - if: github.event.action != 'closed' + if: github.event_name == 'workflow_run' uses: marocchino/sticky-pull-request-comment@v2 with: header: pr-preview + number: ${{ steps.pr-info.outputs.pr_number }} message: | - 🚀 Preview deployment: https://docs.layer5.io/pr-preview/pr-${{ github.event.pull_request.number }}/ + 🚀 Preview deployment: https://docs.layer5.io/pr-preview/pr-${{ steps.pr-info.outputs.pr_number }}/ > *Note: Preview may take a moment (GitHub Pages deployment in progress). Please wait and refresh. Track deployment [here](https://github.com/${{ github.repository }}/actions/workflows/pages/pages-build-deployment)* - name: Comment on pruned previews - if: github.event.action != 'closed' && steps.prune-previews.outputs.removed_prs_json != '[]' + if: github.event_name == 'workflow_run' && steps.prune-previews.outputs.removed_prs_json != '[]' uses: actions/github-script@v7 env: REMOVED_PRS_JSON: ${{ steps.prune-previews.outputs.removed_prs_json }} @@ -200,7 +177,7 @@ jobs: } - name: Cleanup PR preview on close - if: github.event.action == 'closed' + if: github.event_name == 'pull_request_target' && github.event.action == 'closed' uses: rossjrw/pr-preview-action@v1.6.3 with: preview-branch: gh-pages diff --git a/.github/workflows/build-pr-preview.yml b/.github/workflows/build-pr-preview.yml new file mode 100644 index 00000000000..d319e5e65f8 --- /dev/null +++ b/.github/workflows/build-pr-preview.yml @@ -0,0 +1,68 @@ +name: Build PR Preview + +on: + pull_request: + branches: [master] + types: [opened, synchronize, reopened] + +permissions: + contents: read + +concurrency: + group: build-preview-${{ github.event.pull_request.number || github.run_id }} + cancel-in-progress: true + +defaults: + run: + shell: bash + +jobs: + build: + runs-on: ubuntu-24.04 + env: + HUGO_VERSION: 0.158.0 + + steps: + - name: Checkout PR code + uses: actions/checkout@v6 + with: + submodules: recursive + fetch-depth: 0 + + - name: Install Hugo CLI + run: | + wget -O ${{ runner.temp }}/hugo.deb https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.deb \ + && sudo dpkg -i ${{ runner.temp }}/hugo.deb + + - name: Install Dart Sass (npm) + run: | + npm i -g sass + sass --version + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Install dependencies + run: | + [[ -f package-lock.json || -f npm-shrinkwrap.json ]] && npm ci || true + + - name: Build PR preview + env: + BASE_URL: /pr-preview/pr-${{ github.event.pull_request.number }}/ + HUGO_PREVIEW: 'true' + run: | + npm run build:preview + cat > public/robots.txt <<'EOF' + User-agent: * + Disallow: / + EOF + echo "${{ github.event.pull_request.number }}" > public/PR_NUMBER.txt + + - name: Upload preview artifact + uses: actions/upload-artifact@v4 + with: + name: pr-preview-site-${{ github.event.pull_request.number }} + path: public/ + retention-days: 1 diff --git a/content/en/cloud/concepts/identity-and-security/tokens.md b/content/en/cloud/concepts/identity-and-security/tokens.md index a54c6c8ea5b..e2e83902338 100644 --- a/content/en/cloud/concepts/identity-and-security/tokens.md +++ b/content/en/cloud/concepts/identity-and-security/tokens.md @@ -24,9 +24,9 @@ Layer5 Cloud API tokens are scoped to your user account, not to a specific organ ## Privilege Scoping -API tokens in Layer5 are **identity-scoped**. This means they inherently carry the exact same privileges, roles, and permissions as your user account. +API tokens in Layer5 are **identity-scoped**. This means they inherently carry the exact same privileges, roles, and permissions as your user account within the organization and environment selected by the request context. -Layer5 does not currently support fine-grained, token-specific privilege scopes (for example, generating a strictly "read-only" token if you have "read-write" permissions). The token grants the bearer the same level of access you have across all environments and organizations you are a member of. +Users can have different roles and permissions in different organizations, meaning a token does not grant one uniform privilege set across every organization. However, Layer5 does not currently support fine-grained, token-specific privilege scopes (for example, generating a strictly "read-only" token if you have "read-write" permissions). ## Creating tokens From ccacd29a85fa0f6942586da32269df993a088278 Mon Sep 17 00:00:00 2001 From: Aamod007 Date: Tue, 21 Jul 2026 23:35:33 +0530 Subject: [PATCH 3/3] ci: refine PR preview workflows per CodeRabbit review Signed-off-by: Aamod007 --- .github/workflows/build-and-preview-docs.yml | 52 ++++++++++++++------ .github/workflows/build-pr-preview.yml | 7 ++- 2 files changed, 43 insertions(+), 16 deletions(-) diff --git a/.github/workflows/build-and-preview-docs.yml b/.github/workflows/build-and-preview-docs.yml index bf62e093eae..3f626a26021 100644 --- a/.github/workflows/build-and-preview-docs.yml +++ b/.github/workflows/build-and-preview-docs.yml @@ -11,9 +11,10 @@ on: permissions: contents: write pull-requests: write + actions: read concurrency: - group: preview-${{ github.event.workflow_run.pull_requests[0].number || github.event.pull_request.number || github.run_id }} + group: preview-${{ github.event.workflow_run.id || github.event.pull_request.number || github.run_id }} cancel-in-progress: true defaults: @@ -30,24 +31,47 @@ jobs: PREVIEW_RETENTION_LIMIT: 6 steps: - - name: Get PR number + - name: Download preview artifact + if: github.event_name == 'workflow_run' + uses: actions/download-artifact@v4 + with: + name: pr-preview-site + path: ./public + github-token: ${{ secrets.GITHUB_TOKEN }} + run-id: ${{ github.event.workflow_run.id }} + + - name: Resolve PR number id: pr-info run: | if [ "${{ github.event_name }}" = "workflow_run" ]; then - PR_NUM="${{ github.event.workflow_run.pull_requests[0].number }}" + if [ -f ./public/PR_NUMBER.txt ]; then + PR_NUM=$(cat ./public/PR_NUMBER.txt | tr -d '[:space:]') + else + PR_NUM="${{ github.event.workflow_run.pull_requests[0].number }}" + fi else PR_NUM="${{ github.event.pull_request.number }}" fi echo "pr_number=$PR_NUM" >> "$GITHUB_OUTPUT" - - name: Download preview artifact + - name: Check PR status + id: check-pr if: github.event_name == 'workflow_run' - uses: actions/download-artifact@v4 + uses: actions/github-script@v7 with: - name: pr-preview-site-${{ steps.pr-info.outputs.pr_number }} - path: ./public - github-token: ${{ secrets.GITHUB_TOKEN }} - run-id: ${{ github.event.workflow_run.id }} + script: | + const prNumber = '${{ steps.pr-info.outputs.pr_number }}'; + if (!prNumber) { + core.setFailed('Could not determine PR number'); + return; + } + const { data: pr } = await github.rest.pulls.get({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: Number(prNumber) + }); + core.info(`PR #${prNumber} state: ${pr.state}`); + core.setOutput('is_open', pr.state === 'open'); - name: Checkout for cleanup if: github.event_name == 'pull_request_target' && github.event.action == 'closed' @@ -57,7 +81,7 @@ jobs: fetch-depth: 0 - name: Deploy PR preview - if: github.event_name == 'workflow_run' + if: github.event_name == 'workflow_run' && steps.check-pr.outputs.is_open == 'true' uses: rossjrw/pr-preview-action@v1.6.3 with: source-dir: ./public @@ -67,7 +91,7 @@ jobs: comment: false - name: Checkout gh-pages for preview retention - if: github.event_name == 'workflow_run' + if: github.event_name == 'workflow_run' && steps.check-pr.outputs.is_open == 'true' uses: actions/checkout@v6 with: ref: gh-pages @@ -76,7 +100,7 @@ jobs: - name: Prune old PR previews id: prune-previews - if: github.event_name == 'workflow_run' + if: github.event_name == 'workflow_run' && steps.check-pr.outputs.is_open == 'true' run: | cd gh-pages-maintenance mkdir -p pr-preview @@ -117,7 +141,7 @@ jobs: echo "removed_prs_json=$(printf '%s\n' "${removed_prs[@]}" | jq -R . | jq -sc .)" >> "$GITHUB_OUTPUT" - name: Comment PR with Preview URL - if: github.event_name == 'workflow_run' + if: github.event_name == 'workflow_run' && steps.check-pr.outputs.is_open == 'true' uses: marocchino/sticky-pull-request-comment@v2 with: header: pr-preview @@ -127,7 +151,7 @@ jobs: > *Note: Preview may take a moment (GitHub Pages deployment in progress). Please wait and refresh. Track deployment [here](https://github.com/${{ github.repository }}/actions/workflows/pages/pages-build-deployment)* - name: Comment on pruned previews - if: github.event_name == 'workflow_run' && steps.prune-previews.outputs.removed_prs_json != '[]' + if: github.event_name == 'workflow_run' && steps.check-pr.outputs.is_open == 'true' && steps.prune-previews.outputs.removed_prs_json != '[]' uses: actions/github-script@v7 env: REMOVED_PRS_JSON: ${{ steps.prune-previews.outputs.removed_prs_json }} diff --git a/.github/workflows/build-pr-preview.yml b/.github/workflows/build-pr-preview.yml index d319e5e65f8..7b0c6c376fb 100644 --- a/.github/workflows/build-pr-preview.yml +++ b/.github/workflows/build-pr-preview.yml @@ -26,6 +26,7 @@ jobs: - name: Checkout PR code uses: actions/checkout@v6 with: + persist-credentials: false submodules: recursive fetch-depth: 0 @@ -46,7 +47,9 @@ jobs: - name: Install dependencies run: | - [[ -f package-lock.json || -f npm-shrinkwrap.json ]] && npm ci || true + if [[ -f package-lock.json || -f npm-shrinkwrap.json ]]; then + npm ci + fi - name: Build PR preview env: @@ -63,6 +66,6 @@ jobs: - name: Upload preview artifact uses: actions/upload-artifact@v4 with: - name: pr-preview-site-${{ github.event.pull_request.number }} + name: pr-preview-site path: public/ retention-days: 1