Skip to content

Commit

Permalink
Add initial version of login to Octopus with OIDC or API keys (#3)
Browse files Browse the repository at this point in the history
  • Loading branch information
geofflamrock committed Aug 23, 2023
1 parent 9c6c45c commit e635f8e
Show file tree
Hide file tree
Showing 19 changed files with 18,727 additions and 16,963 deletions.
5 changes: 5 additions & 0 deletions .changeset/quick-tools-repeat.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@octopusdeploy/login": minor
---

Add initial version of login to Octopus with OpenID Connect or API Keys
75 changes: 41 additions & 34 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,52 +4,43 @@ module.exports = {
node: true,
es6: true,
},
extends: [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:prettier/recommended",
],
ignorePatterns: ["node_modules/", ".eslintrc.js", "esbuild.js", "esbuild.mjs", "dist/", "bin/", "rollup.config.js", "step-package.config.js", "jest.config.js"],
extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended", "plugin:jest/recommended", "plugin:prettier/recommended"],
ignorePatterns: ["node_modules/**/*", "dist/**/*"],
parser: "@typescript-eslint/parser",
parserOptions: {
project: ["./packages/*/tsconfig.json", "./packages/*/*.tsconfig.json"],
sourceType: "module",
},
plugins: ["prefer-arrow", "@typescript-eslint", "prettier"],
plugins: ["prefer-arrow", "@typescript-eslint", "prettier", "import", "unused-imports"],
rules: {
// The typescript version adds extra checks on top of the eslint version, so we disable the eslint version
// https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/init-declarations.md
"@typescript-eslint/init-declarations": ["error"],
"init-declarations": "off",
"@typescript-eslint/consistent-type-assertions": ["error", { assertionStyle: "never" }],
"init-declarations": "off", // explicitly disable to prefer the TS version: https://typescript-eslint.io/rules/init-declarations/
"no-case-declarations": "off", //should consider enabling
"no-dupe-class-members": "off", // explicitly disable to prefer the TS version: https://typescript-eslint.io/rules/no-dupe-class-members
"no-eq-null": ["error"],
"no-undef": "off", //typescript will catch these
"no-dupe-class-members": "off",
"@typescript-eslint/no-dupe-class-members": ["error"],
"@typescript-eslint/explicit-function-return-type": "off",
"@typescript-eslint/no-inferrable-types": "off",
"no-extra-boolean-cast": "off",
"no-multi-spaces": "error",
"@typescript-eslint/no-unused-vars": "off", //this rule is a bit buggy atm, it picks up things as unused when they are
"@typescript-eslint/adjacent-overload-signatures": "error",
//https://github.com/typescript-eslint/typescript-eslint/issues/1856
"@typescript-eslint/no-var-requires": "off",
"@typescript-eslint/no-use-before-define": "off",
"@typescript-eslint/interface-name-prefix": "off", //deprecated in favor of naming-convention rule
"@typescript-eslint/explicit-module-boundary-types": "off",
"no-irregular-whitespace": "off",
"no-multi-spaces": "error",
"no-prototype-builtins": "off", //should enable, although very unlikely to break in our case
"no-undef": "off", //typescript will catch these
"prefer-rest-params": "off", //should consider setting this to warn
"prefer-spread": "off", //should consider setting this to warn
"no-case-declarations": "off", //should consider enabling

"@typescript-eslint/adjacent-overload-signatures": "error",
"@typescript-eslint/array-type": ["off", { default: "array-simple" }], //consider enabling as error
"@typescript-eslint/consistent-type-assertions": ["error", { assertionStyle: "never" }],
"@typescript-eslint/consistent-type-imports": ["error", { prefer: "type-imports" }],
"@typescript-eslint/explicit-function-return-type": "off",
"@typescript-eslint/explicit-module-boundary-types": "off",
"@typescript-eslint/init-declarations": "error", // The typescript version adds extra checks on top of the eslint version, so we disable the eslint version, https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/init-declarations.md
"@typescript-eslint/interface-name-prefix": "off", //deprecated in favor of naming-convention rule
"@typescript-eslint/no-dupe-class-members": "error",
"@typescript-eslint/no-explicit-any": "error",
"@typescript-eslint/no-inferrable-types": "off",
"@typescript-eslint/no-non-null-assertion": "error",
"@typescript-eslint/no-non-null-asserted-optional-chain": "off",
"@typescript-eslint/array-type": [
//consider enabling as error
"off",
{
default: "array-simple",
},
],
"@typescript-eslint/no-unused-vars": "off", //this rule is a bit buggy atm, it picks up things as unused when they are
"@typescript-eslint/no-use-before-define": "off",
"@typescript-eslint/no-var-requires": "off", //https://github.com/typescript-eslint/typescript-eslint/issues/1856
"@typescript-eslint/quotes": [
"error",
"double",
Expand All @@ -58,7 +49,23 @@ module.exports = {
allowTemplateLiterals: true,
},
],
"@typescript-eslint/no-non-null-assertion": "error",

"jest/expect-expect": ["error", { assertFunctionNames: ["expect", "**.*should*", "assert*", "**.assert*"] }],
"jest/no-standalone-expect": "off",
"jest/valid-expect": ["error", { maxArgs: 2 }],

"import/no-duplicates": "error",
"import/order": [
"error",
{
groups: ["builtin", "external", "internal", "parent", "sibling", "index"],
pathGroups: [{ pattern: "~/**", group: "internal" }],
pathGroupsExcludedImportTypes: [],
"newlines-between": "never",
alphabetize: { order: "asc", caseInsensitive: false },
},
],
"unused-imports/no-unused-imports": "error",
"prettier/prettier": "error",
},
};
29 changes: 18 additions & 11 deletions .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ jobs:
build:
runs-on: ubuntu-latest
name: Build and test
permissions:
contents: read
actions: write
checks: write
statuses: write
steps:
- uses: actions/checkout@v3

