From ea0ad88cf9dbc15bd00112a4ebc1e5666f70e125 Mon Sep 17 00:00:00 2001 From: cbolles Date: Wed, 20 Dec 2023 09:56:51 -0500 Subject: [PATCH 1/2] Add auth module to project and add guard --- package-lock.json | 396 +++++++++++++++++- packages/server/package.json | 5 + packages/server/schema.gql | 2 - packages/server/src/app.module.ts | 4 +- packages/server/src/auth/auth.module.ts | 43 ++ packages/server/src/auth/auth.service.ts | 29 ++ packages/server/src/auth/jwt.guard.ts | 16 + packages/server/src/auth/jwt.strategy.ts | 19 + packages/server/src/auth/jwt.stretegy.ts | 19 + packages/server/src/auth/user.decorator.ts | 7 + packages/server/src/auth/user.dto.ts | 9 + packages/server/src/config/configuration.ts | 3 + .../server/src/dataset/dataset.resolver.ts | 4 +- packages/server/src/entry/entry.module.ts | 2 - .../entry/resolvers/entry-upload.resolver.ts | 18 - .../src/entry/resolvers/entry.resolver.ts | 3 + .../resolvers/upload-session.resolver.ts | 5 +- .../src/organization/organization.resolver.ts | 6 +- .../server/src/project/project.resolver.ts | 7 +- packages/server/src/study/study.resolver.ts | 3 + packages/server/src/tag/tag.resolver.ts | 4 + 21 files changed, 575 insertions(+), 29 deletions(-) create mode 100644 packages/server/src/auth/auth.module.ts create mode 100644 packages/server/src/auth/auth.service.ts create mode 100644 packages/server/src/auth/jwt.guard.ts create mode 100644 packages/server/src/auth/jwt.strategy.ts create mode 100644 packages/server/src/auth/jwt.stretegy.ts create mode 100644 packages/server/src/auth/user.decorator.ts create mode 100644 packages/server/src/auth/user.dto.ts delete mode 100644 packages/server/src/entry/resolvers/entry-upload.resolver.ts diff --git a/package-lock.json b/package-lock.json index b1a8475d..70cafbf4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7248,6 +7248,17 @@ } } }, + "node_modules/@nestjs/axios": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@nestjs/axios/-/axios-3.0.1.tgz", + "integrity": "sha512-VlOZhAGDmOoFdsmewn8AyClAdGpKXQQaY1+3PGB+g6ceurGIdTxZgRX3VXc1T6Zs60PedWjg3A82TDOB05mrzQ==", + "peerDependencies": { + "@nestjs/common": "^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0", + "axios": "^1.3.1", + "reflect-metadata": "^0.1.12", + "rxjs": "^6.0.0 || ^7.0.0" + } + }, "node_modules/@nestjs/cli": { "version": "9.5.0", "resolved": "https://registry.npmjs.org/@nestjs/cli/-/cli-9.5.0.tgz", @@ -7672,6 +7683,18 @@ "uuid": "dist/bin/uuid" } }, + "node_modules/@nestjs/jwt": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/@nestjs/jwt/-/jwt-10.2.0.tgz", + "integrity": "sha512-x8cG90SURkEiLOehNaN2aRlotxT0KZESUliOPKKnjWiyJOcWurkF3w345WOX0P4MgFzUjGoZ1Sy0aZnxeihT0g==", + "dependencies": { + "@types/jsonwebtoken": "9.0.5", + "jsonwebtoken": "9.0.2" + }, + "peerDependencies": { + "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0" + } + }, "node_modules/@nestjs/mapped-types": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-2.0.2.tgz", @@ -7703,6 +7726,15 @@ "rxjs": "^7.0.0" } }, + "node_modules/@nestjs/passport": { + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/@nestjs/passport/-/passport-10.0.3.tgz", + "integrity": "sha512-znJ9Y4S8ZDVY+j4doWAJ8EuuVO7SkQN3yOBmzxbGaXbvcSwFDAdGJ+OMCg52NdzIO4tQoN4pYKx8W6M0ArfFRQ==", + "peerDependencies": { + "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0", + "passport": "^0.4.0 || ^0.5.0 || ^0.6.0 || ^0.7.0" + } + }, "node_modules/@nestjs/platform-express": { "version": "9.4.3", "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-9.4.3.tgz", @@ -11283,6 +11315,14 @@ "integrity": "sha512-s2cfwagOQAS8o06TcwKfr9Wx11dNGbH2E9vJz1cqV+a/LOyhWNLUNd6JSRYNzvB4d29UuJX2M0Dj9vE1T8fRXw==", "dev": true }, + "node_modules/@types/jsonwebtoken": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.5.tgz", + "integrity": "sha512-VRLSGzik+Unrup6BsouBeHsf4d1hOEgYWTm/7Nmw1sXoN1+tRly/Gy/po3yeahnP4jfnQWWAhQAqcNfH7ngOkA==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/lodash": { "version": "4.14.198", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.198.tgz", @@ -11342,6 +11382,33 @@ "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==" }, + "node_modules/@types/passport": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.16.tgz", + "integrity": "sha512-FD0qD5hbPWQzaM0wHUnJ/T0BBCJBxCeemtnCwc/ThhTg3x9jfrAcRUmj5Dopza+MfFS9acTe3wk7rcVnRIp/0A==", + "dependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/passport-jwt": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/@types/passport-jwt/-/passport-jwt-3.0.13.tgz", + "integrity": "sha512-fjHaC6Bv8EpMMqzTnHP32SXlZGaNfBPC/Po5dmRGYi2Ky7ljXPbGnOy+SxZqa6iZvFgVhoJ1915Re3m93zmcfA==", + "dependencies": { + "@types/express": "*", + "@types/jsonwebtoken": "*", + "@types/passport-strategy": "*" + } + }, + "node_modules/@types/passport-strategy": { + "version": "0.2.38", + "resolved": "https://registry.npmjs.org/@types/passport-strategy/-/passport-strategy-0.2.38.tgz", + "integrity": "sha512-GC6eMqqojOooq993Tmnmp7AUTbbQSgilyvpCYQjT+H6JfG/g6RGc7nXEniZlp0zyKJ0WUdOiZWLBZft9Yug1uA==", + "dependencies": { + "@types/express": "*", + "@types/passport": "*" + } + }, "node_modules/@types/prettier": { "version": "2.7.3", "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz", @@ -19588,6 +19655,71 @@ "node": "*" } }, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jsonwebtoken/node_modules/jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jsonwebtoken/node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jsonwebtoken/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jsonwebtoken/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/jwa": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", @@ -19750,6 +19882,36 @@ "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", "dev": true }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" + }, "node_modules/lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", @@ -19767,6 +19929,11 @@ "resolved": "https://registry.npmjs.org/lodash.omit/-/lodash.omit-4.5.0.tgz", "integrity": "sha512-XeqSp49hNGmlkj2EJlfrQFIzQ6lXdNro9sddtQzcJY8QaoC2GO0DT7xaIokHeyM+mIT0mPMlPvkYzg2xCuHdZg==" }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" + }, "node_modules/lodash.sortby": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", @@ -20979,6 +21146,41 @@ "tslib": "^2.0.3" } }, + "node_modules/passport": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/passport/-/passport-0.7.0.tgz", + "integrity": "sha512-cPLl+qZpSc+ireUvt+IzqbED1cHHkDoVYMo30jbJIdOOjQ1MQYZBPiNvmi8UM6lJuOpTPXJGZQk0DtC4y61MYQ==", + "peer": true, + "dependencies": { + "passport-strategy": "1.x.x", + "pause": "0.0.1", + "utils-merge": "^1.0.1" + }, + "engines": { + "node": ">= 0.4.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jaredhanson" + } + }, + "node_modules/passport-jwt": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/passport-jwt/-/passport-jwt-4.0.1.tgz", + "integrity": "sha512-UCKMDYhNuGOBE9/9Ycuoyh7vP6jpeTp/+sfMJl7nLff/t6dps+iaeE0hhNkKN8/HZHcJ7lCdOyDxHdDoxoSvdQ==", + "dependencies": { + "jsonwebtoken": "^9.0.0", + "passport-strategy": "^1.0.0" + } + }, + "node_modules/passport-strategy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", + "integrity": "sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==", + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/path-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/path-case/-/path-case-3.0.4.tgz", @@ -21082,6 +21284,12 @@ "integrity": "sha512-d+RQGp0MAYTIaDBIMmOfMwz3E+LOZnxx1HZd5R18mmCZY0QBlK0LDZfPc8FW8Ed2DlvsuE6PRjroDY+wg4+j/Q==", "dev": true }, + "node_modules/pause": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", + "integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==", + "peer": true + }, "node_modules/peek-stream": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/peek-stream/-/peek-stream-1.1.3.tgz", @@ -26061,16 +26269,21 @@ "@apollo/subgraph": "^2.4.12", "@google-cloud/storage": "^7.7.0", "@nestjs/apollo": "^12.0.7", + "@nestjs/axios": "^3.0.1", "@nestjs/common": "^9.0.0", "@nestjs/config": "^3.0.0", "@nestjs/core": "^9.0.0", "@nestjs/graphql": "^12.0.8", + "@nestjs/jwt": "^10.2.0", "@nestjs/mongoose": "^10.0.1", + "@nestjs/passport": "^10.0.3", "@nestjs/platform-express": "^9.0.0", + "@types/passport-jwt": "^3.0.13", "csv-parser": "^3.0.0", "graphql-type-json": "^0.3.2", "jsonschema": "^1.4.1", "mongoose": "^7.4.3", + "passport-jwt": "^4.0.1", "reflect-metadata": "^0.1.13", "rimraf": "^3.0.2", "rxjs": "^7.2.0" @@ -31395,6 +31608,12 @@ "tslib": "2.6.2" } }, + "@nestjs/axios": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@nestjs/axios/-/axios-3.0.1.tgz", + "integrity": "sha512-VlOZhAGDmOoFdsmewn8AyClAdGpKXQQaY1+3PGB+g6ceurGIdTxZgRX3VXc1T6Zs60PedWjg3A82TDOB05mrzQ==", + "requires": {} + }, "@nestjs/cli": { "version": "9.5.0", "resolved": "https://registry.npmjs.org/@nestjs/cli/-/cli-9.5.0.tgz", @@ -31665,6 +31884,15 @@ } } }, + "@nestjs/jwt": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/@nestjs/jwt/-/jwt-10.2.0.tgz", + "integrity": "sha512-x8cG90SURkEiLOehNaN2aRlotxT0KZESUliOPKKnjWiyJOcWurkF3w345WOX0P4MgFzUjGoZ1Sy0aZnxeihT0g==", + "requires": { + "@types/jsonwebtoken": "9.0.5", + "jsonwebtoken": "9.0.2" + } + }, "@nestjs/mapped-types": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-2.0.2.tgz", @@ -31677,6 +31905,12 @@ "integrity": "sha512-woUViG28WKf/kRiv6NFXu4Oc0DvAPeX4+fT4coDVt2OqndnfJZTXwkkys23uVsBpKSIflRyjPvmcVBHQvcunZw==", "requires": {} }, + "@nestjs/passport": { + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/@nestjs/passport/-/passport-10.0.3.tgz", + "integrity": "sha512-znJ9Y4S8ZDVY+j4doWAJ8EuuVO7SkQN3yOBmzxbGaXbvcSwFDAdGJ+OMCg52NdzIO4tQoN4pYKx8W6M0ArfFRQ==", + "requires": {} + }, "@nestjs/platform-express": { "version": "9.4.3", "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-9.4.3.tgz", @@ -33962,6 +34196,14 @@ "integrity": "sha512-s2cfwagOQAS8o06TcwKfr9Wx11dNGbH2E9vJz1cqV+a/LOyhWNLUNd6JSRYNzvB4d29UuJX2M0Dj9vE1T8fRXw==", "dev": true }, + "@types/jsonwebtoken": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.5.tgz", + "integrity": "sha512-VRLSGzik+Unrup6BsouBeHsf4d1hOEgYWTm/7Nmw1sXoN1+tRly/Gy/po3yeahnP4jfnQWWAhQAqcNfH7ngOkA==", + "requires": { + "@types/node": "*" + } + }, "@types/lodash": { "version": "4.14.198", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.198.tgz", @@ -34021,6 +34263,33 @@ "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==" }, + "@types/passport": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.16.tgz", + "integrity": "sha512-FD0qD5hbPWQzaM0wHUnJ/T0BBCJBxCeemtnCwc/ThhTg3x9jfrAcRUmj5Dopza+MfFS9acTe3wk7rcVnRIp/0A==", + "requires": { + "@types/express": "*" + } + }, + "@types/passport-jwt": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/@types/passport-jwt/-/passport-jwt-3.0.13.tgz", + "integrity": "sha512-fjHaC6Bv8EpMMqzTnHP32SXlZGaNfBPC/Po5dmRGYi2Ky7ljXPbGnOy+SxZqa6iZvFgVhoJ1915Re3m93zmcfA==", + "requires": { + "@types/express": "*", + "@types/jsonwebtoken": "*", + "@types/passport-strategy": "*" + } + }, + "@types/passport-strategy": { + "version": "0.2.38", + "resolved": "https://registry.npmjs.org/@types/passport-strategy/-/passport-strategy-0.2.38.tgz", + "integrity": "sha512-GC6eMqqojOooq993Tmnmp7AUTbbQSgilyvpCYQjT+H6JfG/g6RGc7nXEniZlp0zyKJ0WUdOiZWLBZft9Yug1uA==", + "requires": { + "@types/express": "*", + "@types/passport": "*" + } + }, "@types/prettier": { "version": "2.7.3", "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz", @@ -40333,6 +40602,60 @@ "resolved": "https://registry.npmjs.org/jsonschema/-/jsonschema-1.4.1.tgz", "integrity": "sha512-S6cATIPVv1z0IlxdN+zUk5EPjkGCdnhN4wVSBlvoUO1tOLJootbo9CquNJmbIh4yikWHiUedhRYrNPn1arpEmQ==" }, + "jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "requires": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "dependencies": { + "jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "requires": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "requires": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, "jwa": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", @@ -40457,6 +40780,36 @@ "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", "dev": true }, + "lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" + }, + "lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" + }, + "lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" + }, + "lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" + }, + "lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, + "lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" + }, "lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", @@ -40474,6 +40827,11 @@ "resolved": "https://registry.npmjs.org/lodash.omit/-/lodash.omit-4.5.0.tgz", "integrity": "sha512-XeqSp49hNGmlkj2EJlfrQFIzQ6lXdNro9sddtQzcJY8QaoC2GO0DT7xaIokHeyM+mIT0mPMlPvkYzg2xCuHdZg==" }, + "lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" + }, "lodash.sortby": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", @@ -41363,6 +41721,31 @@ "tslib": "^2.0.3" } }, + "passport": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/passport/-/passport-0.7.0.tgz", + "integrity": "sha512-cPLl+qZpSc+ireUvt+IzqbED1cHHkDoVYMo30jbJIdOOjQ1MQYZBPiNvmi8UM6lJuOpTPXJGZQk0DtC4y61MYQ==", + "peer": true, + "requires": { + "passport-strategy": "1.x.x", + "pause": "0.0.1", + "utils-merge": "^1.0.1" + } + }, + "passport-jwt": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/passport-jwt/-/passport-jwt-4.0.1.tgz", + "integrity": "sha512-UCKMDYhNuGOBE9/9Ycuoyh7vP6jpeTp/+sfMJl7nLff/t6dps+iaeE0hhNkKN8/HZHcJ7lCdOyDxHdDoxoSvdQ==", + "requires": { + "jsonwebtoken": "^9.0.0", + "passport-strategy": "^1.0.0" + } + }, + "passport-strategy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", + "integrity": "sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==" + }, "path-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/path-case/-/path-case-3.0.4.tgz", @@ -41441,6 +41824,12 @@ "integrity": "sha512-d+RQGp0MAYTIaDBIMmOfMwz3E+LOZnxx1HZd5R18mmCZY0QBlK0LDZfPc8FW8Ed2DlvsuE6PRjroDY+wg4+j/Q==", "dev": true }, + "pause": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", + "integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==", + "peer": true + }, "peek-stream": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/peek-stream/-/peek-stream-1.1.3.tgz", @@ -42596,22 +42985,26 @@ "@apollo/subgraph": "^2.4.12", "@google-cloud/storage": "^7.7.0", "@nestjs/apollo": "^12.0.7", + "@nestjs/axios": "^3.0.1", "@nestjs/cli": "^9.0.0", "@nestjs/common": "^9.0.0", "@nestjs/config": "^3.0.0", "@nestjs/core": "^9.0.0", "@nestjs/graphql": "^12.0.8", + "@nestjs/jwt": "^10.2.0", "@nestjs/mongoose": "^10.0.1", + "@nestjs/passport": "^10.0.3", "@nestjs/platform-express": "^9.0.0", "@nestjs/schematics": "^9.0.0", "@nestjs/testing": "^9.0.0", "@types/express": "^4.17.13", "@types/jest": "28.1.8", "@types/node": "^16.0.0", + "@types/passport-jwt": "*", "@types/supertest": "^2.0.11", "@typescript-eslint/eslint-plugin": "^5.0.0", "@typescript-eslint/parser": "^5.0.0", - "csv-parser": "*", + "csv-parser": "^3.0.0", "eslint": "^8.0.1", "eslint-config-prettier": "^8.3.0", "eslint-plugin-prettier": "^4.0.0", @@ -42619,6 +43012,7 @@ "jest": "28.1.3", "jsonschema": "^1.4.1", "mongoose": "^7.4.3", + "passport-jwt": "^4.0.1", "prettier": "^2.3.2", "reflect-metadata": "^0.1.13", "rimraf": "^3.0.2", diff --git a/packages/server/package.json b/packages/server/package.json index 4acdc450..304d0274 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -24,16 +24,21 @@ "@apollo/subgraph": "^2.4.12", "@google-cloud/storage": "^7.7.0", "@nestjs/apollo": "^12.0.7", + "@nestjs/axios": "^3.0.1", "@nestjs/common": "^9.0.0", "@nestjs/config": "^3.0.0", "@nestjs/core": "^9.0.0", "@nestjs/graphql": "^12.0.8", + "@nestjs/jwt": "^10.2.0", "@nestjs/mongoose": "^10.0.1", + "@nestjs/passport": "^10.0.3", "@nestjs/platform-express": "^9.0.0", + "@types/passport-jwt": "^3.0.13", "csv-parser": "^3.0.0", "graphql-type-json": "^0.3.2", "jsonschema": "^1.4.1", "mongoose": "^7.4.3", + "passport-jwt": "^4.0.1", "reflect-metadata": "^0.1.13", "rimraf": "^3.0.2", "rxjs": "^7.2.0" diff --git a/packages/server/schema.gql b/packages/server/schema.gql index e6829de7..622582aa 100644 --- a/packages/server/schema.gql +++ b/packages/server/schema.gql @@ -131,8 +131,6 @@ type Mutation { createEntry(entry: EntryCreate!, dataset: ID!): Entry! createUploadSession(dataset: ID!): UploadSession! completeUploadSession(session: ID!): UploadResult! - uploadEntryCSV: Boolean! - processEntryUploads: Boolean! createTags(study: ID!, entries: [ID!]!): [Tag!]! assignTag(study: ID!): Tag completeTag(tag: ID!, data: JSON!): Boolean! diff --git a/packages/server/src/app.module.ts b/packages/server/src/app.module.ts index 1e049ca6..2e28fdd1 100644 --- a/packages/server/src/app.module.ts +++ b/packages/server/src/app.module.ts @@ -11,6 +11,7 @@ import { StudyModule } from './study/study.module'; import { EntryModule } from './entry/entry.module'; import { TagModule } from './tag/tag.module'; import { SharedModule } from './shared/shared.module'; +import { AuthModule } from './auth/auth.module'; @Module({ imports: [ @@ -38,7 +39,8 @@ import { SharedModule } from './shared/shared.module'; StudyModule, EntryModule, TagModule, - SharedModule + SharedModule, + AuthModule ], }) export class AppModule {} diff --git a/packages/server/src/auth/auth.module.ts b/packages/server/src/auth/auth.module.ts new file mode 100644 index 00000000..44a59682 --- /dev/null +++ b/packages/server/src/auth/auth.module.ts @@ -0,0 +1,43 @@ +import { forwardRef, Module } from '@nestjs/common'; +import { JwtModule, JwtModuleOptions } from '@nestjs/jwt'; +import { AuthService } from './auth.service'; +import { PassportModule } from '@nestjs/passport'; +import { JwtStrategy } from './jwt.strategy'; +import { JwtAuthGuard } from './jwt.guard'; +import { OrganizationModule } from '../organization/organization.module'; +import { HttpModule } from '@nestjs/axios'; + +@Module({ + imports: [ + PassportModule, + OrganizationModule, + HttpModule, + JwtModule.registerAsync({ + imports: [forwardRef(() => AuthModule)], + inject: [AuthService], + useFactory: async (authService: AuthService) => { + const options: JwtModuleOptions = { + publicKey: await authService.getPublicKey(), + signOptions: { + algorithm: 'RS256' + } + }; + return options; + } + }) + ], + providers: [ + AuthService, + JwtAuthGuard, + { + provide: JwtStrategy, + inject: [AuthService], + useFactory: async (authService: AuthService) => { + const key = await authService.getPublicKey(); + return new JwtStrategy(key); + } + } + ], + exports: [AuthService] +}) +export class AuthModule {} diff --git a/packages/server/src/auth/auth.service.ts b/packages/server/src/auth/auth.service.ts new file mode 100644 index 00000000..325c3dbb --- /dev/null +++ b/packages/server/src/auth/auth.service.ts @@ -0,0 +1,29 @@ +import { Injectable } from '@nestjs/common'; +import { HttpService } from '@nestjs/axios'; +import { ConfigService } from '@nestjs/config'; +import { firstValueFrom } from 'rxjs'; + +@Injectable() +export class AuthService { + private publicKey: string | null = null; + + constructor(private readonly httpService: HttpService, private readonly configService: ConfigService) {} + + // TODO: In the future this will be replaced by a library which handles + // key rotation + async queryForPublicKey(): Promise { + const query = this.configService.getOrThrow('auth.publicKeyUrl'); + + const response = await firstValueFrom(this.httpService.get(query)); + return response.data[0]; + } + + async getPublicKey(): Promise { + // TODO: Replace with an actual call to the auth service + if (this.publicKey === null) { + this.publicKey = await this.queryForPublicKey(); + } + + return this.publicKey; + } +} diff --git a/packages/server/src/auth/jwt.guard.ts b/packages/server/src/auth/jwt.guard.ts new file mode 100644 index 00000000..d8da9f86 --- /dev/null +++ b/packages/server/src/auth/jwt.guard.ts @@ -0,0 +1,16 @@ +import { Injectable, ExecutionContext } from '@nestjs/common'; +import { AuthGuard } from '@nestjs/passport'; +import { GqlExecutionContext } from '@nestjs/graphql'; + +@Injectable() +export class JwtAuthGuard extends AuthGuard('jwt') { + getRequest(context: ExecutionContext) { + // Return HTTP context + if (context.getType() === 'http') { + return context.switchToHttp().getRequest(); + } + // Return GraphQL context + const ctx = GqlExecutionContext.create(context); + return ctx.getContext().req; + } +} diff --git a/packages/server/src/auth/jwt.strategy.ts b/packages/server/src/auth/jwt.strategy.ts new file mode 100644 index 00000000..0e1eb5c9 --- /dev/null +++ b/packages/server/src/auth/jwt.strategy.ts @@ -0,0 +1,19 @@ +import { Injectable } from '@nestjs/common'; +import { PassportStrategy } from '@nestjs/passport'; +import { ExtractJwt, Strategy } from 'passport-jwt'; +import { TokenPayload } from './user.dto'; + +@Injectable() +export class JwtStrategy extends PassportStrategy(Strategy) { + constructor(publicKey: string) { + super({ + jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), + ignoreExpiration: false, + secretOrKey: publicKey + }); + } + + validate(payload: TokenPayload) { + return payload; + } +} diff --git a/packages/server/src/auth/jwt.stretegy.ts b/packages/server/src/auth/jwt.stretegy.ts new file mode 100644 index 00000000..0e1eb5c9 --- /dev/null +++ b/packages/server/src/auth/jwt.stretegy.ts @@ -0,0 +1,19 @@ +import { Injectable } from '@nestjs/common'; +import { PassportStrategy } from '@nestjs/passport'; +import { ExtractJwt, Strategy } from 'passport-jwt'; +import { TokenPayload } from './user.dto'; + +@Injectable() +export class JwtStrategy extends PassportStrategy(Strategy) { + constructor(publicKey: string) { + super({ + jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), + ignoreExpiration: false, + secretOrKey: publicKey + }); + } + + validate(payload: TokenPayload) { + return payload; + } +} diff --git a/packages/server/src/auth/user.decorator.ts b/packages/server/src/auth/user.decorator.ts new file mode 100644 index 00000000..27a37e42 --- /dev/null +++ b/packages/server/src/auth/user.decorator.ts @@ -0,0 +1,7 @@ +import { GqlExecutionContext } from '@nestjs/graphql'; +import { ExecutionContext, createParamDecorator } from '@nestjs/common'; + +export const UserContext = createParamDecorator((_data: unknown, ctx: ExecutionContext) => { + const gqlCtx = GqlExecutionContext.create(ctx); + return gqlCtx.getContext().req.user; +}); diff --git a/packages/server/src/auth/user.dto.ts b/packages/server/src/auth/user.dto.ts new file mode 100644 index 00000000..67da7251 --- /dev/null +++ b/packages/server/src/auth/user.dto.ts @@ -0,0 +1,9 @@ +// TODO: In the future this type will be retrived from the auth microservice +export interface TokenPayload { + id: string; + projectId: string; + role: number; + iat: number; + exp: number; + iss: string; +} diff --git a/packages/server/src/config/configuration.ts b/packages/server/src/config/configuration.ts index f404aa0e..e7d261fe 100644 --- a/packages/server/src/config/configuration.ts +++ b/packages/server/src/config/configuration.ts @@ -18,5 +18,8 @@ export default () => ({ }, entry: { signedURLExpiration: process.env.GCP_STORAGE_ENTRY_SIGNED_URL_EXPIRATION || (15 * 60 * 1000) // 15 minutes + }, + auth: { + publicKeyUrl: process.env.AUTH_PUBLIC_KEY_URL || 'https://test-auth-service.sail.codes/public-key' } }); diff --git a/packages/server/src/dataset/dataset.resolver.ts b/packages/server/src/dataset/dataset.resolver.ts index 00187534..cc9357ce 100644 --- a/packages/server/src/dataset/dataset.resolver.ts +++ b/packages/server/src/dataset/dataset.resolver.ts @@ -5,9 +5,11 @@ import { Organization } from '../organization/organization.model'; import { OrganizationContext } from '../organization/organization.context'; import { DatasetCreate } from './dtos/create.dto'; import { DatasetPipe } from './pipes/dataset.pipe'; -import { BadRequestException } from '@nestjs/common'; +import { BadRequestException, UseGuards } from '@nestjs/common'; +import { JwtAuthGuard } from 'src/auth/jwt.guard'; // TODO: Add authentication +@UseGuards(JwtAuthGuard) @Resolver(() => Dataset) export class DatasetResolver { constructor(private readonly datasetService: DatasetService) {} diff --git a/packages/server/src/entry/entry.module.ts b/packages/server/src/entry/entry.module.ts index 586704a9..947c24ab 100644 --- a/packages/server/src/entry/entry.module.ts +++ b/packages/server/src/entry/entry.module.ts @@ -9,7 +9,6 @@ import { UploadSession, UploadSessionSchema } from './models/upload-session.mode import { EntryUpload, EntryUploadSchema } from './models/entry-upload.model'; import { UploadSessionResolver } from './resolvers/upload-session.resolver'; import { UploadSessionService } from './services/upload-session.service'; -import { EntryUploadResolver } from './resolvers/entry-upload.resolver'; import { EntryUploadService } from './services/entry-upload.service'; import { UploadSessionPipe } from './pipes/upload-session.pipe'; import { GcpModule } from '../gcp/gcp.module'; @@ -33,7 +32,6 @@ import { CsvValidationService } from './services/csv-validation.service'; UploadSessionService, UploadSessionResolver, UploadSessionPipe, - EntryUploadResolver, EntryUploadService, CsvValidationService ], diff --git a/packages/server/src/entry/resolvers/entry-upload.resolver.ts b/packages/server/src/entry/resolvers/entry-upload.resolver.ts deleted file mode 100644 index 74a5e80c..00000000 --- a/packages/server/src/entry/resolvers/entry-upload.resolver.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Mutation, Resolver } from '@nestjs/graphql'; -import { EntryUploadService } from '../services/entry-upload.service'; - -@Resolver() -export class EntryUploadResolver { - constructor(private readonly entryUploadService: EntryUploadService) {} - - // TODO: Create a result type to carry any potential errors - @Mutation(() => Boolean) - async uploadEntryCSV(): Promise { - return false; - } - - @Mutation(() => Boolean) - async processEntryUploads(): Promise { - return false; - } -} diff --git a/packages/server/src/entry/resolvers/entry.resolver.ts b/packages/server/src/entry/resolvers/entry.resolver.ts index 0f59274c..23ef0bf5 100644 --- a/packages/server/src/entry/resolvers/entry.resolver.ts +++ b/packages/server/src/entry/resolvers/entry.resolver.ts @@ -4,7 +4,10 @@ import { Entry } from '../models/entry.model'; import { EntryCreate } from '../dtos/create.dto'; import { EntryService } from '../services/entry.service'; import { DatasetPipe } from '../../dataset/pipes/dataset.pipe'; +import { UseGuards } from '@nestjs/common'; +import { JwtAuthGuard } from '../../auth/jwt.guard'; +@UseGuards(JwtAuthGuard) @Resolver(() => Entry) export class EntryResolver { constructor(private readonly entryService: EntryService) {} diff --git a/packages/server/src/entry/resolvers/upload-session.resolver.ts b/packages/server/src/entry/resolvers/upload-session.resolver.ts index d5a407a5..3074e908 100644 --- a/packages/server/src/entry/resolvers/upload-session.resolver.ts +++ b/packages/server/src/entry/resolvers/upload-session.resolver.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@nestjs/common'; +import { Injectable, UseGuards } from '@nestjs/common'; import { UploadSession } from '../models/upload-session.model'; import { UploadSessionService } from '../services/upload-session.service'; import { Dataset } from '../../dataset/dataset.model'; @@ -6,7 +6,10 @@ import { Args, ID, Mutation, Query } from '@nestjs/graphql'; import { UploadSessionPipe } from '../pipes/upload-session.pipe'; import { DatasetPipe } from '../../dataset/pipes/dataset.pipe'; import { UploadResult } from '../dtos/upload-result.dto'; +import { JwtAuthGuard } from '../../auth/jwt.guard'; + +@UseGuards(JwtAuthGuard) @Injectable() export class UploadSessionResolver { constructor(private readonly uploadSessionService: UploadSessionService) {} diff --git a/packages/server/src/organization/organization.resolver.ts b/packages/server/src/organization/organization.resolver.ts index e4d3807c..48119bc5 100644 --- a/packages/server/src/organization/organization.resolver.ts +++ b/packages/server/src/organization/organization.resolver.ts @@ -1,8 +1,10 @@ import { Resolver, Query, Mutation, Args } from '@nestjs/graphql'; -import {OrganizationCreate} from './dtos/create.dto'; +import { OrganizationCreate } from './dtos/create.dto'; import { Organization } from './organization.model'; import { OrganizationService } from './organization.service'; import { CreateOrganizationPipe } from './pipes/create.pipe'; +import { UseGuards } from '@nestjs/common'; +import { JwtAuthGuard } from 'src/auth/jwt.guard'; @Resolver(() => Organization) export class OrganizationResolver { @@ -13,6 +15,7 @@ export class OrganizationResolver { return this.orgService.find(); } + @UseGuards(JwtAuthGuard) @Query(() => Boolean) async exists(@Args('name') name: string): Promise { const existingProject = await this.orgService.findByName(name); @@ -20,6 +23,7 @@ export class OrganizationResolver { } // TODO: Add authentication guard + @UseGuards(JwtAuthGuard) @Mutation(() => Organization) async createOrganization(@Args('organization', CreateOrganizationPipe) organization: OrganizationCreate): Promise { return this.orgService.create(organization); diff --git a/packages/server/src/project/project.resolver.ts b/packages/server/src/project/project.resolver.ts index 7ca89eb0..9f74a1a3 100644 --- a/packages/server/src/project/project.resolver.ts +++ b/packages/server/src/project/project.resolver.ts @@ -1,12 +1,15 @@ -import {BadRequestException} from '@nestjs/common'; +import { BadRequestException, UseGuards } from '@nestjs/common'; import { Resolver, Mutation, Query, Args, ID } from '@nestjs/graphql'; import { OrganizationContext } from 'src/organization/organization.context'; import { Organization } from 'src/organization/organization.model'; import { ProjectCreate } from './dtos/create.dto'; import { Project } from './project.model'; import { ProjectService } from './project.service'; -import {ProjectPipe} from './pipes/project.pipe'; +import { ProjectPipe } from './pipes/project.pipe'; +import { JwtAuthGuard } from '../auth/jwt.guard'; + +@UseGuards(JwtAuthGuard) @Resolver(() => Project) export class ProjectResolver { constructor(private readonly projectService: ProjectService) {} diff --git a/packages/server/src/study/study.resolver.ts b/packages/server/src/study/study.resolver.ts index 8bac41d5..7ae0e935 100644 --- a/packages/server/src/study/study.resolver.ts +++ b/packages/server/src/study/study.resolver.ts @@ -6,7 +6,10 @@ import { StudyPipe } from './pipes/study.pipe'; import { StudyCreate } from './dtos/create.dto'; import { StudyService } from './study.service'; import { StudyCreatePipe } from './pipes/create.pipe'; +import { UseGuards } from '@nestjs/common'; +import { JwtAuthGuard } from '../auth/jwt.guard'; +@UseGuards(JwtAuthGuard) @Resolver(() => Study) export class StudyResolver { constructor(private readonly studyService: StudyService) {} diff --git a/packages/server/src/tag/tag.resolver.ts b/packages/server/src/tag/tag.resolver.ts index c2032795..147d459a 100644 --- a/packages/server/src/tag/tag.resolver.ts +++ b/packages/server/src/tag/tag.resolver.ts @@ -7,7 +7,11 @@ import { EntriesPipe, EntryPipe } from '../entry/pipes/entry.pipe'; import { Entry } from '../entry/models/entry.model'; import { TagPipe } from './pipes/tag.pipe'; import JSON from 'graphql-type-json'; +import { UseGuards } from '@nestjs/common'; +import { JwtAuthGuard } from '../auth/jwt.guard'; + +@UseGuards(JwtAuthGuard) @Resolver(() => Tag) export class TagResolver { constructor(private readonly tagService: TagService, private readonly entryPipe: EntryPipe, private readonly studyPipe: StudyPipe) {} From 5c72da4717675568f94e9ab11cc83062ceae2fa7 Mon Sep 17 00:00:00 2001 From: cbolles Date: Wed, 20 Dec 2023 10:00:21 -0500 Subject: [PATCH 2/2] Remove duplicated file --- packages/server/src/auth/jwt.stretegy.ts | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 packages/server/src/auth/jwt.stretegy.ts diff --git a/packages/server/src/auth/jwt.stretegy.ts b/packages/server/src/auth/jwt.stretegy.ts deleted file mode 100644 index 0e1eb5c9..00000000 --- a/packages/server/src/auth/jwt.stretegy.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { PassportStrategy } from '@nestjs/passport'; -import { ExtractJwt, Strategy } from 'passport-jwt'; -import { TokenPayload } from './user.dto'; - -@Injectable() -export class JwtStrategy extends PassportStrategy(Strategy) { - constructor(publicKey: string) { - super({ - jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), - ignoreExpiration: false, - secretOrKey: publicKey - }); - } - - validate(payload: TokenPayload) { - return payload; - } -}