From 8d35ced40f14918e57d08d3acc7e425ab3e988b2 Mon Sep 17 00:00:00 2001 From: Sebastian Seedorf Date: Mon, 16 Nov 2020 12:40:47 +0100 Subject: [PATCH] inital #3 --- README.md | 32 +++++++++++- out/auth-proxy.d.ts | 5 -- out/auth-proxy.js | 49 ------------------- out/auto-reload.d.ts | 3 -- out/auto-reload.js | 61 ----------------------- out/config.d.ts | 17 ------- out/config.js | 36 -------------- out/helpers/resolvable.d.ts | 27 ---------- out/helpers/resolvable.js | 85 -------------------------------- out/helpers/urlJoin.d.ts | 2 - out/helpers/urlJoin.js | 5 -- out/helpers/userinfo.d.ts | 10 ---- out/helpers/userinfo.js | 2 - out/index.d.ts | 11 ----- out/index.js | 25 ---------- out/logging.d.ts | 13 ----- out/logging.js | 47 ------------------ out/permissions.d.ts | 24 --------- out/permissions.js | 92 ----------------------------------- out/polyfill-worker.d.ts | 1 - out/polyfill-worker.js | 25 ---------- out/polyfill.d.ts | 7 --- out/polyfill.js | 39 --------------- out/redis.d.ts | 9 ---- out/redis.js | 76 ----------------------------- out/session.d.ts | 7 --- out/session.js | 18 ------- package-lock.json | 60 +++++++++++++++++++++++ package.json | 5 +- src/index.ts | 2 +- src/polyfill-worker.ts | 2 +- src/polyfill.ts | 11 ++++- src/types/auth-proxy.d.ts | 9 ---- src/types/extend-request.d.ts | 28 +++++++++++ src/types/logging.d.ts | 5 -- src/types/permissions.d.ts | 5 -- 36 files changed, 134 insertions(+), 721 deletions(-) delete mode 100644 out/auth-proxy.d.ts delete mode 100644 out/auth-proxy.js delete mode 100644 out/auto-reload.d.ts delete mode 100644 out/auto-reload.js delete mode 100644 out/config.d.ts delete mode 100644 out/config.js delete mode 100644 out/helpers/resolvable.d.ts delete mode 100644 out/helpers/resolvable.js delete mode 100644 out/helpers/urlJoin.d.ts delete mode 100644 out/helpers/urlJoin.js delete mode 100644 out/helpers/userinfo.d.ts delete mode 100644 out/helpers/userinfo.js delete mode 100644 out/index.d.ts delete mode 100644 out/index.js delete mode 100644 out/logging.d.ts delete mode 100644 out/logging.js delete mode 100644 out/permissions.d.ts delete mode 100644 out/permissions.js delete mode 100644 out/polyfill-worker.d.ts delete mode 100644 out/polyfill-worker.js delete mode 100644 out/polyfill.d.ts delete mode 100644 out/polyfill.js delete mode 100644 out/redis.d.ts delete mode 100644 out/redis.js delete mode 100644 out/session.d.ts delete mode 100644 out/session.js delete mode 100644 src/types/auth-proxy.d.ts create mode 100644 src/types/extend-request.d.ts delete mode 100644 src/types/logging.d.ts delete mode 100644 src/types/permissions.d.ts diff --git a/README.md b/README.md index 44ac9ab..4164685 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,36 @@ npm install git+https://git.biotronik.int/scm/coe-bs-website/node-pkg-express-ut ##### 2. Copy the types -Copy the folder `src/types` to some location within the source folder of your project. +Copy the content below to any location within the source folder of your project and name it `extend-request.d.ts`. It just needs to sit there with no more action required. +```ts +// extend-request.d.ts + +declare global { + namespace Express { + interface UserInfo { + email: string, + email_verified: boolean, + family_name: string, + given_name: string, + groups: string[], + name: string, + preferred_username: string, + sub: string, + } + + interface Request { + getUserInfo(): Promise; + noLogging: boolean|undefined; + permissionDetails?: import('role-acl').Permission; + } + + interface Response { + initLogout(): boolean; + } + } +} + +export {}; +``` diff --git a/out/auth-proxy.d.ts b/out/auth-proxy.d.ts deleted file mode 100644 index 68ae7b7..0000000 --- a/out/auth-proxy.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -/// -import { RequestHandler } from 'express'; -export declare const AuthProxy: { - router: RequestHandler; -}; diff --git a/out/auth-proxy.js b/out/auth-proxy.js deleted file mode 100644 index 20b53c8..0000000 --- a/out/auth-proxy.js +++ /dev/null @@ -1,49 +0,0 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.AuthProxy = void 0; -const _1 = require("."); -const node_fetch_1 = require("node-fetch"); -const router = (req, res, next) => { - const resolvable = new _1.Resolvable(() => __awaiter(void 0, void 0, void 0, function* () { - if (!_1.DefaultConfig.USERINFO_HEADER) { - return undefined; - } - const token = req.header(_1.DefaultConfig.USERINFO_HEADER); - const url = _1.DefaultConfig.AUTH_PROXY_USERINFO_URL || - _1.DefaultConfig.AUTH_PROXY_URL && _1.urlJoin(_1.DefaultConfig.AUTH_PROXY_URL, "userinfo"); - if (token === undefined || url === undefined) { - return undefined; - } - try { - const res = yield node_fetch_1.default(url, { headers: [[_1.DefaultConfig.USERINFO_HEADER, token]] }); - return yield res.json(); - } - catch (e) { - _1.Logger.warn(e); - return undefined; - } - })); - req.getUserInfo = () => resolvable.resolve(); - res.initLogout = function () { - const url = _1.DefaultConfig.AUTH_PROXY_INIT_LOGOUT_URL || - _1.DefaultConfig.AUTH_PROXY_URL && _1.urlJoin(_1.DefaultConfig.AUTH_PROXY_URL, "init-logout"); - if (url === undefined) { - return false; - } - this.redirect(307, url); - return true; - }; - next(); -}; -exports.AuthProxy = { - router, -}; diff --git a/out/auto-reload.d.ts b/out/auto-reload.d.ts deleted file mode 100644 index 9586b23..0000000 --- a/out/auto-reload.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -export declare const AutoReloader: { - router: import("express-serve-static-core").Router; -}; diff --git a/out/auto-reload.js b/out/auto-reload.js deleted file mode 100644 index 1fdbfc9..0000000 --- a/out/auto-reload.js +++ /dev/null @@ -1,61 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.AutoReloader = void 0; -const express_1 = require("express"); -const _1 = require("."); -const uuid_1 = require("uuid"); -const router = express_1.Router(); -if (!_1.DefaultConfig.isProduction) { - let uuid = uuid_1.v4(); - let updateTimeout = undefined; - Promise.resolve().then(() => require("node-watch")).then((watch) => { - watch.default('public', { recursive: true }, () => { - if (updateTimeout !== undefined) - clearTimeout(updateTimeout); - updateTimeout = setTimeout(() => { - uuid = uuid_1.v4(); - }, 200); - }); - }).catch((err) => { _1.Logger.error(err); }); - router.get("/auto-reload/client.js", (req, res) => { - _1.Logger.debug(req.url, req.originalUrl, req.baseUrl); - res.setHeader('Content-Type', "application/javascript"); - // language=JavaScript - res.send(` - const loc = window.location; - const url = loc.protocol+'//'+loc.host+'${_1.urlJoin(req.baseUrl, "/auto-reload")}'; - // const parse = async res => (await res.json()).uuid; - const parse = function(res) { - return res.json() - .then(function(json) {return json.uuid;}) } - let hash = undefined; - let hadError = false; - setInterval(function() { - try { - fetch(url) - .then(function(res) { return parse(res) }) - .then(function(data) { - if (data) { - hash = hash === undefined ? data : hash; - if (hash !== data) { - window.location.reload(); - } - } - }); - } catch (e) { - if (hadError === false) { - console.log(e); - hadError = true; - } - } - }, 3000); - `.replace(/\t/g, "")); - }); - router.get("/auto-reload", (req, res) => { - req.noLogging = true; - res.json({ uuid }); - }); -} -exports.AutoReloader = { - router, -}; diff --git a/out/config.d.ts b/out/config.d.ts deleted file mode 100644 index e3b7bef..0000000 --- a/out/config.d.ts +++ /dev/null @@ -1,17 +0,0 @@ -declare function requireEnv(name: string, onlyInProduction?: boolean): void; -export declare const DefaultConfig: { - EXTERNAL_BASE_URL: string; - isProduction: boolean; - requireEnv: typeof requireEnv; - NODE_ENV: string; - PORT: number; - HOSTNAME: string; - BASE_PATH: string; - REDIS_URL: string | undefined; - SESSION_SECRET: string | undefined; - USERINFO_HEADER: string | undefined; - AUTH_PROXY_URL: string | undefined; - AUTH_PROXY_USERINFO_URL: string | undefined; - AUTH_PROXY_INIT_LOGOUT_URL: string | undefined; -}; -export {}; diff --git a/out/config.js b/out/config.js deleted file mode 100644 index dcced38..0000000 --- a/out/config.js +++ /dev/null @@ -1,36 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.DefaultConfig = void 0; -const env = require("env-var"); -const _1 = require("."); -const NODE_ENV = env.get('NODE_ENV').default("development").asString(); -const isProduction = NODE_ENV === 'production'; -const envs = { - NODE_ENV, - // port of the server - PORT: env.get('PORT').default('3000').asPortNumber(), - // hostname of the server ('0.0.0.0' listens on all network interfaces) - HOSTNAME: env.get('HOSTNAME').default('0.0.0.0').asString(), - // base path - BASE_PATH: env.get('BASE_PATH').default('/').asString(), - // external base url - EXTERNAL_BASE_URL: env.get('EXTERNAL_BASE_URL').asString(), - // url of redis session store (required in production using InMemory) - REDIS_URL: env.get('REDIS_URL').asString() || undefined, - // cookie secret for the session id (required in production using Session) - SESSION_SECRET: env.get('SESSION_SECRET').asString() || undefined, - // header where user info token is stored to request auth proxy - USERINFO_HEADER: env.get('USERINFO_HEADER').asString() || undefined, - // base url to init a logout or request user info - AUTH_PROXY_URL: env.get('AUTH_PROXY_URL').asString() || undefined, - // override base url to request user info - AUTH_PROXY_USERINFO_URL: env.get('AUTH_PROXY_USERINFO_URL').asString() || undefined, - // override base url to init a logout - AUTH_PROXY_INIT_LOGOUT_URL: env.get('AUTH_PROXY_INIT_LOGOUT_URL').asString() || undefined, -}; -function requireEnv(name, onlyInProduction = false) { - env.get(name).required(!onlyInProduction || isProduction).asString(); -} -exports.DefaultConfig = Object.assign(Object.assign({}, envs), { EXTERNAL_BASE_URL: envs.EXTERNAL_BASE_URL || - _1.urlJoin(`http://${envs.HOSTNAME}${envs.PORT !== 80 ? `:${envs.PORT}` : ""}`, envs.BASE_PATH), isProduction, - requireEnv }); diff --git a/out/helpers/resolvable.d.ts b/out/helpers/resolvable.d.ts deleted file mode 100644 index b5bec50..0000000 --- a/out/helpers/resolvable.d.ts +++ /dev/null @@ -1,27 +0,0 @@ -declare enum ResolvableState { - WAITING = 0, - PENDING = 1, - ERROR = 2, - DONE = 3 -} -declare class FetchOnce> { - protected fetchMethod?: ((...args: U) => Promise) | undefined; - protected data: T | undefined; - protected error: unknown | undefined; - protected state: ResolvableState; - protected pendings: [(res: Promise | T) => void, (reason: unknown) => void][]; - constructor(fetchMethod?: ((...args: U) => Promise) | undefined); - resolve(...args: U): Promise; - protected isFinished(): boolean; - protected parsePromise(promise: Promise): void; -} -export declare class Resolvable> extends FetchOnce { - constructor(fetchMethod: (...args: U) => Promise); -} -export declare class WaitForSync extends FetchOnce { - protected state: ResolvableState; - constructor(); - setData(data: T): void; - setError(error: unknown): void; -} -export {}; diff --git a/out/helpers/resolvable.js b/out/helpers/resolvable.js deleted file mode 100644 index c155b3c..0000000 --- a/out/helpers/resolvable.js +++ /dev/null @@ -1,85 +0,0 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.WaitForSync = exports.Resolvable = void 0; -var ResolvableState; -(function (ResolvableState) { - ResolvableState[ResolvableState["WAITING"] = 0] = "WAITING"; - ResolvableState[ResolvableState["PENDING"] = 1] = "PENDING"; - ResolvableState[ResolvableState["ERROR"] = 2] = "ERROR"; - ResolvableState[ResolvableState["DONE"] = 3] = "DONE"; -})(ResolvableState || (ResolvableState = {})); -class FetchOnce { - constructor(fetchMethod) { - this.fetchMethod = fetchMethod; - this.state = ResolvableState.WAITING; - this.pendings = []; - } - resolve(...args) { - // eslint-disable-next-line promise/avoid-new - return new Promise((resolve, reject) => { - switch (this.state) { - case ResolvableState.WAITING: - this.state = ResolvableState.PENDING; - this.pendings.push([resolve, reject]); - if (this.fetchMethod) - this.parsePromise(this.fetchMethod(...args)); - break; - case ResolvableState.PENDING: - this.pendings.push([resolve, reject]); - break; - case ResolvableState.DONE: - resolve(this.data); - break; - case ResolvableState.ERROR: - reject(this.error); - break; - } - }); - } - isFinished() { - return this.state === ResolvableState.DONE || this.state === ResolvableState.ERROR; - } - parsePromise(promise) { - promise.then((data) => { - this.data = data; - this.state = ResolvableState.DONE; - this.pendings.forEach(pending => pending[0](data)); - }).catch(err => { - this.error = err; - this.state = ResolvableState.ERROR; - this.pendings.forEach(pending => pending[1](err)); - }); - } -} -class Resolvable extends FetchOnce { - constructor(fetchMethod) { - super(fetchMethod); - } -} -exports.Resolvable = Resolvable; -class WaitForSync extends FetchOnce { - constructor() { - super(undefined); - this.state = ResolvableState.PENDING; - } - setData(data) { - if (!this.isFinished()) { - this.parsePromise((() => __awaiter(this, void 0, void 0, function* () { return data; }))()); - } - } - setError(error) { - if (!this.isFinished()) { - this.parsePromise((() => __awaiter(this, void 0, void 0, function* () { throw error; }))()); - } - } -} -exports.WaitForSync = WaitForSync; diff --git a/out/helpers/urlJoin.d.ts b/out/helpers/urlJoin.d.ts deleted file mode 100644 index a972bb6..0000000 --- a/out/helpers/urlJoin.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -import * as properUrlJoin from 'proper-url-join'; -export declare const urlJoin: properUrlJoin.default; diff --git a/out/helpers/urlJoin.js b/out/helpers/urlJoin.js deleted file mode 100644 index 2103c9b..0000000 --- a/out/helpers/urlJoin.js +++ /dev/null @@ -1,5 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.urlJoin = void 0; -const properUrlJoin = require("proper-url-join"); -exports.urlJoin = properUrlJoin; diff --git a/out/helpers/userinfo.d.ts b/out/helpers/userinfo.d.ts deleted file mode 100644 index eb054cb..0000000 --- a/out/helpers/userinfo.d.ts +++ /dev/null @@ -1,10 +0,0 @@ -export declare type UserInfo = { - email: string; - email_verified: boolean; - family_name: string; - given_name: string; - groups: string[]; - name: string; - preferred_username: string; - sub: string; -}; diff --git a/out/helpers/userinfo.js b/out/helpers/userinfo.js deleted file mode 100644 index c8ad2e5..0000000 --- a/out/helpers/userinfo.js +++ /dev/null @@ -1,2 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); diff --git a/out/index.d.ts b/out/index.d.ts deleted file mode 100644 index 63d66d8..0000000 --- a/out/index.d.ts +++ /dev/null @@ -1,11 +0,0 @@ -export { DefaultConfig } from './config'; -export { Redis } from './redis'; -export { Logger, HttpLogger } from './logging'; -export { AuthProxy } from './auth-proxy'; -export { Resolvable, WaitForSync } from './helpers/resolvable'; -export { urlJoin } from './helpers/urlJoin'; -export { AutoReloader } from './auto-reload'; -export { Polyfill } from './polyfill'; -export { Session } from './session'; -export { Permissions } from './permissions'; -export { UserInfo } from './helpers/userinfo'; diff --git a/out/index.js b/out/index.js deleted file mode 100644 index 9e7d2af..0000000 --- a/out/index.js +++ /dev/null @@ -1,25 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.Permissions = exports.Session = exports.Polyfill = exports.AutoReloader = exports.urlJoin = exports.WaitForSync = exports.Resolvable = exports.AuthProxy = exports.HttpLogger = exports.Logger = exports.Redis = exports.DefaultConfig = void 0; -var config_1 = require("./config"); -Object.defineProperty(exports, "DefaultConfig", { enumerable: true, get: function () { return config_1.DefaultConfig; } }); -var redis_1 = require("./redis"); -Object.defineProperty(exports, "Redis", { enumerable: true, get: function () { return redis_1.Redis; } }); -var logging_1 = require("./logging"); -Object.defineProperty(exports, "Logger", { enumerable: true, get: function () { return logging_1.Logger; } }); -Object.defineProperty(exports, "HttpLogger", { enumerable: true, get: function () { return logging_1.HttpLogger; } }); -var auth_proxy_1 = require("./auth-proxy"); -Object.defineProperty(exports, "AuthProxy", { enumerable: true, get: function () { return auth_proxy_1.AuthProxy; } }); -var resolvable_1 = require("./helpers/resolvable"); -Object.defineProperty(exports, "Resolvable", { enumerable: true, get: function () { return resolvable_1.Resolvable; } }); -Object.defineProperty(exports, "WaitForSync", { enumerable: true, get: function () { return resolvable_1.WaitForSync; } }); -var urlJoin_1 = require("./helpers/urlJoin"); -Object.defineProperty(exports, "urlJoin", { enumerable: true, get: function () { return urlJoin_1.urlJoin; } }); -var auto_reload_1 = require("./auto-reload"); -Object.defineProperty(exports, "AutoReloader", { enumerable: true, get: function () { return auto_reload_1.AutoReloader; } }); -var polyfill_1 = require("./polyfill"); -Object.defineProperty(exports, "Polyfill", { enumerable: true, get: function () { return polyfill_1.Polyfill; } }); -var session_1 = require("./session"); -Object.defineProperty(exports, "Session", { enumerable: true, get: function () { return session_1.Session; } }); -var permissions_1 = require("./permissions"); -Object.defineProperty(exports, "Permissions", { enumerable: true, get: function () { return permissions_1.Permissions; } }); diff --git a/out/logging.d.ts b/out/logging.d.ts deleted file mode 100644 index f2b5179..0000000 --- a/out/logging.d.ts +++ /dev/null @@ -1,13 +0,0 @@ -import * as winston from 'winston'; -import { RequestHandler } from 'express'; -export declare const Logger: { - error: (...args: unknown[]) => winston.Logger; - http: (...args: unknown[]) => winston.Logger; - info: (...args: unknown[]) => winston.Logger; - silly: (...args: unknown[]) => winston.Logger; - warn: (...args: unknown[]) => winston.Logger; - verbose: (...args: unknown[]) => winston.Logger; - debug: (...args: unknown[]) => winston.Logger; - log: (...args: unknown[]) => winston.Logger; -}; -export declare const HttpLogger: RequestHandler; diff --git a/out/logging.js b/out/logging.js deleted file mode 100644 index 5509f56..0000000 --- a/out/logging.js +++ /dev/null @@ -1,47 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.HttpLogger = exports.Logger = void 0; -const winston = require("winston"); -const colors = require("colors"); -const _1 = require("."); -const prune = require("json-prune"); -const logger = winston.createLogger({ - level: _1.DefaultConfig.isProduction ? "info" : "silly", - format: winston.format.json(), - transports: [ - new winston.transports.Console({ - format: winston.format.simple(), - }), - ], -}); -const levels = ["error", "warn", "info", "http", "verbose", "debug", "silly"]; -const wrapper = (original) => { - return (...args) => { - return original(args.map((obj) => typeof obj === "string" ? obj : prune(obj)).join(" ")); - }; -}; -exports.Logger = {}; -for (const level of levels) { - exports.Logger[level] = wrapper(logger[level]); -} -exports.Logger.log = wrapper(logger["silly"]); -exports.HttpLogger = (req, res, next) => { - const start = Date.now(); - const path = req.path; - const end = res.end; - res.end = function (...args) { - const statusCode = res.statusCode; - const colorFunction = statusCode >= 500 ? colors.red - : statusCode >= 400 ? colors.yellow - : statusCode >= 300 ? colors.cyan - : statusCode >= 200 ? colors.green - : colors.gray; - const status = colorFunction(res.statusCode.toString(10)); - const method = req.method.toUpperCase().padEnd(6, " "); - const responseTime = (Date.now() - start).toString(10).padStart(3, " "); - if (!req.noLogging) - exports.Logger.http(`${status} ${method} ${responseTime}ms ${path}`); - end.apply(res, args); - }; - next(); -}; diff --git a/out/permissions.d.ts b/out/permissions.d.ts deleted file mode 100644 index 6a1297b..0000000 --- a/out/permissions.d.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { AccessControl, IQueryInfo, Permission } from 'role-acl'; -import { Query } from 'role-acl/lib/src/core/Query'; -import { Request, RequestHandler } from 'express'; -declare class PermissionManager extends AccessControl { - can(roleOrRequest: Request | string | string[] | IQueryInfo): PermQuery; - getRouter(resource: string, opts: Partial): RequestHandler; -} -export declare type RermRouterOpts = { - context: unknown; - action: string; - skipConditions: boolean; -}; -export declare class PermQuery extends Query { - protected resolveRequest: Request | undefined; - constructor(grants: unknown, roleOrRequest: Request | string | string[] | IQueryInfo); - on(resource: string, skipConditions?: boolean): Promise; - context(context: unknown): PermQuery; - skipConditions(value: boolean): PermQuery; - with(context: unknown): PermQuery; - execute(action: string): PermQuery; - sync(): PermQuery; -} -export declare const Permissions: PermissionManager; -export {}; diff --git a/out/permissions.js b/out/permissions.js deleted file mode 100644 index 4178f92..0000000 --- a/out/permissions.js +++ /dev/null @@ -1,92 +0,0 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.Permissions = exports.PermQuery = void 0; -const role_acl_1 = require("role-acl"); -const Query_1 = require("role-acl/lib/src/core/Query"); -// see https://www.npmjs.com/package/role-acl -class PermissionManager extends role_acl_1.AccessControl { - can(roleOrRequest) { - return new PermQuery(this.getGrants(), roleOrRequest); - } - getRouter(resource, opts) { - return (req, res, next) => __awaiter(this, void 0, void 0, function* () { - let query = this.can(req); - if (opts.context) - query = query.context(opts.context); - if (opts.action) - query = query.execute(opts.action); - if (opts.skipConditions) - query = query.skipConditions(opts.skipConditions); - const permission = yield query.on(resource); - if (permission.granted) { - req.permissionDetails = permission; - next(); - } - else { - res.sendStatus(403); - } - }); - } -} -class PermQuery extends Query_1.Query { - constructor(grants, roleOrRequest) { - function isRequest(obj) { - // eslint-disable-next-line no-prototype-builtins - return typeof obj === 'object' && obj && obj.hasOwnProperty('path') || false; - } - if (isRequest(roleOrRequest)) { - super(grants, []); - this.resolveRequest = roleOrRequest; - } - else { - super(grants, roleOrRequest); - } - } - on(resource, skipConditions) { - const _super = Object.create(null, { - on: { get: () => super.on } - }); - var _a; - return __awaiter(this, void 0, void 0, function* () { - if (this.resolveRequest) { - const userInfo = yield this.resolveRequest.getUserInfo(); - this.role((_a = userInfo === null || userInfo === void 0 ? void 0 : userInfo.groups) !== null && _a !== void 0 ? _a : []); - } - if (typeof this._.role === 'object' && this._.role.includes('noaccess') || - typeof this._.role === 'string' && this._.role === 'noaccess') { - this.role([]); - } - return _super.on.call(this, resource, skipConditions); - }); - } - context(context) { - super.context(context); - return this; - } - skipConditions(value) { - super.skipConditions(value); - return this; - } - with(context) { - super.with(context); - return this; - } - execute(action) { - super.execute(action); - return this; - } - sync() { - throw new role_acl_1.AccessControlError("Sync method is not allowed on PermissionManager!"); - } -} -exports.PermQuery = PermQuery; -exports.Permissions = new PermissionManager(); diff --git a/out/polyfill-worker.d.ts b/out/polyfill-worker.d.ts deleted file mode 100644 index cb0ff5c..0000000 --- a/out/polyfill-worker.d.ts +++ /dev/null @@ -1 +0,0 @@ -export {}; diff --git a/out/polyfill-worker.js b/out/polyfill-worker.js deleted file mode 100644 index ab2ec1b..0000000 --- a/out/polyfill-worker.js +++ /dev/null @@ -1,25 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -// workers/add.js -const worker_1 = require("threads/worker"); -const fs = require("fs"); -const polyfill_analyzer_1 = require("@10xjs/polyfill-analyzer"); -const polyfills_1 = require("@10xjs/polyfill-analyzer/dist/polyfills"); -worker_1.expose(() => { - const exclude = [ - "console.markTimeline", - "console.timeline", - "console.timelineEnd", - ]; - const featureList = polyfill_analyzer_1.analyze({ - source: fs.readFileSync('./public/js/bundle.js', 'utf-8'), - include: polyfills_1.default.filter(x => !exclude.includes(x)), - // Not all features listed by polyfillLibrary.listAllPolyfills()` can be detected. - unsupportedPolyfill: 'ignore', - }); - const feats = {}; - for (const feature of featureList) { - feats[feature] = {}; - } - return feats; -}); diff --git a/out/polyfill.d.ts b/out/polyfill.d.ts deleted file mode 100644 index db7fc52..0000000 --- a/out/polyfill.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { RequestHandler } from 'express'; -import { PolyfillOptions } from 'polyfill-library'; -declare function getRouter(opts?: Partial): RequestHandler; -export declare const Polyfill: { - getRouter: typeof getRouter; -}; -export {}; diff --git a/out/polyfill.js b/out/polyfill.js deleted file mode 100644 index e1db6ba..0000000 --- a/out/polyfill.js +++ /dev/null @@ -1,39 +0,0 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.Polyfill = void 0; -const polyfillLibrary = require("polyfill-library"); -const _1 = require("."); -const threads_1 = require("threads"); -const features = new _1.WaitForSync(); -(() => __awaiter(void 0, void 0, void 0, function* () { - const worker = yield threads_1.spawn(new threads_1.Worker("./polyfill-worker")); - const feats = yield worker(); - yield threads_1.Thread.terminate(worker); - return feats; -}))() - .then(feats => { - feats["fetch"] = {}; - _1.Logger.debug("Polyfill analysed:", Object.keys(feats)); - features.setData(feats); -}) - .catch(err => features.setError(err)); -function getRouter(opts) { - const options = Object.assign({ minify: _1.DefaultConfig.isProduction, unknown: "polyfill" }, opts); - return (req, res) => __awaiter(this, void 0, void 0, function* () { - const polyfillBundle = yield polyfillLibrary.getPolyfillString(Object.assign(Object.assign({}, options), { uaString: req.header("user-agent"), features: yield features.resolve(), stream: false })); - res.setHeader('Content-Type', 'text/javascript'); - res.send(polyfillBundle); - }); -} -exports.Polyfill = { - getRouter: getRouter, -}; diff --git a/out/redis.d.ts b/out/redis.d.ts deleted file mode 100644 index e28d55f..0000000 --- a/out/redis.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -import * as redis from 'redis'; -interface RedisType { - client: redis.RedisClient | undefined; - get(key: string): Promise; - set(key: string, value: string): Promise; - del(...keys: string[]): Promise; -} -export declare const Redis: RedisType; -export {}; diff --git a/out/redis.js b/out/redis.js deleted file mode 100644 index 6d0587e..0000000 --- a/out/redis.js +++ /dev/null @@ -1,76 +0,0 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.Redis = void 0; -const redis = require("redis"); -const _1 = require("."); -const util_1 = require("util"); -class RealRedis { - get client() { - if (this._client !== undefined) - return this._client; - _1.DefaultConfig.requireEnv('REDIS_URL'); - this._client = redis.createClient({ url: _1.DefaultConfig.REDIS_URL }); - return this._client; - } - get(key) { - return __awaiter(this, void 0, void 0, function* () { - if (!this._promGet) - this._promGet = util_1.promisify(this.client.get); - const value = yield this._promGet(key); - // eslint-disable-next-line no-null/no-null - return value === null ? undefined : value; - }); - } - set(key, value) { - return __awaiter(this, void 0, void 0, function* () { - if (!this._promSet) - this._promSet = util_1.promisify(this.client.set); - yield this._promSet(key, value); - }); - } - del(...keys) { - return __awaiter(this, void 0, void 0, function* () { - if (!this._promDel) - this._promDel = util_1.promisify(this.client.del); - return yield this._promDel(...keys); - }); - } -} -class MockRedis { - constructor() { - this.inMemory = {}; - } - get client() { - return undefined; - } - get(key) { - return __awaiter(this, void 0, void 0, function* () { - return this.inMemory[key] || undefined; - }); - } - set(key, value) { - return __awaiter(this, void 0, void 0, function* () { - this.inMemory[key] = value; - }); - } - del(...keys) { - return __awaiter(this, void 0, void 0, function* () { - for (const key of keys) { - delete this.inMemory[key]; - } - return keys.length; - }); - } -} -exports.Redis = _1.DefaultConfig.REDIS_URL || _1.DefaultConfig.isProduction - ? new RealRedis() - : new MockRedis(); diff --git a/out/session.d.ts b/out/session.d.ts deleted file mode 100644 index 03fd7d3..0000000 --- a/out/session.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -import * as session from 'express-session'; -import { RequestHandler } from 'express'; -declare function getRouter(options?: Partial): RequestHandler; -export declare const Session: { - getRouter: typeof getRouter; -}; -export {}; diff --git a/out/session.js b/out/session.js deleted file mode 100644 index e7f5889..0000000 --- a/out/session.js +++ /dev/null @@ -1,18 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.Session = void 0; -const session = require("express-session"); -const _1 = require("."); -const redisStore = require("connect-redis"); -let sessionStore = undefined; -function getRouter(options) { - _1.DefaultConfig.requireEnv('SESSION_SECRET', true); - if (_1.Redis.client && sessionStore !== undefined) { - const RedisStore = redisStore(session); - sessionStore = new RedisStore({ client: _1.Redis.client }); - } - return session(Object.assign({ store: sessionStore, secret: _1.DefaultConfig.SESSION_SECRET || 'keyboard cat', resave: false, saveUninitialized: true, cookie: { secure: false } }, options)); -} -exports.Session = { - getRouter, -}; diff --git a/package-lock.json b/package-lock.json index 56206ed..a3bc38b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2369,6 +2369,66 @@ "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", "dev": true }, + "pkg-express-utils-types": { + "version": "file:../node-pkg-express-utils-types", + "dependencies": { + "@types/node": { + "version": "14.14.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.7.tgz", + "integrity": "sha512-Zw1vhUSQZYw+7u5dAwNbIA9TuTotpzY/OF7sJM9FqPOF3SPjKnxrjoTktXDZgUjybf4cWVBP7O8wvKdSaGHweg==" + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + }, + "jsonpath-plus": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/jsonpath-plus/-/jsonpath-plus-0.18.1.tgz", + "integrity": "sha512-4yQiuV641HROc4z9YGvnsr8yAdmzbu8JjdAen8WfiXsXewKvTG4ie2bSygF2maek9PcbtROmS6aLQs31BD+oNQ==" + }, + "lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=" + }, + "lodash.flattendeep": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", + "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=" + }, + "matcher": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/matcher/-/matcher-1.1.1.tgz", + "integrity": "sha512-+BmqxWIubKTRKNWx/ahnCkk3mG8m7OturVlqq6HiojGJTd5hVYbgZm6WzcYPCoB+KBT4Vd6R7WSRG2OADNaCjg==", + "requires": { + "escape-string-regexp": "^1.0.4" + } + }, + "notation": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/notation/-/notation-1.3.6.tgz", + "integrity": "sha512-DIuJmrP/Gg1DcXKaApsqcjsJD6jEccqKSfmU3BUx/f1GHsMiTJh70cERwYc64tOmTRTARCeMwkqNNzjh3AHhiw==" + }, + "role-acl": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/role-acl/-/role-acl-4.5.4.tgz", + "integrity": "sha512-T7baCc5BCFzLaxUrtAEIDcfK8HM/WTt6l2SLiMts6zLADSp7wOrrU3op0HVKhwjKi01d0Q6T4NeZ0jdLii9WuQ==", + "requires": { + "jsonpath-plus": "^0.18.0", + "lodash.clonedeep": "^4.5.0", + "lodash.flattendeep": "^4.4.0", + "matcher": "^1.0.0", + "notation": "^1.3.5" + } + }, + "typescript": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.5.tgz", + "integrity": "sha512-ywmr/VrTVCmNTJ6iV2LwIrfG1P+lv6luD8sUJs+2eI9NLGigaN+nUQc13iHqisq7bra9lnmUSYqbJvegraBOPQ==" + } + } + }, "polyfill-library": { "version": "3.97.0", "resolved": "https://registry.npmjs.org/polyfill-library/-/polyfill-library-3.97.0.tgz", diff --git a/package.json b/package.json index e461505..00823ce 100644 --- a/package.json +++ b/package.json @@ -25,15 +25,16 @@ "express-session": "^1.17.1", "json-prune": "^1.1.0", "node-fetch": "^2.6.1", + "pkg-express-utils-types": "file:../node-pkg-express-utils-types", "polyfill-library": "^3.97.0", "proper-url-join": "^2.1.1", "redis": "^3.0.2", "role-acl": "^4.5.4", + "run-script-os": "^1.1.3", "threads": "^1.6.3", "tiny-worker": "^2.3.0", "uuid": "^8.3.1", - "winston": "^3.3.3", - "run-script-os": "^1.1.3" + "winston": "^3.3.3" }, "devDependencies": { "@types/connect-redis": "0.0.15", diff --git a/src/index.ts b/src/index.ts index 7853cb5..a1ee247 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,7 +5,7 @@ export {AuthProxy} from './auth-proxy'; export {Resolvable, WaitForSync} from './helpers/resolvable'; export {urlJoin} from './helpers/urlJoin'; export {AutoReloader} from './auto-reload'; -export {Polyfill} from './polyfill'; +export {Polyfill, PolyfillOptions} from './polyfill'; export {Session} from './session'; export {Permissions} from './permissions'; diff --git a/src/polyfill-worker.ts b/src/polyfill-worker.ts index 77ddc29..838a823 100644 --- a/src/polyfill-worker.ts +++ b/src/polyfill-worker.ts @@ -3,7 +3,7 @@ import {expose} from "threads/worker"; import * as fs from "fs"; import {analyze} from '@10xjs/polyfill-analyzer'; import allPolyfills from '@10xjs/polyfill-analyzer/dist/polyfills'; -import {PolyfillFeatureList} from "polyfill-library"; +import {PolyfillFeatureList} from 'polyfill-library'; expose(() => { const exclude = [ diff --git a/src/polyfill.ts b/src/polyfill.ts index 0ea5c5d..4ff15df 100644 --- a/src/polyfill.ts +++ b/src/polyfill.ts @@ -1,10 +1,19 @@ import {RequestHandler} from 'express'; import * as polyfillLibrary from 'polyfill-library'; -import {PolyfillFeatureList, PolyfillOptions} from 'polyfill-library'; +import {PolyfillFeatureList} from 'polyfill-library'; import {DefaultConfig, Logger, WaitForSync} from '.'; import {spawn, Thread, Worker} from 'threads'; import {WorkerFunction} from 'threads/dist/types/worker'; +export type PolyfillOptions = { + minify: boolean, + unknown: 'polyfill'|'ignore', + features: PolyfillFeatureList, + excludes: string[], + uaString: string, + rum: boolean, +} + const features = new WaitForSync(); (async () => { diff --git a/src/types/auth-proxy.d.ts b/src/types/auth-proxy.d.ts deleted file mode 100644 index 0fde67d..0000000 --- a/src/types/auth-proxy.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -declare namespace Express { - interface Request { - getUserInfo(): Promise; - } - - interface Response { - initLogout(): boolean; - } -} diff --git a/src/types/extend-request.d.ts b/src/types/extend-request.d.ts new file mode 100644 index 0000000..4b557a2 --- /dev/null +++ b/src/types/extend-request.d.ts @@ -0,0 +1,28 @@ +// extend-request.d.ts + +declare global { + namespace Express { + interface UserInfo { + email: string, + email_verified: boolean, + family_name: string, + given_name: string, + groups: string[], + name: string, + preferred_username: string, + sub: string, + } + + interface Request { + getUserInfo(): Promise; + noLogging: boolean|undefined; + permissionDetails?: import('role-acl').Permission; + } + + interface Response { + initLogout(): boolean; + } + } +} + +export {}; diff --git a/src/types/logging.d.ts b/src/types/logging.d.ts deleted file mode 100644 index 83b2916..0000000 --- a/src/types/logging.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -declare namespace Express { - interface Request { - noLogging: boolean|undefined; - } -} diff --git a/src/types/permissions.d.ts b/src/types/permissions.d.ts deleted file mode 100644 index 49ed626..0000000 --- a/src/types/permissions.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -declare namespace Express { - interface Request { - permissionDetails?: import('role-acl').Permission; - } -}