From 4ec18711f18c0524886c13d2997310d13570f772 Mon Sep 17 00:00:00 2001 From: Justin Wind Date: Sat, 12 Oct 2024 12:53:57 -0700 Subject: [PATCH] enable more route path name configurability --- config/default.js | 7 +++++ src/manager.js | 5 ++- src/service.js | 76 ++++++++++++++++++++++++++++----------------- test/src/service.js | 33 ++++++++++++++++++++ 4 files changed, 92 insertions(+), 29 deletions(-) diff --git a/config/default.js b/config/default.js index 3b3fdcb..818bfe4 100644 --- a/config/default.js +++ b/config/default.js @@ -28,6 +28,7 @@ const defaultOptions = { // The terminal portions of API route path endpoints. route: { + static: 'static', authorization: 'auth', consent: 'consent', healthcheck: 'healthcheck', @@ -37,6 +38,12 @@ const defaultOptions = { ticket: 'ticket', token: 'token', userinfo: 'userinfo', + admin: 'admin', + 'admin-ticket': 'admin/ticket', + 'admin-maintenance': 'admin/maintenance', + 'auth-login': 'admin/login', + 'auth-logout': 'admin/logout', + 'auth-settings': 'admin/settings', }, // Database options diff --git a/src/manager.js b/src/manager.js index ce544b9..f1ef180 100644 --- a/src/manager.js +++ b/src/manager.js @@ -54,6 +54,9 @@ class Manager { } + /** + * Establish AMQP connection and plumbing. + */ async _connectQueues() { await this.queuePublisher.connect(); await this.queuePublisher.establishAMQPPlumbing(this.options.queues.ticketPublishName); @@ -1766,7 +1769,7 @@ class Manager { } try { - ctx.token = await this.mysteryBox.unpack(ctx.parsedBody['token']); + ctx.token = await this.mysteryBox.unpack(token); } catch (e) { this.logger.debug(_scope, 'failed to unpack token', { error: e, ctx }); } diff --git a/src/service.js b/src/service.js index f3b7be0..e1f2a5a 100644 --- a/src/service.js +++ b/src/service.js @@ -33,29 +33,26 @@ class Service extends Dingus { this.authenticator = new Authenticator(logger, db, options); this.sessionManager = new SessionManager(logger, this.authenticator, options); this.resourceAuthenticator = new ResourceAuthenticator(logger, db, options); - this.loginPath = `${options.dingus.proxyPrefix}/admin/login`; - - // N.B. /admin routes not currently configurable - const route = (r) => `/${options.route[r]}`; // eslint-disable-line security/detect-object-injection + this.loginPath = this._routeExternal('auth-login'); // eslint-disable-line sonarjs/no-duplicate-string // Service discovery - this.on(['GET'], route('metadata'), this.handlerGetMeta.bind(this)); + this.on(['GET'], this._route('metadata'), this.handlerGetMeta.bind(this)); // Also respond with metadata on well-known oauth2 endpoint if base has no prefix if ((options?.dingus?.selfBaseUrl?.match(/\//g) || []).length === 3) { this.on(['GET'], '/.well-known/oauth-authorization-server', this.handlerGetMeta.bind(this)); } // Primary endpoints - this.on(['GET'], route('authorization'), this.handlerGetAuthorization.bind(this)); - this.on(['POST'], route('authorization'), this.handlerPostAuthorization.bind(this)); - this.on(['POST'], route('consent'), this.handlerPostConsent.bind(this)); - this.on(['POST'], route('revocation'), this.handlerPostRevocation.bind(this)); - this.on(['POST'], route('ticket'), this.handlerPostTicket.bind(this)); - this.on(['POST'], route('token'), this.handlerPostToken.bind(this)); + this.on(['GET'], this._route('authorization'), this.handlerGetAuthorization.bind(this)); + this.on(['POST'], this._route('authorization'), this.handlerPostAuthorization.bind(this)); + this.on(['POST'], this._route('consent'), this.handlerPostConsent.bind(this)); + this.on(['POST'], this._route('revocation'), this.handlerPostRevocation.bind(this)); + this.on(['POST'], this._route('ticket'), this.handlerPostTicket.bind(this)); + this.on(['POST'], this._route('token'), this.handlerPostToken.bind(this)); // Resource endpoints - this.on('POST', route('introspection'), this.handlerPostIntrospection.bind(this)); - this.on('POST', route('userinfo'), this.handlerPostUserInfo.bind(this)); + this.on('POST', this._route('introspection'), this.handlerPostIntrospection.bind(this)); + this.on('POST', this._route('userinfo'), this.handlerPostUserInfo.bind(this)); // Information page about service this.on(['GET'], '/', this.handlerGetRoot.bind(this)); @@ -64,35 +61,58 @@ class Service extends Dingus { this.on(['POST'], '/', this.handlerWhaGwan.bind(this)); // Give load-balancers something to check - this.on(['GET'], route('healthcheck'), this.handlerGetHealthcheck.bind(this)); + this.on(['GET'], this._route('healthcheck'), this.handlerGetHealthcheck.bind(this)); // These routes are intended for accessing static content during development. // In production, a proxy server would likely handle these first. - this.on(['GET'], '/static', this.handlerRedirect.bind(this), `${options.dingus.proxyPrefix}/static/`); - this.on(['GET'], '/static/', this.handlerGetStaticFile.bind(this), 'index.html'); - this.on(['GET'], '/static/:file', this.handlerGetStaticFile.bind(this)); + this.on(['GET'], this._route('static'), this.handlerRedirect.bind(this), `${options.dingus.proxyPrefix}/static/`); + this.on(['GET'], this._route('static', ''), this.handlerGetStaticFile.bind(this), 'index.html'); + this.on(['GET'], this._route('static', ':file'), this.handlerGetStaticFile.bind(this)); this.on(['GET'], '/favicon.ico', this.handlerGetStaticFile.bind(this), 'favicon.ico'); this.on(['GET'], '/robots.txt', this.handlerGetStaticFile.bind(this), 'robots.txt'); // Profile and token management for authenticated sessions - this.on(['GET'], '/admin', this.handlerRedirect.bind(this), `${options.dingus.proxyPrefix}/admin/`); - this.on(['GET'], '/admin/', this.handlerGetAdmin.bind(this)); - this.on(['POST'], '/admin/', this.handlerPostAdmin.bind(this)); + this.on(['GET'], this._route('admin'), this.handlerRedirect.bind(this), `${options.dingus.proxyPrefix}/admin/`); + this.on(['GET'], this._route('admin', ''), this.handlerGetAdmin.bind(this)); + this.on(['POST'], this._route('admin', ''), this.handlerPostAdmin.bind(this)); // Ticket-proffering interface for authenticated sessions - this.on(['GET'], '/admin/ticket', this.handlerGetAdminTicket.bind(this)); - this.on(['POST'], '/admin/ticket', this.handlerPostAdminTicket.bind(this)); + this.on(['GET'], this._route('admin-ticket'), this.handlerGetAdminTicket.bind(this)); + this.on(['POST'], this._route('admin-ticket'), this.handlerPostAdminTicket.bind(this)); // User authentication and session establishment - this.on(['GET'], '/admin/login', this.handlerGetAdminLogin.bind(this)); - this.on(['POST'], '/admin/login', this.handlerPostAdminLogin.bind(this)); - this.on(['GET'], '/admin/logout', this.handlerGetAdminLogout.bind(this)); - this.on(['GET'], '/admin/settings', this.handlerGetAdminSettings.bind(this)); - this.on(['POST'], '/admin/settings', this.handlerPostAdminSettings.bind(this)); + this.on(['GET'], this._route('auth-login'), this.handlerGetAdminLogin.bind(this)); + this.on(['POST'], this._route('auth-login'), this.handlerPostAdminLogin.bind(this)); + this.on(['GET'], this._route('auth-logout'), this.handlerGetAdminLogout.bind(this)); + this.on(['GET'], this._route('auth-settings'), this.handlerGetAdminSettings.bind(this)); + this.on(['POST'], this._route('auth-settings'), this.handlerPostAdminSettings.bind(this)); // Page for upkeep info et cetera - this.on(['GET'], '/admin/maintenance', this.handlerGetAdminMaintenance.bind(this)); + this.on(['GET'], this._route('admin-maintenance'), this.handlerGetAdminMaintenance.bind(this)); + + } + + + /** + * Returns the configured route path for the given route name. + * @param {string} r route name + * @param {string=} t trailer to append to route + * @returns {string} route path + */ + _route(r, t) { + return `/${this.options.route[r]}${t !== undefined ? '/' + t : ''}`; // eslint-disable-line security/detect-object-injection + + } + + /** + * Returns the external route path for the given route name. + * @param {string} r route name + * @param {string=} t trailer to append to route + * @returns {string} route path + */ + _routeExternal(r, t) { + return this.options.dingus.proxyPrefix + this._route(r, t); } diff --git a/test/src/service.js b/test/src/service.js index 55cfec2..777552a 100644 --- a/test/src/service.js +++ b/test/src/service.js @@ -62,6 +62,39 @@ describe('Service', function () { assert(service); }); + describe('_route', function () { + it('returns route', function () { + const r = service._route('admin'); + assert.strictEqual(r, '/admin'); + }); + it('returns trailing-slash route', function () { + const r = service._route('admin', ''); + assert.strictEqual(r, '/admin/'); + }); + it('returns route with trailer', function () { + const r = service._route('admin', ':id'); + assert.strictEqual(r, '/admin/:id'); + }); + }); // _route + + describe('_routeExternal', function () { + beforeEach(function () { + service.options.dingus.proxyPrefix = '/prefix'; + }); + it('returns route', function () { + const r = service._routeExternal('admin'); + assert.strictEqual(r, '/prefix/admin'); + }); + it('returns trailing-slash route', function () { + const r = service._routeExternal('admin', ''); + assert.strictEqual(r, '/prefix/admin/'); + }); + it('returns route with trailer', function () { + const r = service._routeExternal('admin', ':id'); + assert.strictEqual(r, '/prefix/admin/:id'); + }); + }); // _routeExternal + describe('initialize', function () { it('covers', async function () { await service.initialize(); -- 2.45.2