Skip to content

Commit

Permalink
introducing OIDC integration (#696)
Browse files Browse the repository at this point in the history
  • Loading branch information
eranturgeman authored May 19, 2024
1 parent 54d0cfe commit ed6e024
Show file tree
Hide file tree
Showing 195 changed files with 31,302 additions and 35 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/action-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ on:
tags-ignore:
- '**'
pull_request:
types: [ labeled ]
jobs:
test:
runs-on: ${{ matrix.os }}
Expand All @@ -29,4 +30,4 @@ jobs:
- name: Lint
run: npm run lint
- name: Unit tests
run: npm t
run: npm t
84 changes: 84 additions & 0 deletions .github/workflows/oidc-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
name: "OIDC Test"
on:
push:
pull_request_target:
types: [ labeled ]
permissions:
contents: write
pull-requests: write
security-events: write
id-token: write
jobs:
oidc-test:
if: contains(github.event.pull_request.labels.*.name, 'safe to test') || github.event_name == 'push'
name: OIDC-Access integration test (${{ matrix.os }})
strategy:
fail-fast: false
matrix:
os: [ ubuntu, windows, macos ]
runs-on: ${{ matrix.os }}-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Install Go
uses: actions/setup-go@v5
with:
go-version: 1.21.x
cache: false

# Generating a unique name for the Integration Configuration that will be created in the following step
- name: Generate unique OIDC config name
shell: bash
run: echo "OIDC_PROVIDER_NAME=oidc-integration-test-provider-$(date +%Y%m%d%H%M%S)" >> $GITHUB_ENV

- name: Create OpenID Connect integration
shell: bash
run: |
curl -X POST "${{ secrets.PLATFORM_URL }}/access/api/v1/oidc" -H "Content-Type: application/json" -H "Authorization: Bearer ${{ secrets.PLATFORM_ADMIN_TOKEN }}" -d '{
"name": "${{ env.OIDC_PROVIDER_NAME }}",
"issuer_url": "https://token.actions.githubusercontent.com/",
"provider_type": "GitHub",
"description": "This is a test configuration created for OIDC-Access integration test" }'
- name: Create OIDC integration Identity Mapping
shell: bash
run: |
curl -X POST ${{ secrets.PLATFORM_URL }}/access/api/v1/oidc/${{ env.OIDC_PROVIDER_NAME }}/identity_mappings \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer ${{ secrets.PLATFORM_ADMIN_TOKEN }}' \
-d '{
"name": "oidc-test-identity-mapping",
"priority": "1",
"claims": {
"repository": "${{ github.repository_owner }}/frogbot"
},
"token_spec": {
"username": "admin",
"scope": "applied-permissions/admin",
"audience": "*@*",
"expires_in": 300
}
}'
# Running frogbot with the OIDC integration
- name: Run Frogbot
uses: ./
env:
ACTIONS_STEP_DEBUG: true
JF_URL: ${{ secrets.PLATFORM_URL }}
JF_GIT_TOKEN: ${{ secrets.GITHUB_TOKEN }}
JF_GIT_BASE_BRANCH: ${{ matrix.branch }}
JF_WORKING_DIR: ./testdata/projects/noIssuesProject
JF_FAIL: "FALSE"
# @todo remove once scanners are stable
JFROG_CLI_ANALYZER_MANAGER_VERSION: "1.6.4"
with:
oidc-provider-name: ${{ env.OIDC_PROVIDER_NAME }}

# Removing the OIDC integration will remove the Identity Mapping as well
- name: Delete OIDC integration
shell: bash
if: always()
run: |
curl -X DELETE ${{ secrets.PLATFORM_URL }}/access/api/v1/oidc/${{ env.OIDC_PROVIDER_NAME }} -H 'Authorization: Bearer ${{ secrets.PLATFORM_ADMIN_TOKEN }}'
30 changes: 28 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,10 @@ jobs:
ref: ${{ github.event.pull_request.head.sha }}

- name: Install Go
uses: actions/setup-go@v3
uses: actions/setup-go@v5
with:
go-version: 1.21.x
cache: false

- name: Go Cache
uses: actions/cache@v3
Expand Down Expand Up @@ -76,9 +77,10 @@ jobs:
ref: ${{ github.event.pull_request.head.sha }}

- name: Setup Go
uses: actions/setup-go@v3
uses: actions/setup-go@v5
with:
go-version: 1.21.x
cache: false

- name: Go Cache
uses: actions/cache@v3
Expand Down Expand Up @@ -134,6 +136,12 @@ jobs:
with:
ref: ${{ github.event.pull_request.head.sha }}

- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: 1.21.x
cache: false

- name: Run Tests
run: go test github_test.go integrationutils.go commands.go -v -race -timeout 30m -cover
env:
Expand All @@ -156,6 +164,12 @@ jobs:
with:
ref: ${{ github.event.pull_request.head.sha }}

- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: 1.21.x
cache: false

- name: Run Tests
run: go test azure_test.go integrationutils.go commands.go -v -race -timeout 30m -cover
env:
Expand All @@ -178,6 +192,12 @@ jobs:
with:
ref: ${{ github.event.pull_request.head.sha }}

- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: 1.21.x
cache: false

- name: Run Tests
run: go test gitlab_test.go integrationutils.go commands.go -v -race -timeout 30m -cover
env:
Expand All @@ -194,6 +214,12 @@ jobs:
with:
ref: ${{ github.event.pull_request.head.sha }}

- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: 1.21.x
cache: false

