Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .nycrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
"text-summary"
],
"check-coverage": true,
"lines": 70,
"branches": 70,
"statements": 70,
"functions": 70
"lines": 95,
"branches": 95,
"statements": 95,
"functions": 95
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"require": [
"nock",
"test/setup-env.js",
"test/setup-test-idp.js",
"mocha-suppress-logs"
],
"recursive": "true",
Expand Down
9 changes: 9 additions & 0 deletions src/auth/exchange-token.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,15 @@ import {
import { setAuthCookie } from './cookie.js';
import localJWKS from '../idp-configs/jwks-json.js';

/**
* Fetch tokens from IDP.
*
* @param {import('../support/AdminContext').AdminContext} context context
* @param {import('../support/RequestInfo').RequestInfo} info request info
* @param {import('./auth.d.ts').IDPConfig} idp IDP config
* @param {string} [tenantId] optional tenant id for the IDP
* @return {Promise<Response>} response
*/
async function fetchTokens(context, info, idp, tenantId) {
const { data, log } = context;

Expand Down
3 changes: 2 additions & 1 deletion src/cache/purge.js
Original file line number Diff line number Diff line change
Expand Up @@ -636,7 +636,8 @@ const purge = {

// create new path info with the ref given. this allows code-purges to force a purge of the branch
const forcedInfo = {
...info,
org: info.org,
site: info.site,
owner: info.owner,
repo: info.repo,
ref,
Expand Down
2 changes: 1 addition & 1 deletion src/idp-configs/jwks-json.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/

/**
* Public key store for the custom IDP emulation. The helix-html-pipeline reads this via.
* https://admin.hlx.page/auth/discovery/keys
*
* Whenever the private key is rotated, it's public counterpart should be added here.
* @todo add expiration and certificates
*/

export default {
keys: [
{
Expand Down
10 changes: 5 additions & 5 deletions src/login/handler.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { createCloseHtml, createSendMessageHtml } from '../auth/responses.js';
import { IDPS, LOGOUT_PATH, PROFILE_PATH } from '../auth/support.js';
import { createErrorResponse } from '../contentbus/utils.js';
import { isDAMountpoint } from '../support/adobe-source.js';
import { RequestInfo } from '../support/RequestInfo.js';
import idpAdobe from '../idp-configs/adobe.js';
import localJWKS from '../idp-configs/jwks-json.js';
import { loadSiteConfig } from '../config/utils.js';
Expand Down Expand Up @@ -220,10 +221,9 @@ export async function auth(context, info) {
data.org = data.state.org;
data.site = data.state.site;

// eslint-disable-next-line no-param-reassign
// TODO: info.org = data.org;
// eslint-disable-next-line no-param-reassign
// TODO: info.site = data.site;
const clone = RequestInfo.clone(info, {
org: data.org, site: data.site,
});

if (!data.code) {
if (data.state.prompt === 'none') {
Expand All @@ -239,7 +239,7 @@ export async function auth(context, info) {
status: 401,
});
}
return exchangeToken(context, info, idp, data.state.tenantId);
return exchangeToken(context, clone, idp, data.state.tenantId);
}
}
return new Response('', {
Expand Down
186 changes: 158 additions & 28 deletions src/support/RequestInfo.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
/* eslint-disable max-classes-per-file */

import { parse } from 'cookie';
import { sanitizeName } from '@adobe/helix-shared-string';
import { StatusCodeError } from './StatusCodeError.js';
Expand Down Expand Up @@ -136,17 +138,10 @@ export function computePaths(path) {
};
}

export class RequestInfo {
#owner;

#repo;

#ref;

/**
* @constructs RequestInfo
* @param {import('@adobe/fetch').Request} request request
*/
/**
* Class containing the aspects of the HTTP request.
*/
class HttpRequest {
constructor(request) {
this.method = request.method.toUpperCase();
this.headers = request.headers.plain();
Expand All @@ -159,6 +154,71 @@ export class RequestInfo {
this.host = process.env.HLX_DEV_SERVER_HOST ?? 'api.aem.live';
this.query = {};
}
}

/**
* Class containing the aspects of the decomposed path.
*/
class PathInfo {
constructor(route, org, site, path) {
this.route = route;
this.org = org;
this.site = site;
this.path = path;

if (path) {
const { webPath, resourcePath, ext } = computePaths(path);
if (ext === '.aspx') {
// onedrive doesn't like .aspx extension and reports wit 500. so we just reject it.
throw new StatusCodeError('', 404);
}
Object.assign(this, {
rawPath: path, webPath, resourcePath, ext,
});
}
}

/**
* Clone another path info.
*
* @param {PathInfo} other other info
* @param {object} param0 params
* @param {string} [param0.org] org, optional
* @param {string} [param0.site] site, optional
* @param {string} [param0.path] path, optional
] * @param {string} [param0.route] route, optional
* @returns {PathInfo} clone with the params overwritten
*/
static clone(other, {
route, org, site, path,
}) {
return new PathInfo(
route ?? other.route,
org ?? other.org,
site ?? other.site,
path ?? other.path,
);
}
}

/**
* Class containing the aspects of both HTTP request and decomposed path.
*/
export class RequestInfo {
#request;

#pathInfo;

#owner;

#repo;

#ref;

constructor(request, pathInfo) {
this.#request = request;
this.#pathInfo = pathInfo;
}

// eslint-disable-next-line class-methods-use-this
get path() {
Expand All @@ -178,6 +238,34 @@ export class RequestInfo {
return this;
}

get method() {
return this.#request.method;
}

get headers() {
return this.#request.headers;
}

get buffer() {
return this.#request.buffer;
}

get cookies() {
return this.#request.cookies;
}

get scheme() {
return this.#request.scheme;
}

get host() {
return this.#request.host;
}

get query() {
return this.#request.query;
}

get owner() {
return this.#owner;
}
Expand All @@ -190,37 +278,79 @@ export class RequestInfo {
return this.#ref ?? 'main';
}

get route() {
return this.#pathInfo.route;
}

get org() {
return this.#pathInfo.org;
}

get site() {
return this.#pathInfo.site;
}

get rawPath() {
return this.#pathInfo.rawPath;
}

get webPath() {
return this.#pathInfo.webPath;
}

get resourcePath() {
return this.#pathInfo.resourcePath;
}

get ext() {
return this.#pathInfo.ext;
}

/**
* Create a new request info.
*
* @param {import('@adobe/fetch').Request} request request
* @param {object} param0 params
* @param {string} [param0.org] org
* @param {string} [param0.org] org, optional
* @param {string} [param0.site] site, optional
* @param {string} [param0.path] path, optional
* @param {string} [param0.route] route
* @param {string} [param0.ref] ref, optional
* @param {string} [param0.route] route, optional
* @returns {RequestInfo}
*/
static create(request, {
org, site, path, ref, route,
} = {}) {
const info = new RequestInfo(request)
.withRef(ref);
const httpRequest = new HttpRequest(request);
const pathInfo = new PathInfo(route, org, site, path);

info.route = route;
info.org = org;
info.site = site;
return Object.freeze(new RequestInfo(httpRequest, pathInfo).withRef(ref));
}

/**
* Clone an existing request info.
*
* @param {RequestInfo} other
* @param {object} param0 params
* @param {string} [param0.org] org
* @param {string} [param0.site] site, optional
* @param {string} [param0.path] path, optional
* @param {string} [param0.route] route
* @returns {RequestInfo}
*/
static clone(other, {
org, site, path, route,
}) {
const info = new RequestInfo(
other.#request,
PathInfo.clone(other.#pathInfo, {
org, site, path, route,
}),
);
info.#owner = other.#owner;
info.#repo = other.#repo;
info.#ref = other.#ref;

if (path) {
const { webPath, resourcePath, ext } = computePaths(path);
if (ext === '.aspx') {
// onedrive doesn't like .aspx extension and reports wit 500. so we just reject it.
throw new StatusCodeError('', 404);
}
Object.assign(info, {
rawPath: path, webPath, resourcePath, ext,
});
}
return Object.freeze(info);
}

Expand Down
2 changes: 1 addition & 1 deletion test/contentproxy/onedrive.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ describe('OneDrive Integration Tests (docx)', () => {
it('Retrieves Document from Word via custom tenant', async () => {
nock.onedrive(SITE_1D_CONFIG.content)
.user()
.login(undefined, '01234567-abcd')
.login(undefined, '01234567-abcd', '01234567-abcd')
.resolve('')
.getDocument('/index.docx', { size: 3956 });

Expand Down
Loading