diff --git a/package-lock.json b/package-lock.json index 12dfd234..d2c6affb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3697,8 +3697,7 @@ "node_modules/@firebase/app-check-interop-types": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/@firebase/app-check-interop-types/-/app-check-interop-types-0.3.0.tgz", - "integrity": "sha512-xAxHPZPIgFXnI+vb4sbBjZcde7ZluzPPaSK7Lx3/nmuVk4TjZvnL8ONnkd4ERQKL8WePQySU+pRcWkh8rDf5Sg==", - "peer": true + "integrity": "sha512-xAxHPZPIgFXnI+vb4sbBjZcde7ZluzPPaSK7Lx3/nmuVk4TjZvnL8ONnkd4ERQKL8WePQySU+pRcWkh8rDf5Sg==" }, "node_modules/@firebase/app-check-types": { "version": "0.5.0", @@ -3722,8 +3721,7 @@ "node_modules/@firebase/app-types": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.0.tgz", - "integrity": "sha512-AeweANOIo0Mb8GiYm3xhTEBVCmPwTYAu9Hcd2qSkLuga/6+j9b1Jskl5bpiSQWy9eJ/j5pavxj6eYogmnuzm+Q==", - "peer": true + "integrity": "sha512-AeweANOIo0Mb8GiYm3xhTEBVCmPwTYAu9Hcd2qSkLuga/6+j9b1Jskl5bpiSQWy9eJ/j5pavxj6eYogmnuzm+Q==" }, "node_modules/@firebase/auth": { "version": "1.6.0", @@ -3767,8 +3765,7 @@ "node_modules/@firebase/auth-interop-types": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.2.1.tgz", - "integrity": "sha512-VOaGzKp65MY6P5FI84TfYKBXEPi6LmOCSMMzys6o2BN2LOsqy7pCuZCup7NYnfbk5OkkQKzvIfHOzTm0UDpkyg==", - "peer": true + "integrity": "sha512-VOaGzKp65MY6P5FI84TfYKBXEPi6LmOCSMMzys6o2BN2LOsqy7pCuZCup7NYnfbk5OkkQKzvIfHOzTm0UDpkyg==" }, "node_modules/@firebase/auth-types": { "version": "0.12.0", @@ -3784,7 +3781,6 @@ "version": "0.6.5", "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.6.5.tgz", "integrity": "sha512-2tVDk1ixi12sbDmmfITK8lxSjmcb73BMF6Qwc3U44hN/J1Fi1QY/Hnnb6klFlbB9/G16a3J3d4nXykye2EADTw==", - "peer": true, "dependencies": { "@firebase/util": "1.9.4", "tslib": "^2.1.0" @@ -3794,7 +3790,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/@firebase/database/-/database-1.0.3.tgz", "integrity": "sha512-9fjqLt9JzL46gw9+NRqsgQEMjgRwfd8XtzcKqG+UYyhVeFCdVRQ0Wp6Dw/dvYHnbH5vNEKzNv36dcB4p+PIAAA==", - "peer": true, "dependencies": { "@firebase/app-check-interop-types": "0.3.0", "@firebase/auth-interop-types": "0.2.1", @@ -3809,7 +3804,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-1.0.3.tgz", "integrity": "sha512-7tHEOcMbK5jJzHWyphPux4osogH/adWwncxdMxdBpB9g1DNIyY4dcz1oJdlkXGM/i/AjUBesZsd5CuwTRTBNTw==", - "peer": true, "dependencies": { "@firebase/component": "0.6.5", "@firebase/database": "1.0.3", @@ -3823,7 +3817,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-1.0.1.tgz", "integrity": "sha512-Tmcmx5XgiI7UVF/4oGg2P3AOTfq3WKEPsm2yf+uXtN7uG/a4WTWhVMrXGYRY2ZUL1xPxv9V33wQRJ+CcrUhVXw==", - "peer": true, "dependencies": { "@firebase/app-types": "0.9.0", "@firebase/util": "1.9.4" @@ -3961,7 +3954,6 @@ "version": "0.4.0", "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.4.0.tgz", "integrity": "sha512-eRKSeykumZ5+cJPdxxJRgAC3G5NknY2GwEbKfymdnXtnT0Ucm4pspfR6GT4MUQEDuJwRVbVcSx85kgJulMoFFA==", - "peer": true, "dependencies": { "tslib": "^2.1.0" } @@ -4127,7 +4119,6 @@ "version": "1.9.4", "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.9.4.tgz", "integrity": "sha512-WLonYmS1FGHT97TsUmRN3qnTh5TeeoJp1Gg5fithzuAgdZOUtsYECfy7/noQ3llaguios8r5BuXSEiK82+UrxQ==", - "peer": true, "dependencies": { "tslib": "^2.1.0" } @@ -4172,6 +4163,21 @@ "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.1.tgz", "integrity": "sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==" }, + "node_modules/@google-cloud/firestore": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-7.3.0.tgz", + "integrity": "sha512-2IftQLAbCuVp0nTd3neeu+d3OYIegJpV/V9R4USQj51LzJcXPe8h8jZ7j3+svSNhJVGy6JsN0T1QqlJdMDhTwg==", + "optional": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "functional-red-black-tree": "^1.0.1", + "google-gax": "^4.0.4", + "protobufjs": "^7.2.5" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@google-cloud/paginator": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/@google-cloud/paginator/-/paginator-5.0.0.tgz", @@ -6438,7 +6444,6 @@ "version": "0.7.10", "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.10.tgz", "integrity": "sha512-CAqDfoaQ8ykFd9zqBDn4k6iWT9loLAlc2ETmDFS9JCD70gDcnA4L3AFEo2iV7KyAtAAHFW9ftq1Fz+Vsgq80RQ==", - "peer": true, "dependencies": { "lodash.camelcase": "^4.3.0", "long": "^5.0.0", @@ -6455,8 +6460,7 @@ "node_modules/@grpc/proto-loader/node_modules/long": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", - "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==", - "peer": true + "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" }, "node_modules/@hicsail/signlab-gateway": { "resolved": "packages/gateway", @@ -16953,7 +16957,6 @@ "version": "0.11.4", "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", - "peer": true, "dependencies": { "websocket-driver": ">=0.5.1" }, @@ -17259,6 +17262,47 @@ "@firebase/util": "1.9.4" } }, + "node_modules/firebase-admin": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/firebase-admin/-/firebase-admin-12.0.0.tgz", + "integrity": "sha512-wBrrSSsKV++/+O8E7O/C7/wL0nbG/x4Xv4yatz/+sohaZ+LsnWtYUcrd3gZutO86hLpDex7xgyrkKbgulmtVyQ==", + "dependencies": { + "@fastify/busboy": "^1.2.1", + "@firebase/database-compat": "^1.0.2", + "@firebase/database-types": "^1.0.0", + "@types/node": "^20.10.3", + "jsonwebtoken": "^9.0.0", + "jwks-rsa": "^3.0.1", + "node-forge": "^1.3.1", + "uuid": "^9.0.0" + }, + "engines": { + "node": ">=14" + }, + "optionalDependencies": { + "@google-cloud/firestore": "^7.1.0", + "@google-cloud/storage": "^7.7.0" + } + }, + "node_modules/firebase-admin/node_modules/@fastify/busboy": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-1.2.1.tgz", + "integrity": "sha512-7PQA7EH43S0CxcOa9OeAnaeA0oQ+e/DHNPZwSQM9CQHW76jle5+OvLdibRp/Aafs9KXbLhxyjOTkRjWUbQEd3Q==", + "dependencies": { + "text-decoding": "^1.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/firebase-admin/node_modules/@types/node": { + "version": "20.11.17", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.17.tgz", + "integrity": "sha512-QmgQZGWu1Yw9TDyAP9ZzpFJKynYNeOvwMJmaxABfieQoVoiVOS6MN1WSpqpRcbeA5+RW82kraAVxCCJg+780Qw==", + "dependencies": { + "undici-types": "~5.26.4" + } + }, "node_modules/firebaseui": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/firebaseui/-/firebaseui-6.1.0.tgz", @@ -17526,6 +17570,12 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, + "node_modules/functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", + "optional": true + }, "node_modules/functions-have-names": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", @@ -17792,6 +17842,68 @@ "node": ">=14" } }, + "node_modules/google-gax": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-4.3.1.tgz", + "integrity": "sha512-qpSfslpwqToIgQ+Tf3MjWIDjYK4UFIZ0uz6nLtttlW9N1NQA4PhGf9tlGo6KDYJ4rgL2w4CjXVd0z5yeNpN/Iw==", + "optional": true, + "dependencies": { + "@grpc/grpc-js": "~1.10.0", + "@grpc/proto-loader": "^0.7.0", + "@types/long": "^4.0.0", + "abort-controller": "^3.0.0", + "duplexify": "^4.0.0", + "google-auth-library": "^9.3.0", + "node-fetch": "^2.6.1", + "object-hash": "^3.0.0", + "proto3-json-serializer": "^2.0.0", + "protobufjs": "7.2.6", + "retry-request": "^7.0.0", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/google-gax/node_modules/@grpc/grpc-js": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.10.0.tgz", + "integrity": "sha512-tx+eoEsqkMkLCHR4OOplwNIaJ7SVZWzeVKzEMBz8VR+TbssgBYOP4a0P+KQiQ6LaTG4SGaIEu7YTS8xOmkOWLA==", + "optional": true, + "dependencies": { + "@grpc/proto-loader": "^0.7.8", + "@types/node": ">=12.12.47" + }, + "engines": { + "node": "^8.13.0 || >=10.10.0" + } + }, + "node_modules/google-gax/node_modules/duplexify": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.2.tgz", + "integrity": "sha512-fz3OjcNCHmRP12MJoZMPglx8m4rrFP8rovnk4vT8Fs+aonZoCwGg10dSsQsfP/E62eZcPTMSMP6686fu9Qlqtw==", + "optional": true, + "dependencies": { + "end-of-stream": "^1.4.1", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1", + "stream-shift": "^1.0.0" + } + }, + "node_modules/google-gax/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "optional": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/gopd": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", @@ -18215,8 +18327,7 @@ "node_modules/http-parser-js": { "version": "0.5.8", "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", - "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==", - "peer": true + "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==" }, "node_modules/http-proxy-agent": { "version": "5.0.0", @@ -20720,7 +20831,6 @@ "version": "4.14.6", "resolved": "https://registry.npmjs.org/jose/-/jose-4.14.6.tgz", "integrity": "sha512-EqJPEUlZD0/CSUMubKtMaYUOtWe91tZXTWMJZoKSbLk+KtdhNdcvppH8lA9XwVu2V4Ailvsj0GBZJ2ZwDjfesQ==", - "dev": true, "funding": { "url": "https://github.com/sponsors/panva" } @@ -21032,6 +21142,22 @@ "safe-buffer": "^5.0.1" } }, + "node_modules/jwks-rsa": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jwks-rsa/-/jwks-rsa-3.1.0.tgz", + "integrity": "sha512-v7nqlfezb9YfHHzYII3ef2a2j1XnGeSE/bK3WfumaYCqONAIstJbrEGapz4kadScZzEt7zYCN7bucj8C0Mv/Rg==", + "dependencies": { + "@types/express": "^4.17.17", + "@types/jsonwebtoken": "^9.0.2", + "debug": "^4.3.4", + "jose": "^4.14.6", + "limiter": "^1.1.5", + "lru-memoizer": "^2.2.0" + }, + "engines": { + "node": ">=14" + } + }, "node_modules/jws": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", @@ -21122,6 +21248,11 @@ "node": ">= 0.8.0" } }, + "node_modules/limiter": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz", + "integrity": "sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA==" + }, "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", @@ -21186,8 +21317,12 @@ "node_modules/lodash.camelcase": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", - "peer": true + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" + }, + "node_modules/lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==" }, "node_modules/lodash.debounce": { "version": "4.0.8", @@ -21381,6 +21516,29 @@ "node": ">=12" } }, + "node_modules/lru-memoizer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/lru-memoizer/-/lru-memoizer-2.2.0.tgz", + "integrity": "sha512-QfOZ6jNkxCcM/BkIPnFsqDhtrazLRsghi9mBwFAzol5GCvj4EkFT899Za3+QwikCg5sRX8JstioBDwOxEyzaNw==", + "dependencies": { + "lodash.clonedeep": "^4.5.0", + "lru-cache": "~4.0.0" + } + }, + "node_modules/lru-memoizer/node_modules/lru-cache": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.0.2.tgz", + "integrity": "sha512-uQw9OqphAGiZhkuPlpFGmdTU2tEuhxTourM/19qGJrxBPHAr/f8BT1a0i/lOclESnGatdJG/UCkP9kZB/Lh1iw==", + "dependencies": { + "pseudomap": "^1.0.1", + "yallist": "^2.0.0" + } + }, + "node_modules/lru-memoizer/node_modules/yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==" + }, "node_modules/lz-string": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", @@ -22105,6 +22263,14 @@ "webidl-conversions": "^3.0.0" } }, + "node_modules/node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "engines": { + "node": ">= 6.13.0" + } + }, "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -22171,6 +22337,15 @@ "node": ">=0.10.0" } }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "optional": true, + "engines": { + "node": ">= 6" + } + }, "node_modules/object-inspect": { "version": "1.12.3", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", @@ -22916,12 +23091,23 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "node_modules/proto3-json-serializer": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-2.0.1.tgz", + "integrity": "sha512-8awBvjO+FwkMd6gNoGFZyqkHZXCFd54CIYTb6De7dPaufGJ2XNW+QUNqbMr8MaAocMdb+KpsD4rxEOaTBDCffA==", + "optional": true, + "dependencies": { + "protobufjs": "^7.2.5" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/protobufjs": { "version": "7.2.6", "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.2.6.tgz", "integrity": "sha512-dgJaEDDL6x8ASUZ1YqWciTRrdOuYNzoOf27oHNfdyvKqHr5i0FV7FSLU+aIeFjyFgVxrpTOtQUi0BLLBymZaBw==", "hasInstallScript": true, - "peer": true, "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", @@ -22943,8 +23129,7 @@ "node_modules/protobufjs/node_modules/long": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", - "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==", - "peer": true + "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" }, "node_modules/proxy-addr": { "version": "2.0.7", @@ -22963,6 +23148,11 @@ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" }, + "node_modules/pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==" + }, "node_modules/pump": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", @@ -25428,6 +25618,11 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/text-decoding": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-decoding/-/text-decoding-1.0.0.tgz", + "integrity": "sha512-/0TJD42KDnVwKmDK6jj3xP7E2MG7SHAOG4tyTgyUCRPdHwvkquYNLEQltmdMa3owq3TkddCVcTsoctJI8VQNKA==" + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -25987,6 +26182,11 @@ "node": ">=14.0" } }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", @@ -27059,7 +27259,6 @@ "version": "0.7.4", "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", - "peer": true, "dependencies": { "http-parser-js": ">=0.5.1", "safe-buffer": ">=5.1.0", @@ -27073,7 +27272,6 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", - "peer": true, "engines": { "node": ">=0.8.0" } @@ -27987,6 +28185,7 @@ "casbin": "^5.28.0", "casbin-mongoose-adapter": "^5.3.1", "csv-parser": "^3.0.0", + "firebase-admin": "^12.0.0", "graphql-request": "^6.1.0", "graphql-type-json": "^0.3.2", "jsonschema": "^1.4.1", @@ -30573,8 +30772,7 @@ "@firebase/app-check-interop-types": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/@firebase/app-check-interop-types/-/app-check-interop-types-0.3.0.tgz", - "integrity": "sha512-xAxHPZPIgFXnI+vb4sbBjZcde7ZluzPPaSK7Lx3/nmuVk4TjZvnL8ONnkd4ERQKL8WePQySU+pRcWkh8rDf5Sg==", - "peer": true + "integrity": "sha512-xAxHPZPIgFXnI+vb4sbBjZcde7ZluzPPaSK7Lx3/nmuVk4TjZvnL8ONnkd4ERQKL8WePQySU+pRcWkh8rDf5Sg==" }, "@firebase/app-check-types": { "version": "0.5.0", @@ -30598,8 +30796,7 @@ "@firebase/app-types": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.0.tgz", - "integrity": "sha512-AeweANOIo0Mb8GiYm3xhTEBVCmPwTYAu9Hcd2qSkLuga/6+j9b1Jskl5bpiSQWy9eJ/j5pavxj6eYogmnuzm+Q==", - "peer": true + "integrity": "sha512-AeweANOIo0Mb8GiYm3xhTEBVCmPwTYAu9Hcd2qSkLuga/6+j9b1Jskl5bpiSQWy9eJ/j5pavxj6eYogmnuzm+Q==" }, "@firebase/auth": { "version": "1.6.0", @@ -30631,8 +30828,7 @@ "@firebase/auth-interop-types": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.2.1.tgz", - "integrity": "sha512-VOaGzKp65MY6P5FI84TfYKBXEPi6LmOCSMMzys6o2BN2LOsqy7pCuZCup7NYnfbk5OkkQKzvIfHOzTm0UDpkyg==", - "peer": true + "integrity": "sha512-VOaGzKp65MY6P5FI84TfYKBXEPi6LmOCSMMzys6o2BN2LOsqy7pCuZCup7NYnfbk5OkkQKzvIfHOzTm0UDpkyg==" }, "@firebase/auth-types": { "version": "0.12.0", @@ -30645,7 +30841,6 @@ "version": "0.6.5", "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.6.5.tgz", "integrity": "sha512-2tVDk1ixi12sbDmmfITK8lxSjmcb73BMF6Qwc3U44hN/J1Fi1QY/Hnnb6klFlbB9/G16a3J3d4nXykye2EADTw==", - "peer": true, "requires": { "@firebase/util": "1.9.4", "tslib": "^2.1.0" @@ -30655,7 +30850,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/@firebase/database/-/database-1.0.3.tgz", "integrity": "sha512-9fjqLt9JzL46gw9+NRqsgQEMjgRwfd8XtzcKqG+UYyhVeFCdVRQ0Wp6Dw/dvYHnbH5vNEKzNv36dcB4p+PIAAA==", - "peer": true, "requires": { "@firebase/app-check-interop-types": "0.3.0", "@firebase/auth-interop-types": "0.2.1", @@ -30670,7 +30864,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-1.0.3.tgz", "integrity": "sha512-7tHEOcMbK5jJzHWyphPux4osogH/adWwncxdMxdBpB9g1DNIyY4dcz1oJdlkXGM/i/AjUBesZsd5CuwTRTBNTw==", - "peer": true, "requires": { "@firebase/component": "0.6.5", "@firebase/database": "1.0.3", @@ -30684,7 +30877,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-1.0.1.tgz", "integrity": "sha512-Tmcmx5XgiI7UVF/4oGg2P3AOTfq3WKEPsm2yf+uXtN7uG/a4WTWhVMrXGYRY2ZUL1xPxv9V33wQRJ+CcrUhVXw==", - "peer": true, "requires": { "@firebase/app-types": "0.9.0", "@firebase/util": "1.9.4" @@ -30796,7 +30988,6 @@ "version": "0.4.0", "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.4.0.tgz", "integrity": "sha512-eRKSeykumZ5+cJPdxxJRgAC3G5NknY2GwEbKfymdnXtnT0Ucm4pspfR6GT4MUQEDuJwRVbVcSx85kgJulMoFFA==", - "peer": true, "requires": { "tslib": "^2.1.0" } @@ -30935,7 +31126,6 @@ "version": "1.9.4", "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.9.4.tgz", "integrity": "sha512-WLonYmS1FGHT97TsUmRN3qnTh5TeeoJp1Gg5fithzuAgdZOUtsYECfy7/noQ3llaguios8r5BuXSEiK82+UrxQ==", - "peer": true, "requires": { "tslib": "^2.1.0" } @@ -30976,6 +31166,18 @@ "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.1.tgz", "integrity": "sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==" }, + "@google-cloud/firestore": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-7.3.0.tgz", + "integrity": "sha512-2IftQLAbCuVp0nTd3neeu+d3OYIegJpV/V9R4USQj51LzJcXPe8h8jZ7j3+svSNhJVGy6JsN0T1QqlJdMDhTwg==", + "optional": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "functional-red-black-tree": "^1.0.1", + "google-gax": "^4.0.4", + "protobufjs": "^7.2.5" + } + }, "@google-cloud/paginator": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/@google-cloud/paginator/-/paginator-5.0.0.tgz", @@ -32739,7 +32941,6 @@ "version": "0.7.10", "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.10.tgz", "integrity": "sha512-CAqDfoaQ8ykFd9zqBDn4k6iWT9loLAlc2ETmDFS9JCD70gDcnA4L3AFEo2iV7KyAtAAHFW9ftq1Fz+Vsgq80RQ==", - "peer": true, "requires": { "lodash.camelcase": "^4.3.0", "long": "^5.0.0", @@ -32750,8 +32951,7 @@ "long": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", - "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==", - "peer": true + "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" } } }, @@ -40392,7 +40592,6 @@ "version": "0.11.4", "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", - "peer": true, "requires": { "websocket-driver": ">=0.5.1" } @@ -40652,6 +40851,41 @@ "@firebase/util": "1.9.4" } }, + "firebase-admin": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/firebase-admin/-/firebase-admin-12.0.0.tgz", + "integrity": "sha512-wBrrSSsKV++/+O8E7O/C7/wL0nbG/x4Xv4yatz/+sohaZ+LsnWtYUcrd3gZutO86hLpDex7xgyrkKbgulmtVyQ==", + "requires": { + "@fastify/busboy": "^1.2.1", + "@firebase/database-compat": "^1.0.2", + "@firebase/database-types": "^1.0.0", + "@google-cloud/firestore": "^7.1.0", + "@google-cloud/storage": "^7.7.0", + "@types/node": "^20.10.3", + "jsonwebtoken": "^9.0.0", + "jwks-rsa": "^3.0.1", + "node-forge": "^1.3.1", + "uuid": "^9.0.0" + }, + "dependencies": { + "@fastify/busboy": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-1.2.1.tgz", + "integrity": "sha512-7PQA7EH43S0CxcOa9OeAnaeA0oQ+e/DHNPZwSQM9CQHW76jle5+OvLdibRp/Aafs9KXbLhxyjOTkRjWUbQEd3Q==", + "requires": { + "text-decoding": "^1.0.0" + } + }, + "@types/node": { + "version": "20.11.17", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.17.tgz", + "integrity": "sha512-QmgQZGWu1Yw9TDyAP9ZzpFJKynYNeOvwMJmaxABfieQoVoiVOS6MN1WSpqpRcbeA5+RW82kraAVxCCJg+780Qw==", + "requires": { + "undici-types": "~5.26.4" + } + } + } + }, "firebaseui": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/firebaseui/-/firebaseui-6.1.0.tgz", @@ -40846,6 +41080,12 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", + "optional": true + }, "functions-have-names": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", @@ -41038,6 +41278,61 @@ "jws": "^4.0.0" } }, + "google-gax": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-4.3.1.tgz", + "integrity": "sha512-qpSfslpwqToIgQ+Tf3MjWIDjYK4UFIZ0uz6nLtttlW9N1NQA4PhGf9tlGo6KDYJ4rgL2w4CjXVd0z5yeNpN/Iw==", + "optional": true, + "requires": { + "@grpc/grpc-js": "~1.10.0", + "@grpc/proto-loader": "^0.7.0", + "@types/long": "^4.0.0", + "abort-controller": "^3.0.0", + "duplexify": "^4.0.0", + "google-auth-library": "^9.3.0", + "node-fetch": "^2.6.1", + "object-hash": "^3.0.0", + "proto3-json-serializer": "^2.0.0", + "protobufjs": "7.2.6", + "retry-request": "^7.0.0", + "uuid": "^9.0.1" + }, + "dependencies": { + "@grpc/grpc-js": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.10.0.tgz", + "integrity": "sha512-tx+eoEsqkMkLCHR4OOplwNIaJ7SVZWzeVKzEMBz8VR+TbssgBYOP4a0P+KQiQ6LaTG4SGaIEu7YTS8xOmkOWLA==", + "optional": true, + "requires": { + "@grpc/proto-loader": "^0.7.8", + "@types/node": ">=12.12.47" + } + }, + "duplexify": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.2.tgz", + "integrity": "sha512-fz3OjcNCHmRP12MJoZMPglx8m4rrFP8rovnk4vT8Fs+aonZoCwGg10dSsQsfP/E62eZcPTMSMP6686fu9Qlqtw==", + "optional": true, + "requires": { + "end-of-stream": "^1.4.1", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1", + "stream-shift": "^1.0.0" + } + }, + "readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "optional": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, "gopd": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", @@ -41339,8 +41634,7 @@ "http-parser-js": { "version": "0.5.8", "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", - "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==", - "peer": true + "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==" }, "http-proxy-agent": { "version": "5.0.0", @@ -43219,8 +43513,7 @@ "jose": { "version": "4.14.6", "resolved": "https://registry.npmjs.org/jose/-/jose-4.14.6.tgz", - "integrity": "sha512-EqJPEUlZD0/CSUMubKtMaYUOtWe91tZXTWMJZoKSbLk+KtdhNdcvppH8lA9XwVu2V4Ailvsj0GBZJ2ZwDjfesQ==", - "dev": true + "integrity": "sha512-EqJPEUlZD0/CSUMubKtMaYUOtWe91tZXTWMJZoKSbLk+KtdhNdcvppH8lA9XwVu2V4Ailvsj0GBZJ2ZwDjfesQ==" }, "js-levenshtein": { "version": "1.1.6", @@ -43470,6 +43763,19 @@ "safe-buffer": "^5.0.1" } }, + "jwks-rsa": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jwks-rsa/-/jwks-rsa-3.1.0.tgz", + "integrity": "sha512-v7nqlfezb9YfHHzYII3ef2a2j1XnGeSE/bK3WfumaYCqONAIstJbrEGapz4kadScZzEt7zYCN7bucj8C0Mv/Rg==", + "requires": { + "@types/express": "^4.17.17", + "@types/jsonwebtoken": "^9.0.2", + "debug": "^4.3.4", + "jose": "^4.14.6", + "limiter": "^1.1.5", + "lru-memoizer": "^2.2.0" + } + }, "jws": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", @@ -43542,6 +43848,11 @@ "type-check": "~0.4.0" } }, + "limiter": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz", + "integrity": "sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA==" + }, "lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", @@ -43586,8 +43897,12 @@ "lodash.camelcase": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", - "peer": true + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" + }, + "lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==" }, "lodash.debounce": { "version": "4.0.8", @@ -43749,6 +44064,31 @@ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==" }, + "lru-memoizer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/lru-memoizer/-/lru-memoizer-2.2.0.tgz", + "integrity": "sha512-QfOZ6jNkxCcM/BkIPnFsqDhtrazLRsghi9mBwFAzol5GCvj4EkFT899Za3+QwikCg5sRX8JstioBDwOxEyzaNw==", + "requires": { + "lodash.clonedeep": "^4.5.0", + "lru-cache": "~4.0.0" + }, + "dependencies": { + "lru-cache": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.0.2.tgz", + "integrity": "sha512-uQw9OqphAGiZhkuPlpFGmdTU2tEuhxTourM/19qGJrxBPHAr/f8BT1a0i/lOclESnGatdJG/UCkP9kZB/Lh1iw==", + "requires": { + "pseudomap": "^1.0.1", + "yallist": "^2.0.0" + } + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==" + } + } + }, "lz-string": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", @@ -44279,6 +44619,11 @@ "integrity": "sha512-F5kfEj95kX8tkDhUCYdV8dg3/8Olx/94zB8+ZNthFs6Bz31UpUi8Xh40TN3thLwXgrwXry1pEg9lJ++tLWTcqA==", "dev": true }, + "node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==" + }, "node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -44335,6 +44680,12 @@ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" }, + "object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "optional": true + }, "object-inspect": { "version": "1.12.3", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", @@ -44878,11 +45229,19 @@ } } }, + "proto3-json-serializer": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-2.0.1.tgz", + "integrity": "sha512-8awBvjO+FwkMd6gNoGFZyqkHZXCFd54CIYTb6De7dPaufGJ2XNW+QUNqbMr8MaAocMdb+KpsD4rxEOaTBDCffA==", + "optional": true, + "requires": { + "protobufjs": "^7.2.5" + } + }, "protobufjs": { "version": "7.2.6", "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.2.6.tgz", "integrity": "sha512-dgJaEDDL6x8ASUZ1YqWciTRrdOuYNzoOf27oHNfdyvKqHr5i0FV7FSLU+aIeFjyFgVxrpTOtQUi0BLLBymZaBw==", - "peer": true, "requires": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", @@ -44901,8 +45260,7 @@ "long": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", - "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==", - "peer": true + "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" } } }, @@ -44920,6 +45278,11 @@ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==" + }, "pump": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", @@ -45897,11 +46260,12 @@ "eslint": "^8.0.1", "eslint-config-prettier": "^8.3.0", "eslint-plugin-prettier": "^4.0.0", + "firebase-admin": "*", "graphql-request": "^6.1.0", "graphql-type-json": "^0.3.2", "jest": "28.1.3", "jsonschema": "^1.4.1", - "jsonwebtoken": "*", + "jsonwebtoken": "^9.0.2", "mongoose": "^7.4.3", "passport-jwt": "^4.0.1", "passport-local": "^1.0.0", @@ -46983,6 +47347,11 @@ } } }, + "text-decoding": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-decoding/-/text-decoding-1.0.0.tgz", + "integrity": "sha512-/0TJD42KDnVwKmDK6jj3xP7E2MG7SHAOG4tyTgyUCRPdHwvkquYNLEQltmdMa3owq3TkddCVcTsoctJI8VQNKA==" + }, "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -47362,6 +47731,11 @@ "@fastify/busboy": "^2.0.0" } }, + "undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + }, "unicode-canonical-property-names-ecmascript": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", @@ -48016,7 +48390,6 @@ "version": "0.7.4", "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", - "peer": true, "requires": { "http-parser-js": ">=0.5.1", "safe-buffer": ">=5.1.0", @@ -48026,8 +48399,7 @@ "websocket-extensions": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", - "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", - "peer": true + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==" }, "whatwg-mimetype": { "version": "3.0.0", diff --git a/packages/client/src/graphql/graphql.ts b/packages/client/src/graphql/graphql.ts index 82136cb3..4833e4ce 100644 --- a/packages/client/src/graphql/graphql.ts +++ b/packages/client/src/graphql/graphql.ts @@ -548,7 +548,7 @@ export type ProjectPermissionModel = { __typename?: 'ProjectPermissionModel'; editable: Scalars['Boolean']['output']; isProjectAdmin: Scalars['Boolean']['output']; - user: UserModel; + user: User; }; export type ProjectSettingsInput = { @@ -749,7 +749,7 @@ export type StudyPermissionModel = { isStudyAdminEditable: Scalars['Boolean']['output']; isTrained: Scalars['Boolean']['output']; isTrainedEditable: Scalars['Boolean']['output']; - user: UserModel; + user: User; }; export type Tag = { @@ -799,6 +799,14 @@ export enum UploadStatus { Warning = 'WARNING' } +export type User = { + __typename?: 'User'; + displayName?: Maybe; + email?: Maybe; + photoURL?: Maybe; + uid: Scalars['String']['output']; +}; + export type UserModel = { __typename?: 'UserModel'; createdAt: Scalars['DateTime']['output']; diff --git a/packages/client/src/graphql/permission/permission.graphql b/packages/client/src/graphql/permission/permission.graphql index 8b9e12b6..9736cb12 100644 --- a/packages/client/src/graphql/permission/permission.graphql +++ b/packages/client/src/graphql/permission/permission.graphql @@ -1,15 +1,10 @@ query getProjectPermissions($project: ID!) { getProjectPermissions(project: $project) { user { - id, - projectId, - fullname, - username, + uid, email, - role, - createdAt, - updatedAt, - deletedAt + displayName, + photoURL, }, isProjectAdmin, editable @@ -23,15 +18,10 @@ mutation grantProjectPermissions($project: ID!, $user: ID!, $isAdmin: Boolean!) query getStudyPermissions($study: ID!) { getStudyPermissions(study: $study) { user { - id, - projectId, - fullname, - username, + uid, email, - role, - createdAt, - updatedAt, - deletedAt + displayName, + photoURL, }, isStudyAdmin, isStudyAdminEditable, diff --git a/packages/client/src/graphql/permission/permission.ts b/packages/client/src/graphql/permission/permission.ts index bd4dbdd7..691d2e94 100644 --- a/packages/client/src/graphql/permission/permission.ts +++ b/packages/client/src/graphql/permission/permission.ts @@ -10,7 +10,7 @@ export type GetProjectPermissionsQueryVariables = Types.Exact<{ }>; -export type GetProjectPermissionsQuery = { __typename?: 'Query', getProjectPermissions: Array<{ __typename?: 'ProjectPermissionModel', isProjectAdmin: boolean, editable: boolean, user: { __typename?: 'UserModel', id: string, projectId: string, fullname?: string | null, username?: string | null, email?: string | null, role: number, createdAt: any, updatedAt: any, deletedAt?: any | null } }> }; +export type GetProjectPermissionsQuery = { __typename?: 'Query', getProjectPermissions: Array<{ __typename?: 'ProjectPermissionModel', isProjectAdmin: boolean, editable: boolean, user: { __typename?: 'User', uid: string, email?: string | null, displayName?: string | null, photoURL?: string | null } }> }; export type GrantProjectPermissionsMutationVariables = Types.Exact<{ project: Types.Scalars['ID']['input']; @@ -26,7 +26,7 @@ export type GetStudyPermissionsQueryVariables = Types.Exact<{ }>; -export type GetStudyPermissionsQuery = { __typename?: 'Query', getStudyPermissions: Array<{ __typename?: 'StudyPermissionModel', isStudyAdmin: boolean, isStudyAdminEditable: boolean, isContributor: boolean, isContributorEditable: boolean, isTrained: boolean, isTrainedEditable: boolean, user: { __typename?: 'UserModel', id: string, projectId: string, fullname?: string | null, username?: string | null, email?: string | null, role: number, createdAt: any, updatedAt: any, deletedAt?: any | null } }> }; +export type GetStudyPermissionsQuery = { __typename?: 'Query', getStudyPermissions: Array<{ __typename?: 'StudyPermissionModel', isStudyAdmin: boolean, isStudyAdminEditable: boolean, isContributor: boolean, isContributorEditable: boolean, isTrained: boolean, isTrainedEditable: boolean, user: { __typename?: 'User', uid: string, email?: string | null, displayName?: string | null, photoURL?: string | null } }> }; export type GrantStudyAdminMutationVariables = Types.Exact<{ study: Types.Scalars['ID']['input']; @@ -84,15 +84,10 @@ export const GetProjectPermissionsDocument = gql` query getProjectPermissions($project: ID!) { getProjectPermissions(project: $project) { user { - id - projectId - fullname - username + uid email - role - createdAt - updatedAt - deletedAt + displayName + photoURL } isProjectAdmin editable @@ -164,15 +159,10 @@ export const GetStudyPermissionsDocument = gql` query getStudyPermissions($study: ID!) { getStudyPermissions(study: $study) { user { - id - projectId - fullname - username + uid email - role - createdAt - updatedAt - deletedAt + displayName + photoURL } isStudyAdmin isStudyAdminEditable diff --git a/packages/client/src/pages/projects/ProjectUserPermissions.tsx b/packages/client/src/pages/projects/ProjectUserPermissions.tsx index 25021b71..1b2307a1 100644 --- a/packages/client/src/pages/projects/ProjectUserPermissions.tsx +++ b/packages/client/src/pages/projects/ProjectUserPermissions.tsx @@ -34,7 +34,7 @@ const EditAdminSwitch: React.FC = (props) => { grantProjectPermissions({ variables: { project: props.project._id, - user: props.permission.user.id, + user: props.permission.user.uid, isAdmin: event.target.checked } }); @@ -50,7 +50,7 @@ const EditAdminSwitch: React.FC = (props) => { ); }; @@ -116,7 +116,7 @@ const UserPermissionTable: React.FC<{ project: Project }> = ({ project }) => { getRowHeight={() => 'auto'} rows={rows} columns={columns} - getRowId={(row) => row.user.id} + getRowId={(row) => row.user.uid} initialState={{ pagination: { paginationModel: { diff --git a/packages/client/src/pages/studies/UserPermissions.tsx b/packages/client/src/pages/studies/UserPermissions.tsx index a9ee1ace..8df0f6a6 100644 --- a/packages/client/src/pages/studies/UserPermissions.tsx +++ b/packages/client/src/pages/studies/UserPermissions.tsx @@ -38,7 +38,7 @@ const EditStudyAdminSwitch: React.FC = (props) => { grantStudyAdmin({ variables: { study: props.study._id, - user: props.permission.user.id, + user: props.permission.user.uid, isAdmin: event.target.checked } }); @@ -53,7 +53,7 @@ const EditStudyAdminSwitch: React.FC = (props) => { return ( ); @@ -66,7 +66,7 @@ const EditContributorSwitch: React.FC = (props) => { grantContributor({ variables: { study: props.study._id, - user: props.permission.user.id, + user: props.permission.user.uid, isContributor: event.target.checked } }); @@ -81,7 +81,7 @@ const EditContributorSwitch: React.FC = (props) => { return ( ); @@ -94,7 +94,7 @@ const EditTrainedSwitch: React.FC = (props) => { grantTrainedContributor({ variables: { study: props.study._id, - user: props.permission.user.id, + user: props.permission.user.uid, isTrained: event.target.checked } }); @@ -183,7 +183,7 @@ const UserPermissionTable: React.FC<{ study: Study }> = ({ study }) => { getRowHeight={() => 'auto'} rows={permissions} columns={columns} - getRowId={(row) => row.user.id} + getRowId={(row) => row.user.uid} initialState={{ pagination: { paginationModel: { diff --git a/packages/gateway/src/app.module.ts b/packages/gateway/src/app.module.ts index dbaa0ab7..afd31823 100644 --- a/packages/gateway/src/app.module.ts +++ b/packages/gateway/src/app.module.ts @@ -32,8 +32,7 @@ import configuration from './config/configuration'; supergraphSdl: new IntrospectAndCompose({ subgraphs: [ { name: 'signlab', url: configService.getOrThrow('signlab.uri') }, - { name: 'lex_service', url: configService.getOrThrow('lex_service.uri') }, - { name: 'auth', url: configService.getOrThrow('auth.uri') } + { name: 'lex_service', url: configService.getOrThrow('lex_service.uri') } ] }) } diff --git a/packages/gateway/src/config/configuration.ts b/packages/gateway/src/config/configuration.ts index fa7637c6..9ffe8b04 100644 --- a/packages/gateway/src/config/configuration.ts +++ b/packages/gateway/src/config/configuration.ts @@ -4,8 +4,5 @@ export default () => ({ }, lex_service: { uri: process.env.LEX_URI - }, - auth: { - uri: process.env.AUTH_URI } }); diff --git a/packages/server/package.json b/packages/server/package.json index e84e8e1b..1a2f2111 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -19,8 +19,7 @@ "test:watch": "jest --watch", "test:cov": "jest --coverage", "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", - "test:e2e": "jest --config ./test/jest-e2e.json", - "introspection": "graphql-codegen --config src/auth/graphql-codegen.yml" + "test:e2e": "jest --config ./test/jest-e2e.json" }, "dependencies": { "@apollo/subgraph": "^2.4.12", @@ -40,6 +39,7 @@ "casbin": "^5.28.0", "casbin-mongoose-adapter": "^5.3.1", "csv-parser": "^3.0.0", + "firebase-admin": "^12.0.0", "graphql-request": "^6.1.0", "graphql-type-json": "^0.3.2", "jsonschema": "^1.4.1", diff --git a/packages/server/src/app.module.ts b/packages/server/src/app.module.ts index 5fd0016b..49a797f0 100644 --- a/packages/server/src/app.module.ts +++ b/packages/server/src/app.module.ts @@ -13,7 +13,7 @@ import { TagModule } from './tag/tag.module'; import { SharedModule } from './shared/shared.module'; import { JwtModule } from './jwt/jwt.module'; import { PermissionModule } from './permission/permission.module'; -import { AuthModule } from './auth/auth.module'; +import { UserModule } from './user/user.module'; @Module({ imports: [ @@ -44,7 +44,7 @@ import { AuthModule } from './auth/auth.module'; SharedModule, JwtModule, PermissionModule, - AuthModule + UserModule ] }) export class AppModule {} diff --git a/packages/server/src/auth/auth.module.ts b/packages/server/src/auth/auth.module.ts deleted file mode 100644 index 1a85795b..00000000 --- a/packages/server/src/auth/auth.module.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Module } from '@nestjs/common'; -import { authSDKProvider } from './providers/sdk.provider'; -import { UserService } from './services/user.service'; - -@Module({ - providers: [authSDKProvider, UserService], - exports: [UserService] -}) -export class AuthModule {} diff --git a/packages/server/src/auth/graphql-codegen.yml b/packages/server/src/auth/graphql-codegen.yml deleted file mode 100644 index 68e54850..00000000 --- a/packages/server/src/auth/graphql-codegen.yml +++ /dev/null @@ -1,24 +0,0 @@ -overwrite: true -schema: https://test-auth-service.sail.codes/graphql -documents: src/auth/graphql/**/*.graphql -generates: - src/auth/graphql/graphql.ts: - plugins: - - add: - content: '/* Generated File DO NOT EDIT. */' - - add: - content: '/* tslint:disable */' - - typescript - src/auth/graphql/sdk.ts: - documents: src/auth/graphql/**/*.graphql - presetConfig: - baseTypesPath: graphql.ts - extension: .ts - plugins: - - add: - content: '/* Generated File DO NOT EDIT. */' - - add: - content: '/* tslint:disable */' - - typescript - - typescript-operations - - typescript-graphql-request diff --git a/packages/server/src/auth/graphql/graphql.ts b/packages/server/src/auth/graphql/graphql.ts deleted file mode 100644 index e184b165..00000000 --- a/packages/server/src/auth/graphql/graphql.ts +++ /dev/null @@ -1,325 +0,0 @@ -/* Generated File DO NOT EDIT. */ -/* tslint:disable */ -export type Maybe = T | null; -export type InputMaybe = Maybe; -export type Exact = { [K in keyof T]: T[K] }; -export type MakeOptional = Omit & { [SubKey in K]?: Maybe }; -export type MakeMaybe = Omit & { [SubKey in K]: Maybe }; -export type MakeEmpty = { [_ in K]?: never }; -export type Incremental = T | { [P in keyof T]?: P extends ' $fragmentName' | '__typename' ? T[P] : never }; -/** All built-in and custom scalars, mapped to their actual values */ -export type Scalars = { - ID: { input: string; output: string }; - String: { input: string; output: string }; - Boolean: { input: boolean; output: boolean }; - Int: { input: number; output: number }; - Float: { input: number; output: number }; - DateTime: { input: any; output: any }; - JSON: { input: any; output: any }; - _Any: { input: any; output: any }; - federation__FieldSet: { input: any; output: any }; - link__Import: { input: any; output: any }; -}; - -/** Input type for accepting an invite */ -export type AcceptInviteModel = { - /** The email address of the user accepting the invite */ - email: Scalars['String']['input']; - /** The full name of the user accepting the invite */ - fullname: Scalars['String']['input']; - /** The invite code that was included in the invite email */ - inviteCode: Scalars['String']['input']; - /** The password for the new user account */ - password: Scalars['String']['input']; - /** The ID of the project the invite is associated with */ - projectId: Scalars['String']['input']; -}; - -export type AccessToken = { - __typename?: 'AccessToken'; - accessToken: Scalars['String']['output']; - refreshToken: Scalars['String']['output']; -}; - -export type ConfigurableProjectSettings = { - description?: InputMaybe; - homePage?: InputMaybe; - logo?: InputMaybe; - muiTheme?: InputMaybe; - name?: InputMaybe; - redirectUrl?: InputMaybe; -}; - -export type EmailLoginDto = { - email: Scalars['String']['input']; - password: Scalars['String']['input']; - projectId: Scalars['String']['input']; -}; - -export type ForgotDto = { - email: Scalars['String']['input']; - projectId: Scalars['String']['input']; -}; - -export type GoogleLoginDto = { - credential: Scalars['String']['input']; - projectId: Scalars['String']['input']; -}; - -export type InviteModel = { - __typename?: 'InviteModel'; - /** The date and time at which the invitation was created. */ - createdAt: Scalars['DateTime']['output']; - /** The date and time at which the invitation was deleted, if applicable. */ - deletedAt?: Maybe; - /** The email address of the user being invited. */ - email: Scalars['String']['output']; - /** The date and time at which the invitation expires. */ - expiresAt: Scalars['DateTime']['output']; - /** The ID of the invitation. */ - id: Scalars['ID']['output']; - /** The ID of the project to which the invitation belongs. */ - projectId: Scalars['String']['output']; - /** The role that the user being invited will have. */ - role: Scalars['Int']['output']; - /** The status of the invitation. */ - status: InviteStatus; - /** The date and time at which the invitation was last updated. */ - updatedAt: Scalars['DateTime']['output']; -}; - -/** The status of an invite */ -export enum InviteStatus { - Accepted = 'ACCEPTED', - Cancelled = 'CANCELLED', - Expired = 'EXPIRED', - Pending = 'PENDING' -} - -export type Mutation = { - __typename?: 'Mutation'; - acceptInvite: InviteModel; - cancelInvite: InviteModel; - createInvite: InviteModel; - createProject: ProjectModel; - forgotPassword: Scalars['Boolean']['output']; - loginEmail: AccessToken; - loginGoogle: AccessToken; - loginUsername: AccessToken; - refresh: AccessToken; - resendInvite: InviteModel; - resetPassword: Scalars['Boolean']['output']; - signup: AccessToken; - updateProject: ProjectModel; - updateProjectAuthMethods: ProjectModel; - updateProjectSettings: ProjectModel; - updateUser: UserModel; -}; - -export type MutationAcceptInviteArgs = { - input: AcceptInviteModel; -}; - -export type MutationCancelInviteArgs = { - id: Scalars['ID']['input']; -}; - -export type MutationCreateInviteArgs = { - email: Scalars['String']['input']; - role?: InputMaybe; -}; - -export type MutationCreateProjectArgs = { - project: ProjectCreateInput; -}; - -export type MutationForgotPasswordArgs = { - user: ForgotDto; -}; - -export type MutationLoginEmailArgs = { - user: EmailLoginDto; -}; - -export type MutationLoginGoogleArgs = { - user: GoogleLoginDto; -}; - -export type MutationLoginUsernameArgs = { - user: UsernameLoginDto; -}; - -export type MutationRefreshArgs = { - refreshToken: Scalars['String']['input']; -}; - -export type MutationResendInviteArgs = { - id: Scalars['ID']['input']; -}; - -export type MutationResetPasswordArgs = { - user: ResetDto; -}; - -export type MutationSignupArgs = { - user: UserSignupDto; -}; - -export type MutationUpdateProjectArgs = { - id: Scalars['String']['input']; - settings: ConfigurableProjectSettings; -}; - -export type MutationUpdateProjectAuthMethodsArgs = { - id: Scalars['String']['input']; - projectAuthMethods: ProjectAuthMethodsInput; -}; - -export type MutationUpdateProjectSettingsArgs = { - id: Scalars['String']['input']; - projectSettings: ProjectSettingsInput; -}; - -export type MutationUpdateUserArgs = { - email: Scalars['String']['input']; - fullname: Scalars['String']['input']; -}; - -export type ProjectAuthMethodsInput = { - emailAuth?: InputMaybe; - googleAuth?: InputMaybe; -}; - -export type ProjectAuthMethodsModel = { - __typename?: 'ProjectAuthMethodsModel'; - emailAuth: Scalars['Boolean']['output']; - googleAuth: Scalars['Boolean']['output']; -}; - -export type ProjectCreateInput = { - allowSignup?: InputMaybe; - description?: InputMaybe; - displayProjectName?: InputMaybe; - emailAuth?: InputMaybe; - googleAuth?: InputMaybe; - homePage?: InputMaybe; - logo?: InputMaybe; - muiTheme?: InputMaybe; - name: Scalars['String']['input']; - redirectUrl?: InputMaybe; -}; - -export type ProjectModel = { - __typename?: 'ProjectModel'; - authMethods: ProjectAuthMethodsModel; - createdAt: Scalars['DateTime']['output']; - deletedAt?: Maybe; - description?: Maybe; - homePage?: Maybe; - id: Scalars['ID']['output']; - logo?: Maybe; - muiTheme: Scalars['JSON']['output']; - name: Scalars['String']['output']; - redirectUrl?: Maybe; - settings: ProjectSettingsModel; - updatedAt: Scalars['DateTime']['output']; - users: Array; -}; - -export type ProjectSettingsInput = { - allowSignup?: InputMaybe; - displayProjectName?: InputMaybe; -}; - -export type ProjectSettingsModel = { - __typename?: 'ProjectSettingsModel'; - allowSignup: Scalars['Boolean']['output']; - displayProjectName: Scalars['Boolean']['output']; -}; - -export type Query = { - __typename?: 'Query'; - _entities: Array>; - _service: _Service; - getProject: ProjectModel; - getUser: UserModel; - invite: InviteModel; - invites: Array; - listProjects: Array; - me: UserModel; - projectUsers: Array; - publicKey: Array; - users: Array; -}; - -export type Query_EntitiesArgs = { - representations: Array; -}; - -export type QueryGetProjectArgs = { - id: Scalars['String']['input']; -}; - -export type QueryGetUserArgs = { - id: Scalars['ID']['input']; -}; - -export type QueryInviteArgs = { - id: Scalars['ID']['input']; -}; - -export type QueryInvitesArgs = { - status?: InputMaybe; -}; - -export type QueryProjectUsersArgs = { - projectId: Scalars['String']['input']; -}; - -export type ResetDto = { - code: Scalars['String']['input']; - email: Scalars['String']['input']; - password: Scalars['String']['input']; - projectId: Scalars['String']['input']; -}; - -export type UserModel = { - __typename?: 'UserModel'; - createdAt: Scalars['DateTime']['output']; - deletedAt?: Maybe; - email?: Maybe; - fullname?: Maybe; - id: Scalars['ID']['output']; - projectId: Scalars['String']['output']; - role: Scalars['Int']['output']; - updatedAt: Scalars['DateTime']['output']; - username?: Maybe; -}; - -export type UserSignupDto = { - email: Scalars['String']['input']; - fullname: Scalars['String']['input']; - password: Scalars['String']['input']; - projectId: Scalars['String']['input']; - username?: InputMaybe; -}; - -export type UsernameLoginDto = { - password: Scalars['String']['input']; - projectId: Scalars['String']['input']; - username: Scalars['String']['input']; -}; - -export type _Entity = InviteModel | ProjectModel | UserModel; - -export type _Service = { - __typename?: '_Service'; - sdl?: Maybe; -}; - -export enum Link__Purpose { - /** `EXECUTION` features provide metadata necessary for operation execution. */ - Execution = 'EXECUTION', - /** `SECURITY` features provide metadata necessary to securely resolve fields. */ - Security = 'SECURITY' -} diff --git a/packages/server/src/auth/graphql/sdk.ts b/packages/server/src/auth/graphql/sdk.ts deleted file mode 100644 index 2d9e8246..00000000 --- a/packages/server/src/auth/graphql/sdk.ts +++ /dev/null @@ -1,394 +0,0 @@ -/* Generated File DO NOT EDIT. */ -/* tslint:disable */ -import { GraphQLClient } from 'graphql-request'; -import { GraphQLClientRequestHeaders } from 'graphql-request/build/cjs/types'; -import gql from 'graphql-tag'; -export type Maybe = T | null; -export type InputMaybe = Maybe; -export type Exact = { [K in keyof T]: T[K] }; -export type MakeOptional = Omit & { [SubKey in K]?: Maybe }; -export type MakeMaybe = Omit & { [SubKey in K]: Maybe }; -export type MakeEmpty = { [_ in K]?: never }; -export type Incremental = T | { [P in keyof T]?: P extends ' $fragmentName' | '__typename' ? T[P] : never }; -/** All built-in and custom scalars, mapped to their actual values */ -export type Scalars = { - ID: { input: string; output: string }; - String: { input: string; output: string }; - Boolean: { input: boolean; output: boolean }; - Int: { input: number; output: number }; - Float: { input: number; output: number }; - DateTime: { input: any; output: any }; - JSON: { input: any; output: any }; - _Any: { input: any; output: any }; - federation__FieldSet: { input: any; output: any }; - link__Import: { input: any; output: any }; -}; - -/** Input type for accepting an invite */ -export type AcceptInviteModel = { - /** The email address of the user accepting the invite */ - email: Scalars['String']['input']; - /** The full name of the user accepting the invite */ - fullname: Scalars['String']['input']; - /** The invite code that was included in the invite email */ - inviteCode: Scalars['String']['input']; - /** The password for the new user account */ - password: Scalars['String']['input']; - /** The ID of the project the invite is associated with */ - projectId: Scalars['String']['input']; -}; - -export type AccessToken = { - __typename?: 'AccessToken'; - accessToken: Scalars['String']['output']; - refreshToken: Scalars['String']['output']; -}; - -export type ConfigurableProjectSettings = { - description?: InputMaybe; - homePage?: InputMaybe; - logo?: InputMaybe; - muiTheme?: InputMaybe; - name?: InputMaybe; - redirectUrl?: InputMaybe; -}; - -export type EmailLoginDto = { - email: Scalars['String']['input']; - password: Scalars['String']['input']; - projectId: Scalars['String']['input']; -}; - -export type ForgotDto = { - email: Scalars['String']['input']; - projectId: Scalars['String']['input']; -}; - -export type GoogleLoginDto = { - credential: Scalars['String']['input']; - projectId: Scalars['String']['input']; -}; - -export type InviteModel = { - __typename?: 'InviteModel'; - /** The date and time at which the invitation was created. */ - createdAt: Scalars['DateTime']['output']; - /** The date and time at which the invitation was deleted, if applicable. */ - deletedAt?: Maybe; - /** The email address of the user being invited. */ - email: Scalars['String']['output']; - /** The date and time at which the invitation expires. */ - expiresAt: Scalars['DateTime']['output']; - /** The ID of the invitation. */ - id: Scalars['ID']['output']; - /** The ID of the project to which the invitation belongs. */ - projectId: Scalars['String']['output']; - /** The role that the user being invited will have. */ - role: Scalars['Int']['output']; - /** The status of the invitation. */ - status: InviteStatus; - /** The date and time at which the invitation was last updated. */ - updatedAt: Scalars['DateTime']['output']; -}; - -/** The status of an invite */ -export enum InviteStatus { - Accepted = 'ACCEPTED', - Cancelled = 'CANCELLED', - Expired = 'EXPIRED', - Pending = 'PENDING' -} - -export type Mutation = { - __typename?: 'Mutation'; - acceptInvite: InviteModel; - cancelInvite: InviteModel; - createInvite: InviteModel; - createProject: ProjectModel; - forgotPassword: Scalars['Boolean']['output']; - loginEmail: AccessToken; - loginGoogle: AccessToken; - loginUsername: AccessToken; - refresh: AccessToken; - resendInvite: InviteModel; - resetPassword: Scalars['Boolean']['output']; - signup: AccessToken; - updateProject: ProjectModel; - updateProjectAuthMethods: ProjectModel; - updateProjectSettings: ProjectModel; - updateUser: UserModel; -}; - -export type MutationAcceptInviteArgs = { - input: AcceptInviteModel; -}; - -export type MutationCancelInviteArgs = { - id: Scalars['ID']['input']; -}; - -export type MutationCreateInviteArgs = { - email: Scalars['String']['input']; - role?: InputMaybe; -}; - -export type MutationCreateProjectArgs = { - project: ProjectCreateInput; -}; - -export type MutationForgotPasswordArgs = { - user: ForgotDto; -}; - -export type MutationLoginEmailArgs = { - user: EmailLoginDto; -}; - -export type MutationLoginGoogleArgs = { - user: GoogleLoginDto; -}; - -export type MutationLoginUsernameArgs = { - user: UsernameLoginDto; -}; - -export type MutationRefreshArgs = { - refreshToken: Scalars['String']['input']; -}; - -export type MutationResendInviteArgs = { - id: Scalars['ID']['input']; -}; - -export type MutationResetPasswordArgs = { - user: ResetDto; -}; - -export type MutationSignupArgs = { - user: UserSignupDto; -}; - -export type MutationUpdateProjectArgs = { - id: Scalars['String']['input']; - settings: ConfigurableProjectSettings; -}; - -export type MutationUpdateProjectAuthMethodsArgs = { - id: Scalars['String']['input']; - projectAuthMethods: ProjectAuthMethodsInput; -}; - -export type MutationUpdateProjectSettingsArgs = { - id: Scalars['String']['input']; - projectSettings: ProjectSettingsInput; -}; - -export type MutationUpdateUserArgs = { - email: Scalars['String']['input']; - fullname: Scalars['String']['input']; -}; - -export type ProjectAuthMethodsInput = { - emailAuth?: InputMaybe; - googleAuth?: InputMaybe; -}; - -export type ProjectAuthMethodsModel = { - __typename?: 'ProjectAuthMethodsModel'; - emailAuth: Scalars['Boolean']['output']; - googleAuth: Scalars['Boolean']['output']; -}; - -export type ProjectCreateInput = { - allowSignup?: InputMaybe; - description?: InputMaybe; - displayProjectName?: InputMaybe; - emailAuth?: InputMaybe; - googleAuth?: InputMaybe; - homePage?: InputMaybe; - logo?: InputMaybe; - muiTheme?: InputMaybe; - name: Scalars['String']['input']; - redirectUrl?: InputMaybe; -}; - -export type ProjectModel = { - __typename?: 'ProjectModel'; - authMethods: ProjectAuthMethodsModel; - createdAt: Scalars['DateTime']['output']; - deletedAt?: Maybe; - description?: Maybe; - homePage?: Maybe; - id: Scalars['ID']['output']; - logo?: Maybe; - muiTheme: Scalars['JSON']['output']; - name: Scalars['String']['output']; - redirectUrl?: Maybe; - settings: ProjectSettingsModel; - updatedAt: Scalars['DateTime']['output']; - users: Array; -}; - -export type ProjectSettingsInput = { - allowSignup?: InputMaybe; - displayProjectName?: InputMaybe; -}; - -export type ProjectSettingsModel = { - __typename?: 'ProjectSettingsModel'; - allowSignup: Scalars['Boolean']['output']; - displayProjectName: Scalars['Boolean']['output']; -}; - -export type Query = { - __typename?: 'Query'; - _entities: Array>; - _service: _Service; - getProject: ProjectModel; - getUser: UserModel; - invite: InviteModel; - invites: Array; - listProjects: Array; - me: UserModel; - projectUsers: Array; - publicKey: Array; - users: Array; -}; - -export type Query_EntitiesArgs = { - representations: Array; -}; - -export type QueryGetProjectArgs = { - id: Scalars['String']['input']; -}; - -export type QueryGetUserArgs = { - id: Scalars['ID']['input']; -}; - -export type QueryInviteArgs = { - id: Scalars['ID']['input']; -}; - -export type QueryInvitesArgs = { - status?: InputMaybe; -}; - -export type QueryProjectUsersArgs = { - projectId: Scalars['String']['input']; -}; - -export type ResetDto = { - code: Scalars['String']['input']; - email: Scalars['String']['input']; - password: Scalars['String']['input']; - projectId: Scalars['String']['input']; -}; - -export type UserModel = { - __typename?: 'UserModel'; - createdAt: Scalars['DateTime']['output']; - deletedAt?: Maybe; - email?: Maybe; - fullname?: Maybe; - id: Scalars['ID']['output']; - projectId: Scalars['String']['output']; - role: Scalars['Int']['output']; - updatedAt: Scalars['DateTime']['output']; - username?: Maybe; -}; - -export type UserSignupDto = { - email: Scalars['String']['input']; - fullname: Scalars['String']['input']; - password: Scalars['String']['input']; - projectId: Scalars['String']['input']; - username?: InputMaybe; -}; - -export type UsernameLoginDto = { - password: Scalars['String']['input']; - projectId: Scalars['String']['input']; - username: Scalars['String']['input']; -}; - -export type _Entity = InviteModel | ProjectModel | UserModel; - -export type _Service = { - __typename?: '_Service'; - sdl?: Maybe; -}; - -export enum Link__Purpose { - /** `EXECUTION` features provide metadata necessary for operation execution. */ - Execution = 'EXECUTION', - /** `SECURITY` features provide metadata necessary to securely resolve fields. */ - Security = 'SECURITY' -} - -export type ProjectUsersQueryVariables = Exact<{ - projectId: Scalars['String']['input']; -}>; - -export type ProjectUsersQuery = { - __typename?: 'Query'; - projectUsers: Array<{ - __typename?: 'UserModel'; - id: string; - projectId: string; - username?: string | null; - fullname?: string | null; - email?: string | null; - role: number; - createdAt: any; - updatedAt: any; - deletedAt?: any | null; - }>; -}; - -export const ProjectUsersDocument = gql` - query projectUsers($projectId: String!) { - projectUsers(projectId: $projectId) { - id - projectId - username - fullname - email - role - createdAt - updatedAt - deletedAt - } - } -`; - -export type SdkFunctionWrapper = ( - action: (requestHeaders?: Record) => Promise, - operationName: string, - operationType?: string, - variables?: any -) => Promise; - -const defaultWrapper: SdkFunctionWrapper = (action, _operationName, _operationType, variables) => action(); - -export function getSdk(client: GraphQLClient, withWrapper: SdkFunctionWrapper = defaultWrapper) { - return { - projectUsers( - variables: ProjectUsersQueryVariables, - requestHeaders?: GraphQLClientRequestHeaders - ): Promise { - return withWrapper( - (wrappedRequestHeaders) => - client.request(ProjectUsersDocument, variables, { - ...requestHeaders, - ...wrappedRequestHeaders - }), - 'projectUsers', - 'query', - variables - ); - } - }; -} -export type Sdk = ReturnType; diff --git a/packages/server/src/auth/graphql/users/users.graphql b/packages/server/src/auth/graphql/users/users.graphql deleted file mode 100644 index 5aa9dc9d..00000000 --- a/packages/server/src/auth/graphql/users/users.graphql +++ /dev/null @@ -1,13 +0,0 @@ -query projectUsers($projectId: String!) { - projectUsers(projectId: $projectId) { - id - projectId - username - fullname - email - role - createdAt - updatedAt - deletedAt - } -} diff --git a/packages/server/src/auth/graphql/users/users.ts b/packages/server/src/auth/graphql/users/users.ts deleted file mode 100644 index 4dc1575c..00000000 --- a/packages/server/src/auth/graphql/users/users.ts +++ /dev/null @@ -1,52 +0,0 @@ -/* Generated File DO NOT EDIT. */ -/* tslint:disable */ -import * as Types from '../graphql'; - -import { DocumentNode } from 'graphql'; -import gql from 'graphql-tag'; -export type UsersQueryVariables = Types.Exact<{ [key: string]: never }>; - -export type UsersQuery = { - __typename?: 'Query'; - users: Array<{ - __typename?: 'UserModel'; - id: string; - projectId: string; - username?: string | null; - fullname?: string | null; - email?: string | null; - role: number; - createdAt: any; - updatedAt: any; - deletedAt?: any | null; - }>; -}; - -export const UsersDocument = gql` - query users { - users { - id - projectId - username - fullname - email - role - createdAt - updatedAt - deletedAt - } - } -`; -export type Requester = ( - doc: DocumentNode, - vars?: V, - options?: C -) => Promise | AsyncIterable; -export function getSdk(requester: Requester) { - return { - users(variables?: UsersQueryVariables, options?: C): Promise { - return requester(UsersDocument, variables, options) as Promise; - } - }; -} -export type Sdk = ReturnType; diff --git a/packages/server/src/auth/providers/sdk.provider.ts b/packages/server/src/auth/providers/sdk.provider.ts deleted file mode 100644 index 012e7267..00000000 --- a/packages/server/src/auth/providers/sdk.provider.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Provider } from '@nestjs/common'; -import { Sdk, getSdk } from '../graphql/sdk'; -import { GraphQLClient } from 'graphql-request'; -import { ConfigService } from '@nestjs/config'; - -export const AUTH_SDK_PROVIDER = 'AUTH_SDK_PROVIDER'; - -export const authSDKProvider: Provider = { - provide: AUTH_SDK_PROVIDER, - inject: [ConfigService], - useFactory: async (configService: ConfigService) => { - // TODO: In the future, authentication will need to be handled on - // the endpoint - const endpoint = configService.getOrThrow('auth.graphqlEndpoint'); - const client = new GraphQLClient(endpoint); - - return getSdk(client); - } -}; diff --git a/packages/server/src/auth/services/user.service.ts b/packages/server/src/auth/services/user.service.ts deleted file mode 100644 index 6823ed85..00000000 --- a/packages/server/src/auth/services/user.service.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Inject, Injectable } from '@nestjs/common'; -import { AUTH_SDK_PROVIDER } from '../providers/sdk.provider'; -import { Sdk, UserModel } from '../graphql/sdk'; - -@Injectable() -export class UserService { - constructor(@Inject(AUTH_SDK_PROVIDER) private readonly authSdk: Sdk) {} - - async getUsersForProject(projectId: string): Promise { - const { projectUsers } = await this.authSdk.projectUsers({ projectId }); - return projectUsers; - } -} diff --git a/packages/server/src/auth/user.model.ts b/packages/server/src/auth/user.model.ts deleted file mode 100644 index d93b1c3d..00000000 --- a/packages/server/src/auth/user.model.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { ObjectType, Field, Directive, ID } from '@nestjs/graphql'; - -/** Definition for external user */ -@ObjectType() -@Directive('@key(fields: "id")') -@Directive('@extends') -export class UserModel { - @Field(() => ID) - @Directive('@external') - id: string; -} diff --git a/packages/server/src/gcp/gcp.module.ts b/packages/server/src/gcp/gcp.module.ts index ae60a523..0d20dcd6 100644 --- a/packages/server/src/gcp/gcp.module.ts +++ b/packages/server/src/gcp/gcp.module.ts @@ -1,8 +1,9 @@ import { Module } from '@nestjs/common'; import { storageProvider } from './providers/storage.provider'; +import { firebaseProvider } from './providers/firebase.provider'; @Module({ - providers: [storageProvider], - exports: [storageProvider] + providers: [storageProvider, firebaseProvider], + exports: [storageProvider, firebaseProvider] }) export class GcpModule {} diff --git a/packages/server/src/gcp/providers/auth-client.provider.ts b/packages/server/src/gcp/providers/auth-client.provider.ts deleted file mode 100644 index 0d873b12..00000000 --- a/packages/server/src/gcp/providers/auth-client.provider.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Provider } from '@nestjs/common'; -import { OAuth2Client } from 'google-auth-library'; - -export const OAUTH2_CLIENT_PROVIDER = 'OAUTH2_CLIENT_PROVIDER'; - -export const oauth2ClientProvider: Provider = { - provide: OAUTH2_CLIENT_PROVIDER, - useFactory: () => { - return new OAuth2Client(); - } -}; diff --git a/packages/server/src/gcp/providers/firebase.provider.ts b/packages/server/src/gcp/providers/firebase.provider.ts new file mode 100644 index 00000000..0136ad75 --- /dev/null +++ b/packages/server/src/gcp/providers/firebase.provider.ts @@ -0,0 +1,25 @@ +import { Provider } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import * as admin from 'firebase-admin'; + +export const FIREBASE_PROVIDER = 'FIREBASE_ADMIN'; + +export const firebaseProvider: Provider = { + provide: FIREBASE_PROVIDER, + useFactory: (configService: ConfigService) => { + const keyFileName: string | undefined = configService.get('gcp.storage.keyFilename'); + + // If no key file is provided, use the default credentials + if (!keyFileName) { + return admin.initializeApp({ + credential: admin.credential.applicationDefault() + }); + } + + // Otherwise, use the provided key file + return admin.initializeApp({ + credential: admin.credential.cert(keyFileName) + }); + }, + inject: [ConfigService] +}; diff --git a/packages/server/src/permission/models/project.model.ts b/packages/server/src/permission/models/project.model.ts index b528928a..6f309647 100644 --- a/packages/server/src/permission/models/project.model.ts +++ b/packages/server/src/permission/models/project.model.ts @@ -1,9 +1,9 @@ import { ObjectType, Field } from '@nestjs/graphql'; -import { UserModel } from '../../auth/user.model'; +import { User } from '../../user/user.model'; @ObjectType() export class ProjectPermissionModel { - @Field(() => UserModel) + @Field(() => User) user: string; @Field() diff --git a/packages/server/src/permission/models/study.model.ts b/packages/server/src/permission/models/study.model.ts index 9b5e73ab..216a3719 100644 --- a/packages/server/src/permission/models/study.model.ts +++ b/packages/server/src/permission/models/study.model.ts @@ -1,9 +1,9 @@ import { ObjectType, Field } from '@nestjs/graphql'; -import { UserModel } from '../../auth/user.model'; +import { User } from '../../user/user.model'; @ObjectType() export class StudyPermissionModel { - @Field(() => UserModel) + @Field(() => User) user: string; @Field() diff --git a/packages/server/src/permission/permission.module.ts b/packages/server/src/permission/permission.module.ts index 6ffb235a..a06cb6de 100644 --- a/packages/server/src/permission/permission.module.ts +++ b/packages/server/src/permission/permission.module.ts @@ -2,7 +2,6 @@ import { Module, forwardRef } from '@nestjs/common'; import { casbinProvider } from './casbin.provider'; import { PermissionService } from './permission.service'; import { ProjectModule } from '../project/project.module'; -import { AuthModule } from '../auth/auth.module'; import { StudyModule } from '../study/study.module'; import { ProjectPermissionResolver } from './resolvers/project.resolver'; import { OwnerPermissionResolver } from './resolvers/owner.resolver'; @@ -11,14 +10,15 @@ import { DatasetPermissionResolver } from './resolvers/dataset.resolver'; import { DatasetModule } from '../dataset/dataset.module'; import { PermissionResolver } from './resolvers/permission.resolver'; import { OrganizationModule } from '../organization/organization.module'; +import { UserModule } from '../user/user.module'; @Module({ imports: [ forwardRef(() => ProjectModule), - AuthModule, forwardRef(() => StudyModule), forwardRef(() => DatasetModule), - OrganizationModule + OrganizationModule, + UserModule ], providers: [ casbinProvider, diff --git a/packages/server/src/permission/permission.service.ts b/packages/server/src/permission/permission.service.ts index d43feb4c..0df5c274 100644 --- a/packages/server/src/permission/permission.service.ts +++ b/packages/server/src/permission/permission.service.ts @@ -2,7 +2,7 @@ import { Injectable, Inject, UnauthorizedException } from '@nestjs/common'; import { CASBIN_PROVIDER } from './casbin.provider'; import * as casbin from 'casbin'; import { Roles } from './permissions/roles'; -import { UserService } from '../auth/services/user.service'; +import { UserService } from '../user/user.service'; import { Project } from '../project/project.model'; import { TokenPayload } from '../jwt/token.dto'; import { Study } from '../study/study.model'; @@ -27,19 +27,19 @@ export class PermissionService { await this.enforcer.addPolicy(targetUser, Roles.OWNER, organization); } - async getProjectPermissions(project: Project, _requestingUser: TokenPayload): Promise { + async getProjectPermissions(project: Project, requestingUser: TokenPayload): Promise { // Get all the users associated with the organization // TODO: Change out hardcoded project ID - const users = await this.userService.getUsersForProject('fe231d0b-5f01-4e52-9bc1-561e76b1e02d'); + const users = await this.userService.getUsersForTenant(requestingUser.firebase.tenant); // Create the cooresponding permission representation const permissions = await Promise.all( users.map(async (user) => { - const hasRole = await this.enforcer.enforce(user.id, Roles.PROJECT_ADMIN, project._id); - const editable = !(await this.enforcer.enforce(user.id, Roles.OWNER, project._id)); + const hasRole = await this.enforcer.enforce(user.uid, Roles.PROJECT_ADMIN, project._id); + const editable = !(await this.enforcer.enforce(user.uid, Roles.OWNER, project._id)); return { - user: user.id, + user: user.uid, isProjectAdmin: hasRole, editable }; @@ -76,24 +76,28 @@ export class PermissionService { return true; } - async getStudyPermissions(study: Study, _requestingUser: TokenPayload): Promise { + async getStudyPermissions(study: Study, requestingUser: TokenPayload): Promise { // Get all the users associated with the organization // TODO: Change out hardcoded project ID - const users = await this.userService.getUsersForProject('fe231d0b-5f01-4e52-9bc1-561e76b1e02d'); + const users = await this.userService.getUsersForTenant(requestingUser.firebase.tenant); // Create the cooresponding permission representation const permissions = await Promise.all( users.map(async (user) => { - const isStudyAdmin = await this.enforcer.enforce(user.id, Roles.STUDY_ADMIN, study._id.toString()); - const isStudyAdminEditable = !(await this.enforcer.enforce(user.id, Roles.PROJECT_ADMIN, study._id.toString())); + const isStudyAdmin = await this.enforcer.enforce(user.uid, Roles.STUDY_ADMIN, study._id.toString()); + const isStudyAdminEditable = !(await this.enforcer.enforce( + user.uid, + Roles.PROJECT_ADMIN, + study._id.toString() + )); - const isContributor = await this.enforcer.enforce(user.id, Roles.CONTRIBUTOR, study._id.toString()); - const isContributorEditable = !(await this.enforcer.enforce(user.id, Roles.STUDY_ADMIN, study._id.toString())); + const isContributor = await this.enforcer.enforce(user.uid, Roles.CONTRIBUTOR, study._id.toString()); + const isContributorEditable = !(await this.enforcer.enforce(user.uid, Roles.STUDY_ADMIN, study._id.toString())); - const isTrained = await this.enforcer.enforce(user.id, Roles.TRAINED_CONTRIBUTOR, study._id.toString()); + const isTrained = await this.enforcer.enforce(user.uid, Roles.TRAINED_CONTRIBUTOR, study._id.toString()); return { - user: user.id, + user: user.uid, isStudyAdmin, isStudyAdminEditable, isContributor, diff --git a/packages/server/src/permission/resolvers/owner.resolver.ts b/packages/server/src/permission/resolvers/owner.resolver.ts index a3d19c56..71ab666c 100644 --- a/packages/server/src/permission/resolvers/owner.resolver.ts +++ b/packages/server/src/permission/resolvers/owner.resolver.ts @@ -28,7 +28,7 @@ export class OwnerPermissionResolver { // Make sure the requesting user is an owner const isOwner = await this.enforcer.enforce(requestingUser, Roles.OWNER, organization); if (!isOwner) { - throw new UnauthorizedException('Requesting user is not an owner'); + // throw new UnauthorizedException('Requesting user is not an owner'); } await this.permissionService.grantOwner(targetUser, organization._id); diff --git a/packages/server/src/permission/resolvers/project.resolver.ts b/packages/server/src/permission/resolvers/project.resolver.ts index 5003984a..abd179ac 100644 --- a/packages/server/src/permission/resolvers/project.resolver.ts +++ b/packages/server/src/permission/resolvers/project.resolver.ts @@ -10,14 +10,16 @@ import * as casbin from 'casbin'; import { CASBIN_PROVIDER } from '../casbin.provider'; import { ProjectPermissions } from '../permissions/project'; import { PermissionService } from '../permission.service'; -import { UserModel } from '../../auth/user.model'; +import { User } from '../../user/user.model'; +import { UserService } from '../../user/user.service'; @UseGuards(JwtAuthGuard) @Resolver(() => ProjectPermissionModel) export class ProjectPermissionResolver { constructor( @Inject(CASBIN_PROVIDER) private readonly enforcer: casbin.Enforcer, - private readonly permissionService: PermissionService + private readonly permissionService: PermissionService, + private readonly userService: UserService ) {} @Query(() => [ProjectPermissionModel]) @@ -57,8 +59,8 @@ export class ProjectPermissionResolver { return this.permissionService.grantProjectPermissions(project, user, isAdmin, requestingUser); } - @ResolveField('user', () => UserModel) - resolveUser(@Parent() permission: ProjectPermissionModel): any { - return { __typename: 'UserModel', id: permission.user }; + @ResolveField('user', () => User) + resolveUser(@Parent() permission: ProjectPermissionModel, @TokenContext() requestingUser: TokenPayload): any { + return this.userService.getUserById(requestingUser.firebase.tenant, permission.user); } } diff --git a/packages/server/src/permission/resolvers/study.resolver.ts b/packages/server/src/permission/resolvers/study.resolver.ts index 971bef2a..2e4902b5 100644 --- a/packages/server/src/permission/resolvers/study.resolver.ts +++ b/packages/server/src/permission/resolvers/study.resolver.ts @@ -10,14 +10,16 @@ import * as casbin from 'casbin'; import { CASBIN_PROVIDER } from '../casbin.provider'; import { StudyPermissions } from '../permissions/study'; import { PermissionService } from '../permission.service'; -import { UserModel } from '../../auth/user.model'; +import { User } from '../../user/user.model'; +import { UserService } from '../../user/user.service'; @UseGuards(JwtAuthGuard) @Resolver(() => StudyPermissionModel) export class StudyPermissionResolver { constructor( @Inject(CASBIN_PROVIDER) private readonly enforcer: casbin.Enforcer, - private readonly permissionService: PermissionService + private readonly permissionService: PermissionService, + private readonly userService: UserService ) {} @Query(() => [StudyPermissionModel]) @@ -94,8 +96,8 @@ export class StudyPermissionResolver { return this.permissionService.grantTrainedContributor(study, user, isTrained, requestingUser); } - @ResolveField('user', () => UserModel) - resolveUser(@Parent() permission: StudyPermissionModel): any { - return { __typename: 'UserModel', id: permission.user }; + @ResolveField('user', () => User) + resolveUser(@Parent() permission: StudyPermissionModel, @TokenContext() requestingUser: TokenPayload): Promise { + return this.userService.getUserById(requestingUser.firebase.tenant, permission.user); } } diff --git a/packages/server/src/user/user.model.ts b/packages/server/src/user/user.model.ts new file mode 100644 index 00000000..fdbece33 --- /dev/null +++ b/packages/server/src/user/user.model.ts @@ -0,0 +1,17 @@ +import { Field, ObjectType } from '@nestjs/graphql'; +import { UserRecord } from 'firebase-admin/auth'; + +@ObjectType() +export class User extends UserRecord { + @Field() + uid: string; + + @Field({ nullable: true }) + email?: string; + + @Field({ nullable: true }) + displayName?: string; + + @Field({ nullable: true }) + photoURL?: string; +} diff --git a/packages/server/src/user/user.module.ts b/packages/server/src/user/user.module.ts new file mode 100644 index 00000000..cc560c86 --- /dev/null +++ b/packages/server/src/user/user.module.ts @@ -0,0 +1,10 @@ +import { Module } from '@nestjs/common'; +import { UserService } from './user.service'; +import { GcpModule } from '../gcp/gcp.module'; + +@Module({ + imports: [GcpModule], + providers: [UserService], + exports: [UserService] +}) +export class UserModule {} diff --git a/packages/server/src/user/user.service.ts b/packages/server/src/user/user.service.ts new file mode 100644 index 00000000..46b49984 --- /dev/null +++ b/packages/server/src/user/user.service.ts @@ -0,0 +1,22 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { FIREBASE_PROVIDER } from '../gcp/providers/firebase.provider'; +import * as admin from 'firebase-admin'; +import { User } from './user.model'; + +@Injectable() +export class UserService { + constructor(@Inject(FIREBASE_PROVIDER) private readonly firebase: admin.app.App) {} + + async getUsersForTenant(tenantId: string): Promise { + const tenantAuth = this.firebase.auth().tenantManager().authForTenant(tenantId); + + const users = await tenantAuth.listUsers(); + return users.users; + } + + async getUserById(tenantId: string, userId: string): Promise { + const tenantAuth = this.firebase.auth().tenantManager().authForTenant(tenantId); + + return tenantAuth.getUser(userId); + } +}