diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000..c3b22ed2 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,11 @@ +{ + "arrowParens": "avoid", + "bracketSpacing": true, + "trailingComma": "es5", + "useTabs": true, + "semi": true, + "singleQuote": true, + "jsxSingleQuote": true, + "jsxBracketSameLine": true, + "printWidth": 140 +} \ No newline at end of file diff --git a/cloudbuild.yaml b/cloudbuild.yaml index f3326e1b..92f08e45 100644 --- a/cloudbuild.yaml +++ b/cloudbuild.yaml @@ -1,6 +1,9 @@ steps: - name: 'gcr.io/cloud-builders/docker' - args: ['build', '-t', 'gcr.io/$PROJECT_ID/${_APP_NAME}:latest', '.'] + entrypoint: 'bash' + args: ['-c', 'docker pull gcr.io/$PROJECT_ID/${_APP_NAME}:latest || exit 0'] +- name: 'gcr.io/cloud-builders/docker' + args: ['build', '-t', 'gcr.io/$PROJECT_ID/${_APP_NAME}:latest', '--cache-from', 'gcr.io/$PROJECT_ID/${_APP_NAME}:latest', '.'] - name: 'gcr.io/cloud-builders/docker' args: ['push', 'gcr.io/$PROJECT_ID/${_APP_NAME}:latest'] - name: 'gcr.io/cloud-builders/gcloud' @@ -15,3 +18,5 @@ steps: args: ['run', 'deploy', 'uatbeta-api', '--image', 'gcr.io/$PROJECT_ID/${_APP_NAME}:latest', '--platform', 'managed', '--region', '${_REGION}', '--allow-unauthenticated'] images: - gcr.io/$PROJECT_ID/${_APP_NAME}:latest +options: + machineType: 'E2_HIGHCPU_8' \ No newline at end of file diff --git a/cloudbuild_dynamic.yaml b/cloudbuild_dynamic.yaml new file mode 100644 index 00000000..3470fe8a --- /dev/null +++ b/cloudbuild_dynamic.yaml @@ -0,0 +1,19 @@ +steps: +- name: 'gcr.io/cloud-builders/docker' + entrypoint: 'bash' + args: ['-c', 'docker pull gcr.io/$PROJECT_ID/${_APP_NAME}:${_ENVIRONMENT} || exit 0'] +- name: 'gcr.io/cloud-builders/docker' + args: [ + 'build', + '-t', 'gcr.io/$PROJECT_ID/${_APP_NAME}:${_ENVIRONMENT}', + '--cache-from', 'gcr.io/$PROJECT_ID/${_APP_NAME}:${_ENVIRONMENT}', + '.' + ] +- name: 'gcr.io/cloud-builders/docker' + args: ['push', 'gcr.io/$PROJECT_ID/${_APP_NAME}:${_ENVIRONMENT}'] +- name: 'gcr.io/cloud-builders/gcloud' + args: ['run', 'deploy', '${_ENVIRONMENT}-api', '--image', 'gcr.io/$PROJECT_ID/${_APP_NAME}:${_ENVIRONMENT}', '--platform', 'managed', '--region', '${_REGION}', '--allow-unauthenticated'] +images: +- gcr.io/$PROJECT_ID/${_APP_NAME}:${_ENVIRONMENT} +options: + machineType: 'E2_HIGHCPU_8' \ No newline at end of file diff --git a/package.json b/package.json index aacd177d..9fa5d3df 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "btoa": "^1.2.1", "cookie-parser": "^1.4.5", "cors": "^2.8.5", + "crypto": "^1.0.1", "crypto-js": "^4.0.0", "discourse-sso": "^1.0.3", "dotenv": "^8.2.0", @@ -41,7 +42,9 @@ "passport-jwt": "^4.0.0", "passport-linkedin-oauth2": "^2.0.0", "passport-openidconnect": "0.0.2", + "prettier": "^2.2.1", "query-string": "^6.12.1", + "randomstring": "^1.1.5", "snyk": "^1.334.0", "swagger-ui-express": "^4.1.4", "test": "^0.6.0", @@ -65,7 +68,9 @@ "test": "jest --runInBand", "eject": "", "snyk-protect": "snyk protect", - "prepublish": "npm run snyk-protect" + "prepublish": "npm run snyk-protect", + "prettify": "prettier --write \"src/**/*.{scss,js,jsx}\"", + "prettify-test": "prettier --write \"test/**/*.js\"" }, "proxy": "http://localhost:3001", "snyk": true diff --git a/src/config/account.js b/src/config/account.js index 2b487e05..65c82c8c 100755 --- a/src/config/account.js +++ b/src/config/account.js @@ -1,67 +1,68 @@ import { getUserByUserId } from '../resources/user/user.repository'; -import { to } from 'await-to-js' +import { to } from 'await-to-js'; const store = new Map(); const logins = new Map(); const { nanoid } = require('nanoid'); - class Account { - constructor(id, profile) { - this.accountId = id || nanoid(); - this.profile = profile; - store.set(this.accountId, this); - } + constructor(id, profile) { + this.accountId = id || nanoid(); + this.profile = profile; + store.set(this.accountId, this); + } - /** - * @param use - can either be "id_token" or "userinfo", depending on - * where the specific claims are intended to be put in. - * @param scope - the intended scope, while oidc-provider will mask - * claims depending on the scope automatically you might want to skip - * loading some claims from external resources etc. based on this detail - * or not return them in id tokens but only userinfo and so on. - */ - async claims(use, scope) { // eslint-disable-line no-unused-vars - if (this.profile) { - return { - sub: this.accountId, // it is essential to always return a sub claim - email: this.profile.email, - firstname: this.profile.firstname, - lastname: this.profile.lastname - }; - } + /** + * @param use - can either be "id_token" or "userinfo", depending on + * where the specific claims are intended to be put in. + * @param scope - the intended scope, while oidc-provider will mask + * claims depending on the scope automatically you might want to skip + * loading some claims from external resources etc. based on this detail + * or not return them in id tokens but only userinfo and so on. + */ + async claims(use, scope) { + // eslint-disable-line no-unused-vars + if (this.profile) { + return { + sub: this.accountId, // it is essential to always return a sub claim + email: this.profile.email, + firstname: this.profile.firstname, + lastname: this.profile.lastname, + }; + } - return { - sub: this.accountId, // it is essential to always return a sub claim - }; - } + return { + sub: this.accountId, // it is essential to always return a sub claim + }; + } - static async findByFederated(provider, claims) { - const id = `${provider}.${claims.sub}`; - if (!logins.get(id)) { - logins.set(id, new Account(id, claims)); - } - return logins.get(id); - } + static async findByFederated(provider, claims) { + const id = `${provider}.${claims.sub}`; + if (!logins.get(id)) { + logins.set(id, new Account(id, claims)); + } + return logins.get(id); + } - static async findByLogin(login) { - if (!logins.get(login)) { - logins.set(login, new Account(login)); - } + static async findByLogin(login) { + if (!logins.get(login)) { + logins.set(login, new Account(login)); + } - return logins.get(login); - } + return logins.get(login); + } - static async findAccount(ctx, id, token) { // eslint-disable-line no-unused-vars - // token is a reference to the token used for which a given account is being loaded, - // it is undefined in scenarios where account claims are returned from authorization endpoint - // ctx is the koa request context - if (!store.get(id)) { - let [err, user] = await to(getUserByUserId(parseInt(id))) - new Account(id, user); // eslint-disable-line no-new - } - return store.get(id); - } + static async findAccount(ctx, id, token) { + // eslint-disable-line no-unused-vars + // token is a reference to the token used for which a given account is being loaded, + // it is undefined in scenarios where account claims are returned from authorization endpoint + // ctx is the koa request context + if (!store.get(id)) { + let [err, user] = await to(getUserByUserId(parseInt(id))); + new Account(id, user); // eslint-disable-line no-new + } + return store.get(id); + } } module.exports = Account; diff --git a/src/config/configuration.js b/src/config/configuration.js index 172059c7..889e6248 100755 --- a/src/config/configuration.js +++ b/src/config/configuration.js @@ -1,90 +1,92 @@ import oidcProvider from 'oidc-provider'; -const { interactionPolicy: { Prompt, base: policy } } = oidcProvider; +const { + interactionPolicy: { Prompt, base: policy }, +} = oidcProvider; // copies the default policy, already has login and consent prompt policies const interactions2 = policy(); // create a requestable prompt with no implicit checks const selectAccount = new Prompt({ - name: 'select_account', - requestable: true, + name: 'select_account', + requestable: true, }); // add to index 0, order goes select_account > login > consent interactions2.add(selectAccount, 0); export const clients = [ - { - //Metadata works - client_id: process.env.MDWClientID || '', - client_secret: process.env.MDWClientSecret || '', - grant_types: ['authorization_code'], - response_types: ['code'], - //grant_types: ['authorization_code', 'implicit'], - //response_types: ['code id_token'], - redirect_uris: process.env.MDWRedirectURI.split(",") || [''], - id_token_signed_response_alg: 'HS256', - post_logout_redirect_uris: ['https://hdruk-auth.metadata.works/auth/logout'] - }, - { - //BC Platforms - client_id: process.env.BCPClientID || '', - client_secret: process.env.BCPClientSecret || '', - grant_types: ['authorization_code', 'implicit'], - response_types: ['code id_token'], - redirect_uris: process.env.BCPRedirectURI.split(",") || [''], - id_token_signed_response_alg: 'HS256', - post_logout_redirect_uris: ['https://web.uatbeta.healthdatagateway.org/search?search=&logout=true'] - } + { + //Metadata works + client_id: process.env.MDWClientID || '', + client_secret: process.env.MDWClientSecret || '', + grant_types: ['authorization_code'], + response_types: ['code'], + //grant_types: ['authorization_code', 'implicit'], + //response_types: ['code id_token'], + redirect_uris: process.env.MDWRedirectURI.split(',') || [''], + id_token_signed_response_alg: 'HS256', + post_logout_redirect_uris: ['https://hdruk-auth.metadata.works/auth/logout'], + }, + { + //BC Platforms + client_id: process.env.BCPClientID || '', + client_secret: process.env.BCPClientSecret || '', + grant_types: ['authorization_code', 'implicit'], + response_types: ['code id_token'], + redirect_uris: process.env.BCPRedirectURI.split(',') || [''], + id_token_signed_response_alg: 'HS256', + post_logout_redirect_uris: ['https://web.uatbeta.healthdatagateway.org/search?search=&logout=true'], + }, ]; export const interactions = { - policy: interactions2, - url(ctx, interaction) { - return `/api/v1/openid/interaction/${ctx.oidc.uid}`; - }, + policy: interactions2, + url(ctx, interaction) { + return `/api/v1/openid/interaction/${ctx.oidc.uid}`; + }, }; export const cookies = { - long: { signed: true, maxAge: (1 * 24 * 60 * 60) * 1000 }, - short: { signed: true }, - keys: ['some secret key', 'and also the old rotated away some time ago', 'and one more'], + long: { signed: true, maxAge: 1 * 24 * 60 * 60 * 1000 }, + short: { signed: true }, + keys: ['some secret key', 'and also the old rotated away some time ago', 'and one more'], }; export const claims = { - email: ['email'], - profile: ['firstname', 'lastname'], + email: ['email'], + profile: ['firstname', 'lastname'], }; export const features = { - devInteractions: { enabled: false }, - deviceFlow: { enabled: true }, - introspection: { enabled: true }, - revocation: { enabled: true }, - encryption: { enabled: true }, - rpInitiatedLogout: { - enabled: true, - logoutSource, - postLogoutSuccessSource - } + devInteractions: { enabled: false }, + deviceFlow: { enabled: true }, + introspection: { enabled: true }, + revocation: { enabled: true }, + encryption: { enabled: true }, + rpInitiatedLogout: { + enabled: true, + logoutSource, + postLogoutSuccessSource, + }, }; export const jwks = require('./jwks.json'); export const ttl = { - AccessToken: 1 * 60 * 60, - AuthorizationCode: 10 * 60, - IdToken: 1 * 60 * 60, - DeviceCode: 10 * 60, - RefreshToken: 1 * 24 * 60 * 60, + AccessToken: 1 * 60 * 60, + AuthorizationCode: 10 * 60, + IdToken: 1 * 60 * 60, + DeviceCode: 10 * 60, + RefreshToken: 1 * 24 * 60 * 60, }; async function logoutSource(ctx, form) { - // @param ctx - koa request context - // @param form - form source (id="op.logoutForm") to be embedded in the page and submitted by - // the End-User - ctx.body = ` + // @param ctx - koa request context + // @param form - form source (id="op.logoutForm") to be embedded in the page and submitted by + // the End-User + ctx.body = `