Skip to content

Commit

Permalink
feat: Allow passing site token for hlx5 authenticated sites (#2465)
Browse files Browse the repository at this point in the history
  • Loading branch information
andreituicu authored Dec 16, 2024
1 parent 98a88c2 commit f3bde8f
Show file tree
Hide file tree
Showing 10 changed files with 150 additions and 2 deletions.
7 changes: 6 additions & 1 deletion src/server/HeadHtmlSupport.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,14 +76,15 @@ export default class HeadHtmlSupport {
}

constructor({
proxyUrl, directory, allowInsecure, log,
proxyUrl, directory, allowInsecure, log, siteToken,
}) {
this.remoteHtml = '';
this.remoteDom = null;
this.remoteStatus = 0;
this.localHtml = '';
this.localStatus = 0;
this.cookie = '';
this.siteToken = siteToken;
this.url = new URL(proxyUrl);
this.url.pathname = '/head.html';
this.filePath = resolve(directory, 'head.html');
Expand All @@ -97,6 +98,10 @@ export default class HeadHtmlSupport {
if (this.cookie) {
headers.cookie = this.cookie;
}
// hlx 5 site auth
if (this.siteToken) {
headers.authorization = `token ${this.siteToken}`;
}
const resp = await getFetch(this.allowInsecure)(this.url, {
cache: 'no-store',
headers,
Expand Down
6 changes: 6 additions & 0 deletions src/server/HelixProject.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ export class HelixProject extends BaseProject {
return this;
}

withSiteToken(value) {
this._server.withSiteToken(value);
return this;
}

withProxyUrl(value) {
this._proxyUrl = value;
return this;
Expand Down Expand Up @@ -88,6 +93,7 @@ export class HelixProject extends BaseProject {
log: this.log,
proxyUrl: this.proxyUrl,
allowInsecure: this.allowInsecure,
siteToken: this.siteToken,
});

// register local head in live-reload
Expand Down
7 changes: 7 additions & 0 deletions src/server/HelixServer.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ export class HelixServer extends BaseServer {
return this;
}

withSiteToken(value) {
this._siteToken = value;
return this;
}

/**
* Proxy Mode route handler
* @param {Express.Request} req request
Expand Down Expand Up @@ -98,12 +103,14 @@ export class HelixServer extends BaseServer {
for (const [key, value] of proxyUrl.searchParams.entries()) {
url.searchParams.append(key, value);
}

await utils.proxyRequest(ctx, url.href, req, res, {
injectLiveReload: this._project.liveReload,
headHtml: this._project.headHtml,
indexer: this._project.indexer,
cacheDirectory: this._project.cacheDirectory,
file404html: this._project.file404html,
siteToken: this._siteToken,
});
} catch (err) {
log.error(`${pfx}failed to proxy AEM request ${ctx.path}: ${err.message}`);
Expand Down
7 changes: 7 additions & 0 deletions src/server/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,12 @@ window.LiveReloadOptions = {
...req.headers,
...(opts.headers || {}),
};

// hlx 5 site auth
if (opts.siteToken) {
headers.authorization = `token ${opts.siteToken}`;
}

// preserve hlx-auth-token cookie
const cookies = cookie.parse(headers.cookie || '');
delete headers.cookie;
Expand All @@ -255,6 +261,7 @@ window.LiveReloadOptions = {
'hlx-auth-token': hlxAuthToken,
}).toString();
}

delete headers.connection;
delete headers['proxy-connection'];
delete headers.host;
Expand Down
9 changes: 8 additions & 1 deletion src/up.cmd.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ export default class UpCommand extends AbstractServerCommand {
return this;
}

withSiteToken(value) {
this._siteToken = value;
return this;
}

async doStop() {
await super.doStop();
if (this._watcher) {
Expand Down Expand Up @@ -71,7 +76,9 @@ export default class UpCommand extends AbstractServerCommand {
.withLogger(this._logger)
.withKill(this._kill)
.withPrintIndex(this._printIndex)
.withAllowInsecure(this._allowInsecure);
.withAllowInsecure(this._allowInsecure)
.withSiteToken(this._siteToken);

this.log.info(chalk`{yellow ___ ________ ___ __ __ v${pkgJson.version}}`);
this.log.info(chalk`{yellow / | / ____/ |/ / _____(_)___ ___ __ __/ /___ _/ /_____ _____}`);
this.log.info(chalk`{yellow / /| | / __/ / /|_/ / / ___/ / __ \`__ \\/ / / / / __ \`/ __/ __ \\/ ___/}`);
Expand Down
6 changes: 6 additions & 0 deletions src/up.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ export default function up() {
type: 'int',
default: 3000,
})
.option('site-token', {
alias: 'siteToken',
describe: 'Site token to be used by the cli to access the website',
type: 'string',
})
.option('addr', {
describe: 'Bind development server on addr. use * to bind to any address and allow external connections.',
type: 'string',
Expand Down Expand Up @@ -116,6 +121,7 @@ export default function up() {
.withOpen(path.basename(argv.$0) === 'aem' ? argv.open : false)
.withTLS(argv.tlsKey, argv.tlsCert)
.withLiveReload(argv.livereload)
.withSiteToken(argv.siteToken)
.withUrl(argv.url)
.withPrintIndex(argv.printIndex)
.withAllowInsecure(argv.allowInsecure)
Expand Down
1 change: 1 addition & 0 deletions test/fixtures/all.env
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ AEM_HOST=www.project-helix.io
AEM_NO_OPEN=true
AEM_LIVERELOAD=false
AEM_PORT=1234
AEM_SITE_TOKEN=secret-site-token
AEM_ADDR=*
AEM_LOCAL_REPO="., ../foo-content, ../bar-content"
#AEM_SAVE_CONFIG <forbidden use through env>
Expand Down
21 changes: 21 additions & 0 deletions test/head-html.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,27 @@ describe('Head.html loading tests', () => {
await hhs.update();
});

it('update loads remote head.html with site token', async () => {
const siteToken = 'hlxtst_site-token';
const directory = await setupProject(path.join(__rootdir, 'test', 'fixtures', 'project'), testRoot);

nock('https://main--blog--adobe.hlx.page')
.get('/head.html')
.reply(function request() {
assert.strictEqual(this.req.headers.authorization, `token ${siteToken}`);
return [200, '<!-- remote head html --><a>fooo</a>\n'];
});

const hhs = new HeadHtmlSupport({
log: console,
proxyUrl: 'https://main--blog--adobe.hlx.page',
directory,
siteToken,
});
hhs.localStatus = 200;
await hhs.update();
});

it('update loads remote head.html can handle errors', async () => {
const directory = await setupProject(path.join(__rootdir, 'test', 'fixtures', 'project'), testRoot);

Expand Down
8 changes: 8 additions & 0 deletions test/up-cli.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ describe('hlx up', () => {
mockUp.withAllowInsecure.returnsThis();
mockUp.withKill.returnsThis();
mockUp.withCache.returnsThis();
mockUp.withSiteToken.returnsThis();
mockUp.run.returnsThis();
cli = (await new CLI().initCommands()).withCommandExecutor('up', mockUp);
});
Expand Down Expand Up @@ -72,6 +73,7 @@ describe('hlx up', () => {
sinon.assert.calledWith(mockUp.withHttpPort, 1234);
sinon.assert.calledWith(mockUp.withBindAddr, '*');
sinon.assert.calledWith(mockUp.withPrintIndex, true);
sinon.assert.calledWith(mockUp.withSiteToken, 'secret-site-token');
sinon.assert.calledOnce(mockUp.run);
});

Expand Down Expand Up @@ -139,4 +141,10 @@ describe('hlx up', () => {
sinon.assert.calledWith(mockUp.withKill, false);
sinon.assert.calledOnce(mockUp.run);
});

it('aem up can set site token', async () => {
await cli.run(['up', '--site-token', 'secret-site-token']);
sinon.assert.calledWith(mockUp.withSiteToken, 'secret-site-token');
sinon.assert.calledOnce(mockUp.run);
});
});
80 changes: 80 additions & 0 deletions test/up-cmd.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,86 @@ describe('Integration test for up command with helix pages', function suite() {
assert.strictEqual(opened, `http://localhost:${port}/`);
});

it('up command opens browser and delivers correct response on helix 5 with auth.', async () => {
const TOKEN = 'secret-site-token';
function checkTokenAndReply(req, response) {
const { authorization } = req.headers;
if (!authorization) {
return [401, 'Unauthorized'];
}

if (authorization !== `token ${TOKEN}`) {
return [403, 'Forbidden'];
}

return response;
}

let opened;
const MockedCommand = await esmock('../src/up.cmd.js', {
'../src/abstract-server.cmd.js': await esmock('../src/abstract-server.cmd.js', {
open: (url) => {
opened = url;
},
}),
});
initGit(testDir, 'https://github.com/adobe/dummy-foo.git');
const cmd = new MockedCommand()
.withLiveReload(false)
.withDirectory(testDir)
.withOpen('/')
.withHttpPort(0)
.withSiteToken(TOKEN);

nock('https://main--dummy-foo--adobe.aem.page')
.get('/index.html')
.reply(function f() {
return checkTokenAndReply(this.req, [200, '## Welcome']);
})
.get('/not-found.txt')
.reply(function f() {
return checkTokenAndReply(this.req, [404, 'Not Found']);
});

nock('https://admin.hlx.page:443')
.get('/sidekick/adobe/dummy-foo/main/config.json')
.reply(200, {
host: 'example.com',
liveHost: 'main--dummy-foo--adobe.aem.live',
previewHost: 'main--dummy-foo--adobe..aem.page',
project: 'Example Project on Helix 5',
testProperty: 'header',
});

let port;
await new Promise((resolve, reject) => {
let error = null;
cmd
.on('started', async () => {
try {
port = cmd.project.server.port;
let ret = await assertHttp(`http://127.0.0.1:${port}/index.html`, 200);
assert.strictEqual(ret.trim(), '## Welcome');
ret = await assertHttp(`http://127.0.0.1:${port}/local.txt`, 200);
assert.strictEqual(ret.trim(), 'Hello, world.');
await assertHttp(`http://127.0.0.1:${port}/not-found.txt`, 404);
} catch (e) {
error = e;
}
await cmd.stop();
})
.on('stopped', () => {
if (error) {
reject(error);
}
resolve();
})
.run()
.catch(reject);
});
assert.strictEqual(opened, `http://localhost:${port}/`);
});

it('up command correctly replaces variables in url', async () => {
const MockedCommand = await esmock('../src/up.cmd.js', {
'../src/abstract-server.cmd.js': await esmock('../src/abstract-server.cmd.js'),
Expand Down

0 comments on commit f3bde8f

Please sign in to comment.