Skip to content

Commit

Permalink
feat: config auth (#59)
Browse files Browse the repository at this point in the history
* feat: config auth

* add config route

* fix tests

* fix: dont double stringify
  • Loading branch information
maxakuru authored Dec 10, 2024
1 parent 78cac07 commit fbf0000
Show file tree
Hide file tree
Showing 7 changed files with 113 additions and 9 deletions.
38 changes: 38 additions & 0 deletions src/config/handler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright 2024 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/

import { errorResponse } from '../utils/http.js';
import { assertAuthorization } from '../utils/auth.js';

/**
* @param {Context} ctx
* @returns {Promise<Response>}
*/
export default async function configHandler(ctx) {
const { method } = ctx.info;
if (!['GET', 'POST'].includes(method)) {
return errorResponse(405, 'method not allowed');
}

await assertAuthorization(ctx);

if (method === 'GET') {
return new Response(ctx.config.confMapStr, {
headers: {
'Content-Type': 'application/json',
},
});
}

// TODO: validate config body, set config in kv
return errorResponse(501, 'not implemented');
}
6 changes: 4 additions & 2 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,18 @@
*/

import { errorResponse } from './utils/http.js';
import { resolveConfig } from './config.js';
import { resolveConfig } from './utils/config.js';
import content from './content/handler.js';
import catalog from './catalog/handler.js';
import configHandler from './config/handler.js';

/**
* @type {Record<string, (ctx: Context, request: Request) => Promise<Response>>}
*/
const handlers = {
content,
catalog,
config: configHandler,
// eslint-disable-next-line no-unused-vars
graphql: async (ctx) => errorResponse(501, 'not implemented'),
};
Expand All @@ -41,7 +43,7 @@ export function makeContext(pctx, req, env) {
ctx.url = new URL(req.url);
ctx.log = console;
ctx.info = {
method: req.method,
method: req.method.toUpperCase(),
headers: Object.fromEntries(
[...req.headers.entries()]
.map(([k, v]) => [k.toLowerCase(), v]),
Expand Down
3 changes: 3 additions & 0 deletions src/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ declare global {
imageParams?: Record<string, string>;

confMap: ConfigMap;
confMapStr: string;
}

export interface Env {
Expand All @@ -58,6 +59,7 @@ declare global {

// KV namespaces
CONFIGS: KVNamespace<string>;
KEYS: KVNamespace<string>;

[key: string]: string | KVNamespace<string> | R2Bucket;
}
Expand All @@ -74,6 +76,7 @@ declare global {
attributes: {
htmlTemplate?: HTMLTemplate;
jsonTemplate?: JSONTemplate;
key?: string;
[key: string]: any;
}
}
Expand Down
35 changes: 35 additions & 0 deletions src/utils/auth.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright 2024 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/

import { errorWithResponse } from './http.js';

/**
* @param {Context} ctx
*/
export async function assertAuthorization(ctx) {
let actual;
if (typeof ctx.attributes.key === 'undefined') {
ctx.attributes.key = ctx.info.headers.authorization?.slice('Bearer '.length);
actual = ctx.attributes.key;
}
if (!actual) {
throw errorWithResponse(403, 'invalid key');
}

const expected = await ctx.env.KEYS.get(ctx.config.siteKey);
if (!expected) {
throw errorWithResponse(403, 'no key found for site');
}
if (actual !== expected) {
throw errorWithResponse(403, 'access denied');
}
}
21 changes: 20 additions & 1 deletion src/config.js → src/utils/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
* governing permissions and limitations under the License.
*/

import { errorWithResponse } from './utils/http.js';
import { errorWithResponse } from './http.js';

/**
* This function finds ordered matches between a list of patterns and a given path.
Expand Down Expand Up @@ -65,6 +65,7 @@ export async function resolveConfig(ctx, overrides = {}) {
* @type {ConfigMap}
*/
const confMap = await ctx.env.CONFIGS.get(siteKey, 'json');
const confMapStr = JSON.stringify(confMap);
if (!confMap) {
return null;
}
Expand All @@ -73,6 +74,23 @@ export async function resolveConfig(ctx, overrides = {}) {
return null;
}

// if route is `config` don't resolve further
if (route === 'config') {
return {
...confMap.base,
headers: confMap.base?.headers ?? {},
params: {},
confMap,
confMapStr,
org,
site,
route,
siteKey,
matchedPatterns: [],
...overrides,
};
}

// order paths by preference
const suffix = `/${ctx.url.pathname.split('/').slice(3).join('/')}`;
const paths = findOrderedMatches(
Expand Down Expand Up @@ -101,6 +119,7 @@ export async function resolveConfig(ctx, overrides = {}) {
params: {},
}),
confMap,
confMapStr,
org,
site,
route,
Expand Down
10 changes: 7 additions & 3 deletions test/config.test.js → test/utils/config.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@
// @ts-nocheck

import assert from 'node:assert';
import { resolveConfig } from '../src/config.js';
import { TEST_CONTEXT } from './fixtures/context.js';
import { defaultTenantConfigs } from './fixtures/kv.js';
import { resolveConfig } from '../../src/utils/config.js';
import { TEST_CONTEXT } from '../fixtures/context.js';
import { defaultTenantConfigs } from '../fixtures/kv.js';

describe('config tests', () => {
it('should extract path params', async () => {
Expand Down Expand Up @@ -52,6 +52,7 @@ describe('config tests', () => {
apiKey: 'bad',
},
},
confMapStr: '{"base":{"apiKey":"bad"},"/us/p/{{urlkey}}/{{sku}}":{"pageType":"product","apiKey":"good"}}',
});
});

Expand Down Expand Up @@ -105,6 +106,7 @@ describe('config tests', () => {
},
},
},
confMapStr: '{"base":{"apiKey":"bad","headers":{"foo":"1","baz":"1"}},"/us/p/{{urlkey}}/{{sku}}":{"pageType":"product","apiKey":"good","headers":{"foo":"2","bar":"2"}}}',
});
});

