Skip to content

Commit

Permalink
Merge pull request #82 from sergiitk/feature/http-auth-bypass-token
Browse files Browse the repository at this point in the history
[FEATURE core-web] Support for Authentication Bearer access_token
  • Loading branch information
sergiitk authored Feb 3, 2019
2 parents d1b8c1c + 87be8c0 commit e217a79
Show file tree
Hide file tree
Showing 24 changed files with 574 additions and 74 deletions.
33 changes: 29 additions & 4 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,18 @@ docker-compose up
3. Open [localhost:8080](http://localhost:8080)

PagerBeauty for local development is preconfigured, no `.env` file is necessary.
It's using [Mockserver](https://github.com/namshi/mockserver) in place of real PagerBeaty API v2.
It's using [Mockserver](https://github.com/namshi/mockserver) in place of real PagerBeaty API v2: [localhost:8090](http://localhost:8090).

Application HTTP server and webpack are running in `watch` mode.
All changes you make should be picked up automatically on the next page refresh. It is available on [localhost:8081](http://localhost:8081).
All changes you make should be picked up automatically on the next page refresh: [localhost:8080](http://localhost:8080).

Additional version of PagerBeauty with HTTP authentication enabled is available at [localhost:8081](http://localhost:8081). Default credentials:

```sh
PAGERBEAUTY_HTTP_USER=basic_username
PAGERBEAUTY_HTTP_PASSWORD=basic_password
PAGERBEAUTY_HTTP_ACCESS_TOKEN=803651A9-E3B7-4153-9566-6E54F5F0CEAB
```

### Additional configuration

Expand All @@ -40,7 +48,7 @@ for example:
PAGERBEAUTY_LOG_LEVEL=verbose
```

**Custom HTTP port**
**Custom HTTP ports**

In case you have another service running on port 8080, the following option
will start PagerBeauty on custom port and bind it to the same port on the host.
Expand All @@ -51,6 +59,23 @@ will start PagerBeauty on custom port and bind it to the same port on the host.
PAGERBEAUTY_HTTP_PORT=8181
```

In the same manner, you can choose custom port for `pagerbeauty-dev-with-auth` service:

```sh
# Start PagerBeauty with enabled HTTP authentication on port 8182
# in Docker and bind it to port 8181 on the host:
# http://localhost:8182/
PAGERBEAUTY_WITH_AUTH_HTTP_PORT=8182
```

To change PagerDuty Mock API port, you need to update the following
```sh
# Use custom port for PagerDuty Mock API:
PAGERBEAUTY_PD_API_MOCK_PORT=9090
# Instruct PagerBeauty use different PagerDuty API:
PAGERBEAUTY_PD_API_URL=http://mock-pagerduty-api:9090
```

**Using real PagerDuty API**

```sh
Expand All @@ -59,7 +84,7 @@ PAGERBEAUTY_PD_API_URL=https://api.pagerduty.com
PAGERBEAUTY_PD_API_KEY=real_key
```

Note, using real PagerDuty API will break local acceptance tests. It shouldn't be a problem for development.
Note, using real PagerDuty API will break local acceptance tests. It shouldn't be a problem for a routine development.

### Other

Expand Down
5 changes: 4 additions & 1 deletion Dockerfile-dev
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,12 @@ RUN npm config set scripts-prepend-node-path true \
# Install the rest
RUN yarn install --frozen-lockfile

# Acceptance test mock server
# Pagerbeauty with HTTP auth
EXPOSE 8081

# Acceptance test mock server
EXPOSE 8090

# Bundle app source
COPY . .

Expand Down
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,19 @@ PAGERBEAUTY_PD_SCHEDULES=SCHEDL1,SCHEDL2
# PAGERBEAUTY_HTTP_PORT=80

# (Optional) Enable basic HTTP authentication
# Note: embedding iframes with basic HTTP auth is not supported by all browsers.
# Default: disabled
# PAGERBEAUTY_HTTP_USER=basic_username
# PAGERBEAUTY_HTTP_PASSWORD=basic_password

# (Optional) Enable authentication access token (RFC6750)
# Note: embedding iframes that link to a page with basic HTTP name/password
# authentication is not supported by most modern browsers. To bypass it, you
# can set random access_token and append it to schedule URL. For example, if you can't embed schedule
# https://pb.example.com/v1/schedules/P538IZH.html, you can append your access token like so:
# https://pb.example.com/v1/schedules/P538IZH.html?acccess_token=your_token
# This link is embeddable now. Please use HTTPS.
# Default: disabled
# PAGERBEAUTY_HTTP_ACCESS_TOKEN=your_token
```

## Add to DataDog dashboard
Expand Down
50 changes: 43 additions & 7 deletions docker-compose.circleci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,46 +5,82 @@ services:
image: sergiitk/pagerbeauty:ci-${CIRCLE_SHA1}
environment:
# Use PagerDuty API Mock server for development
PAGERBEAUTY_PD_API_URL: http://mock-pagerduty-api:8081
PAGERBEAUTY_PD_API_URL: http://mock-pagerduty-api:8090
# API key is required, but Mock PD API will ignore it
PAGERBEAUTY_PD_API_KEY: v2_api_key
# Mock Mock PD APInow only responds with these two schedules
# Mock PD API provides three predefined schedules
# UNKNOWN is to ensure incorrect schedules don't prevent valid from loading
PAGERBEAUTY_PD_SCHEDULES: UNKNOWN,P538IZH,PJ1P5JQ,P2RFGIP
# Faster refreshes for dev server
# Schedules and oncalls every 6 seconds
PAGERBEAUTY_REFRESH_RATE_MINUTES: 0.1
# Schedules and oncalls every 3 seconds
PAGERBEAUTY_INCIDENTS_REFRESH_RATE_MINUTES: 0.05
# Verbose
PAGERBEAUTY_LOG_LEVEL: silly
ports:
- "8080:8080"
- '8080:8080'
depends_on:
- mock-pagerduty-api

pagerbeauty-ci-with-auth:
image: sergiitk/pagerbeauty:ci-${CIRCLE_SHA1}
environment:
# Use PagerDuty API Mock server for development
PAGERBEAUTY_PD_API_URL: http://mock-pagerduty-api:8090
# API key is required, but Mock PD API will ignore it
PAGERBEAUTY_PD_API_KEY: v2_api_key
# Test on two schedules
PAGERBEAUTY_PD_SCHEDULES: P538IZH,PJ1P5JQ
# Faster refreshes for dev server
# Schedules and oncalls every 6 seconds
PAGERBEAUTY_REFRESH_RATE_MINUTES: 0.1
# Schedules and oncalls every 3 seconds
PAGERBEAUTY_INCIDENTS_REFRESH_RATE_MINUTES: 0.05
# Verbose
PAGERBEAUTY_LOG_LEVEL: silly
# Run on different port than no auth app
PAGERBEAUTY_HTTP_PORT: 8081
# Basic HTTP authentication
PAGERBEAUTY_HTTP_USER: basic_username_ci
PAGERBEAUTY_HTTP_PASSWORD: basic_password_ci
PAGERBEAUTY_HTTP_ACCESS_TOKEN: 9A37F64B-931B-4767-94D3-E41B92991F7C
ports:
- '8081:8081'
depends_on:
- mock-pagerduty-api

mock-pagerduty-api:
build:
context: .
dockerfile: ./Dockerfile-dev
command: ["yarn", "run", "mock:pagerduty_api"]
command: ['yarn', 'run', 'mock:pagerduty_api', '-p', '8090']
ports:
- "8081:8081"
- "8090:8090"
volumes:
- node_modules:/usr/src/app/node_modules

test-acceptance:
build:
context: .
dockerfile: ./Dockerfile-test-acceptance
entrypoint: ""
command: ["./test/test-with-xunit.sh", "acceptance"]
entrypoint: ''
command: ['./test/test-with-xunit.sh', 'acceptance']
environment:
PAGERBEAUTY_URL: http://pagerbeauty-ci:8080
PAGERBEAUTY_URL_WITH_AUTH: http://pagerbeauty-ci-with-auth:8081
# Basic HTTP authentication
PAGERBEAUTY_HTTP_USER: basic_username_ci
PAGERBEAUTY_HTTP_PASSWORD: basic_password_ci
PAGERBEAUTY_HTTP_ACCESS_TOKEN: 9A37F64B-931B-4767-94D3-E41B92991F7C
volumes:
# Uncomment for verifying service locally.
# - .:/usr/src/app/
- node_modules:/usr/src/app/node_modules
- ./tmp:/usr/src/app/tmp
depends_on:
- pagerbeauty-ci
- pagerbeauty-ci-with-auth

# Use shared node_modules
volumes:
Expand Down
68 changes: 57 additions & 11 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,52 @@ services:
build:
context: .
dockerfile: ./Dockerfile-dev
command: ["yarn", "run", "app:watch"]
command: ['yarn', 'run', 'app:watch']
environment:
# Use PagerDuty API Mock server for development
PAGERBEAUTY_PD_API_URL: ${PAGERBEAUTY_PD_API_URL:-http://mock-pagerduty-api:8081}
PAGERBEAUTY_PD_API_URL: ${PAGERBEAUTY_PD_API_URL:-http://mock-pagerduty-api:8090}
# API key is required, but Mock PD API will ignore it
PAGERBEAUTY_PD_API_KEY: ${PAGERBEAUTY_PD_API_KEY:-v2_api_key}
# Mock Mock PD APInow only responds with these two schedules
PAGERBEAUTY_PD_SCHEDULES: ${PAGERBEAUTY_PD_SCHEDULES:-UNKNOWN,P538IZH,PJ1P5JQ,P2RFGIP}
# Mock PD API provides three predefined schedules
# UNKNOWN is to ensure incorrect schedules don't prevent valid from loading
PAGERBEAUTY_PD_SCHEDULES: >-
${PAGERBEAUTY_PD_SCHEDULES:-UNKNOWN,P538IZH,PJ1P5JQ,P2RFGIP}
# Faster refresh for dev server
PAGERBEAUTY_REFRESH_RATE_MINUTES: ${PAGERBEAUTY_REFRESH_RATE_MINUTES:-0.1}
PAGERBEAUTY_INCIDENTS_REFRESH_RATE_MINUTES: ${PAGERBEAUTY_INCIDENTS_REFRESH_RATE_MINUTES:-0.05}
# Schedules and oncalls every 15 seconds
PAGERBEAUTY_REFRESH_RATE_MINUTES: ${PAGERBEAUTY_REFRESH_RATE_MINUTES:-0.25}
# Incidents every 12 seconds
PAGERBEAUTY_INCIDENTS_REFRESH_RATE_MINUTES: >-
${PAGERBEAUTY_INCIDENTS_REFRESH_RATE_MINUTES:-0.20}
ports:
- "${PAGERBEAUTY_HTTP_PORT:-8080}:${PAGERBEAUTY_HTTP_PORT:-8080}"
- ${PAGERBEAUTY_HTTP_PORT:-8080}:${PAGERBEAUTY_HTTP_PORT:-8080}
volumes:
- .:/usr/src/app/
- node_modules:/usr/src/app/node_modules
depends_on:
- mock-pagerduty-api
- pagerbeauty-dev-assets

pagerbeauty-dev-with-auth:
build:
context: .
dockerfile: ./Dockerfile-dev
command: ['yarn', 'run', 'app:watch']
environment:
# Use PagerDuty API Mock server for development
PAGERBEAUTY_PD_API_URL: ${PAGERBEAUTY_PD_API_URL:-http://mock-pagerduty-api:8090}
# API key is required, but Mock PD API will ignore it
PAGERBEAUTY_PD_API_KEY: ${PAGERBEAUTY_PD_API_KEY:-v2_api_key}
# Test on two schedules
PAGERBEAUTY_PD_SCHEDULES: ${PAGERBEAUTY_PD_SCHEDULES:-P538IZH,PJ1P5JQ}
# Run on different port than no auth app
PAGERBEAUTY_HTTP_PORT: ${PAGERBEAUTY_WITH_AUTH_HTTP_PORT:-8081}
# Basic HTTP authentication
PAGERBEAUTY_HTTP_USER: ${PAGERBEAUTY_HTTP_USER:-basic_username}
PAGERBEAUTY_HTTP_PASSWORD: ${PAGERBEAUTY_HTTP_PASSWORD:-basic_password}
PAGERBEAUTY_HTTP_ACCESS_TOKEN: >-
${PAGERBEAUTY_HTTP_ACCESS_TOKEN:-803651A9-E3B7-4153-9566-6E54F5F0CEAB}
ports:
- ${PAGERBEAUTY_WITH_AUTH_HTTP_PORT:-8081}:${PAGERBEAUTY_WITH_AUTH_HTTP_PORT:-8081}
volumes:
- .:/usr/src/app/
- node_modules:/usr/src/app/node_modules
Expand All @@ -29,7 +62,7 @@ services:
build:
context: .
dockerfile: ./Dockerfile-dev
command: ["yarn", "run", "build:watch"]
command: ['yarn', 'run', 'build:watch']
volumes:
- .:/usr/src/app/
- node_modules:/usr/src/app/node_modules
Expand All @@ -38,9 +71,14 @@ services:
build:
context: .
dockerfile: ./Dockerfile-dev
command: ["yarn", "run", "mock:pagerduty_api"]
command:
- yarn
- run
- mock:pagerduty_api
- '-p'
- ${PAGERBEAUTY_PD_API_MOCK_PORT:-8090}
ports:
- "8081:8081"
- ${PAGERBEAUTY_PD_API_MOCK_PORT:-8090}:${PAGERBEAUTY_PD_API_MOCK_PORT:-8090}
volumes:
- .:/usr/src/app/
- node_modules:/usr/src/app/node_modules
Expand All @@ -51,17 +89,25 @@ services:
dockerfile: ./Dockerfile-test-acceptance
environment:
PAGERBEAUTY_URL: http://pagerbeauty-dev:${PAGERBEAUTY_HTTP_PORT:-8080}
PAGERBEAUTY_URL_WITH_AUTH: >-
http://pagerbeauty-dev-with-auth:${PAGERBEAUTY_WITH_AUTH_HTTP_PORT:-8081}
# Basic HTTP authentication
PAGERBEAUTY_HTTP_USER: ${PAGERBEAUTY_HTTP_USER:-basic_username}
PAGERBEAUTY_HTTP_PASSWORD: ${PAGERBEAUTY_HTTP_PASSWORD:-basic_password}
PAGERBEAUTY_HTTP_ACCESS_TOKEN: >-
${PAGERBEAUTY_HTTP_ACCESS_TOKEN:-803651A9-E3B7-4153-9566-6E54F5F0CEAB}
volumes:
- .:/usr/src/app/
- node_modules:/usr/src/app/node_modules
depends_on:
- pagerbeauty-dev
- pagerbeauty-dev-with-auth

yarn:
build:
context: .
dockerfile: ./Dockerfile-dev
entrypoint: ["yarn"]
entrypoint: ['yarn']
command: []
volumes:
- .:/usr/src/app/
Expand Down
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"coverage:report:codecov": "nyc report --reporter=lcovonly && codecov",
"lint": "eslint --ext .js --ext .mjs --ext .jsx -f unix .",
"eslint": "eslint --ext .js --ext .mjs --ext .jsx",
"mock:pagerduty_api": "mockserver -p 8081 -m test/mocks",
"mock:pagerduty_api": "mockserver -m test/mocks",
"build": "webpack --config webpack.dev.js",
"build:watch": "webpack --watch --config webpack.dev.js",
"build:prod": "NODE_ENV=production webpack --config webpack.prod.js --display-modules",
Expand All @@ -34,16 +34,17 @@
"app:prod": "NODE_ENV=production node --experimental-modules src/pagerbeauty.mjs"
},
"dependencies": {
"basic-auth": "^2.0.1",
"dotenv": "^6.1.0",
"koa": "^2.6.1",
"koa-basic-auth": "^4.0.0",
"koa-mount": "^4.0.0",
"koa-route": "^3.2.0",
"koa-static": "^5.0.0",
"koa-views": "^6.1.4",
"luxon": "^1.10.0",
"node-fetch": "^2.2.0",
"nunjucks": "^3.1.3",
"tsscmp": "^1.0.6",
"winston": "^3.1.0"
},
"devDependencies": {
Expand Down
11 changes: 8 additions & 3 deletions src/app/PagerBeautyWeb.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import http from 'http';
import path from 'path';

import auth from 'koa-basic-auth';
import Koa from 'koa';
import logger from 'winston';
import mount from 'koa-mount';
Expand All @@ -16,6 +15,7 @@ import views from 'koa-views';

import { SchedulesController } from '../controllers/SchedulesController';
import { redirect } from '../middleware/redirect';
import { authentication } from '../middleware/authentication';
import {
PagerBeautyConfigError,
PagerBeautyHttpServerStartError,
Expand Down Expand Up @@ -103,8 +103,13 @@ export class PagerBeautyWeb {

// @todo: Enforce https?
// @todo: Generate unique request id?
if (this.auth && this.auth.name && this.auth.pass) {
app.use(auth(this.auth));

// Baseic Authentication
if (this.auth && this.auth.name) {
app.use(mount(
'/v1',
authentication(this.auth.name, this.auth.pass, this.auth.token),
));
}

// Static assets
Expand Down
18 changes: 17 additions & 1 deletion src/assets/javascripts/ui-backend-drivers.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,27 @@ export function withAjaxBackend({
WrappedComponent,
endpoint,
pollIntervalSeconds = 30,
authenticationStrategy = false,
}) {
// AJAX backend driver
class WithAjaxBackend extends React.Component {
static async fetchData(url) {
const response = await fetch(url);
const headers = new Headers();
// See https://developer.mozilla.org/en-US/docs/Web/API/Request/Request
const requestInit = {
method: 'GET',
headers,
cache: 'no-cache',
};

// Delegate authentication to the caller.
if (authenticationStrategy) {
authenticationStrategy(requestInit);
}

const fetchRequest = new Request(url, requestInit);

const response = await fetch(fetchRequest);
if (!response.ok) {
if (response.status === 404) {
throw new PagerBeautyFetchNotFoundUiError(
Expand Down
Loading

0 comments on commit e217a79

Please sign in to comment.