- name: Unzip Preconfigured Bitbucket Home
run: unzip ${{ github.workspace }}/testdata/resources/bitbucket_server_home.zip -d ${PWD}

Expand Down
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
__tests__/runner/*
frogbot
frogbot.exe
*mock*
coverage
.idea
.vscode
Expand Down
6 changes: 6 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ inputs:
description: "Frogbot version"
default: "latest"
required: false
oidc-provider-name:
description: "Provider Name's value that was set in OpenId Connect integration in the JFrog platform."
required: false
oidc-audience:
description: "By default, this is the URL of the GitHub repository owner, such as the organization that owns the repository."
required: false
runs:
using: "node16"
main: "action/lib/main.js"
Expand Down
2 changes: 2 additions & 0 deletions action/lib/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ function main() {
return __awaiter(this, void 0, void 0, function* () {
try {
core.startGroup('Frogbot');
let jfrogUrl = yield utils_1.Utils.getJfrogPlatformUrl();
yield utils_1.Utils.setupOidcTokenIfNeeded(jfrogUrl);
const eventName = yield utils_1.Utils.setFrogbotEnv();
yield utils_1.Utils.addToPath();
switch (eventName) {
Expand Down
78 changes: 78 additions & 0 deletions action/lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ const fs_1 = require("fs");
const os_1 = require("os");
const path_1 = require("path");
const simple_git_1 = require("simple-git");
const http_client_1 = require("@actions/http-client");
class Utils {
static addToPath() {
var _a;
Expand Down Expand Up @@ -200,9 +201,86 @@ class Utils {
static isWindows() {
return (0, os_1.platform)().startsWith('win');
}
static getJfrogPlatformUrl() {
var _a;
return __awaiter(this, void 0, void 0, function* () {
let jfrogUrl = (_a = process.env.JF_URL) !== null && _a !== void 0 ? _a : '';
if (!jfrogUrl) {
throw new Error('JF_URL must be provided and point on your full platform URL, for example: https://mycompany.jfrog.io/');
}
return jfrogUrl;
});
}
/**
* This method will set up an OIDC token if the OIDC integration is set.
* If OIDC integration is set but not working, the action will fail causing frogbot to fail
* @param jfrogUrl - The JFrog platform URL
*/
static setupOidcTokenIfNeeded(jfrogUrl) {
return __awaiter(this, void 0, void 0, function* () {
const oidcProviderName = core.getInput(Utils.OIDC_INTEGRATION_PROVIDER_NAME_ARG);
if (!oidcProviderName) {
// No token is set if an oidc-provider-name wasn't provided
return;
}
core.debug('Obtaining an access token through OpenID Connect...');
const audience = core.getInput(Utils.OIDC_AUDIENCE_ARG);
let jsonWebToken;
try {
core.debug('Fetching JSON web token');
jsonWebToken = yield core.getIDToken(audience);
}
catch (error) {
throw new Error(`Getting openID Connect JSON web token failed: ${error.message}`);
}
try {
return yield this.initJfrogAccessTokenThroughOidcProtocol(jfrogUrl, jsonWebToken, oidcProviderName);
}
catch (error) {
throw new Error(`OIDC authentication against JFrog platform failed, please check OIDC settings and mappings on the JFrog platform: ${error.message}`);
}
});
}
/**
* This method exchanges a JSON web token with a JFrog access token through the OpenID Connect protocol
* If we've reached this stage, the jfrogUrl field should hold a non-empty value obtained from process.env.JF_URL
* @param jfrogUrl - The JFrog platform URL
* @param jsonWebToken - The JSON web token used in the token exchange
* @param oidcProviderName - The OpenID Connect provider name
*/
static initJfrogAccessTokenThroughOidcProtocol(jfrogUrl, jsonWebToken, oidcProviderName) {
return __awaiter(this, void 0, void 0, function* () {
const exchangeUrl = jfrogUrl.replace(/\/$/, '') + '/access/api/v1/oidc/token';
core.debug('Exchanging GitHub JSON web token with a JFrog access token...');
const httpClient = new http_client_1.HttpClient();
const data = `{
"grant_type": "urn:ietf:params:oauth:grant-type:token-exchange",
"subject_token_type": "urn:ietf:params:oauth:token-type:id_token",
"subject_token": "${jsonWebToken}",
"provider_name": "${oidcProviderName}"
}`;
const additionalHeaders = {
'Content-Type': 'application/json',
};
const response = yield httpClient.post(exchangeUrl, data, additionalHeaders);
const responseString = yield response.readBody();
const responseJson = JSON.parse(responseString);
process.env.JF_ACCESS_TOKEN = responseJson.access_token;
if (responseJson.access_token) {
core.setSecret(responseJson.access_token);
}
if (responseJson.errors) {
throw new Error(`${JSON.stringify(responseJson.errors)}`);
}
});
}
}
exports.Utils = Utils;
Utils.LATEST_RELEASE_VERSION = '[RELEASE]';
Utils.LATEST_CLI_VERSION_ARG = 'latest';
Utils.VERSION_ARG = 'version';
Utils.TOOL_NAME = 'frogbot';
// OpenID Connect audience input
Utils.OIDC_AUDIENCE_ARG = 'oidc-audience';
// OpenID Connect provider_name input
Utils.OIDC_INTEGRATION_PROVIDER_NAME_ARG = 'oidc-provider-name';
29 changes: 25 additions & 4 deletions action/node_modules/.package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion action/node_modules/@actions/http-client/lib/auth.js.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit ed6e024

Please sign in to comment.