From 276fb83c2ee4f75844e1c7a21f7bf61eef3abd62 Mon Sep 17 00:00:00 2001
From: a3957273 <89583054+a3957273@users.noreply.github.com>
Date: Tue, 16 May 2023 12:22:25 +0000
Subject: [PATCH 01/13] Initial commit for oauth
---
backend/config/default.cjs | 23 ++
backend/form.html | 30 +++
backend/package-lock.json | 475 ++++++++++++++++++++++++++++++++++--
backend/package.json | 3 +
backend/src/routes.ts | 36 +++
backend/src/utils/config.ts | 9 +
6 files changed, 561 insertions(+), 15 deletions(-)
create mode 100644 backend/form.html
diff --git a/backend/config/default.cjs b/backend/config/default.cjs
index 3e22006e3..102934125 100644
--- a/backend/config/default.cjs
+++ b/backend/config/default.cjs
@@ -141,6 +141,29 @@ module.exports = {
],
},
+ session: {
+ secret: '',
+ },
+
+ oauth: {
+ enabled: false,
+
+ defaults: {
+ origin: '',
+ prefix: '/api/connect',
+ transport: 'session',
+ },
+
+ cognito: {
+ key: '',
+ secret: '',
+ dynamic: ['scope'],
+ response: ['tokens', 'raw', 'jwt'],
+ callback: '/',
+ subdomain: '',
+ },
+ },
+
// These settings are PUBLIC and shared with the UI
ui: {
// Show a banner at the top of the screen on all pages
diff --git a/backend/form.html b/backend/form.html
new file mode 100644
index 000000000..48b79afdf
--- /dev/null
+++ b/backend/form.html
@@ -0,0 +1,30 @@
+
+
+
+ Grant - Dynamic HTTP Override
+
+
+
+
+
diff --git a/backend/package-lock.json b/backend/package-lock.json
index ca204f35d..86a0ee712 100644
--- a/backend/package-lock.json
+++ b/backend/package-lock.json
@@ -14,13 +14,16 @@
"bunyan": "^1.8.15",
"chalk": "^5.2.0",
"config": "^3.3.9",
+ "connect-mongo": "^5.0.0",
"cross-fetch": "^3.1.5",
"dedent-js": "^1.0.1",
"dev-null": "^0.1.1",
"eslint-plugin-prettier": "^4.2.1",
"express": "^5.0.0-beta.1",
+ "express-session": "^1.17.3",
"form-data-encoder": "^2.1.4",
"formdata-node": "^5.0.0",
+ "grant": "^5.4.21",
"indent-string": "^5.0.0",
"jsonschema": "^1.4.1",
"jsonwebtoken": "^9.0.0",
@@ -4949,6 +4952,22 @@
"proto-list": "~1.2.1"
}
},
+ "node_modules/connect-mongo": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/connect-mongo/-/connect-mongo-5.0.0.tgz",
+ "integrity": "sha512-s93jiP6GkRApn5duComx6RLwtP23YrulPxShz+8peX7svd6Q+MS8nKLhKCCazbP92C13eTVaIOxgeLt0ezIiCg==",
+ "dependencies": {
+ "debug": "^4.3.1",
+ "kruptein": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=12.9.0"
+ },
+ "peerDependencies": {
+ "express-session": "^1.17.1",
+ "mongodb": "^5.1.0"
+ }
+ },
"node_modules/content-disposition": {
"version": "0.5.4",
"license": "MIT",
@@ -6171,6 +6190,53 @@
"node": ">= 4"
}
},
+ "node_modules/express-session": {
+ "version": "1.17.3",
+ "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.3.tgz",
+ "integrity": "sha512-4+otWXlShYlG1Ma+2Jnn+xgKUZTMJ5QD3YvfilX3AcocOAbIkVylSWEklzALe/+Pu4qV6TYBj5GwOBFfdKqLBw==",
+ "dependencies": {
+ "cookie": "0.4.2",
+ "cookie-signature": "1.0.6",
+ "debug": "2.6.9",
+ "depd": "~2.0.0",
+ "on-headers": "~1.0.2",
+ "parseurl": "~1.3.3",
+ "safe-buffer": "5.2.1",
+ "uid-safe": "~2.1.5"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/express-session/node_modules/cookie": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz",
+ "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/express-session/node_modules/debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/express-session/node_modules/depd": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+ "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/express-session/node_modules/ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
+ },
"node_modules/express/node_modules/debug": {
"version": "3.1.0",
"license": "MIT",
@@ -6781,6 +6847,69 @@
"version": "4.2.11",
"license": "ISC"
},
+ "node_modules/grant": {
+ "version": "5.4.21",
+ "resolved": "https://registry.npmjs.org/grant/-/grant-5.4.21.tgz",
+ "integrity": "sha512-QaoZudI9Gmh2W415gd71Iul6gpVH9sG1SkjfnGHtqYZopQDQ5PUVxRol5zFCrwGi9S0EbExbelHlZScgdChg2w==",
+ "dependencies": {
+ "qs": "^6.10.2",
+ "request-compose": "^2.1.4",
+ "request-oauth": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "optionalDependencies": {
+ "cookie": "^0.4.1",
+ "cookie-signature": "^1.1.0",
+ "jwk-to-pem": "^2.0.5",
+ "jws": "^4.0.0"
+ }
+ },
+ "node_modules/grant/node_modules/cookie-signature": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.1.tgz",
+ "integrity": "sha512-78KWk9T26NhzXtuL26cIJ8/qNHANyJ/ZYrmEXFzUmhZdjpBv+DlWlOANRTGBt48YcyslsLrj0bMLFTmXvLRCOw==",
+ "optional": true,
+ "engines": {
+ "node": ">=6.6.0"
+ }
+ },
+ "node_modules/grant/node_modules/jwa": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz",
+ "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==",
+ "optional": true,
+ "dependencies": {
+ "buffer-equal-constant-time": "1.0.1",
+ "ecdsa-sig-formatter": "1.0.11",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/grant/node_modules/jws": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz",
+ "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==",
+ "optional": true,
+ "dependencies": {
+ "jwa": "^2.0.0",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/grant/node_modules/qs": {
+ "version": "6.11.2",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz",
+ "integrity": "sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==",
+ "dependencies": {
+ "side-channel": "^1.0.4"
+ },
+ "engines": {
+ "node": ">=0.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/grapheme-splitter": {
"version": "1.0.4",
"license": "MIT"
@@ -8831,6 +8960,17 @@
"safe-buffer": "^5.0.1"
}
},
+ "node_modules/jwk-to-pem": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/jwk-to-pem/-/jwk-to-pem-2.0.5.tgz",
+ "integrity": "sha512-L90jwellhO8jRKYwbssU9ifaMVqajzj3fpRjDKcsDzrslU9syRbFqfkXtT4B89HYAap+xsxNcxgBSB09ig+a7A==",
+ "optional": true,
+ "dependencies": {
+ "asn1.js": "^5.3.0",
+ "elliptic": "^6.5.4",
+ "safe-buffer": "^5.0.1"
+ }
+ },
"node_modules/jws": {
"version": "3.2.2",
"license": "MIT",
@@ -8861,6 +9001,17 @@
"node": ">=6"
}
},
+ "node_modules/kruptein": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/kruptein/-/kruptein-3.0.6.tgz",
+ "integrity": "sha512-EQJjTwAJfQkC4NfdQdo3HXM2a9pmBm8oidzH270cYu1MbgXPNPMJuldN7OPX+qdhPO5rw4X3/iKz0BFBfkXGKA==",
+ "dependencies": {
+ "asn1.js": "^5.4.1"
+ },
+ "engines": {
+ "node": ">8"
+ }
+ },
"node_modules/kubernetes-client": {
"version": "9.0.0",
"license": "MIT",
@@ -9727,19 +9878,36 @@
}
},
"node_modules/mongodb": {
- "version": "4.16.0",
- "license": "Apache-2.0",
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-5.5.0.tgz",
+ "integrity": "sha512-XgrkUgAAdfnZKQfk5AsYL8j7O99WHd4YXPxYxnh8dZxD+ekYWFRA3JktUsBnfg+455Smf75/+asoU/YLwNGoQQ==",
+ "peer": true,
"dependencies": {
- "bson": "^4.7.2",
- "mongodb-connection-string-url": "^2.5.4",
+ "bson": "^5.3.0",
+ "mongodb-connection-string-url": "^2.6.0",
"socks": "^2.7.1"
},
"engines": {
- "node": ">=12.9.0"
+ "node": ">=14.20.1"
},
"optionalDependencies": {
- "@aws-sdk/credential-providers": "^3.186.0",
"saslprep": "^1.0.3"
+ },
+ "peerDependencies": {
+ "@aws-sdk/credential-providers": "^3.201.0",
+ "mongodb-client-encryption": ">=2.3.0 <3",
+ "snappy": "^7.2.2"
+ },
+ "peerDependenciesMeta": {
+ "@aws-sdk/credential-providers": {
+ "optional": true
+ },
+ "mongodb-client-encryption": {
+ "optional": true
+ },
+ "snappy": {
+ "optional": true
+ }
}
},
"node_modules/mongodb-connection-string-url": {
@@ -9798,6 +9966,33 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/mongodb-memory-server-core/node_modules/mongodb": {
+ "version": "4.16.0",
+ "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-4.16.0.tgz",
+ "integrity": "sha512-0EB113Fsucaq1wsY0dOhi1fmZOwFtLOtteQkiqOXGklvWMnSH3g2QS53f0KTP+/6qOkuoXE2JksubSZNmxeI+g==",
+ "dev": true,
+ "dependencies": {
+ "bson": "^4.7.2",
+ "mongodb-connection-string-url": "^2.5.4",
+ "socks": "^2.7.1"
+ },
+ "engines": {
+ "node": ">=12.9.0"
+ },
+ "optionalDependencies": {
+ "@aws-sdk/credential-providers": "^3.186.0",
+ "saslprep": "^1.0.3"
+ }
+ },
+ "node_modules/mongodb/node_modules/bson": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/bson/-/bson-5.3.0.tgz",
+ "integrity": "sha512-ukmCZMneMlaC5ebPHXIkP8YJzNl5DC41N5MAIvKDqLggdao342t4McltoJBQfQya/nHBWAcSsYRqlXPoQkTJag==",
+ "peer": true,
+ "engines": {
+ "node": ">=14.20.1"
+ }
+ },
"node_modules/mongoose": {
"version": "6.11.0",
"license": "MIT",
@@ -9825,6 +10020,23 @@
"mongoose": "4.x || 5.x || 6.x"
}
},
+ "node_modules/mongoose/node_modules/mongodb": {
+ "version": "4.16.0",
+ "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-4.16.0.tgz",
+ "integrity": "sha512-0EB113Fsucaq1wsY0dOhi1fmZOwFtLOtteQkiqOXGklvWMnSH3g2QS53f0KTP+/6qOkuoXE2JksubSZNmxeI+g==",
+ "dependencies": {
+ "bson": "^4.7.2",
+ "mongodb-connection-string-url": "^2.5.4",
+ "socks": "^2.7.1"
+ },
+ "engines": {
+ "node": ">=12.9.0"
+ },
+ "optionalDependencies": {
+ "@aws-sdk/credential-providers": "^3.186.0",
+ "saslprep": "^1.0.3"
+ }
+ },
"node_modules/mongoose/node_modules/ms": {
"version": "2.1.3",
"license": "MIT"
@@ -10217,7 +10429,6 @@
},
"node_modules/object-inspect": {
"version": "1.12.3",
- "dev": true,
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
@@ -10947,6 +11158,14 @@
],
"license": "MIT"
},
+ "node_modules/random-bytes": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz",
+ "integrity": "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ==",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
"node_modules/randombytes": {
"version": "2.1.0",
"license": "MIT",
@@ -11058,6 +11277,35 @@
"node": ">= 6"
}
},
+ "node_modules/request-compose": {
+ "version": "2.1.6",
+ "resolved": "https://registry.npmjs.org/request-compose/-/request-compose-2.1.6.tgz",
+ "integrity": "sha512-S07L+2VbJB32WddD/o/PnYGKym63zLVbymygVWXvt8L79VAngcjAxhHaGuFOICLxEV90EasEPzqPKKHPspXP8w==",
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
+ "node_modules/request-oauth": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/request-oauth/-/request-oauth-1.0.1.tgz",
+ "integrity": "sha512-85THTg1RgOYtqQw42JON6AqvHLptlj1biw265Tsq4fD4cPdUvhDB2Qh9NTv17yCD322ROuO9aOmpc4GyayGVBA==",
+ "dependencies": {
+ "oauth-sign": "^0.9.0",
+ "qs": "^6.9.6",
+ "uuid": "^8.3.2"
+ },
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/request-oauth/node_modules/uuid": {
+ "version": "8.3.2",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
+ "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
+ "bin": {
+ "uuid": "dist/bin/uuid"
+ }
+ },
"node_modules/request/node_modules/form-data": {
"version": "2.3.3",
"license": "MIT",
@@ -11411,7 +11659,6 @@
},
"node_modules/side-channel": {
"version": "1.0.4",
- "dev": true,
"license": "MIT",
"dependencies": {
"call-bind": "^1.0.0",
@@ -12180,6 +12427,17 @@
"node": ">=0.8.0"
}
},
+ "node_modules/uid-safe": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz",
+ "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==",
+ "dependencies": {
+ "random-bytes": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
"node_modules/undefsafe": {
"version": "2.0.5",
"dev": true,
@@ -16259,6 +16517,15 @@
"proto-list": "~1.2.1"
}
},
+ "connect-mongo": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/connect-mongo/-/connect-mongo-5.0.0.tgz",
+ "integrity": "sha512-s93jiP6GkRApn5duComx6RLwtP23YrulPxShz+8peX7svd6Q+MS8nKLhKCCazbP92C13eTVaIOxgeLt0ezIiCg==",
+ "requires": {
+ "debug": "^4.3.1",
+ "kruptein": "^3.0.0"
+ }
+ },
"content-disposition": {
"version": "0.5.4",
"requires": {
@@ -17057,6 +17324,46 @@
}
}
},
+ "express-session": {
+ "version": "1.17.3",
+ "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.3.tgz",
+ "integrity": "sha512-4+otWXlShYlG1Ma+2Jnn+xgKUZTMJ5QD3YvfilX3AcocOAbIkVylSWEklzALe/+Pu4qV6TYBj5GwOBFfdKqLBw==",
+ "requires": {
+ "cookie": "0.4.2",
+ "cookie-signature": "1.0.6",
+ "debug": "2.6.9",
+ "depd": "~2.0.0",
+ "on-headers": "~1.0.2",
+ "parseurl": "~1.3.3",
+ "safe-buffer": "5.2.1",
+ "uid-safe": "~2.1.5"
+ },
+ "dependencies": {
+ "cookie": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz",
+ "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA=="
+ },
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "depd": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+ "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
+ }
+ }
+ },
"ext": {
"version": "1.7.0",
"requires": {
@@ -17428,6 +17735,57 @@
"graceful-fs": {
"version": "4.2.11"
},
+ "grant": {
+ "version": "5.4.21",
+ "resolved": "https://registry.npmjs.org/grant/-/grant-5.4.21.tgz",
+ "integrity": "sha512-QaoZudI9Gmh2W415gd71Iul6gpVH9sG1SkjfnGHtqYZopQDQ5PUVxRol5zFCrwGi9S0EbExbelHlZScgdChg2w==",
+ "requires": {
+ "cookie": "^0.4.1",
+ "cookie-signature": "^1.1.0",
+ "jwk-to-pem": "^2.0.5",
+ "jws": "^4.0.0",
+ "qs": "^6.10.2",
+ "request-compose": "^2.1.4",
+ "request-oauth": "^1.0.1"
+ },
+ "dependencies": {
+ "cookie-signature": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.1.tgz",
+ "integrity": "sha512-78KWk9T26NhzXtuL26cIJ8/qNHANyJ/ZYrmEXFzUmhZdjpBv+DlWlOANRTGBt48YcyslsLrj0bMLFTmXvLRCOw==",
+ "optional": true
+ },
+ "jwa": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz",
+ "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==",
+ "optional": true,
+ "requires": {
+ "buffer-equal-constant-time": "1.0.1",
+ "ecdsa-sig-formatter": "1.0.11",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "jws": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz",
+ "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==",
+ "optional": true,
+ "requires": {
+ "jwa": "^2.0.0",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "qs": {
+ "version": "6.11.2",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz",
+ "integrity": "sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==",
+ "requires": {
+ "side-channel": "^1.0.4"
+ }
+ }
+ }
+ },
"grapheme-splitter": {
"version": "1.0.4"
},
@@ -18701,6 +19059,17 @@
"safe-buffer": "^5.0.1"
}
},
+ "jwk-to-pem": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/jwk-to-pem/-/jwk-to-pem-2.0.5.tgz",
+ "integrity": "sha512-L90jwellhO8jRKYwbssU9ifaMVqajzj3fpRjDKcsDzrslU9syRbFqfkXtT4B89HYAap+xsxNcxgBSB09ig+a7A==",
+ "optional": true,
+ "requires": {
+ "asn1.js": "^5.3.0",
+ "elliptic": "^6.5.4",
+ "safe-buffer": "^5.0.1"
+ }
+ },
"jws": {
"version": "3.2.2",
"requires": {
@@ -18721,6 +19090,14 @@
"version": "3.0.3",
"dev": true
},
+ "kruptein": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/kruptein/-/kruptein-3.0.6.tgz",
+ "integrity": "sha512-EQJjTwAJfQkC4NfdQdo3HXM2a9pmBm8oidzH270cYu1MbgXPNPMJuldN7OPX+qdhPO5rw4X3/iKz0BFBfkXGKA==",
+ "requires": {
+ "asn1.js": "^5.4.1"
+ }
+ },
"kubernetes-client": {
"version": "9.0.0",
"requires": {
@@ -19379,13 +19756,23 @@
"optional": true
},
"mongodb": {
- "version": "4.16.0",
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-5.5.0.tgz",
+ "integrity": "sha512-XgrkUgAAdfnZKQfk5AsYL8j7O99WHd4YXPxYxnh8dZxD+ekYWFRA3JktUsBnfg+455Smf75/+asoU/YLwNGoQQ==",
+ "peer": true,
"requires": {
- "@aws-sdk/credential-providers": "^3.186.0",
- "bson": "^4.7.2",
- "mongodb-connection-string-url": "^2.5.4",
+ "bson": "^5.3.0",
+ "mongodb-connection-string-url": "^2.6.0",
"saslprep": "^1.0.3",
"socks": "^2.7.1"
+ },
+ "dependencies": {
+ "bson": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/bson/-/bson-5.3.0.tgz",
+ "integrity": "sha512-ukmCZMneMlaC5ebPHXIkP8YJzNl5DC41N5MAIvKDqLggdao342t4McltoJBQfQya/nHBWAcSsYRqlXPoQkTJag==",
+ "peer": true
+ }
}
},
"mongodb-connection-string-url": {
@@ -19426,6 +19813,19 @@
"camelcase": {
"version": "6.3.0",
"dev": true
+ },
+ "mongodb": {
+ "version": "4.16.0",
+ "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-4.16.0.tgz",
+ "integrity": "sha512-0EB113Fsucaq1wsY0dOhi1fmZOwFtLOtteQkiqOXGklvWMnSH3g2QS53f0KTP+/6qOkuoXE2JksubSZNmxeI+g==",
+ "dev": true,
+ "requires": {
+ "@aws-sdk/credential-providers": "^3.186.0",
+ "bson": "^4.7.2",
+ "mongodb-connection-string-url": "^2.5.4",
+ "saslprep": "^1.0.3",
+ "socks": "^2.7.1"
+ }
}
}
},
@@ -19441,6 +19841,18 @@
"sift": "16.0.1"
},
"dependencies": {
+ "mongodb": {
+ "version": "4.16.0",
+ "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-4.16.0.tgz",
+ "integrity": "sha512-0EB113Fsucaq1wsY0dOhi1fmZOwFtLOtteQkiqOXGklvWMnSH3g2QS53f0KTP+/6qOkuoXE2JksubSZNmxeI+g==",
+ "requires": {
+ "@aws-sdk/credential-providers": "^3.186.0",
+ "bson": "^4.7.2",
+ "mongodb-connection-string-url": "^2.5.4",
+ "saslprep": "^1.0.3",
+ "socks": "^2.7.1"
+ }
+ },
"ms": {
"version": "2.1.3"
}
@@ -19679,8 +20091,7 @@
"version": "2.2.0"
},
"object-inspect": {
- "version": "1.12.3",
- "dev": true
+ "version": "1.12.3"
},
"oidc-token-hash": {
"version": "5.0.3"
@@ -20098,6 +20509,11 @@
"queue-microtask": {
"version": "1.2.3"
},
+ "random-bytes": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz",
+ "integrity": "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ=="
+ },
"randombytes": {
"version": "2.1.0",
"requires": {
@@ -20194,6 +20610,28 @@
}
}
},
+ "request-compose": {
+ "version": "2.1.6",
+ "resolved": "https://registry.npmjs.org/request-compose/-/request-compose-2.1.6.tgz",
+ "integrity": "sha512-S07L+2VbJB32WddD/o/PnYGKym63zLVbymygVWXvt8L79VAngcjAxhHaGuFOICLxEV90EasEPzqPKKHPspXP8w=="
+ },
+ "request-oauth": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/request-oauth/-/request-oauth-1.0.1.tgz",
+ "integrity": "sha512-85THTg1RgOYtqQw42JON6AqvHLptlj1biw265Tsq4fD4cPdUvhDB2Qh9NTv17yCD322ROuO9aOmpc4GyayGVBA==",
+ "requires": {
+ "oauth-sign": "^0.9.0",
+ "qs": "^6.9.6",
+ "uuid": "^8.3.2"
+ },
+ "dependencies": {
+ "uuid": {
+ "version": "8.3.2",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
+ "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="
+ }
+ }
+ },
"require-directory": {
"version": "2.1.1"
},
@@ -20388,7 +20826,6 @@
},
"side-channel": {
"version": "1.0.4",
- "dev": true,
"requires": {
"call-bind": "^1.0.0",
"get-intrinsic": "^1.0.2",
@@ -20844,6 +21281,14 @@
"uglify-js": {
"version": "3.17.4"
},
+ "uid-safe": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz",
+ "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==",
+ "requires": {
+ "random-bytes": "~1.0.0"
+ }
+ },
"undefsafe": {
"version": "2.0.5",
"dev": true
diff --git a/backend/package.json b/backend/package.json
index d58f576e3..45e664d4e 100644
--- a/backend/package.json
+++ b/backend/package.json
@@ -35,13 +35,16 @@
"bunyan": "^1.8.15",
"chalk": "^5.2.0",
"config": "^3.3.9",
+ "connect-mongo": "^5.0.0",
"cross-fetch": "^3.1.5",
"dedent-js": "^1.0.1",
"dev-null": "^0.1.1",
"eslint-plugin-prettier": "^4.2.1",
"express": "^5.0.0-beta.1",
+ "express-session": "^1.17.3",
"form-data-encoder": "^2.1.4",
"formdata-node": "^5.0.0",
+ "grant": "^5.4.21",
"indent-string": "^5.0.0",
"jsonschema": "^1.4.1",
"jsonwebtoken": "^9.0.0",
diff --git a/backend/src/routes.ts b/backend/src/routes.ts
index 93c09f726..8f7d977d4 100644
--- a/backend/src/routes.ts
+++ b/backend/src/routes.ts
@@ -1,4 +1,10 @@
+import parser from 'body-parser'
+import MongoStore from 'connect-mongo'
import express from 'express'
+import session from 'express-session'
+import fs from 'fs'
+import grant from 'grant'
+import { omit } from 'lodash-es'
import { getApplicationLogs, getItemLogs } from './routes/v1/admin.js'
import { getApprovals, getNumApprovals, postApprovalResponse } from './routes/v1/approvals.js'
@@ -38,6 +44,7 @@ import {
putUpdateLastViewed,
putVersion,
} from './routes/v1/version.js'
+import config from './utils/config.js'
import { expressErrorHandler, expressLogger } from './utils/logger.js'
import { getUser } from './utils/user.js'
@@ -46,6 +53,35 @@ export const server = express()
server.use(getUser)
server.use(expressLogger)
+if (config.oauth.enabled) {
+ server.use(
+ session({
+ secret: config.session.secret,
+ resave: true,
+ saveUninitialized: true,
+ cookie: { maxAge: 30 * 24 * 60 * 60000 }, // store for 30 days
+ // store: MongoStore.create({
+ // mongoUrl: config.mongo.uri,
+ // }),
+ })
+ )
+
+ server.use(parser.urlencoded({ extended: true }))
+
+ console.log(omit(config.oauth, 'enabled'))
+
+ server.use(grant.default.express(omit(config.oauth, 'enabled')))
+
+ server.get('/api/login', (req, res) => {
+ res.writeHead(200, { 'content-type': 'text/html' })
+ res.end(fs.readFileSync('./form.html', 'utf8'))
+ })
+
+ server.get('/api/hello', (req, res) => {
+ res.end(JSON.stringify((req as any).session.grant.response, null, 2))
+ })
+}
+
server.post('/api/v1/model', ...postUpload)
server.get('/api/v1/models', ...getModels)
diff --git a/backend/src/utils/config.ts b/backend/src/utils/config.ts
index bed68d72d..f8b5733f0 100644
--- a/backend/src/utils/config.ts
+++ b/backend/src/utils/config.ts
@@ -107,6 +107,12 @@ export interface Config {
deployment: Array
}
+ oauth: unknown & { enabled: boolean }
+
+ session: {
+ secret: string
+ }
+
ui: {
banner: {
enabled: boolean
@@ -210,6 +216,9 @@ const config: Config = {
defaultSchemas: _config.get('defaultSchemas'),
+ oauth: _config.get('oauth'),
+ session: _config.get('session'),
+
ui: _config.get('ui'),
}
From 77f65dd6ce12e116003b265a75a5f909cc912be7 Mon Sep 17 00:00:00 2001
From: a3957273 <89583054+a3957273@users.noreply.github.com>
Date: Tue, 16 May 2023 14:45:08 +0000
Subject: [PATCH 02/13] Update backend oauth implementation
---
backend/form.html | 30 --------------
backend/global.d.ts | 4 ++
backend/src/external/Authorisation.ts | 4 +-
.../Authorisation/AuthorisationDefault.ts} | 6 +--
.../Authorisation/AuthorisationOAuth.ts | 39 +++++++++++++++++++
backend/src/routes.ts | 24 ++++++------
backend/src/routes/v1/approvals.ts | 10 ++---
backend/src/routes/v1/deployment.ts | 8 ++--
backend/src/utils/user.ts | 4 +-
frontend/utils/fetcher.ts | 4 ++
10 files changed, 74 insertions(+), 59 deletions(-)
delete mode 100644 backend/form.html
rename backend/src/{utils/AuthorisationBase.ts => external/Authorisation/AuthorisationDefault.ts} (86%)
create mode 100644 backend/src/external/Authorisation/AuthorisationOAuth.ts
diff --git a/backend/form.html b/backend/form.html
deleted file mode 100644
index 48b79afdf..000000000
--- a/backend/form.html
+++ /dev/null
@@ -1,30 +0,0 @@
-
-
-
- Grant - Dynamic HTTP Override
-
-
-
-
-
diff --git a/backend/global.d.ts b/backend/global.d.ts
index f6b7e4b5b..43580b81a 100644
--- a/backend/global.d.ts
+++ b/backend/global.d.ts
@@ -6,6 +6,10 @@ declare namespace Express {
reqId: string
log: Logger
+
+ session: {
+ grant: any
+ }
}
interface Response {
diff --git a/backend/src/external/Authorisation.ts b/backend/src/external/Authorisation.ts
index 972954d9e..6986829ae 100644
--- a/backend/src/external/Authorisation.ts
+++ b/backend/src/external/Authorisation.ts
@@ -4,8 +4,8 @@
// TypeScript will ensure at build time that any updates made to the
// Authorisation layout are reflected in your class.
-import AuthorisationBase from '../utils/AuthorisationBase.js'
+import AuthorisationOAuth from './Authorisation/AuthorisationOAuth.js'
-class Authorisation extends AuthorisationBase {}
+class Authorisation extends AuthorisationOAuth {}
export default Authorisation
diff --git a/backend/src/utils/AuthorisationBase.ts b/backend/src/external/Authorisation/AuthorisationDefault.ts
similarity index 86%
rename from backend/src/utils/AuthorisationBase.ts
rename to backend/src/external/Authorisation/AuthorisationDefault.ts
index d60d8f5f0..00ae34a09 100644
--- a/backend/src/utils/AuthorisationBase.ts
+++ b/backend/src/external/Authorisation/AuthorisationDefault.ts
@@ -1,9 +1,9 @@
import { Request } from 'express'
-import { DeploymentDoc, ModelDoc, UserDoc, VersionDoc } from '../types/types.js'
-import { Model } from '../types/types.js'
+import { DeploymentDoc, ModelDoc, UserDoc, VersionDoc } from '../../types/types.js'
+import { Model } from '../../types/types.js'
-export default class AuthorisationBase {
+export default class AuthorisationDefault {
async getUserFromReq(req: Request) {
const userId = 'user'
const email = 'user@example.com'
diff --git a/backend/src/external/Authorisation/AuthorisationOAuth.ts b/backend/src/external/Authorisation/AuthorisationOAuth.ts
new file mode 100644
index 000000000..3f5a6bf8d
--- /dev/null
+++ b/backend/src/external/Authorisation/AuthorisationOAuth.ts
@@ -0,0 +1,39 @@
+import { Request } from 'express'
+
+import { DeploymentDoc, ModelDoc, UserDoc, VersionDoc } from '../../types/types.js'
+import { Model } from '../../types/types.js'
+
+export default class AuthorisationOAuth {
+ async getUserFromReq(req: Request) {
+ console.log(req.session)
+
+ if (!req.session?.grant?.response?.jwt) {
+ return {}
+ }
+
+ const userId = req.session.grant.response.jwt.id_token.payload['identities'][0].userId
+ const email = userId
+
+ const data = {}
+ const roles = ['user']
+
+ return {
+ userId,
+ email,
+ data,
+ roles,
+ }
+ }
+
+ async canUserSeeModel(_user: UserDoc, _model: Model | ModelDoc) {
+ return true
+ }
+
+ async canUserSeeVersion(_user: UserDoc, _model: VersionDoc) {
+ return true
+ }
+
+ async canUserSeeDeployment(_user: UserDoc, _deployment: DeploymentDoc) {
+ return true
+ }
+}
diff --git a/backend/src/routes.ts b/backend/src/routes.ts
index 8f7d977d4..455068bae 100644
--- a/backend/src/routes.ts
+++ b/backend/src/routes.ts
@@ -50,9 +50,6 @@ import { getUser } from './utils/user.js'
export const server = express()
-server.use(getUser)
-server.use(expressLogger)
-
if (config.oauth.enabled) {
server.use(
session({
@@ -60,25 +57,26 @@ if (config.oauth.enabled) {
resave: true,
saveUninitialized: true,
cookie: { maxAge: 30 * 24 * 60 * 60000 }, // store for 30 days
- // store: MongoStore.create({
- // mongoUrl: config.mongo.uri,
- // }),
+ store: MongoStore.create({
+ mongoUrl: config.mongo.uri,
+ }),
})
)
+}
- server.use(parser.urlencoded({ extended: true }))
-
- console.log(omit(config.oauth, 'enabled'))
+server.use(getUser)
+server.use(expressLogger)
+if (config.oauth.enabled) {
+ server.use(parser.urlencoded({ extended: true }))
server.use(grant.default.express(omit(config.oauth, 'enabled')))
server.get('/api/login', (req, res) => {
- res.writeHead(200, { 'content-type': 'text/html' })
- res.end(fs.readFileSync('./form.html', 'utf8'))
+ res.redirect('/api/connect/cognito/login')
})
- server.get('/api/hello', (req, res) => {
- res.end(JSON.stringify((req as any).session.grant.response, null, 2))
+ server.get('/api/grant', (req, res) => {
+ res.end(JSON.stringify(req.session.grant.response, null, 2))
})
}
diff --git a/backend/src/routes/v1/approvals.ts b/backend/src/routes/v1/approvals.ts
index 150a2a8c8..50f9f2d44 100644
--- a/backend/src/routes/v1/approvals.ts
+++ b/backend/src/routes/v1/approvals.ts
@@ -18,7 +18,7 @@ import { reviewedApproval } from '../../templates/reviewedApproval.js'
import { ApprovalCategory, ApprovalStates, DeploymentDoc, Entity, ModelDoc, VersionDoc } from '../../types/types.js'
import { getUserListFromEntityList, isUserInEntityList } from '../../utils/entity.js'
import { getDeploymentQueue } from '../../utils/queues.js'
-import { BadReq, Unauthorised } from '../../utils/result.js'
+import { BadReq, Forbidden } from '../../utils/result.js'
import { sendEmail } from '../../utils/smtp.js'
import { ensureUserRole, hasRole } from '../../utils/user.js'
@@ -37,10 +37,10 @@ export const getApprovals = [
if (filter === 'all') {
if (!hasRole(['admin'], req.user)) {
- return res.error(401, [
+ throw Forbidden(
{ code: 'unauthorised_admin_role_missing', roles: req.user.roles },
- 'Forbidden. Your user does not have the "admin" role',
- ])
+ 'Forbidden. Your user does not have the "admin" role'
+ )
}
} else {
req.log.info({ code: 'fetching_user_approvals' }, 'Getting approvals for user')
@@ -83,7 +83,7 @@ export const postApprovalResponse = [
const approval = await getApproval({ approvalId: id })
if (!(await isUserInEntityList(req.user, approval.approvers)) && !hasRole(['admin'], req.user)) {
- throw Unauthorised(
+ throw Forbidden(
{
code: 'unauthorised_to_approve',
approvalId: id,
diff --git a/backend/src/routes/v1/deployment.ts b/backend/src/routes/v1/deployment.ts
index 657e01f8a..fb23931a4 100644
--- a/backend/src/routes/v1/deployment.ts
+++ b/backend/src/routes/v1/deployment.ts
@@ -1,4 +1,3 @@
-import { ModelDoc } from '../../types/types.js'
import bodyParser from 'body-parser'
import { Request, Response } from 'express'
import { customAlphabet } from 'nanoid'
@@ -8,11 +7,12 @@ import { createDeployment, findDeploymentByUuid, findDeployments } from '../../s
import { findModelByUuid } from '../../services/model.js'
import { findSchemaByRef } from '../../services/schema.js'
import { findVersionById, findVersionByName } from '../../services/version.js'
+import { ModelDoc } from '../../types/types.js'
import { ApprovalStates, EntityKind } from '../../types/types.js'
import config from '../../utils/config.js'
import { isUserInEntityList, parseEntityList } from '../../utils/entity.js'
import { getClient } from '../../utils/minio.js'
-import { BadReq, Forbidden, NotFound, Unauthorised } from '../../utils/result.js'
+import { BadReq, Forbidden, NotFound } from '../../utils/result.js'
import { ensureUserRole } from '../../utils/user.js'
import { validateSchema } from '../../utils/validateSchema.js'
@@ -248,7 +248,7 @@ export const fetchRawModelFiles = [
if (!(await isUserInEntityList(req.user, deployment.metadata.contacts.owner))) {
const owners = deployment.metadata.contacts.owner.map((owner: any) => owner.id).join(', ')
- throw Unauthorised(
+ throw Forbidden(
{ deploymentOwner: deployment.metadata.contacts.owner },
`User is not authorised to download this file. Requester: ${req.user.id}, owners: ${owners}`
)
@@ -261,7 +261,7 @@ export const fetchRawModelFiles = [
}
if (deployment.managerApproved !== 'Accepted') {
- throw Unauthorised(
+ throw Forbidden(
{ approvalStatus: deployment.managerApproved },
'User is not authorised to download this file as it has not been approved.'
)
diff --git a/backend/src/utils/user.ts b/backend/src/utils/user.ts
index 1e8feb064..73ce78ec7 100644
--- a/backend/src/utils/user.ts
+++ b/backend/src/utils/user.ts
@@ -89,14 +89,14 @@ export function hasRole(roles: Array | string, user: UserDoc) {
export function ensureUserRole(roles: Array | string) {
return function ensureUserRoleMiddleware(req: Request, _res: Response, next: NextFunction) {
if (!req.user) {
- throw Forbidden({}, `Unable to authenticate request`)
+ throw Unauthorised({}, `Unable to authenticate request`)
}
const arrayRoles = typeof roles === 'string' ? [roles] : roles
for (const role of arrayRoles) {
if (!req.user.roles.includes(role)) {
- throw Unauthorised({ requestedRole: role, currentRoles: req.user.roles }, `You do not have the '${role}' role`)
+ throw Forbidden({ requestedRole: role, currentRoles: req.user.roles }, `You do not have the '${role}' role`)
}
}
diff --git a/frontend/utils/fetcher.ts b/frontend/utils/fetcher.ts
index 0b814b72c..18bea5394 100644
--- a/frontend/utils/fetcher.ts
+++ b/frontend/utils/fetcher.ts
@@ -29,6 +29,10 @@ export const fetcher = async (input: RequestInfo, init: RequestInit) => {
// If the status code is not in the range 200-299,
// we still try to parse and throw it.
if (!res.ok) {
+ if (res.status === 401) {
+ window.location.href = '/api/login'
+ }
+
const error: ErrorInfo = {
...new Error('An error occurred while fetching the data.'),
info: await res.json(),
From f1cde19144611c2ae2cf3f9958534c602dc4555c Mon Sep 17 00:00:00 2001
From: a3957273 <89583054+a3957273@users.noreply.github.com>
Date: Tue, 16 May 2023 15:04:37 +0000
Subject: [PATCH 03/13] Tidy up grant config
---
backend/config/default.cjs | 31 ++++++++++++++++++-------------
backend/global.d.ts | 1 +
backend/src/routes.ts | 12 ++++++------
backend/src/utils/config.ts | 7 ++++++-
4 files changed, 31 insertions(+), 20 deletions(-)
diff --git a/backend/config/default.cjs b/backend/config/default.cjs
index 102934125..8ba6fc89d 100644
--- a/backend/config/default.cjs
+++ b/backend/config/default.cjs
@@ -147,20 +147,25 @@ module.exports = {
oauth: {
enabled: false,
+ provider: 'cognito',
+
+ grant: {
+ // Grant configuration options, provide any option from:
+ // https://www.npmjs.com/package/grant
+ defaults: {
+ origin: '',
+ prefix: '/api/connect',
+ transport: 'session',
+ },
- defaults: {
- origin: '',
- prefix: '/api/connect',
- transport: 'session',
- },
-
- cognito: {
- key: '',
- secret: '',
- dynamic: ['scope'],
- response: ['tokens', 'raw', 'jwt'],
- callback: '/',
- subdomain: '',
+ cognito: {
+ key: '',
+ secret: '',
+ dynamic: ['scope'],
+ response: ['tokens', 'raw', 'jwt'],
+ callback: '/',
+ subdomain: '',
+ },
},
},
diff --git a/backend/global.d.ts b/backend/global.d.ts
index 43580b81a..ca5f0ca9c 100644
--- a/backend/global.d.ts
+++ b/backend/global.d.ts
@@ -8,6 +8,7 @@ declare namespace Express {
log: Logger
session: {
+ destroy: Function
grant: any
}
}
diff --git a/backend/src/routes.ts b/backend/src/routes.ts
index 455068bae..c44dc273c 100644
--- a/backend/src/routes.ts
+++ b/backend/src/routes.ts
@@ -2,9 +2,7 @@ import parser from 'body-parser'
import MongoStore from 'connect-mongo'
import express from 'express'
import session from 'express-session'
-import fs from 'fs'
import grant from 'grant'
-import { omit } from 'lodash-es'
import { getApplicationLogs, getItemLogs } from './routes/v1/admin.js'
import { getApprovals, getNumApprovals, postApprovalResponse } from './routes/v1/approvals.js'
@@ -69,14 +67,16 @@ server.use(expressLogger)
if (config.oauth.enabled) {
server.use(parser.urlencoded({ extended: true }))
- server.use(grant.default.express(omit(config.oauth, 'enabled')))
+ server.use(grant.default.express(config.oauth.grant))
server.get('/api/login', (req, res) => {
- res.redirect('/api/connect/cognito/login')
+ res.redirect(`/api/connect/${config.oauth.provider}/login`)
})
- server.get('/api/grant', (req, res) => {
- res.end(JSON.stringify(req.session.grant.response, null, 2))
+ server.get('/api/logout', (req, res) => {
+ req.session.destroy(function (err) {
+ res.redirect('/')
+ })
})
}
diff --git a/backend/src/utils/config.ts b/backend/src/utils/config.ts
index f8b5733f0..0a22cfb25 100644
--- a/backend/src/utils/config.ts
+++ b/backend/src/utils/config.ts
@@ -1,4 +1,5 @@
import _config from 'config'
+import { GrantConfig, GrantOptions } from 'grant'
interface DefaultSchema {
name: string
@@ -107,7 +108,11 @@ export interface Config {
deployment: Array
}
- oauth: unknown & { enabled: boolean }
+ oauth: {
+ enabled: boolean
+ provider: string
+ grant: GrantOptions | GrantConfig
+ }
session: {
secret: string
From 90064b9e0e33a017e8022dc8c727afe94ba4fd7a Mon Sep 17 00:00:00 2001
From: ARADDCC012 <110473008+ARADDCC012@users.noreply.github.com>
Date: Tue, 16 May 2023 15:29:50 +0000
Subject: [PATCH 04/13] Added UI login redirects
---
frontend/data/api.ts | 10 +++++++++-
frontend/src/Wrapper.tsx | 9 +++++++++
frontend/utils/fetcher.ts | 8 ++++++--
frontend/utils/loginUtils.ts | 3 +++
4 files changed, 27 insertions(+), 3 deletions(-)
create mode 100644 frontend/utils/loginUtils.ts
diff --git a/frontend/data/api.ts b/frontend/data/api.ts
index 2f233e631..b35f88c4e 100644
--- a/frontend/data/api.ts
+++ b/frontend/data/api.ts
@@ -1,5 +1,7 @@
+import { redirectToLoginPage } from 'utils/loginUtils'
+
export async function fetchEndpoint(url: string, method: string, data?: unknown) {
- return fetch(url, {
+ const response = await fetch(url, {
method,
headers: {
...(!data && data !== 0 && { 'Content-Length': '0' }),
@@ -7,6 +9,12 @@ export async function fetchEndpoint(url: string, method: string, data?: unknown)
},
...(data !== undefined && { body: JSON.stringify(data) }),
})
+
+ if (response.status === 401) {
+ redirectToLoginPage()
+ }
+
+ return response
}
export async function getEndpoint(url: string) {
diff --git a/frontend/src/Wrapper.tsx b/frontend/src/Wrapper.tsx
index e14131edd..3a20faa77 100644
--- a/frontend/src/Wrapper.tsx
+++ b/frontend/src/Wrapper.tsx
@@ -6,6 +6,7 @@ import DashboardIcon from '@mui/icons-material/DashboardTwoTone'
import FileUploadIcon from '@mui/icons-material/FileUploadTwoTone'
import LinkIcon from '@mui/icons-material/LinkTwoTone'
import ListAltIcon from '@mui/icons-material/ListAlt'
+import LogoutIcon from '@mui/icons-material/LogoutTwoTone'
import MenuIcon from '@mui/icons-material/MenuTwoTone'
import SchemaIcon from '@mui/icons-material/SchemaTwoTone'
import Settings from '@mui/icons-material/SettingsTwoTone'
@@ -244,6 +245,14 @@ export default function Wrapper({ title, page, children }: WrapperProps): ReactE
Settings
+
+
+
>
diff --git a/frontend/utils/fetcher.ts b/frontend/utils/fetcher.ts
index 18bea5394..470d62d99 100644
--- a/frontend/utils/fetcher.ts
+++ b/frontend/utils/fetcher.ts
@@ -1,3 +1,5 @@
+import { redirectToLoginPage } from 'utils/loginUtils'
+
export type ErrorInfo = Error & {
info: {
message: string
@@ -12,6 +14,9 @@ export const textFetcher = async (input: RequestInfo, init: RequestInit) => {
// If the status code is not in the range 200-299,
// we still try to parse and throw it.
if (!res.ok) {
+ if (res.status === 401) {
+ redirectToLoginPage()
+ }
const error: ErrorInfo = {
...new Error('An error occurred while fetching the data.'),
info: await res.json(),
@@ -30,9 +35,8 @@ export const fetcher = async (input: RequestInfo, init: RequestInit) => {
// we still try to parse and throw it.
if (!res.ok) {
if (res.status === 401) {
- window.location.href = '/api/login'
+ redirectToLoginPage()
}
-
const error: ErrorInfo = {
...new Error('An error occurred while fetching the data.'),
info: await res.json(),
diff --git a/frontend/utils/loginUtils.ts b/frontend/utils/loginUtils.ts
new file mode 100644
index 000000000..9085c497a
--- /dev/null
+++ b/frontend/utils/loginUtils.ts
@@ -0,0 +1,3 @@
+export const redirectToLoginPage = () => {
+ window.location.href = '/api/login'
+}
From 626134ae786a1ebde4e5e4713194bea21ba46696 Mon Sep 17 00:00:00 2001
From: JR40159 <126243293+JR40159@users.noreply.github.com>
Date: Thu, 18 May 2023 08:54:17 +0000
Subject: [PATCH 05/13] Fix backend build issues
---
backend/src/external/Authorisation/AuthorisationOAuth.ts | 7 ++++++-
backend/src/services/logs.ts | 2 +-
backend/src/services/user.ts | 2 +-
3 files changed, 8 insertions(+), 3 deletions(-)
diff --git a/backend/src/external/Authorisation/AuthorisationOAuth.ts b/backend/src/external/Authorisation/AuthorisationOAuth.ts
index 3f5a6bf8d..8f92b683f 100644
--- a/backend/src/external/Authorisation/AuthorisationOAuth.ts
+++ b/backend/src/external/Authorisation/AuthorisationOAuth.ts
@@ -8,7 +8,12 @@ export default class AuthorisationOAuth {
console.log(req.session)
if (!req.session?.grant?.response?.jwt) {
- return {}
+ return {
+ userId: undefined,
+ email: undefined ,
+ data: undefined,
+ roles: undefined,
+ }
}
const userId = req.session.grant.response.jwt.id_token.payload['identities'][0].userId
diff --git a/backend/src/services/logs.ts b/backend/src/services/logs.ts
index 317a40f5a..d8daef04a 100644
--- a/backend/src/services/logs.ts
+++ b/backend/src/services/logs.ts
@@ -58,7 +58,7 @@ export interface GetLogsArgs {
buildId?: string
approvalId?: string
}
-export async function getLogs({ after, before, level, types, search, isRegex, buildId, approvalId }: GetLogsArgs) {
+export async function getLogs({ after, before, level, types, search, isRegex, buildId, approvalId }: GetLogsArgs): Promise {
await connectToMongoose()
const { db } = mongoose.connection
diff --git a/backend/src/services/user.ts b/backend/src/services/user.ts
index cfcffc846..f7f6f2bb1 100644
--- a/backend/src/services/user.ts
+++ b/backend/src/services/user.ts
@@ -30,7 +30,7 @@ interface FindAndUpdateUserArgs {
userId?: string
email?: string
data?: any
- roles: Array
+ roles?: Array
}
export async function findAndUpdateUser({ userId, email, data, roles }: FindAndUpdateUserArgs) {
From 2a367ad2a9254557bd3864c77da9edcea4014290 Mon Sep 17 00:00:00 2001
From: ARADDCC013 <111884477+ARADDCC013@users.noreply.github.com>
Date: Fri, 19 May 2023 07:37:29 +0000
Subject: [PATCH 06/13] update helm oauth
---
.../templates/bailo/bailo.configmap.yaml | 19 +++++++++++++++++++
infrastructure/helm/bailo/values.yaml | 16 ++++++++++++++++
2 files changed, 35 insertions(+)
diff --git a/infrastructure/helm/bailo/templates/bailo/bailo.configmap.yaml b/infrastructure/helm/bailo/templates/bailo/bailo.configmap.yaml
index 80590dcfc..ce47c2236 100644
--- a/infrastructure/helm/bailo/templates/bailo/bailo.configmap.yaml
+++ b/infrastructure/helm/bailo/templates/bailo/bailo.configmap.yaml
@@ -22,6 +22,25 @@ data:
s2i: {
path: '/s2i/s2i',
},
+ session: {
+ secret: '{{ .Values.cookie.secret }}',
+ },
+ oauth: {
+ enabled: {{ .Values.oauth.enabled }},
+ grant: {
+ defaults: {
+ origin: '{{ .Values.oauth.origin }}',
+ },
+ cognito: {
+ key: '{{ .Values.oauth.cognito.key }}',
+ secret: '{{ .Values.oauth.cognito.secret }}',
+ dynamic: {{ .Values.oauth.cognito.dynamic }},
+ response: {{ .Values.oauth.cognito.response }},
+ callback: '{{ .Values.oauth.cognito.callback }}',
+ subdomain: '{{ .Values.oauth.cognito.subdomain }}',
+ },
+ },
+ },
build: {
environment: '{{ .Values.config.build.environment }}',
openshift: {
diff --git a/infrastructure/helm/bailo/values.yaml b/infrastructure/helm/bailo/values.yaml
index b53cda154..1aa6ebd4d 100644
--- a/infrastructure/helm/bailo/values.yaml
+++ b/infrastructure/helm/bailo/values.yaml
@@ -63,6 +63,22 @@ route:
enabled: false # false for k8s | aws
appPublicRoute: "route-url" # can also be used for aws loadbalancer url
+# Oauth | Congnito
+# using bash for generating cookie secret example
+# dd if=/dev/urandom bs=32 count=1 2>/dev/null | base64 | tr -d -- '\n' | tr -- '+/' '-_'; echo
+cookie:
+ secret: 'somerandomstring12341234567890AB'
+oauth:
+ enabled: false
+ origin: 'bailo fqdn'
+ cognito:
+ key: 'cognito app client id'
+ secret: 'cognito app client secret'
+ dynamic: [] # example 'scope'
+ response: [] #example 'tokens', 'raw', 'jwt'
+ callback: '' # exmaple /
+ subdomain: 'cognito domain'
+
# Used for aws storage
aws:
enabled: false
From 6c43176dadedd3026d2c4f118587cadd03b8f556 Mon Sep 17 00:00:00 2001
From: a3957273 <89583054+a3957273@users.noreply.github.com>
Date: Sun, 4 Jun 2023 13:43:21 +0000
Subject: [PATCH 07/13] Default to stupid authorisation
---
backend/src/external/Authorisation.ts | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/backend/src/external/Authorisation.ts b/backend/src/external/Authorisation.ts
index 6986829ae..76e70b0b1 100644
--- a/backend/src/external/Authorisation.ts
+++ b/backend/src/external/Authorisation.ts
@@ -4,8 +4,8 @@
// TypeScript will ensure at build time that any updates made to the
// Authorisation layout are reflected in your class.
-import AuthorisationOAuth from './Authorisation/AuthorisationOAuth.js'
+import AuthorisationDefault from './Authorisation/AuthorisationDefault'
-class Authorisation extends AuthorisationOAuth {}
+class Authorisation extends AuthorisationDefault {}
export default Authorisation
From b78d9be75833149fa5f9c3e9972dbead51b3752c Mon Sep 17 00:00:00 2001
From: a3957273 <89583054+a3957273@users.noreply.github.com>
Date: Sun, 4 Jun 2023 13:51:48 +0000
Subject: [PATCH 08/13] Fix OAuth import
---
backend/src/external/Authorisation.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/backend/src/external/Authorisation.ts b/backend/src/external/Authorisation.ts
index 76e70b0b1..a71e792ce 100644
--- a/backend/src/external/Authorisation.ts
+++ b/backend/src/external/Authorisation.ts
@@ -4,7 +4,7 @@
// TypeScript will ensure at build time that any updates made to the
// Authorisation layout are reflected in your class.
-import AuthorisationDefault from './Authorisation/AuthorisationDefault'
+import AuthorisationDefault from './Authorisation/AuthorisationDefault.js'
class Authorisation extends AuthorisationDefault {}
From e1837e53ed1fef1cb3240c5f6fb7ab32aa3e84c9 Mon Sep 17 00:00:00 2001
From: a3957273 <89583054+a3957273@users.noreply.github.com>
Date: Sun, 4 Jun 2023 13:57:58 +0000
Subject: [PATCH 09/13] Fix OAuth styling
---
backend/src/external/Authorisation/AuthorisationOAuth.ts | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/backend/src/external/Authorisation/AuthorisationOAuth.ts b/backend/src/external/Authorisation/AuthorisationOAuth.ts
index 8f92b683f..1bcb2dacc 100644
--- a/backend/src/external/Authorisation/AuthorisationOAuth.ts
+++ b/backend/src/external/Authorisation/AuthorisationOAuth.ts
@@ -9,10 +9,10 @@ export default class AuthorisationOAuth {
if (!req.session?.grant?.response?.jwt) {
return {
- userId: undefined,
- email: undefined ,
- data: undefined,
- roles: undefined,
+ userId: undefined,
+ email: undefined,
+ data: undefined,
+ roles: undefined,
}
}
From e61a8fd516a58f70b66ea47c18b1dd515843a755 Mon Sep 17 00:00:00 2001
From: a3957273 <89583054+a3957273@users.noreply.github.com>
Date: Sun, 4 Jun 2023 13:58:08 +0000
Subject: [PATCH 10/13] Fix tsconfig styling
---
backend/tsconfig.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/backend/tsconfig.json b/backend/tsconfig.json
index 2da1cb15f..312496e23 100644
--- a/backend/tsconfig.json
+++ b/backend/tsconfig.json
@@ -10,7 +10,7 @@
"outDir": "dist",
"resolveJsonModule": true,
"noImplicitAny": false,
- "composite": true,
+ "composite": true
},
"include": ["global.d.ts", "src/**/*", "src/**/*.json", "package.json", "package-lock.json"],
"ts-node": {
From 9f89ce959673e30b7520bdc474033fc895e9f216 Mon Sep 17 00:00:00 2001
From: a3957273 <89583054+a3957273@users.noreply.github.com>
Date: Sun, 4 Jun 2023 14:22:15 +0000
Subject: [PATCH 11/13] Define proper function type for session destroyer
---
backend/global.d.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/backend/global.d.ts b/backend/global.d.ts
index ca5f0ca9c..4331d52bb 100644
--- a/backend/global.d.ts
+++ b/backend/global.d.ts
@@ -8,7 +8,7 @@ declare namespace Express {
log: Logger
session: {
- destroy: Function
+ destroy: () => void
grant: any
}
}
From 4d1eb3d515d427a8a05ce31bb1e5680f59c019e1 Mon Sep 17 00:00:00 2001
From: a3957273 <89583054+a3957273@users.noreply.github.com>
Date: Sun, 4 Jun 2023 15:08:12 +0000
Subject: [PATCH 12/13] Use timeout instead of javascript function
---
frontend/cypress/e2e/model.cy.ts | 13 ++++++++-----
frontend/cypress/scripts/pullContainer.sh | 3 +++
2 files changed, 11 insertions(+), 5 deletions(-)
create mode 100755 frontend/cypress/scripts/pullContainer.sh
diff --git a/frontend/cypress/e2e/model.cy.ts b/frontend/cypress/e2e/model.cy.ts
index b379a6548..42bcb282d 100644
--- a/frontend/cypress/e2e/model.cy.ts
+++ b/frontend/cypress/e2e/model.cy.ts
@@ -110,11 +110,11 @@ describe('Model with code and binary files', () => {
cy.get('[data-test=submitButton]').click()
cy.log('Checking URL has been updated')
- cy.fixture('deployment.json').then((deploymentMetadata) => {
+ cy.fixture('deployment.json').then({ timeout: 60000 }, (deploymentMetadata) => {
cy.url({ timeout: 10000 })
.as('deploymentUrl')
.should('contain', `/deployment/${convertNameToUrlFormat(deploymentMetadata.highLevelDetails.name)}`)
- .then(async (url) => {
+ .then({ timeout: 60000 }, async (url) => {
deploymentUuid = getUuidFromUrl(url)
cy.log('Navigating to review page')
@@ -143,14 +143,17 @@ describe('Model with code and binary files', () => {
cy.get('[data-test=dockerPassword]').should('not.contain.text', 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxx')
cy.get('[data-test=dockerPassword]')
.invoke('text')
- .then((dockerPassword) => {
- cy.fixture('minimal_metadata.json').then((modelMetadata) => {
+ .then({ timeout: 60000 }, (dockerPassword) => {
+ cy.fixture('minimal_metadata.json').then({ timeout: 60000 }, async (modelMetadata) => {
const imageName = `${registryUrl}/${deploymentUuid}/${modelUuid}:${modelMetadata.highLevelDetails.modelCardVersion}`
cy.exec(`docker login ${registryUrl} -u ${'user'} -p ${dockerPassword}`)
- cy.exec(`docker pull ${imageName}`)
+
+ cy.exec(`cypress/scripts/pullContainer.sh "${imageName}"`)
+
cy.exec(`cypress/scripts/startContainer.sh "${imageName}"`)
// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(5000)
+
cy.request('POST', `${containerUrl}/predict`, {
jsonData: { data: ['should be returned backwards'] },
}).then((response) => {
diff --git a/frontend/cypress/scripts/pullContainer.sh b/frontend/cypress/scripts/pullContainer.sh
new file mode 100755
index 000000000..949968e96
--- /dev/null
+++ b/frontend/cypress/scripts/pullContainer.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+timeout 2m bash -c "until docker pull $1; do sleep 1; done"
\ No newline at end of file
From b72ebc03e80187628dcc6daa4db0c13508c35c2d Mon Sep 17 00:00:00 2001
From: a3957273 <89583054+a3957273@users.noreply.github.com>
Date: Sun, 4 Jun 2023 15:15:30 +0000
Subject: [PATCH 13/13] Add callback type
---
backend/global.d.ts | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/backend/global.d.ts b/backend/global.d.ts
index 4331d52bb..e02469319 100644
--- a/backend/global.d.ts
+++ b/backend/global.d.ts
@@ -1,5 +1,7 @@
declare module 'dev-null'
+type callback = (err: string | undefined) => void
+
declare namespace Express {
interface Request {
user: UserDoc
@@ -8,7 +10,7 @@ declare namespace Express {
log: Logger
session: {
- destroy: () => void
+ destroy: (callback) => void
grant: any
}
}