diff --git a/.github/workflows/cd.yaml b/.github/workflows/cd.yaml new file mode 100644 index 0000000..3f73aef --- /dev/null +++ b/.github/workflows/cd.yaml @@ -0,0 +1,76 @@ +name: cd + +on: + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + inputs: + image_version: + description: 'The beta image to push' + required: false + default: 'none' + release: + types: [created] + +env: + IMAGE_NAME: authzen-todo-backend + VAULT_ADDR: https://vault.eng.aserto.com/ + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - name: Get Version + id: get_version + run: | + EVENT_NAME=${{ github.event_name }} + IS_GITHUB_TAG=${{ startsWith(github.ref, 'refs/tags/v') }} + if [[ $EVENT_NAME == 'release' && $IS_GITHUB_TAG == 'true' ]]; then + IMAGE_VERSION=${GITHUB_REF/refs\/tags\/v/} + else + IMAGE_VERSION=${{ github.event.inputs.image_version }} + fi + echo "using version: $IMAGE_VERSION" + echo ::set-output name=VERSION::$IMAGE_VERSION + + - name: Read Configuration + uses: hashicorp/vault-action@v2.5.0 + id: vault + with: + url: https://vault.eng.aserto.com/ + token: ${{ secrets.VAULT_TOKEN }} + secrets: | + kv/data/github "SSH_PRIVATE_KEY" | SSH_PRIVATE_KEY; + kv/data/github "READ_WRITE_TOKEN" | READ_WRITE_TOKEN; + + - uses: actions/checkout@v2 + + - name: Setup git + run: | + mkdir -p $HOME/.ssh + umask 0077 && echo -e "${SSH_PRIVATE_KEY}" > $HOME/.ssh/id_rsa + ssh-keyscan github.com >> $HOME/.ssh/known_hosts + git config --global url."git@github.com:".insteadOf https://github.com/ + git config --global user.email "github-bot@aserto.com" + git config --global user.name "Aserto Bot" + - name: Checkout kube-play + uses: actions/checkout@master + with: + repository: aserto-dev/kube-play + fetch-depth: '0' + ref: ${{ startsWith(github.ref, 'refs/tags/v') && 'refs/heads/main' || 'refs/heads/beta' }} + path: kube-play + # github personal access token, for pushing commits + token: ${{ steps.vault.outputs.READ_WRITE_TOKEN }} + + - name: Update version + uses: mikefarah/yq@v4.6.1 + with: + cmd: yq eval --inplace '.spec.template.spec.containers[0].image = "ghcr.io/aserto-dev/${{ env.IMAGE_NAME }}:${{ steps.get_version.outputs.VERSION }}"' ./kube-play/demo-authzen-todo-backend/templates/deployment.yaml + + - name: Commit and push + id: push + run: | + cd kube-play + git add . + git commit -m "update authzen-todo-backend" + git push diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 0000000..e5784b2 --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,118 @@ +name: ci + +on: + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + push: + branches: + - main + - beta + - release-* + # Publish `v1.2.3` tags as releases. + tags: + - v* + # Run tests for PRs + pull_request: + branches: + - main + - beta + - release-* +env: + VAULT_ADDR: https://vault.eng.aserto.com/ + PRE_RELEASE: ${{ github.ref == 'refs/heads/main' && 'development' || '' }} + DOCKER_IMAGE: "ghcr.io/aserto-dev/authzen-todo-backend" +defaults: + run: + working-directory: ./interop/authzen-todo-backend + +jobs: + push: + runs-on: ubuntu-latest + outputs: + IMAGE_VERSION: ${{ steps.calc_tags.outputs.version }} + # when on a branch only push if the branch is main + # always push when ref is a tag + if: github.event_name == 'push' && ( github.ref == 'refs/heads/main' || github.ref == 'refs/heads/beta' || startsWith(github.ref, 'refs/tags/v') ) + steps: + - name: Read Configuration + uses: hashicorp/vault-action@v2.5.0 + id: vault + with: + url: https://vault.eng.aserto.com/ + token: ${{ secrets.VAULT_TOKEN }} + secrets: | + kv/data/github "USERNAME" | DOCKER_USERNAME; + kv/data/github "DOCKER_PUSH_TOKEN" | DOCKER_PASSWORD; + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Login to GitHub Packages Docker Registry + uses: docker/login-action@v1 + with: + registry: https://ghcr.io + username: ${{ env.DOCKER_USERNAME }} + password: ${{ env.DOCKER_PASSWORD }} + - name: Calculate Tags + id: "calc_tags" + uses: aserto-dev/sver-action@v0.0.13 + with: + docker_image: "aserto-dev/authzen-todo-backend" + docker_registry: "https://ghcr.io" + docker_username: ${{ env.DOCKER_USERNAME }} + docker_password: ${{ env.DOCKER_PASSWORD }} + - name: Build Image + run: | + docker build -t "${{ env.DOCKER_IMAGE }}" . + - name: Push image to GitHub Container Registry + run: | + echo "${{ steps.calc_tags.outputs.version }}" | \ + xargs -I{} bash -c "docker tag '${{ env.DOCKER_IMAGE }}' '${{ env.DOCKER_IMAGE }}:{}' && docker push '${{ env.DOCKER_IMAGE }}:{}'" + + trigger-cd: + runs-on: ubuntu-latest + needs: push + if: github.event_name == 'push' && github.ref == 'refs/heads/beta' + steps: + - name: Read Configuration + uses: hashicorp/vault-action@v2.5.0 + id: vault + with: + url: https://vault.eng.aserto.com/ + token: ${{ secrets.VAULT_TOKEN }} + secrets: | + kv/data/github "ROOT_TOKEN" | ROOT_TOKEN; + - name: Trigger CD workflow + uses: benc-uk/workflow-dispatch@v1 + with: + workflow: cd + token: ${{ env.ROOT_TOKEN }} + inputs: '{"image_version": "${{ needs.push.outputs.IMAGE_VERSION }}"}' + + + release: + needs: push + runs-on: ubuntu-latest + # Only release when ref is a tag + if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') + steps: + - name: Read Configuration + uses: hashicorp/vault-action@v2.5.0 + id: vault + with: + url: https://vault.eng.aserto.com/ + token: ${{ secrets.VAULT_TOKEN }} + secrets: | + kv/data/github "SSH_PRIVATE_KEY" | SSH_PRIVATE_KEY; + kv/data/github "READ_WRITE_TOKEN" | READ_WRITE_TOKEN; + - name: Create Release + id: create_release + uses: actions/create-release@v1 + env: + # A PAT is required to work around + # https://docs.github.com/en/actions/reference/events-that-trigger-workflows#triggering-new-workflows-using-a-personal-access-token + GITHUB_TOKEN: ${{ steps.vault.outputs.READ_WRITE_TOKEN }} + with: + tag_name: ${{ github.ref }} + release_name: ${{ github.ref }} + draft: false + prerelease: false diff --git a/.github/workflows/nodejs.yaml b/.github/workflows/nodejs.yaml new file mode 100644 index 0000000..7832d78 --- /dev/null +++ b/.github/workflows/nodejs.yaml @@ -0,0 +1,71 @@ +name: Node.js CI + +on: + pull_request: + types: [opened, synchronize, reopened] + +env: + AUTHZEN_PDP_URL: https://authzen-proxy.demo.aserto.com + +jobs: + tests: + #if: ${{ false }} # disable for now + name: Run tests + runs-on: ubuntu-latest + defaults: + run: + working-directory: ./interop/authzen-todo-backend + steps: + - name: Checkout Repo + uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: Use Node.js 16.x + uses: actions/setup-node@v1 + with: + node-version: '16.x' + - name: Get yarn cache directory path + id: yarn-cache-dir-path + run: echo "::set-output name=dir::$(yarn cache dir)" + - uses: actions/cache@v2 + id: yarn-cache + with: + path: | + ${{ steps.yarn-cache-dir-path.outputs.dir }} + **/node_modules + key: ${{ runner.os }}-yarn-cache-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn-cache- + - run: yarn install --frozen-lockfile + - name: tests + run: yarn test + env: + JEST_JUNIT_OUTPUT: 'reports/junit/js-test-results.xml' + + typecheck: + name: Run TypeScript checker + runs-on: ubuntu-latest + steps: + - name: Checkout Repo + uses: actions/checkout@v2 + - name: Use Node.js 16.x + uses: actions/setup-node@v1 + with: + node-version: '16.x' + - name: Get yarn cache directory path + id: yarn-cache-dir-path + run: echo "::set-output name=dir::$(yarn cache dir)" + - uses: actions/cache@v2 + id: yarn-cache + with: + path: | + ${{ steps.yarn-cache-dir-path.outputs.dir }} + **/node_modules + key: ${{ runner.os }}-yarn-cache-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn-cache- + - run: yarn install --frozen-lockfile + - name: typecheck + run: yarn tsc + diff --git a/api/authorization-api-1_0.md b/api/authorization-api-1_0.md index 1cec8b3..a9100a2 100644 --- a/api/authorization-api-1_0.md +++ b/api/authorization-api-1_0.md @@ -219,14 +219,32 @@ The request follows a format similar to that used in XACML-JSON (TBD add normati ### The Access Evaluation API Response -The simplest form of a response is simply a boolean representing a decision. In this specification, assuming the evaluation was successful, there are only 2 possible responses: +The simplest form of a response is simply a boolean representing a decision, indicated by a `"decision"` field. In this specification, assuming the evaluation was successful, there are only 2 possible responses: * `true`: The access request is permitted to go forward * `false`: The access request is denied and MUST NOT be permitted to go forward -#### Additional Parameters +#### Query Decision {#query-decision} -In addition to a `decision`, a response may contain: +The following is a non-normative example of a simple query decision: + +~~~ json +{ + "decision": true +} +~~~ + +#### Additional context in a response + +In addition to a `"decision"`, a response may contain a `"context"` field which can be any JSON object. This context can convey additional information that can be used by the PEP as part of the decision evaluation process. Examples include: + +* XACML's notion of "advice" and "obligations" +* Hints for rendering UI state +* Instructions for step-up authentication + +#### Example context + +An implementation MAY follow a structured approach to `"context"`, in which it presents the reasons that an authorization request failed. * A list of identifiers representing the items (policies, graph nodes, tuples) that were used in the decision-making process * A list of reasons as to why access is permitted or denied. @@ -243,7 +261,7 @@ A Reason Field is a JSON object that has keys and values of type `string`. The f } ~~~ -#### Reason Object {#reason-object} +###### Reason Object {#reason-object} A Reason Object specifies a particular reason. It is a JSON object that has the following fields: id: @@ -271,11 +289,21 @@ The following is a non-normative example of a Reason Object: ~~~ {: #example-reason-object title="Example of a Reason Object"} -#### Sample Response (non-normative) +#### Sample Response with additional context (non-normative) ~~~json { - "decision": true + "decision": true, + "context": { + "id": 0, + "reason_admin": { + "en": "Request failed policy C076E82F" + }, + "reason_user": { + "en-403": "Insufficient privileges. Contact your administrator", + "es-403": "Privilegios insuficientes. PĆ³ngase en contacto con su administrador" + } + } } ~~~ @@ -431,8 +459,8 @@ iat: exp: : OPTIONAL. The time in `integer` format, expressed at epoch milliseconds, after which the response SHOULD NOT be used -reasons: -: OPTIONAL. A Reason Object ({{reason-object}}) which provide details around the decision. +context: +: OPTIONAL. A JSON object that provides additional evaluation context. evaluationDuration: : OPTIONAL. The time in milliseconds, in `integer` format, that it took to respond to the Access Evaluation Request. @@ -500,13 +528,13 @@ X-Request-ID: bfe9eb29-ab87-4ca3-be83-a1d5d8305716 The success response to an Access Evaluations Request is an Access Evaluations Response. It is a HTTP response of type `application/json`. Its body is a JSON object that contains the following fields: iat: -: REQUIRED. The issued at time in `integer` format, expressed as epoch milliseconds +: OPTIONAL. The issued at time in `integer` format, expressed as epoch milliseconds exp: -: REQUIRED. The time in `integer` format, expressed at epoch milliseconds, after which the response SHOULD NOT be used +: OPTIONAL. The time in `integer` format, expressed at epoch milliseconds, after which the response SHOULD NOT be used subject: -: REQUIRED. The subject for which the response is being issued. The format of this field is as described in the Subjects section ({{subjects}}) +: OPTIONAL. The subject for which the response is being issued. The format of this field is as described in the Subjects section ({{subjects}}) decisions: : REQUIRED. An array of Resource Query Decisions as described in the Resource Query Decision section ({{resource-query-decision}}). @@ -515,7 +543,7 @@ reasons: : OPTIONAL. An array of Reason Objects ({{reason-object}}) which provide details of every reason identifier specified in the `decisions` field. This field is REQUIRED if there is at least one decision in the `decisions` field that specifies a `reason_ids` field. The content of the `reasons` field MUST provide details of every identifier in the `reason_ids` fields in the `decisions` array. evaluationDuration: -: REQUIRED. The time in milliseconds, in `integer` format, that it took to respond to the Access Evaluation Request. +: OPTIONAL. The time in milliseconds, in `integer` format, that it took to respond to the Access Evaluation Request. Following is a non-normative example of an Access Evaluations Response: diff --git a/interop/authzen-todo-backend/package.json b/interop/authzen-todo-backend/package.json index 5dd05af..ecd2b0c 100644 --- a/interop/authzen-todo-backend/package.json +++ b/interop/authzen-todo-backend/package.json @@ -1,6 +1,6 @@ { "name": "authzen-todo-backend", - "version": "0.1.4", + "version": "0.1.9", "description": "AuthZEN todo backend interop sample", "main": "src/index.ts", "scripts": { @@ -10,7 +10,7 @@ "docker-build": "scripts/docker-build.sh", "docker-run": "scripts/docker-run.sh", "tsc": "tsc --noEmit --incremental", - "release": "npm version patch && git push --follow-tags", + "release": "./scripts/release.sh", "test": "./scripts/test.sh" }, "keywords": [],