Skip to content

Commit

Permalink
CORS (#300)
Browse files Browse the repository at this point in the history
  • Loading branch information
AleF83 authored Mar 15, 2021
1 parent f95369e commit 748932c
Show file tree
Hide file tree
Showing 8 changed files with 22,552 additions and 9,931 deletions.
32,338 changes: 22,407 additions & 9,931 deletions services/package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions services/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"env-var": "^6.2.0",
"fastify": "^2.15.1",
"fastify-auth": "^0.7.2",
"fastify-cors": "^3.0.3",
"fastify-jwt": "^1.5.0",
"fastify-metrics": "^5.0.1",
"fastify-plugin": "^1.6.1",
Expand Down
2 changes: 2 additions & 0 deletions services/src/gateway.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as fastify from 'fastify';
import * as corsPlugin from 'fastify-cors';
import * as fastifyMetrics from 'fastify-metrics';
import * as jwtPlugin from 'fastify-jwt';
import * as authPlugin from 'fastify-auth';
Expand All @@ -22,6 +23,7 @@ export async function createServer() {
await loadPlugins();

const app = fastify()
.register(corsPlugin as any, config.corsConfiguration)
.register(authPlugin)
.register(jwtPlugin, {
secret: getSecret,
Expand Down
3 changes: 3 additions & 0 deletions services/src/modules/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as path from 'path';
import * as envVar from 'env-var';
import { LoggerOptions } from 'pino';
import { AuthenticationConfig } from './authentication/types';
import { CorsConfiguration } from './cors';

const envVarExt = envVar.from(process.env, {
asSet: (value: string) => new Set(value.split(',')),
Expand Down Expand Up @@ -58,3 +59,5 @@ export const knownApolloDirectives = envVarExt
.asSet();

export const loggerConfiguration = envVar.get('LOGGER_CONFIGURATION').default({}).asJsonObject() as LoggerOptions;

export const corsConfiguration = envVar.get('CORS_CONFIGURATION').default({}).asJson() as CorsConfiguration;
53 changes: 53 additions & 0 deletions services/src/modules/cors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
export interface CorsConfiguration {
/**
* Configures the Access-Control-Allow-Origin CORS header.
*/
origin?: string | string[];
/**
* Configures the Access-Control-Allow-Credentials CORS header.
* Set to true to pass the header, otherwise it is omitted.
*/
credentials?: boolean;
/**
* Configures the Access-Control-Expose-Headers CORS header.
* Expects a comma-delimited string (ex: 'Content-Range,X-Content-Range')
* or an array (ex: ['Content-Range', 'X-Content-Range']).
* If not specified, no custom headers are exposed.
*/
exposedHeaders?: string | string[];
/**
* Configures the Access-Control-Allow-Headers CORS header.
* Expects a comma-delimited string (ex: 'Content-Type,Authorization')
* or an array (ex: ['Content-Type', 'Authorization']). If not
* specified, defaults to reflecting the headers specified in the
* request's Access-Control-Request-Headers header.
*/
allowedHeaders?: string | string[];
/**
* Configures the Access-Control-Allow-Methods CORS header.
* Expects a comma-delimited string (ex: 'GET,PUT,POST') or an array (ex: ['GET', 'PUT', 'POST']).
*/
methods?: string | string[];
/**
* Configures the Access-Control-Max-Age CORS header.
* Set to an integer to pass the header, otherwise it is omitted.
*/
maxAge?: number;
/**
* Pass the CORS preflight response to the route handler (default: false).
*/
preflightContinue?: boolean;
/**
* Provides a status code to use for successful OPTIONS requests,
* since some legacy browsers (IE11, various SmartTVs) choke on 204.
*/
optionsSuccessStatus?: number;
/**
* Pass the CORS preflight response to the route handler (default: false).
*/
preflight?: boolean;
/**
* Hide options route from the documentation built using fastify-swagger (default: true).
*/
hideOptionsRoute?: boolean;
}
17 changes: 17 additions & 0 deletions services/tests/blackbox/__snapshots__/cors.spec.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`CORS Preflight request from valid origin 1`] = `
Object {
"access-control-allow-headers": "x-api-client, authorization",
"access-control-allow-methods": "GET, POST",
"access-control-allow-origin": "http://localhost",
}
`;

exports[`CORS reflight request from invalid origin 1`] = `
Object {
"access-control-allow-headers": "x-api-client, authorization",
"access-control-allow-methods": "GET, POST",
"access-control-allow-origin": false,
}
`;
63 changes: 63 additions & 0 deletions services/tests/blackbox/cors.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import * as fs from 'fs/promises';
import { FastifyInstance } from 'fastify';
import { createServer as createGateway } from '../../src/gateway';
import { ResourceGroup } from '../../src/modules/resource-repository';

describe('CORS', () => {
let app: FastifyInstance;
let dispose: () => Promise<void>;

beforeAll(async () => {
const resources: ResourceGroup = {
schemas: [],
upstreams: [],
upstreamClientCredentials: [],
policies: [],
};

await fs.writeFile(process.env.FS_RESOURCE_REPOSITORY_PATH!, JSON.stringify(resources));

({ app, dispose } = await createGateway());
});

afterAll(async () => {
await dispose();
await fs.unlink(process.env.FS_RESOURCE_REPOSITORY_PATH!);
});

test('Preflight request from valid origin', async () => {
const response = await app.inject({
method: 'OPTIONS',
url: '/graphql',
headers: {
origin: 'http://localhost',
'access-control-request-method': 'POST',
'access-control-request-headers': 'x-api-client,authorization',
},
});

expect(response.statusCode).toEqual(204);
const headers = Object.fromEntries(
Object.entries(response.headers).filter(([k]) => k.startsWith('access-control-allow'))
);
expect(headers).toMatchSnapshot();
});

test('reflight request from invalid origin', async () => {
const response = await app.inject({
method: 'OPTIONS',
url: '/graphql',
headers: {
origin: 'http://unknown-host',
'access-control-request-method': 'PUT',
'access-control-request-headers': 'x-api-client,authorization,unknown-header',
},
});

expect(response.statusCode).toEqual(204);
const headers = Object.fromEntries(
Object.entries(response.headers).filter(([k]) => k.startsWith('access-control-allow'))
);
expect(headers).toMatchSnapshot();
});
});
6 changes: 6 additions & 0 deletions services/tests/blackbox/setup.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ process.env.FS_REGISTRY_RESOURCE_METADATA_PATH = './tests/blackbox/resources/res
process.env.USE_S3_RESOURCE_REPOSITORY = 'false';
process.env.USE_FS_RESOURCE_REPOSITORY = 'true';

process.env.CORS_CONFIGURATION = JSON.stringify({
methods: ['GET', 'POST'],
origin: ['http://localhost', 'http://my-web-app.com'],
allowedHeaders: ['x-api-client', 'authorization'],
});

process.env.AUTHENTICATION_CONFIGURATION = JSON.stringify({
anonymous: {
publicPaths: ['/metrics', '/.well-known/apollo/server-health', '/graphql'],
Expand Down

0 comments on commit 748932c

Please sign in to comment.