Expand All @@ -23,8 +28,20 @@ jobs:
- name: Build
run: npm run build

- name: Lint
run: npm run lint

- name: Test
run: npm run test
run: npm run ci:test

- name: Create test report
uses: phoenix-actions/test-reporting@v12
if: success() || failure() # run this step even if previous step failed
with:
name: Tests # Name of the check run which will be created
path: test-results/jest-*.xml # Path to test results
reporter: jest-junit # Format of test results
output-to: step-summary

- name: Compare the expected and actual dist/ directories
run: |
Expand Down Expand Up @@ -67,13 +84,3 @@ jobs:
publish: npm run ci:publish
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

test:
runs-on: ubuntu-latest
needs: build
name: Test action
steps:
- uses: actions/checkout@v3

- name: Run action
uses: ./
46 changes: 39 additions & 7 deletions .github/workflows/test-login.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,54 @@ on:
inputs:
server:
type: string
description: The url of the Octopus Instance
required: true
description: The url of the Octopus Instance. Will default to repository variable TEST_INSTANCE_URL if not supplied.
service_account_id:
type: string
description: The id of the service account to login for
required: true
description: The id of the service account to login for. Will default to repository variable TEST_INSTANCE_SERVICE_ACCOUNT_ID if not supplied.
api_key:
type: string
description: The API key to login with. Be careful this may be logged in plain text. Will default to repository secret TEST_INSTANCE_API_KEY if not supplied.

jobs:
login:
permissions:
id-token: write # This is required for requesting the JWT
contents: read # This is required for actions/checkout
runs-on: ubuntu-latest
name: Test login to Octopus
steps:
- uses: actions/checkout@v3

- name: Login to Octopus
- name: Login to Octopus using OIDC
uses: ./
id: oidc
with:
server: ${{ inputs.server || vars.TEST_INSTANCE_URL }}
service_account_id: ${{ inputs.service_account_id || vars.TEST_INSTANCE_SERVICE_ACCOUNT_ID }}

- name: Print Octopus credential environment variables from OIDC login
run: |
echo "OCTOPUS_URL = $OCTOPUS_URL"
echo "OCTOPUS_ACCESS_TOKEN = $OCTOPUS_ACCESS_TOKEN"
echo "server = ${{ steps.oidc.outputs.server }}"
echo "access_token = ${{ steps.oidc.outputs.access_token }}"
- name: Login to Octopus using API Key
uses: ./
id: api_key
with:
server: ${{ inputs.server }}
service_account_id: ${{ inputs.service_account_id }}
server: ${{ inputs.server || vars.TEST_INSTANCE_URL }}
api_key: ${{ inputs.api_key || secrets.TEST_INSTANCE_API_KEY }}

- name: Print Octopus credential environment variables from API Key login
run: |
echo "OCTOPUS_URL = $OCTOPUS_URL"
echo "OCTOPUS_API_KEY = $OCTOPUS_API_KEY"
echo "server = ${{ steps.api_key.outputs.server }}"
echo "api_key = ${{ steps.api_key.outputs.api_key }}"
- name: Install Octopus CLI
uses: OctopusDeploy/install-octopus-cli-action@v3

