Skip to content

Commit 387d6ff

Browse files
authored
feat(lambda-tiler)!: allow API keys to be disabled BM-1281 (#3441)
### Motivation We have seen a increase of consumer api keys using scripted methods to pull large amounts of data from basemaps.linz.govt.nz these users should be using bulk data access methods like linz/imagery or limz/elevation, as the keys are only consumer we have no easy way of contacting them. Our only option is to disable their keys and hope that they reach out. ### Modifications Add a environment variable to allow us to block API keys ### Verification unit tests and further tests in nonprod.
1 parent de9df87 commit 387d6ff

File tree

4 files changed

+57
-2
lines changed

4 files changed

+57
-2
lines changed

packages/_infra/src/serve/lambda.tiler.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
import { Env } from '@basemaps/shared';
1+
import assert from 'node:assert';
2+
3+
import { Env, isValidApiKey } from '@basemaps/shared';
24
import * as cdk from 'aws-cdk-lib';
35
import { Duration } from 'aws-cdk-lib';
46
import iam from 'aws-cdk-lib/aws-iam';
@@ -38,6 +40,17 @@ export class LambdaTiler extends Construct {
3840
environment[Env.StaticAssetLocation] = `s3://${props.staticBucketName}/`;
3941
}
4042

43+
// Set blocked api keys if some are present
44+
const blockedKeys = Env.get(Env.BlockedApiKeys);
45+
if (blockedKeys != null) {
46+
const listOfKeys = JSON.parse(blockedKeys) as string[];
47+
if (!Array.isArray(listOfKeys)) throw new Error(` ${Env.BlockedApiKeys} is not valid`);
48+
for (const key of listOfKeys) {
49+
assert.ok(isValidApiKey(key).valid, `${key} is not a valid api key to block ${Env.BlockedApiKeys}`);
50+
}
51+
environment[Env.BlockedApiKeys] = blockedKeys;
52+
}
53+
4154
const code = lambda.Code.fromAsset(CODE_PATH);
4255

4356
/**

packages/lambda-tiler/src/util/__test__/validate.test.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,24 @@ import { GoogleTms, Nztm2000QuadTms, Nztm2000Tms } from '@basemaps/geo';
66
import { mockUrlRequest } from '../../__tests__/xyz.util.js';
77
import { Validate } from '../validate.js';
88

9+
describe('Validate.blockedApiKeys', () => {
10+
const validApiKey = 'c01jswmpe1yn3mwne7e0ggtp8vg';
11+
it('should disable api keys', () => {
12+
const req = mockUrlRequest('/v1/blank', `api=${validApiKey}`);
13+
const parsedKey = Validate.apiKey(req);
14+
assert.equal(parsedKey, validApiKey);
15+
});
16+
17+
it('should disable api keys', () => {
18+
const req = mockUrlRequest('/v1/blank', `api=${validApiKey}`);
19+
Validate.blockedApiKeys.add(validApiKey);
20+
assert.throws(() => Validate.apiKey(req));
21+
22+
Validate.blockedApiKeys.delete(validApiKey);
23+
assert.equal(Validate.apiKey(req), validApiKey);
24+
});
25+
});
26+
927
describe('GetImageFormats', () => {
1028
it('should parse all formats', () => {
1129
const req = mockUrlRequest('/v1/blank', 'format=png&format=jpeg');

packages/lambda-tiler/src/util/validate.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { ConfigTileSetRaster, ConfigTileSetRasterOutput } from '@basemaps/config';
22
import { ImageFormat, LatLon, Projection, TileMatrixSet, TileMatrixSets } from '@basemaps/geo';
3-
import { Const, isValidApiKey, truncateApiKey } from '@basemaps/shared';
3+
import { Const, Env, isValidApiKey, LogConfig, truncateApiKey } from '@basemaps/shared';
44
import { getImageFormat } from '@basemaps/tiler';
55
import { LambdaHttpRequest, LambdaHttpResponse } from '@linzjs/lambda';
66

@@ -23,7 +23,19 @@ export interface TileMatrixRequest {
2323
Params: { tileMatrix?: string };
2424
}
2525

26+
function getBlockedApiKeys(): string[] {
27+
try {
28+
return JSON.parse(Env.get(Env.BlockedApiKeys) ?? '[]') as string[];
29+
} catch (e) {
30+
LogConfig.get().error(`"$${Env.BlockedApiKeys}" is invalid`);
31+
return [];
32+
}
33+
}
34+
2635
export const Validate = {
36+
/** list of API Keys that have been disabled */
37+
blockedApiKeys: new Set<string>(getBlockedApiKeys()),
38+
2739
/**
2840
* Validate that the api key exists and is valid
2941
*
@@ -36,6 +48,11 @@ export const Validate = {
3648
if (!valid.valid) throw new LambdaHttpResponse(400, 'API Key Invalid: ' + valid.message);
3749
// Truncate the API Key so we are not logging the full key
3850
req.set('api', truncateApiKey(apiKey));
51+
52+
if (this.blockedApiKeys.has(apiKey as string)) {
53+
throw new LambdaHttpResponse(429, 'Too many requests! Please contact [email protected] for a developer key');
54+
}
55+
3956
return apiKey as string;
4057
},
4158

packages/shared/src/const.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,13 @@ export const Env = {
4242
/** Github api token */
4343
GitHubToken: 'GITHUB_API_TOKEN',
4444

45+
/**
46+
* JSON encoded array of api keys to block
47+
*
48+
* @example '["apiKeyA","apiKeyB"]'
49+
*/
50+
BlockedApiKeys: 'BASEMAPS_API_KEY_BLOCKS',
51+
4552
Gdal: {
4653
/** Should the gdal docker container be used? */
4754
UseDocker: 'GDAL_DOCKER',

0 commit comments

Comments
 (0)