diff --git a/package-lock.json b/package-lock.json index 989bb4e..799bdfe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,11 +11,17 @@ "dependencies": { "@nestjs/common": "^10.3.9", "@nestjs/core": "^10.0.0", + "@nestjs/jwt": "^10.2.0", + "@nestjs/passport": "^10.0.3", "@nestjs/platform-express": "^10.0.0", "@prisma/client": "^5.15.1", + "@types/passport-jwt": "^4.0.1", + "axios": "^1.7.2", "class-transformer": "^0.5.1", "class-validator": "^0.14.1", "dotenv": "^16.4.5", + "passport": "^0.7.0", + "passport-jwt": "^4.0.1", "reflect-metadata": "^0.2.0", "rxjs": "^7.8.1" }, @@ -1799,6 +1805,35 @@ } } }, + "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/jwt/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/@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": "10.3.9", "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-10.3.9.tgz", @@ -2097,7 +2132,6 @@ "version": "1.19.5", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", - "dev": true, "dependencies": { "@types/connect": "*", "@types/node": "*" @@ -2107,7 +2141,6 @@ "version": "3.4.38", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", - "dev": true, "dependencies": { "@types/node": "*" } @@ -2148,7 +2181,6 @@ "version": "4.17.21", "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", - "dev": true, "dependencies": { "@types/body-parser": "*", "@types/express-serve-static-core": "^4.17.33", @@ -2160,7 +2192,6 @@ "version": "4.19.5", "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.5.tgz", "integrity": "sha512-y6W03tvrACO72aijJ5uF02FRq5cgDR9lUxddQ8vyF+GvmjJQqbzDcJngEjURc+ZsG31VI3hODNZJ2URj86pzmg==", - "dev": true, "dependencies": { "@types/node": "*", "@types/qs": "*", @@ -2180,8 +2211,7 @@ "node_modules/@types/http-errors": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", - "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", - "dev": true + "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==" }, "node_modules/@types/istanbul-lib-coverage": { "version": "2.0.6", @@ -2223,6 +2253,14 @@ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true }, + "node_modules/@types/jsonwebtoken": { + "version": "9.0.6", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.6.tgz", + "integrity": "sha512-/5hndP5dCjloafCXns6SZyESp3Ldq7YjH3zwzwczYnjxIT0Fqzk5ROSYVGfFyczIue7IUEj8hkvLbPoLQ18vQw==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/methods": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/@types/methods/-/methods-1.1.4.tgz", @@ -2232,29 +2270,51 @@ "node_modules/@types/mime": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", - "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", - "dev": true + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==" }, "node_modules/@types/node": { "version": "20.14.8", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.8.tgz", "integrity": "sha512-DO+2/jZinXfROG7j7WKFn/3C6nFwxy2lLpgLjEXJz+0XKphZlTLJ14mo8Vfg8X5BWN6XjyESXq+LcYdT7tR3bA==", - "dev": true, "dependencies": { "undici-types": "~5.26.4" } }, + "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": "4.0.1", + "resolved": "https://registry.npmjs.org/@types/passport-jwt/-/passport-jwt-4.0.1.tgz", + "integrity": "sha512-Y0Ykz6nWP4jpxgEUYq8NoVZeCQPo1ZndJLfapI249g1jHChvRfZRO/LS3tqu26YgAS/laI1qx98sYGz0IalRXQ==", + "dependencies": { + "@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/qs": { "version": "6.9.15", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.15.tgz", - "integrity": "sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==", - "dev": true + "integrity": "sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==" }, "node_modules/@types/range-parser": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", - "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", - "dev": true + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==" }, "node_modules/@types/semver": { "version": "7.5.8", @@ -2266,7 +2326,6 @@ "version": "0.17.4", "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", - "dev": true, "dependencies": { "@types/mime": "^1", "@types/node": "*" @@ -2276,7 +2335,6 @@ "version": "1.15.7", "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", - "dev": true, "dependencies": { "@types/http-errors": "*", "@types/node": "*", @@ -2911,8 +2969,18 @@ "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/axios": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.2.tgz", + "integrity": "sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } }, "node_modules/babel-jest": { "version": "29.7.0", @@ -3227,6 +3295,11 @@ "ieee754": "^1.1.13" } }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -3530,7 +3603,6 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, "dependencies": { "delayed-stream": "~1.0.0" }, @@ -3803,7 +3875,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dev": true, "engines": { "node": ">=0.4.0" } @@ -3903,6 +3974,14 @@ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "dev": true }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -4646,6 +4725,26 @@ "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", "dev": true }, + "node_modules/follow-redirects": { + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/foreground-child": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.2.1.tgz", @@ -4716,7 +4815,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dev": true, "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", @@ -6194,6 +6292,46 @@ "graceful-fs": "^4.1.6" } }, + "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/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/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/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -6275,6 +6413,36 @@ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "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", @@ -6287,6 +6455,11 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "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/log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", @@ -6512,8 +6685,7 @@ "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/multer": { "version": "1.4.4-lts.1", @@ -6805,6 +6977,40 @@ "node": ">= 0.8" } }, + "node_modules/passport": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/passport/-/passport-0.7.0.tgz", + "integrity": "sha512-cPLl+qZpSc+ireUvt+IzqbED1cHHkDoVYMo30jbJIdOOjQ1MQYZBPiNvmi8UM6lJuOpTPXJGZQk0DtC4y61MYQ==", + "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-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -6877,6 +7083,11 @@ "node": ">=8" } }, + "node_modules/pause": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", + "integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==" + }, "node_modules/picocolors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", @@ -7085,6 +7296,12 @@ "node": ">= 0.10" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -7530,7 +7747,6 @@ "version": "7.6.2", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", - "dev": true, "bin": { "semver": "bin/semver.js" }, @@ -8491,8 +8707,7 @@ "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==", - "dev": true + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" }, "node_modules/universalify": { "version": "2.0.1", diff --git a/package.json b/package.json index 1b0f069..671b13f 100644 --- a/package.json +++ b/package.json @@ -22,11 +22,17 @@ "dependencies": { "@nestjs/common": "^10.3.9", "@nestjs/core": "^10.0.0", + "@nestjs/jwt": "^10.2.0", + "@nestjs/passport": "^10.0.3", "@nestjs/platform-express": "^10.0.0", "@prisma/client": "^5.15.1", + "@types/passport-jwt": "^4.0.1", + "axios": "^1.7.2", "class-transformer": "^0.5.1", "class-validator": "^0.14.1", "dotenv": "^16.4.5", + "passport": "^0.7.0", + "passport-jwt": "^4.0.1", "reflect-metadata": "^0.2.0", "rxjs": "^7.8.1" }, diff --git a/prisma/migrations/20240701121336_add_issuer_id_and_verified_schemas/migration.sql b/prisma/migrations/20240701121336_add_issuer_id_and_verified_schemas/migration.sql new file mode 100644 index 0000000..934cf45 --- /dev/null +++ b/prisma/migrations/20240701121336_add_issuer_id_and_verified_schemas/migration.sql @@ -0,0 +1,34 @@ +/* + Warnings: + + - You are about to drop the column `organizationId` on the `Schema` table. All the data in the column will be lost. + - Added the required column `issuerId` to the `Schema` table without a default value. This is not possible if the table is not empty. + +*/ +-- DropForeignKey +ALTER TABLE "Schema" DROP CONSTRAINT "Schema_organizationId_fkey"; + +-- AlterTable +ALTER TABLE "Schema" DROP COLUMN "organizationId", +ADD COLUMN "issuerId" TEXT NOT NULL; + +-- CreateTable +CREATE TABLE "_VerifierSchemas" ( + "A" TEXT NOT NULL, + "B" TEXT NOT NULL +); + +-- CreateIndex +CREATE UNIQUE INDEX "_VerifierSchemas_AB_unique" ON "_VerifierSchemas"("A", "B"); + +-- CreateIndex +CREATE INDEX "_VerifierSchemas_B_index" ON "_VerifierSchemas"("B"); + +-- AddForeignKey +ALTER TABLE "Schema" ADD CONSTRAINT "Schema_issuerId_fkey" FOREIGN KEY ("issuerId") REFERENCES "Organization"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "_VerifierSchemas" ADD CONSTRAINT "_VerifierSchemas_A_fkey" FOREIGN KEY ("A") REFERENCES "Organization"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "_VerifierSchemas" ADD CONSTRAINT "_VerifierSchemas_B_fkey" FOREIGN KEY ("B") REFERENCES "Schema"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/migrations/20240705072819_/migration.sql b/prisma/migrations/20240705072819_/migration.sql new file mode 100644 index 0000000..e7368e7 --- /dev/null +++ b/prisma/migrations/20240705072819_/migration.sql @@ -0,0 +1,71 @@ +/* + Warnings: + + - You are about to drop the column `definition` on the `Schema` table. All the data in the column will be lost. + - You are about to drop the column `issuerId` on the `Schema` table. All the data in the column will be lost. + - You are about to drop the `Credential` table. If the table is not empty, all the data it contains will be lost. + - You are about to drop the `CredentialDefinition` table. If the table is not empty, all the data it contains will be lost. + - You are about to drop the `RevocationRegistry` table. If the table is not empty, all the data it contains will be lost. + - You are about to drop the `_VerifierSchemas` table. If the table is not empty, all the data it contains will be lost. + - Added the required column `assuranceLevelId` to the `Organization` table without a default value. This is not possible if the table is not empty. + - Added the required column `namespaceId` to the `Organization` table without a default value. This is not possible if the table is not empty. + - Added the required column `organizationId` to the `Schema` table without a default value. This is not possible if the table is not empty. + - Added the required column `type` to the `Schema` table without a default value. This is not possible if the table is not empty. + +*/ +-- CreateEnum +CREATE TYPE "SchemaType" AS ENUM ('W3C', 'ANONCREDS'); + +-- DropForeignKey +ALTER TABLE "Credential" DROP CONSTRAINT "Credential_definitionId_fkey"; + +-- DropForeignKey +ALTER TABLE "Credential" DROP CONSTRAINT "Credential_revocationRegistryId_fkey"; + +-- DropForeignKey +ALTER TABLE "Credential" DROP CONSTRAINT "Credential_schemaId_fkey"; + +-- DropForeignKey +ALTER TABLE "Schema" DROP CONSTRAINT "Schema_issuerId_fkey"; + +-- DropForeignKey +ALTER TABLE "_VerifierSchemas" DROP CONSTRAINT "_VerifierSchemas_A_fkey"; + +-- DropForeignKey +ALTER TABLE "_VerifierSchemas" DROP CONSTRAINT "_VerifierSchemas_B_fkey"; + +-- AlterTable +ALTER TABLE "Organization" ADD COLUMN "assuranceLevelId" TEXT NOT NULL, +ADD COLUMN "namespaceId" TEXT NOT NULL; + +-- AlterTable +ALTER TABLE "Schema" DROP COLUMN "definition", +DROP COLUMN "issuerId", +ADD COLUMN "anonCredsDefinitionId" TEXT, +ADD COLUMN "organizationId" TEXT NOT NULL, +ADD COLUMN "type" "SchemaType" NOT NULL, +ADD COLUMN "w3cUri" TEXT; + +-- DropTable +DROP TABLE "Credential"; + +-- DropTable +DROP TABLE "CredentialDefinition"; + +-- DropTable +DROP TABLE "RevocationRegistry"; + +-- DropTable +DROP TABLE "_VerifierSchemas"; + +-- DropEnum +DROP TYPE "CredentialType"; + +-- AddForeignKey +ALTER TABLE "Organization" ADD CONSTRAINT "Organization_namespaceId_fkey" FOREIGN KEY ("namespaceId") REFERENCES "Namespace"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Organization" ADD CONSTRAINT "Organization_assuranceLevelId_fkey" FOREIGN KEY ("assuranceLevelId") REFERENCES "AssuranceLevel"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Schema" ADD CONSTRAINT "Schema_organizationId_fkey" FOREIGN KEY ("organizationId") REFERENCES "Organization"("id") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/prisma/migrations/20240718151011_add_attributes/migration.sql b/prisma/migrations/20240718151011_add_attributes/migration.sql new file mode 100644 index 0000000..5cc56c6 --- /dev/null +++ b/prisma/migrations/20240718151011_add_attributes/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "Organization" ADD COLUMN "attributes" TEXT[]; diff --git a/prisma/migrations/20240718151510_without_attribute/migration.sql b/prisma/migrations/20240718151510_without_attribute/migration.sql new file mode 100644 index 0000000..8191110 --- /dev/null +++ b/prisma/migrations/20240718151510_without_attribute/migration.sql @@ -0,0 +1,8 @@ +/* + Warnings: + + - You are about to drop the column `attributes` on the `Organization` table. All the data in the column will be lost. + +*/ +-- AlterTable +ALTER TABLE "Organization" DROP COLUMN "attributes"; diff --git a/prisma/migrations/20240718151719_name_to_anon_creds_schema_id/migration.sql b/prisma/migrations/20240718151719_name_to_anon_creds_schema_id/migration.sql new file mode 100644 index 0000000..ef54867 --- /dev/null +++ b/prisma/migrations/20240718151719_name_to_anon_creds_schema_id/migration.sql @@ -0,0 +1,9 @@ +/* + Warnings: + + - You are about to drop the column `anonCredsDefinitionId` on the `Schema` table. All the data in the column will be lost. + +*/ +-- AlterTable +ALTER TABLE "Schema" DROP COLUMN "anonCredsDefinitionId", +ADD COLUMN "anonCredsSchemaId" TEXT; diff --git a/prisma/migrations/20240718152250_add_post_route_for_schema/migration.sql b/prisma/migrations/20240718152250_add_post_route_for_schema/migration.sql new file mode 100644 index 0000000..03d93d2 --- /dev/null +++ b/prisma/migrations/20240718152250_add_post_route_for_schema/migration.sql @@ -0,0 +1,8 @@ +-- DropForeignKey +ALTER TABLE "Schema" DROP CONSTRAINT "Schema_organizationId_fkey"; + +-- AlterTable +ALTER TABLE "Schema" ALTER COLUMN "organizationId" DROP NOT NULL; + +-- AddForeignKey +ALTER TABLE "Schema" ADD CONSTRAINT "Schema_organizationId_fkey" FOREIGN KEY ("organizationId") REFERENCES "Organization"("id") ON DELETE SET NULL ON UPDATE CASCADE; diff --git a/prisma/migrations/20240718155719_add_govid_to_schema/migration.sql b/prisma/migrations/20240718155719_add_govid_to_schema/migration.sql new file mode 100644 index 0000000..0c8d3e4 --- /dev/null +++ b/prisma/migrations/20240718155719_add_govid_to_schema/migration.sql @@ -0,0 +1,11 @@ +/* + Warnings: + + - Added the required column `governanceAuthorityId` to the `Schema` table without a default value. This is not possible if the table is not empty. + +*/ +-- AlterTable +ALTER TABLE "Schema" ADD COLUMN "governanceAuthorityId" TEXT NOT NULL; + +-- AddForeignKey +ALTER TABLE "Schema" ADD CONSTRAINT "Schema_governanceAuthorityId_fkey" FOREIGN KEY ("governanceAuthorityId") REFERENCES "GovernanceAuthority"("id") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/prisma/migrations/20240719073117_add_attributes/migration.sql b/prisma/migrations/20240719073117_add_attributes/migration.sql new file mode 100644 index 0000000..7b7e916 --- /dev/null +++ b/prisma/migrations/20240719073117_add_attributes/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "Organization" ADD COLUMN "attributes" JSONB; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 8dca614..abb8e41 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -1,9 +1,6 @@ // This is your Prisma schema file, // learn more about it in the docs: https://pris.ly/d/prisma-schema -// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions? -// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init - datasource db { provider = "postgresql" url = env("DATABASE_URL") @@ -20,6 +17,7 @@ model GovernanceAuthority { organizations Organization[] namespaces Namespace[] assuranceLevels AssuranceLevel[] + schemas Schema[] ownedRelationships RegistryRelationship[] @relation("OwningRegistry") relatedToRelationships RegistryRelationship[] @relation("RelatedRegistry") } @@ -37,6 +35,7 @@ model Organization { assuranceLevel AssuranceLevel @relation(fields: [assuranceLevelId], references: [id]) onboardedAt DateTime @default(now()) schemas Schema[] + attributes Json? } model Schema { @@ -44,15 +43,11 @@ model Schema { name String type SchemaType w3cUri String? - anonCredsDefinitionId String? - organizationId String - organization Organization @relation(fields: [organizationId], references: [id]) -} - -model RevocationRegistry { - id String @id @default(uuid()) - name String - definition Json + anonCredsSchemaId String? + organizationId String? + organization Organization? @relation(fields: [organizationId], references: [id]) + governanceAuthorityId String // Add this line + governanceAuthority GovernanceAuthority @relation(fields: [governanceAuthorityId], references: [id]) } model Namespace { diff --git a/src/app.module.ts b/src/app.module.ts index c378cc4..0b2ef70 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -2,8 +2,8 @@ import { Module } from '@nestjs/common'; import { GovernanceAuthorityModule } from './governance-authority/governance-authority.module'; import { PrismaModule } from './prisma/prisma.module'; - +import { AuthModule } from './auth/auth.module'; @Module({ - imports: [PrismaModule, GovernanceAuthorityModule], + imports: [AuthModule,PrismaModule, GovernanceAuthorityModule], }) export class AppModule {} \ No newline at end of file diff --git a/src/auth/auth.controller.ts b/src/auth/auth.controller.ts new file mode 100644 index 0000000..31feeab --- /dev/null +++ b/src/auth/auth.controller.ts @@ -0,0 +1,15 @@ +import { Controller, Post, Body } from '@nestjs/common'; +import { AuthService } from './auth.service'; +import { LoginDto } from './dto/login.dto'; +import { Public } from './public.decorator'; + +@Controller('auth') +export class AuthController { + constructor(private authService: AuthService) {} + + @Public() + @Post('login') + async login(@Body() loginDto: LoginDto) { + return this.authService.login(loginDto); + } +} diff --git a/src/auth/auth.module.ts b/src/auth/auth.module.ts new file mode 100644 index 0000000..5657c31 --- /dev/null +++ b/src/auth/auth.module.ts @@ -0,0 +1,23 @@ +// src/auth/auth.module.ts +import { Module } from '@nestjs/common'; +import { JwtModule } from '@nestjs/jwt'; +import { PassportModule } from '@nestjs/passport'; +import { AuthService } from './auth.service'; +import { AuthController } from './auth.controller'; +import { JwtStrategy } from './jwt.strategy'; +import { PrismaModule } from '../prisma/prisma.module'; + +@Module({ + imports: [ + PrismaModule, + PassportModule, + JwtModule.register({ + secret: process.env.JWT_SECRET, // Make sure to set this in your .env file + signOptions: { expiresIn: '1h' }, + }), + ], + providers: [AuthService, JwtStrategy], + controllers: [AuthController], + exports: [AuthService], +}) +export class AuthModule {} \ No newline at end of file diff --git a/src/auth/auth.service.ts b/src/auth/auth.service.ts new file mode 100644 index 0000000..78e7525 --- /dev/null +++ b/src/auth/auth.service.ts @@ -0,0 +1,28 @@ + +import { Injectable, UnauthorizedException } from '@nestjs/common'; +import { JwtService } from '@nestjs/jwt'; +import { PrismaService } from '../prisma/prisma.service'; +import { LoginDto } from './dto/login.dto'; + +@Injectable() +export class AuthService { + constructor( + private prisma: PrismaService, + private jwtService: JwtService + ) {} + + async login(loginDto: LoginDto) { + const governanceAuthority = await this.prisma.governanceAuthority.findUnique({ + where: { did: loginDto.did }, + }); + + if (!governanceAuthority) { + throw new UnauthorizedException('Invalid credentials'); + } + + const payload = { did: governanceAuthority.did, sub: governanceAuthority.id }; + return { + access_token: this.jwtService.sign(payload), + }; + } +} \ No newline at end of file diff --git a/src/auth/current-user.decorator.ts b/src/auth/current-user.decorator.ts new file mode 100644 index 0000000..38d332b --- /dev/null +++ b/src/auth/current-user.decorator.ts @@ -0,0 +1,9 @@ + +import { createParamDecorator, ExecutionContext } from '@nestjs/common'; + +export const CurrentUser = createParamDecorator( + (data: unknown, ctx: ExecutionContext) => { + const request = ctx.switchToHttp().getRequest(); + return request.user; + }, +); \ No newline at end of file diff --git a/src/auth/dto/login.dto.ts b/src/auth/dto/login.dto.ts new file mode 100644 index 0000000..f90ffc7 --- /dev/null +++ b/src/auth/dto/login.dto.ts @@ -0,0 +1,6 @@ +import { IsString } from 'class-validator'; + +export class LoginDto { + @IsString() + did: string; +} \ No newline at end of file diff --git a/src/auth/jwt-auth.guard.ts b/src/auth/jwt-auth.guard.ts new file mode 100644 index 0000000..6e4f7af --- /dev/null +++ b/src/auth/jwt-auth.guard.ts @@ -0,0 +1,23 @@ +// src/auth/jwt-auth.guard.ts +import { ExecutionContext, Injectable } from '@nestjs/common'; +import { Reflector } from '@nestjs/core'; +import { AuthGuard } from '@nestjs/passport'; +import { IS_PUBLIC_KEY } from './public.decorator'; + +@Injectable() +export class JwtAuthGuard extends AuthGuard('jwt') { + constructor(private reflector: Reflector) { + super(); + } + + canActivate(context: ExecutionContext) { + const isPublic = this.reflector.getAllAndOverride(IS_PUBLIC_KEY, [ + context.getHandler(), + context.getClass(), + ]); + if (isPublic) { + return true; + } + return super.canActivate(context); + } +} \ No newline at end of file diff --git a/src/auth/jwt.strategy.ts b/src/auth/jwt.strategy.ts new file mode 100644 index 0000000..9b2ddd5 --- /dev/null +++ b/src/auth/jwt.strategy.ts @@ -0,0 +1,23 @@ +// src/auth/jwt.strategy.ts +import { Injectable } from '@nestjs/common'; +import { PassportStrategy } from '@nestjs/passport'; +import { ExtractJwt, Strategy } from 'passport-jwt'; +import { PrismaService } from '../prisma/prisma.service'; + +@Injectable() +export class JwtStrategy extends PassportStrategy(Strategy) { + constructor(private prisma: PrismaService) { + super({ + jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), + ignoreExpiration: false, + secretOrKey: process.env.JWT_SECRET, + }); + } + + async validate(payload: any) { + const governanceAuthority = await this.prisma.governanceAuthority.findUnique({ + where: { did: payload.did }, + }); + return { id: governanceAuthority.id, did: payload.did }; + } +} \ No newline at end of file diff --git a/src/auth/public.decorator.ts b/src/auth/public.decorator.ts new file mode 100644 index 0000000..426c3c8 --- /dev/null +++ b/src/auth/public.decorator.ts @@ -0,0 +1,5 @@ +// src/auth/public.decorator.ts +import { SetMetadata } from '@nestjs/common'; + +export const IS_PUBLIC_KEY = 'isPublic'; +export const Public = () => SetMetadata(IS_PUBLIC_KEY, true); \ No newline at end of file diff --git a/src/common/decorators/schema-validator.decorator.ts b/src/common/decorators/schema-validator.decorator.ts new file mode 100644 index 0000000..09abe5b --- /dev/null +++ b/src/common/decorators/schema-validator.decorator.ts @@ -0,0 +1,20 @@ +import { registerDecorator, ValidationOptions, ValidationArguments } from 'class-validator'; + +export function IsValidSchema(validationOptions?: ValidationOptions) { + return function (object: Object, propertyName: string) { + registerDecorator({ + name: 'isValidSchema', + target: object.constructor, + propertyName: propertyName, + options: validationOptions, + validator: { + validate(value: any, args: ValidationArguments) { + return value.w3cUri || value.anonCredsDefinitionId; + }, + defaultMessage(args: ValidationArguments) { + return 'Schema must have either w3cUri or anonCredsDefinitionId'; + }, + }, + }); + }; +} \ No newline at end of file diff --git a/src/governance-authority/dto/create-assurance-level.dto.ts b/src/governance-authority/dto/create-assurance-level.dto.ts new file mode 100644 index 0000000..507be0e --- /dev/null +++ b/src/governance-authority/dto/create-assurance-level.dto.ts @@ -0,0 +1,9 @@ +import { IsString, IsInt } from 'class-validator'; + +export class CreateAssuranceLevelDto { + @IsString() + name: string; + + @IsInt() + level: number; +} \ No newline at end of file diff --git a/src/governance-authority/dto/create-namespace.dto.ts b/src/governance-authority/dto/create-namespace.dto.ts new file mode 100644 index 0000000..9ed6e40 --- /dev/null +++ b/src/governance-authority/dto/create-namespace.dto.ts @@ -0,0 +1,6 @@ +import { IsString } from 'class-validator'; + +export class CreateNamespaceDto { + @IsString() + name: string; +} \ No newline at end of file diff --git a/src/governance-authority/dto/create-schema.dto.ts b/src/governance-authority/dto/create-schema.dto.ts new file mode 100644 index 0000000..cb50b39 --- /dev/null +++ b/src/governance-authority/dto/create-schema.dto.ts @@ -0,0 +1,24 @@ +// schema.dto.ts +import { IsString, IsEnum, IsOptional } from 'class-validator'; +import { SchemaType } from '@prisma/client'; + +export class CreateSchemaDto { + @IsString() + name: string; + + @IsEnum(SchemaType) + type: SchemaType; + + @IsString() + @IsOptional() + w3cUri?: string; + + @IsString() + @IsOptional() + anonCredsSchemaId?: string; + + @IsString() + @IsOptional() + organizationId?: string; + +} \ No newline at end of file diff --git a/src/governance-authority/dto/onboard-issuer.dto.ts b/src/governance-authority/dto/onboard-issuer.dto.ts new file mode 100644 index 0000000..5e0612c --- /dev/null +++ b/src/governance-authority/dto/onboard-issuer.dto.ts @@ -0,0 +1,48 @@ +import { IsString, IsArray, IsEnum, ValidateNested, IsOptional } from 'class-validator'; +import { Type } from 'class-transformer'; +import { SchemaType } from '@prisma/client'; +import { IsValidSchema } from '../../common/decorators/schema-validator.decorator'; + +class NewSchemaDto { + @IsString() + name: string; + + @IsEnum(SchemaType) + type: SchemaType; + + @IsString() + @IsOptional() + w3cUri?: string; + + @IsString() + @IsOptional() + anonCredsSchemaId?: string; + + @IsValidSchema() + _: any; +} + +export class OnboardIssuerDto { + @IsString() + name: string; + + @IsString() + did: string; + + @IsString() + namespaceId: string; + + @IsString() + assuranceLevelId: string; + + @IsArray() + @IsString({ each: true }) + @IsOptional() + existingSchemaIds?: string[]; + + @IsArray() + @ValidateNested({ each: true }) + @Type(() => NewSchemaDto) + @IsOptional() + newSchemas?: NewSchemaDto[]; +} \ No newline at end of file diff --git a/src/governance-authority/dto/onboard-verifier.dto.ts b/src/governance-authority/dto/onboard-verifier.dto.ts new file mode 100644 index 0000000..d515703 --- /dev/null +++ b/src/governance-authority/dto/onboard-verifier.dto.ts @@ -0,0 +1,23 @@ +import { IsString, IsArray, IsOptional } from 'class-validator'; +export class OnboardVerifierDto { + @IsString() + name: string; + + @IsString() + did: string; + + @IsString() + namespaceId: string; + + @IsString() + assuranceLevelId: string; + + @IsArray() + @IsString({ each: true }) + @IsOptional() + schemaIds?: string[]; + + @IsArray() + @IsString({ each: true }) + attributes:string[]; +} \ No newline at end of file diff --git a/src/governance-authority/governance-authority.controller.ts b/src/governance-authority/governance-authority.controller.ts index f00ed96..a071754 100644 --- a/src/governance-authority/governance-authority.controller.ts +++ b/src/governance-authority/governance-authority.controller.ts @@ -1,13 +1,108 @@ -import { Controller, Post, Body } from '@nestjs/common'; +// src/governance-authority/governance-authority.controller.ts +import { Controller, Post, Body ,Get,Param,UseGuards} from '@nestjs/common'; import { GovernanceAuthorityService } from './governance-authority.service'; import { CreateGovernanceAuthorityDto } from './dto/create-governance-authority.dto'; - -@Controller('registries') +import { OnboardIssuerDto } from './dto/onboard-issuer.dto'; +import { OnboardVerifierDto } from './dto/onboard-verifier.dto'; +import { CurrentUser } from '../auth/current-user.decorator'; +import { Public } from '../auth/public.decorator'; +import { CreateAssuranceLevelDto } from './dto/create-assurance-level.dto'; +import { CreateNamespaceDto } from './dto/create-namespace.dto'; +import { CreateSchemaDto } from './dto/create-schema.dto'; +import { JwtAuthGuard } from 'src/auth/jwt-auth.guard'; +@Controller('registry') export class GovernanceAuthorityController { - constructor(private governanceAuthorityService: GovernanceAuthorityService) {} + constructor( + private readonly governanceAuthorityService: GovernanceAuthorityService, + ) {} + @Public() @Post() - async createGovernanceAuthority(@Body() createGovernanceAuthorityDto: CreateGovernanceAuthorityDto) { - return this.governanceAuthorityService.createGovernanceAuthority(createGovernanceAuthorityDto); + async createTrustRegistry( + @Body() createGovernanceAuthorityDto: CreateGovernanceAuthorityDto, + ) { + return this.governanceAuthorityService.createGovernanceAuthority( + createGovernanceAuthorityDto, + ); + } + + @UseGuards(JwtAuthGuard) + @Post('onboard-issuer') + async onboardIssuer( + @Body() dto: OnboardIssuerDto, + @CurrentUser() user: { id: string; did: string }, + ) { + return this.governanceAuthorityService.onboardIssuer(dto, user.id); + } + + @UseGuards(JwtAuthGuard) + @Post('onboard-verifier') + async onboardVerifier( + @Body() dto: OnboardVerifierDto, + @CurrentUser() user: { id: string; did: string }, + ) { + return this.governanceAuthorityService.onboardVerifier(dto, user.id); + } + + @UseGuards(JwtAuthGuard) + @Post('namespace') + async createNamespace( + @Body() dto: CreateNamespaceDto, + @CurrentUser() user: { id: string; did: string }, + ) { + return this.governanceAuthorityService.createNamespace(dto, user.id); + } + + @UseGuards(JwtAuthGuard) + @Post('assurance-level') + async createAssuranceLevel( + @Body() dto: CreateAssuranceLevelDto, + @CurrentUser() user: { id: string; did: string }, + ) { + return this.governanceAuthorityService.createAssuranceLevel(dto, user.id); + } + + @UseGuards(JwtAuthGuard) + @Post('schema') + async createSchema( + @Body() createSchemaDto: CreateSchemaDto, + @CurrentUser() user: { id: string; did: string } + ) { + return this.governanceAuthorityService.createSchema(createSchemaDto, user.id); + } + + @Get(':governanceAuthorityId/schema') + async getSchemasByGovernanceAuthority(@Param('governanceAuthorityId') governanceAuthorityId: string) { + return this.governanceAuthorityService.getSchemasByGovernanceAuthority(governanceAuthorityId); + } + + @Get(':registryId/namespaces') + async getNamespaces(@Param('registryId') registryId: string) { + return this.governanceAuthorityService.getNamespaces(registryId); + } + + @Get(':registryId/assurance-levels') + async getAssuranceLevels(@Param('registryId') registryId: string) { + return this.governanceAuthorityService.getAssuranceLevels(registryId); + } + + @Get(':registryId/issuers') + async getIssuers(@Param('registryId') registryId: string) { + return this.governanceAuthorityService.getIssuers(registryId); + } + + @Get(':registryId/verifiers') + async getVerifiers(@Param('registryId') registryId: string) { + return this.governanceAuthorityService.getVerifiers(registryId); + } + + @Get('entities/:entityId') + async getEntity(@Param('entityId') entityId: string) { + return this.governanceAuthorityService.getEntity(entityId); + } + + @Get('entities/:entityId/authorizations') + async getEntityAuthorizations(@Param('entityId') entityId: string) { + return this.governanceAuthorityService.getEntityAuthorizations(entityId); } -} \ No newline at end of file +} diff --git a/src/governance-authority/governance-authority.service.ts b/src/governance-authority/governance-authority.service.ts index 67f9978..5d9cc83 100644 --- a/src/governance-authority/governance-authority.service.ts +++ b/src/governance-authority/governance-authority.service.ts @@ -1,18 +1,190 @@ -import { Injectable } from '@nestjs/common'; +import { Injectable, NotFoundException } from '@nestjs/common'; import { PrismaService } from '../prisma/prisma.service'; import { CreateGovernanceAuthorityDto } from './dto/create-governance-authority.dto'; - +import { OnboardIssuerDto } from './dto/onboard-issuer.dto'; +import { OnboardVerifierDto } from './dto/onboard-verifier.dto'; +import { OrganizationType } from '@prisma/client'; +import { CreateAssuranceLevelDto } from './dto/create-assurance-level.dto'; +import { CreateNamespaceDto } from './dto/create-namespace.dto'; +import { CreateSchemaDto } from './dto/create-schema.dto'; @Injectable() export class GovernanceAuthorityService { constructor(private prisma: PrismaService) {} - async createGovernanceAuthority(data: CreateGovernanceAuthorityDto) { + async createGovernanceAuthority(dto: CreateGovernanceAuthorityDto) { return this.prisma.governanceAuthority.create({ data: { - name: data.name, - did: data.did, + name: dto.name, + did: dto.did, + }, + }); + } + + async onboardIssuer(dto: OnboardIssuerDto, governanceAuthorityId: string) { + const issuer = await this.prisma.organization.create({ + data: { + name: dto.name, + did: dto.did, + type: OrganizationType.ISSUER, + governanceAuthorityId: governanceAuthorityId, + namespaceId: dto.namespaceId, + assuranceLevelId: dto.assuranceLevelId, + }, + }); + + // Map existing schemas to the issuer + if (dto.existingSchemaIds && dto.existingSchemaIds.length > 0) { + await this.prisma.schema.updateMany({ + where: { id: { in: dto.existingSchemaIds } }, + data: { organizationId: issuer.id }, + }); + } + + // Create new schemas for the issuer if provided + if (dto.newSchemas && dto.newSchemas.length > 0) { + await this.prisma.schema.createMany({ + data: dto.newSchemas.map(schema => ({ + name: schema.name, + type: schema.type, + w3cUri: schema.w3cUri, + anonCredsDefinitionId: schema.anonCredsSchemaId, + organizationId: issuer.id, + governanceAuthorityId: governanceAuthorityId, + })), + }); + } + + return issuer; + } + + + async onboardVerifier(dto: OnboardVerifierDto, governanceAuthorityId: string) { + const verifier = await this.prisma.organization.create({ + data: { + name: dto.name, + did: dto.did, + type: OrganizationType.VERIFIER, + governanceAuthorityId: governanceAuthorityId, + namespaceId: dto.namespaceId, + assuranceLevelId: dto.assuranceLevelId, + attributes: dto.attributes, + }, + }); + + // We don't need to map schemas to verifiers in the database + // as the relationship is tracked through the schemaIds in the DTO + + return verifier; + } + + async createNamespace(dto: CreateNamespaceDto, governanceAuthorityId: string) { + return this.prisma.namespace.create({ + data: { + name: dto.name, + governanceAuthorityId: governanceAuthorityId, + }, + }); + } + + async createAssuranceLevel(dto: CreateAssuranceLevelDto, governanceAuthorityId: string) { + return this.prisma.assuranceLevel.create({ + data: { + name: dto.name, + level: dto.level, + governanceAuthorityId: governanceAuthorityId, + }, + }); + } + + async createSchema(dto: CreateSchemaDto, governanceAuthorityId: string) { + const { organizationId, ...schemaData } = dto; + + return this.prisma.schema.create({ + data: { + ...schemaData, + governanceAuthorityId, + ...(organizationId && { organizationId }), + }, + }); + } + + async getSchemasByGovernanceAuthority(governanceAuthorityId: string) { + return this.prisma.schema.findMany({ + where: { + governanceAuthorityId, + }, + include: { + organization: true, + }, + }); + } + async getNamespaces(registryId: string) { + return this.prisma.namespace.findMany({ + where: { + governanceAuthorityId: registryId, + }, + }); + } + + async getAssuranceLevels(registryId: string) { + return this.prisma.assuranceLevel.findMany({ + where: { + governanceAuthorityId: registryId, + }, + }); + } + + async getIssuers(registryId: string) { + return this.prisma.organization.findMany({ + where: { + governanceAuthorityId: registryId, + type: 'ISSUER', + }, + }); + } + + async getVerifiers(registryId: string) { + return this.prisma.organization.findMany({ + where: { + governanceAuthorityId: registryId, + type: 'VERIFIER', + }, + }); + } + + async getEntity(entityId: string) { + const entity = await this.prisma.organization.findUnique({ + where: { id: entityId }, + include: { + governanceAuthority: true, + namespace: true, + assuranceLevel: true, + schemas: true, }, }); + + if (!entity) { + throw new NotFoundException(`Entity with ID ${entityId} not found`); + } + + return entity; + } + + async getEntityAuthorizations(entityId: string) { + // This is still a placeholder. Implement based on your authorization model + const entity = await this.prisma.organization.findUnique({ + where: { id: entityId }, + }); + + if (!entity) { + throw new NotFoundException(`Entity with ID ${entityId} not found`); + } + + return { + entityId: entity.id, + authorizations: [], + }; } + } \ No newline at end of file