- name: List environments using CLI
run: octopus environment list --space Default
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -127,3 +127,6 @@ out
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*

# Test outputs
test-results/
80 changes: 78 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,84 @@
# login

GitHub action to login to your Octopus Server
A GitHub action to login to your [Octopus Deploy](https://octopus.com/) server.

## Changesets
After successful login, the GitHub Actions environment will be configured so that credentials do not need to be supplied to later Octopus actions (e.g. [`create-release-action`](https://github.com/OctopusDeploy/create-release-action)) or the [Octopus CLI](https://github.com/OctopusDeploy/cli).

This action supports two ways of logging in:

## API Key

To login using an API Key:

- Provision an API key in Octopus. See [How to create an API key](https://octopus.com/docs/octopus-rest-api/how-to-create-an-api-key) for more information. It is recommended that a service account is used instead of a user account.
- Add the `OctopusDeploy/login` action to your workflow, specifying the `server` and `api_key` inputs.

### Inputs

| Name | Description |
| --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `server` | The URL of your Octopus server. This input is required. |
| `api_key` | The API key you wish to login in with. It is **strongly recommended** to store this as a secret in GitHub Actions. This input is required if using API Key to login. |

### Outputs

| Name | Description |
| --------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| `server` | The URL of your Octopus server that has been logged into. The environment variable `OCTOPUS_URL` will also be set with this value. |
| `api_key` | The API key that was used to login in with. The environment variable `OCTOPUS_API_KEY` will also be set with this value. |

### Example

```yaml
- name: Login to Octopus
uses: OctopusDeploy/login@v0
with:
server: https://my.octopus.app
api_key: ${{ secrets.OCTOPUS_API_KEY }}
```

## OpenID Connect (OIDC)

> Support for OpenID Connect is currently in development and may not be available in your Octopus version just yet.
Using OpenID Connect (OIDC) is the recommended way to login from GitHub Actions to Octopus. It allows the granting of short-lived access tokens for a service account in Octopus that can be used during your GitHub Actions workflow run, without needing to provision or store an API key.

See [About security hardening with OpenID Connect](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect) for more information.

To login using OIDC:

- Create a service account in Octopus with the permissions required.
- Configure an OIDC identity that matches the GitHub Actions subject claim for your repository and workflow. See the [GitHub documentation](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect#defining-trust-conditions-on-cloud-roles-using-oidc-claims) for examples of the subject claim.
- Copy the `Service Account Id` value from the Octopus service account. This will be a GUID.
- Add the `OctopusDeploy/login` action to your workflow, specifying the `server` and `service_account_id` inputs.

### Inputs

| Name | Description |
| -------------------- | -------------------------------------------------------------------------------------------------- |
| `server` | The URL of your Octopus server. This input is required. |
| `service_account_id` | The id of the service account you wish to login as. This input is required if using OIDC to login. |

### Outputs

| Name | Description |
| -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `server` | The URL of your Octopus server that has been logged into. The environment variable `OCTOPUS_URL` will also be set with this value. |
| `access_token` | An access token that can be use to authenticate when making API requests. The environment variable `OCTOPUS_ACCESS_TOKEN` will also be set with this value. |

### Example

```yaml
- name: Login to Octopus
uses: OctopusDeploy/login@v0
with:
server: https://my.octopus.app
service_account_id: 5be4ac10-2679-4041-a8b0-7b05b445e19e
```

## Development

### Changesets

This repository uses [changesets](https://github.com/changesets/changesets) to manage package versions.

Expand Down
15 changes: 15 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,18 @@ author: Octopus Deploy
runs:
using: node16
main: dist/index.js
inputs:
server:
description: The URL of the Octopus Instance (e.g. "https://octopus.example.com/").
required: true
service_account_id:
description: The id of the service account when using OpenID Connect to login. It is strongly recommended that this value retrieved from a GitHub secret.
api_key:
description: The key when using API key login. It is strongly recommended that this value retrieved from a GitHub secret.
outputs:
server:
description: The URL of the Octopus Instance that has been logged into (e.g. "https://octopus.example.com/"). This will be placed into the OCTOPUS_URL environment variable as well.
access_token:
description: The access token obtained for the service account when using OpenID Connect to login. This will be place into the OCTOPUS_ACCESS_TOKEN environment variable as well.
api_key:
description: The API key configured for access when using API key login. This will be placed into the OCTOPUS_API_KEY environment variable as well.

0 comments on commit e635f8e

Please sign in to comment.