diff --git a/nix/nixos/cardano-graphql-service.nix b/nix/nixos/cardano-graphql-service.nix index 61646ce3..03e5da86 100644 --- a/nix/nixos/cardano-graphql-service.nix +++ b/nix/nixos/cardano-graphql-service.nix @@ -82,10 +82,10 @@ in { default = false; description = "Allows introspection queries"; }; - whitelistPath = lib.mkOption { + allowListPath = lib.mkOption { type = lib.types.nullOr lib.types.path; default = null; - description = "Source directory or file to generate whitelist from"; + description = "Source directory or file to generate allow-list from"; }; }; }; @@ -126,7 +126,7 @@ in { } // (lib.optionalAttrs (cfg.allowedOrigins != null) { ALLOWED_ORIGINS = cfg.allowedOrigins; }) // (lib.optionalAttrs (cfg.queryDepthLimit != null) { QUERY_DEPTH_LIMIT = toString cfg.queryDepthLimit; }) // - (lib.optionalAttrs (cfg.whitelistPath != null) { WHITELIST_PATH = cfg.whitelistPath; }); + (lib.optionalAttrs (cfg.allowListPath != null) { ALLOW_LIST_PATH = cfg.allowListPath; }); path = with pkgs; [ netcat curl postgresql jq frontend hasura-cli glibc.bin patchelf ]; preStart = '' set -exuo pipefail diff --git a/packages/server/src/apollo_server_plugins/whitelist_plugin.ts b/packages/server/src/apollo_server_plugins/allow_list_plugin.ts similarity index 58% rename from packages/server/src/apollo_server_plugins/whitelist_plugin.ts rename to packages/server/src/apollo_server_plugins/allow_list_plugin.ts index eb4e0c79..205c2961 100644 --- a/packages/server/src/apollo_server_plugins/whitelist_plugin.ts +++ b/packages/server/src/apollo_server_plugins/allow_list_plugin.ts @@ -1,16 +1,16 @@ import { ForbiddenError } from 'apollo-server-errors' import { PluginDefinition } from 'apollo-server-core' -export function whitelistPlugin (whitelist: {[key: string]: number }): PluginDefinition { +export function allowListPlugin (allowList: {[key: string]: number }): PluginDefinition { return { serverWillStart (_service) { - console.log(`The server is configured to accept ${Object.keys(whitelist).length} operations from the provided whitelist`) + console.log(`The server is configured to accept ${Object.keys(allowList).length} operations from the provided allow-list`) }, requestDidStart () { return { parsingDidStart (context) { - if (whitelist[context.request.query] === undefined) { - throw new ForbiddenError('Operation is forbidden') + if (allowList[context.request.query] === undefined) { + throw new ForbiddenError('Operation is disallowed') } } } diff --git a/packages/server/src/apollo_server_plugins/index.ts b/packages/server/src/apollo_server_plugins/index.ts index aae886d4..37006576 100644 --- a/packages/server/src/apollo_server_plugins/index.ts +++ b/packages/server/src/apollo_server_plugins/index.ts @@ -1,2 +1,2 @@ export * from './prometheus_metrics_plugin' -export * from './whitelist_plugin' +export * from './allow_list_plugin' diff --git a/packages/server/src/config.ts b/packages/server/src/config.ts index c25ef2d8..95970829 100644 --- a/packages/server/src/config.ts +++ b/packages/server/src/config.ts @@ -4,6 +4,7 @@ import { IntrospectionNotPermitted, MissingConfig, TracingRequired } from './err export type Config = { allowIntrospection?: boolean allowedOrigins: CorsOptions['origin'] + allowListPath: string apiPort: number cacheEnabled: boolean genesisFileByron: string @@ -13,13 +14,13 @@ export type Config = { prometheusMetrics: boolean queryDepthLimit: number tracing: boolean - whitelistPath: string } export async function getConfig (): Promise { const { allowIntrospection, allowedOrigins, + allowListPath, apiPort, cacheEnabled, genesisFileByron, @@ -28,8 +29,7 @@ export async function getConfig (): Promise { poolMetadataProxy, prometheusMetrics, queryDepthLimit, - tracing, - whitelistPath + tracing } = filterAndTypecastEnvs(process.env) if (!hasuraUri && !genesisFileShelley) { @@ -38,12 +38,13 @@ export async function getConfig (): Promise { if (prometheusMetrics && process.env.TRACING === 'false') { throw new TracingRequired('Prometheus') } - if (whitelistPath && allowIntrospection) { - throw new IntrospectionNotPermitted('whitelist') + if (allowListPath && allowIntrospection) { + throw new IntrospectionNotPermitted('allowList') } return { allowIntrospection, allowedOrigins: allowedOrigins || true, + allowListPath, apiPort: apiPort || 3100, cacheEnabled: cacheEnabled || false, genesisFileByron, @@ -52,8 +53,7 @@ export async function getConfig (): Promise { poolMetadataProxy, prometheusMetrics, queryDepthLimit: queryDepthLimit || 10, - tracing, - whitelistPath + tracing } } @@ -61,6 +61,7 @@ function filterAndTypecastEnvs (env: any) { const { ALLOW_INTROSPECTION, ALLOWED_ORIGINS, + ALLOW_LIST_PATH, API_PORT, CACHE_ENABLED, GENESIS_FILE_BYRON, @@ -72,9 +73,14 @@ function filterAndTypecastEnvs (env: any) { TRACING, WHITELIST_PATH } = env + if (WHITELIST_PATH) { + console.log(`WHITELIST_PATH is deprecated and will be removed in 3.0.0. + Please update your configuration to use ALLOW_LIST_PATH instead.`) + } return { allowIntrospection: ALLOW_INTROSPECTION === 'true' ? true : undefined, allowedOrigins: ALLOWED_ORIGINS, + allowListPath: ALLOW_LIST_PATH || WHITELIST_PATH, apiPort: Number(API_PORT), cacheEnabled: CACHE_ENABLED === 'true' ? true : undefined, genesisFileByron: GENESIS_FILE_BYRON, @@ -83,7 +89,6 @@ function filterAndTypecastEnvs (env: any) { poolMetadataProxy: POOL_METADATA_PROXY, prometheusMetrics: PROMETHEUS_METRICS === 'true' ? true : undefined, queryDepthLimit: Number(QUERY_DEPTH_LIMIT), - tracing: TRACING === 'true' ? true : undefined, - whitelistPath: WHITELIST_PATH + tracing: TRACING === 'true' ? true : undefined } } diff --git a/packages/server/src/index.ts b/packages/server/src/index.ts index f63fe7b2..22387053 100644 --- a/packages/server/src/index.ts +++ b/packages/server/src/index.ts @@ -12,7 +12,7 @@ import { GraphQLSchema } from 'graphql' export * from './config' export { apolloServerPlugins } -const { prometheusMetricsPlugin, whitelistPlugin } = apolloServerPlugins +const { prometheusMetricsPlugin, allowListPlugin } = apolloServerPlugins async function boot () { const config = await getConfig() @@ -37,9 +37,9 @@ async function boot () { if (config.prometheusMetrics) { plugins.push(prometheusMetricsPlugin(app)) } - if (config.whitelistPath) { - const whitelist = JSON.parse(fs.readFileSync(config.whitelistPath, 'utf8')) - plugins.push(whitelistPlugin(whitelist)) + if (config.allowListPath) { + const allowList = JSON.parse(fs.readFileSync(config.allowListPath, 'utf8')) + plugins.push(allowListPlugin(allowList)) } if (config.queryDepthLimit) { validationRules.push(depthLimit(config.queryDepthLimit)) @@ -61,8 +61,8 @@ async function boot () { app.listen({ port: config.apiPort }, () => { const serverUri = `http://localhost:${config.apiPort}` console.log(`GraphQL HTTP server at ${serverUri}${server.graphqlPath}`) - if (process.env.NODE_ENV !== 'production' && config.whitelistPath) { - console.warn('As a whitelist is in effect, the GraphQL Playground is available, but will not allow schema exploration') + if (process.env.NODE_ENV !== 'production' && config.allowListPath) { + console.warn('As an allow-list is in effect, the GraphQL Playground is available, but will not allow schema exploration') } if (config.prometheusMetrics) { console.log(`Prometheus metrics at ${serverUri}/metrics`) diff --git a/packages/server/test/Server.test.ts b/packages/server/test/Server.test.ts index dccb303a..ceeb60cc 100644 --- a/packages/server/test/Server.test.ts +++ b/packages/server/test/Server.test.ts @@ -11,7 +11,7 @@ import tmp from 'tmp-promise' import util from '@cardano-graphql/util' import { buildSchema as buildGenesisSchema } from '@cardano-graphql/api-genesis' import { Server } from '@src/Server' -import { whitelistPlugin } from '@src/apollo_server_plugins' +import { allowListPlugin } from '@src/apollo_server_plugins' const byronTestnetGenesis = '../../../config/network/testnet/genesis/byron.json' const shelleyTestnetGenesis = '../../../config/network/testnet/genesis/shelley.json' @@ -27,14 +27,14 @@ function listen (app: Application, port: number): Promise { describe('Server', () => { let client: ApolloClient - let whiteListedDocumentNode: DocumentNode + let allowedDocumentNode: DocumentNode let app: express.Application let httpServer: http.Server let genesisSchema: GraphQLSchema beforeAll(async () => { genesisSchema = buildGenesisSchema({ byron: require(byronTestnetGenesis), shelley: require(shelleyTestnetGenesis) }) - whiteListedDocumentNode = await util.loadQueryNode(path.resolve(clientPath, 'src', 'feature_1'), 'maxLovelaceSupply') + allowedDocumentNode = await util.loadQueryNode(path.resolve(clientPath, 'src', 'feature_1'), 'maxLovelaceSupply') }) beforeEach(async () => { @@ -58,8 +58,8 @@ describe('Server', () => { app = express() }) - describe('Whitelisting', () => { - describe('Booting the server without providing a whitelist', () => { + describe('Allowing specific queries', () => { + describe('Booting the server without providing an allow-list', () => { beforeEach(async () => { Server(app, { schema: genesisSchema @@ -85,13 +85,13 @@ describe('Server', () => { }) }) - describe('Providing a whitelist produced by persistgraphql, intended to limit the API for specific application requirements', () => { + describe('Providing an allow-list produced by persistgraphql, intended to limit the API for specific application requirements', () => { beforeEach(async () => { - const whitelistPath = await tmp.tmpName({ postfix: '.json' }) - execSync(`npx persistgraphql ${clientPath} ${whitelistPath}`) - const whitelist = JSON.parse(fs.readFileSync(whitelistPath, 'utf-8')) + const allowlistPath = await tmp.tmpName({ postfix: '.json' }) + execSync(`npx persistgraphql ${clientPath} ${allowlistPath}`) + const allowList = JSON.parse(fs.readFileSync(allowlistPath, 'utf-8')) Server(app, { - plugins: [whitelistPlugin(whitelist)], + plugins: [allowListPlugin(allowList)], schema: genesisSchema }) httpServer = await listen(app, port) @@ -100,14 +100,14 @@ describe('Server', () => { httpServer.close() }) - it('Accepts listed queries', async () => { + it('Accepts allowed queries', async () => { const result = await client.query({ - query: whiteListedDocumentNode + query: allowedDocumentNode }) expect(result.data.genesis.shelley.maxLovelaceSupply).toBeDefined() expect(result.errors).not.toBeDefined() }) - it('Returns a networkError if a valid but unlisted operation is sent', async () => { + it('Returns a networkError if a valid but disallowed operation is sent', async () => { expect.assertions(1) try { await client.query({ diff --git a/shell.nix b/shell.nix index ec1f06e6..b2e981cd 100644 --- a/shell.nix +++ b/shell.nix @@ -39,7 +39,7 @@ let echo "NOTE: you may need to export GITHUB_TOKEN if you hit rate limits with niv" echo "Commands: * niv update - update package - * persistgraphql whitelist.json - generates a whitelist.json to limit graphql queries + * persistgraphql allowList.json - generates an allowList.json to limit graphql queries * export GOPATH="\$\(pwd\)/.go" - enable vgo2nix to use the pwd as it's source * node2nix -l - update node packages, -l if there's a lock file