Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add initial version of login to Octopus with OIDC or API keys #3

Merged
merged 43 commits into from
Aug 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
02604f2
Wip login to octopus
geofflamrock Aug 9, 2023
5657a20
Add pr trigger to get workflow to show up
geofflamrock Aug 9, 2023
e9bd07f
Remove PR trigger
geofflamrock Aug 9, 2023
bca99fe
Merge remote-tracking branch 'origin/main' into geoffl/add-login
geofflamrock Aug 9, 2023
55e4ebe
Change to info logging
geofflamrock Aug 9, 2023
4c2b79f
Add id-token permissions
geofflamrock Aug 9, 2023
59d4d71
Fix permissions
geofflamrock Aug 9, 2023
797fa3d
Add content type header
geofflamrock Aug 9, 2023
1c3fd78
Attempting to see the error
geofflamrock Aug 9, 2023
3d4e189
Fix request body formatting
geofflamrock Aug 9, 2023
e919dbb
Add debug logging
geofflamrock Aug 9, 2023
993cc60
Make debug messages info
geofflamrock Aug 9, 2023
64f6039
Remove debugging message
geofflamrock Aug 9, 2023
a7e3c58
Add output variables and print env vars
geofflamrock Aug 9, 2023
d63ab39
Better error handling and refactoring to include test context
geofflamrock Aug 9, 2023
63f3fbd
More testing of action
geofflamrock Aug 9, 2023
bf168b8
Fix missing space parameter
geofflamrock Aug 9, 2023
78eec29
Use defaults from repo if not supplied to make testing easier
geofflamrock Aug 9, 2023
525a8b8
Improves eslint configuration and cleanup
geofflamrock Aug 10, 2023
4222799
wip adding tests
geofflamrock Aug 11, 2023
9942b77
Merge remote-tracking branch 'origin/main' into geoffl/add-login
geofflamrock Aug 11, 2023
9d0f8b0
Merge remote-tracking branch 'origin/main' into geoffl/add-login
geofflamrock Aug 11, 2023
66027ba
Fix build
geofflamrock Aug 11, 2023
230e394
Add changeset
geofflamrock Aug 11, 2023
7112c7b
More tests
geofflamrock Aug 11, 2023
363360b
Adds login details to readme
geofflamrock Aug 11, 2023
000c850
Fixes examples
geofflamrock Aug 11, 2023
f55c0f3
Adds linting to ci
geofflamrock Aug 13, 2023
80b4d08
Add test report
geofflamrock Aug 14, 2023
1e1da5d
Fix up ci test reporting
geofflamrock Aug 14, 2023
0b80bbb
Set correct permissions
geofflamrock Aug 14, 2023
e254244
Change to different test report action
geofflamrock Aug 14, 2023
79d9af8
Merge remote-tracking branch 'origin/main' into geoffl/add-login
geofflamrock Aug 14, 2023
5b57472
Update lockfile
geofflamrock Aug 14, 2023
d5dc109
Merge remote-tracking branch 'origin/main' into geoffl/add-login
geofflamrock Aug 15, 2023
183debb
Merge from main
geofflamrock Aug 15, 2023
d049e90
Use openid configuration
geofflamrock Aug 21, 2023
4b9c023
Merge remote-tracking branch 'origin/main' into geoffl/add-login
geofflamrock Aug 21, 2023
2b4f2f0
Add user agent
geofflamrock Aug 22, 2023
c942b65
Merge branch 'geoffl/use-openid-configuration' into geoffl/add-login
geofflamrock Aug 22, 2023
440bcd1
Add user agent to openid config request
geofflamrock Aug 22, 2023
ffa3b2a
Fix casing
geofflamrock Aug 23, 2023
b48e7f1
Merge remote-tracking branch 'origin/main' into geoffl/add-login
geofflamrock Aug 23, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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"],
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Have adjusted the eslint config to match up more closely to what we use elsewhere in the Octopus portal. It's not 100% the same, but much closer.

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.