Expand Down Expand Up @@ -142,6 +144,7 @@ describe('config tests', () => {
apiKey: 'bad',
},
},
confMapStr: '{"base":{"apiKey":"bad"},"/us/p/*/{{sku}}":{"pageType":"product","apiKey":"good"}}',
});
});

Expand Down Expand Up @@ -182,6 +185,7 @@ describe('config tests', () => {
apiKey: 'bad1',
},
},
confMapStr: '{"base":{"apiKey":"bad1"},"/us/p/{{sku}}":{"pageType":"product","apiKey":"bad2"}}',
});
});

Expand Down
9 changes: 6 additions & 3 deletions wrangler.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ send_metrics = false
build = { command = "npm install && node build.js" }

kv_namespaces = [
{ binding = "CONFIGS", id = "bb91e12a65a8462282396f32e63406f1", preview_id = "bb91e12a65a8462282396f32e63406f1" }
{ binding = "CONFIGS", id = "bb91e12a65a8462282396f32e63406f1", preview_id = "bb91e12a65a8462282396f32e63406f1" },
{ binding = "KEYS", id = "0ca5d0ba0150453bb8aafcdf4304dc7a", preview_id = "0ca5d0ba0150453bb8aafcdf4304dc7a" }
]

[[r2_buckets]]
Expand All @@ -27,7 +28,8 @@ ENVIRONMENT = "dev"
name = "adobe-commerce-api-ci"

kv_namespaces = [
{ binding = "CONFIGS", id = "bb91e12a65a8462282396f32e63406f1", preview_id = "bb91e12a65a8462282396f32e63406f1" }
{ binding = "CONFIGS", id = "bb91e12a65a8462282396f32e63406f1", preview_id = "bb91e12a65a8462282396f32e63406f1" },
{ binding = "KEYS", id = "0ca5d0ba0150453bb8aafcdf4304dc7a", preview_id = "0ca5d0ba0150453bb8aafcdf4304dc7a" }
]

[[env.ci.r2_buckets]]
Expand All @@ -46,7 +48,8 @@ ENVIRONMENT = "ci"
name = "adobe-commerce-api"

kv_namespaces = [
{ binding = "CONFIGS", id = "bb91e12a65a8462282396f32e63406f1", preview_id = "bb91e12a65a8462282396f32e63406f1" }
{ binding = "CONFIGS", id = "bb91e12a65a8462282396f32e63406f1", preview_id = "bb91e12a65a8462282396f32e63406f1" },
{ binding = "KEYS", id = "0ca5d0ba0150453bb8aafcdf4304dc7a", preview_id = "0ca5d0ba0150453bb8aafcdf4304dc7a" }
]

[[env.production.r2_buckets]]
Expand Down

0 comments on commit fbf0000

Please sign in to comment.