From 93ba5832d88d4cbbede5d3a354ebb01f9c1bf9fa Mon Sep 17 00:00:00 2001 From: Pagan Gazzard Date: Wed, 5 Feb 2020 21:07:49 +0000 Subject: [PATCH] Convert lib/auth/server to typescript Change-type: patch --- lib/auth/server.coffee | 101 ---------------------------- lib/auth/server.ts | 138 ++++++++++++++++++++++++++++++++++++++ npm-shrinkwrap.json | 62 +++++++++++++++++ package.json | 2 + tests/auth/server.spec.ts | 4 +- 5 files changed, 205 insertions(+), 102 deletions(-) delete mode 100644 lib/auth/server.coffee create mode 100644 lib/auth/server.ts diff --git a/lib/auth/server.coffee b/lib/auth/server.coffee deleted file mode 100644 index 9522d717..00000000 --- a/lib/auth/server.coffee +++ /dev/null @@ -1,101 +0,0 @@ -### -Copyright 2016 Balena - -Licensed 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 CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -### - -express = require('express') -path = require('path') -bodyParser = require('body-parser') -Promise = require('bluebird') -balena = require('balena-sdk').fromSharedOptions() -utils = require('./utils') - -createServer = ({ port, isDev } = {}) -> - app = express() - app.use bodyParser.urlencoded - extended: true - - app.set('view engine', 'ejs') - app.set('views', path.join(__dirname, 'pages')) - - if isDev - app.use(express.static(path.join(__dirname, 'pages', 'static'))) - - server = app.listen(port) - - return { app, server } - -###* -# @summary Await for token -# @function -# @protected -# -# @param {Object} options - options -# @param {String} options.path - callback path -# @param {Number} options.port - http port -# -# @example -# server.awaitForToken -# path: '/auth' -# port: 9001 -# .then (token) -> -# console.log(token) -### -exports.awaitForToken = (options) -> - { app, server } = createServer(port: options.port) - - return new Promise (resolve, reject) -> - closeServer = (errorMessage, successPayload) -> - server.close -> - if errorMessage - reject(new Error(errorMessage)) - return - - resolve(successPayload) - - renderAndDone = ({ request, response, viewName, errorMessage, statusCode, token }) -> - return getContext(viewName) - .then (context) -> - response.status(statusCode || 200).render(viewName, context) - request.connection.destroy() - closeServer(errorMessage, token) - - app.post options.path, (request, response) -> - token = request.body.token?.trim() - - Promise.try -> - if not token - throw new Error('No token') - return utils.loginIfTokenValid(token) - .tap (loggedIn) -> - if not loggedIn - throw new Error('Invalid token') - .then -> - renderAndDone({ request, response, viewName: 'success', token }) - .catch (error) -> - renderAndDone({ - request, response, viewName: 'error', - statusCode: 401, errorMessage: error.message - }) - - app.use (request, response) -> - response.status(404).send('Not found') - closeServer('Unknown path or verb') - -exports.getContext = getContext = (viewName) -> - if viewName is 'success' - return Promise.props - dashboardUrl: balena.settings.get('dashboardUrl') - - return Promise.resolve({}) diff --git a/lib/auth/server.ts b/lib/auth/server.ts new file mode 100644 index 00000000..411dae4a --- /dev/null +++ b/lib/auth/server.ts @@ -0,0 +1,138 @@ +/* +Copyright 2016 Balena + +Licensed 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 CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import * as balenaSdk from 'balena-sdk'; +import * as Promise from 'bluebird'; +import * as bodyParser from 'body-parser'; +import * as express from 'express'; +import * as path from 'path'; +import * as utils from './utils'; + +const balena = balenaSdk.fromSharedOptions(); + +const createServer = ({ port }: { port: number }) => { + const app = express(); + app.use( + bodyParser.urlencoded({ + extended: true, + }), + ); + + app.set('view engine', 'ejs'); + app.set('views', path.join(__dirname, 'pages')); + + const server = app.listen(port); + + return { app, server }; +}; + +/** + * @summary Await for token + * @function + * @protected + * + * @param {Object} options - options + * @param {String} options.path - callback path + * @param {Number} options.port - http port + * + * @example + * server.awaitForToken + * path: '/auth' + * port: 9001 + * .then (token) -> + * console.log(token) + */ +export const awaitForToken = (options: { + path: string; + port: number; +}): Promise => { + const { app, server } = createServer({ port: options.port }); + + return new Promise((resolve, reject) => { + const closeServer = ( + errorMessage: string | undefined, + successPayload?: string, + ) => { + server.close(() => { + if (errorMessage) { + reject(new Error(errorMessage)); + return; + } + + resolve(successPayload); + }); + }; + + const renderAndDone = async ({ + request, + response, + viewName, + errorMessage, + statusCode = 200, + token, + }: { + request: express.Request; + response: express.Response; + viewName: 'success' | 'error'; + errorMessage?: string; + statusCode?: number; + token?: string; + }) => { + const context = await getContext(viewName); + response.status(statusCode).render(viewName, context); + request.connection.destroy(); + closeServer(errorMessage, token); + }; + + app.post(options.path, async (request, response) => { + try { + const token = request.body.token?.trim(); + + if (!token) { + throw new Error('No token'); + } + const loggedIn = await utils.loginIfTokenValid(token); + if (!loggedIn) { + throw new Error('Invalid token'); + } + await renderAndDone({ request, response, viewName: 'success', token }); + } catch (error) { + await renderAndDone({ + request, + response, + viewName: 'error', + statusCode: 401, + errorMessage: error.message, + }); + } + }); + + app.use((_request, response) => { + response.status(404).send('Not found'); + closeServer('Unknown path or verb'); + }); + }); +}; + +export const getContext = (viewName: 'success' | 'error') => { + if (viewName === 'success') { + return Promise.props({ + dashboardUrl: balena.settings.get('dashboardUrl'), + }); + } + + return Promise.resolve({}); +}; diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 9a4a9c6a..c4ce749c 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -594,6 +594,16 @@ "resolved": "https://registry.npmjs.org/@types/bluebird/-/bluebird-3.5.29.tgz", "integrity": "sha512-kmVtnxTuUuhCET669irqQmPAez4KFnFVKvpleVRyfC3g+SHD1hIkFZcWLim9BVcwUBLO59o8VZE4yGCmTif8Yw==" }, + "@types/body-parser": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.17.1.tgz", + "integrity": "sha512-RoX2EZjMiFMjZh9lmYrwgoP9RTpAjSHiJxdp4oidAQVO02T7HER3xj9UKue5534ULWeqVEkujhWcyvUce+d68w==", + "dev": true, + "requires": { + "@types/connect": "*", + "@types/node": "*" + } + }, "@types/caseless": { "version": "0.12.2", "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.2.tgz", @@ -635,6 +645,15 @@ "resolved": "https://registry.npmjs.org/@types/common-tags/-/common-tags-1.8.0.tgz", "integrity": "sha512-htRqZr5qn8EzMelhX/Xmx142z218lLyGaeZ3YR8jlze4TATRU9huKKvuBmAJEW4LCC4pnY1N6JAm6p85fMHjhg==" }, + "@types/connect": { + "version": "3.4.33", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.33.tgz", + "integrity": "sha512-2+FrkXY4zllzTNfJth7jOqEHC+enpLeGslEhpnTAkg21GkRrWV4SsAtqchtT4YS9/nODBU2/ZfsBY2X4J/dX7A==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/depcheck": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/@types/depcheck/-/depcheck-0.6.0.tgz", @@ -676,6 +695,27 @@ "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", "integrity": "sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==" }, + "@types/express": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.2.tgz", + "integrity": "sha512-5mHFNyavtLoJmnusB8OKJ5bshSzw+qkMIBAobLrIM48HJvunFva9mOa6aBwh64lBFyNwBbs0xiEFuj4eU/NjCA==", + "dev": true, + "requires": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "*", + "@types/serve-static": "*" + } + }, + "@types/express-serve-static-core": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.2.tgz", + "integrity": "sha512-El9yMpctM6tORDAiBwZVLMcxoTMcqqRO9dVyYcn7ycLWbvR8klrDn8CAOwRfZujZtWD7yS/mshTdz43jMOejbg==", + "dev": true, + "requires": { + "@types/node": "*", + "@types/range-parser": "*" + } + }, "@types/form-data": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-2.2.1.tgz", @@ -754,6 +794,12 @@ "resolved": "https://registry.npmjs.org/@types/memoizee/-/memoizee-0.4.3.tgz", "integrity": "sha512-N6QT0c9ZbEKl33n1wyoTxZs4cpN+YXjs0Aqy5Qim8ipd9PBNIPqOh/p5Pixc4601tqr5GErsdxUbfqviDfubNw==" }, + "@types/mime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.1.tgz", + "integrity": "sha512-FwI9gX75FgVBJ7ywgnq/P7tw+/o1GUbtP0KzbtusLigAOgIgNISRK0ZPl4qertvXSIE8YbsVJueQ90cDt9YYyw==", + "dev": true + }, "@types/minimatch": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", @@ -846,6 +892,12 @@ "integrity": "sha1-ExqJDe1kIbG1RfRRCkrqvG1GUnU=", "dev": true }, + "@types/range-parser": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz", + "integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==", + "dev": true + }, "@types/raven": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/@types/raven/-/raven-2.5.1.tgz", @@ -888,6 +940,16 @@ "resolved": "https://registry.npmjs.org/@types/semver/-/semver-5.5.0.tgz", "integrity": "sha512-41qEJgBH/TWgo5NFSvBCJ1qkoi3Q6ONSF2avrHq1LVEZfYpdHmj0y9SuTK+u9ZhG1sYQKBL1AWXKyLWP4RaUoQ==" }, + "@types/serve-static": { + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.3.tgz", + "integrity": "sha512-oprSwp094zOglVrXdlo/4bAHtKTAxX6VT8FOZlBKrmyLbNvE1zxZyJ6yikMVtHIvwP45+ZQGJn+FdXGKTozq0g==", + "dev": true, + "requires": { + "@types/express-serve-static-core": "*", + "@types/mime": "*" + } + }, "@types/shell-escape": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/@types/shell-escape/-/shell-escape-0.2.0.tgz", diff --git a/package.json b/package.json index 5b48090d..976bb7ae 100644 --- a/package.json +++ b/package.json @@ -98,12 +98,14 @@ "@octokit/rest": "^16.38.1", "@types/archiver": "2.1.2", "@types/bluebird": "^3.5.29", + "@types/body-parser": "^1.17.1", "@types/chai": "^4.2.7", "@types/chai-as-promised": "^7.1.1", "@types/chokidar": "^1.7.5", "@types/common-tags": "^1.8.0", "@types/dockerode": "2.5.6", "@types/ejs": "^3.0.0", + "@types/express": "^4.17.2", "@types/fs-extra": "7.0.0", "@types/intercept-stdout": "^0.1.0", "@types/is-root": "1.0.0", diff --git a/tests/auth/server.spec.ts b/tests/auth/server.spec.ts index 7b8ec9f2..e21b3110 100644 --- a/tests/auth/server.spec.ts +++ b/tests/auth/server.spec.ts @@ -25,7 +25,9 @@ const options = { path: '/auth', }; -const getPage = function(name: string): Promise { +const getPage = function( + name: Parameters[0], +): Promise { const pagePath = path.join( __dirname, '..',