From ee2d7b4838c9008662206faf0d826b32f5753da8 Mon Sep 17 00:00:00 2001 From: NorbiPeti Date: Tue, 16 Nov 2021 02:10:35 +0100 Subject: [PATCH] Add registration endpoint with validation and attempt to add JWT authentication --- backend/package-lock.json | 306 +++++++++++++++++++++ backend/package.json | 4 +- backend/src/application.ts | 8 + backend/src/controllers/user.controller.ts | 125 ++++++--- backend/src/models/user.model.ts | 15 +- 5 files changed, 421 insertions(+), 37 deletions(-) diff --git a/backend/package-lock.json b/backend/package-lock.json index 49ec7d3..9c539eb 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -8,6 +8,8 @@ "name": "szakdolgozat-backend", "version": "0.0.1", "dependencies": { + "@loopback/authentication": "^7.3.4", + "@loopback/authentication-jwt": "^0.9.4", "@loopback/boot": "^3.4.4", "@loopback/core": "^2.18.0", "@loopback/repository": "^3.7.3", @@ -644,6 +646,45 @@ "node": ">=8" } }, + "node_modules/@loopback/authentication": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@loopback/authentication/-/authentication-7.3.4.tgz", + "integrity": "sha512-Ck6ggwiIkTI9Vu3YjQhKHCueuSE/NWYq3ILWF5ToloPDEITaHK5Tr8q/DM3mqM2gASz3OQeTQ28zbGrq5mMzbQ==", + "dependencies": { + "@loopback/security": "^0.5.4", + "@types/express": "^4.17.13", + "@types/lodash": "^4.14.175", + "lodash": "^4.17.21", + "tslib": "^2.3.1" + }, + "engines": { + "node": "^10.16 || 12 || 14 || 16" + }, + "peerDependencies": { + "@loopback/core": "^2.18.0", + "@loopback/rest": "^10.1.0" + } + }, + "node_modules/@loopback/authentication-jwt": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/@loopback/authentication-jwt/-/authentication-jwt-0.9.4.tgz", + "integrity": "sha512-rjunIpqGbjY983k6NcP+K2SB1RZZoLMGbR9xqG3oAVLkWN4ss9Vhb+nylDJEtGzLT1Bvi4cJyeuaZgbZhviZ9g==", + "dependencies": { + "@loopback/security": "^0.5.4", + "@types/bcryptjs": "2.4.2", + "bcryptjs": "^2.4.3", + "debug": "^4.3.2", + "jsonwebtoken": "^8.5.1" + }, + "engines": { + "node": "^10.16 || 12 || 14 || 16" + }, + "peerDependencies": { + "@loopback/authentication": "^7.3.4", + "@loopback/core": "^2.18.0", + "@loopback/rest": "^10.1.0" + } + }, "node_modules/@loopback/boot": { "version": "3.4.4", "resolved": "https://registry.npmjs.org/@loopback/boot/-/boot-3.4.4.tgz", @@ -944,6 +985,21 @@ "@loopback/rest": "^10.1.0" } }, + "node_modules/@loopback/security": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/@loopback/security/-/security-0.5.4.tgz", + "integrity": "sha512-9pgs790AtKCQPTNa+FYQZqHZpuF22XZKTPzK9C/WmUoClF5BGvjPbXwJVOMJPYmgjZIeB0dONWiq+QlIvdcSOw==", + "dependencies": { + "debug": "^4.3.2", + "tslib": "^2.3.1" + }, + "engines": { + "node": "^10.16 || 12 || 14 || 16" + }, + "peerDependencies": { + "@loopback/core": "^2.18.0" + } + }, "node_modules/@loopback/service-proxy": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/@loopback/service-proxy/-/service-proxy-3.2.4.tgz", @@ -1061,6 +1117,11 @@ "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==", "dev": true }, + "node_modules/@types/bcryptjs": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@types/bcryptjs/-/bcryptjs-2.4.2.tgz", + "integrity": "sha512-LiMQ6EOPob/4yUL66SZzu6Yh77cbzJFYll+ZfaPiPPFswtIlA/Fs1MzdKYA7JApHU49zQTbJGX3PDmCpIdDBRQ==" + }, "node_modules/@types/body-parser": { "version": "1.19.1", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.1.tgz", @@ -1166,6 +1227,11 @@ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==" }, + "node_modules/@types/lodash": { + "version": "4.14.177", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.177.tgz", + "integrity": "sha512-0fDwydE2clKe9MNfvXHBHF9WEahRuj+msTuQqOmAApNORFvhMYZKNGGJdCzuhheVjMps/ti0Ak/iJPACMaevvw==" + }, "node_modules/@types/mime": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", @@ -1663,6 +1729,11 @@ "node": ">=0.10" } }, + "node_modules/bcryptjs": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", + "integrity": "sha1-mrVie5PmBiH/fNrF2pczAn3x0Ms=" + }, "node_modules/bignumber.js": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.0.tgz", @@ -1813,6 +1884,11 @@ "url": "https://opencollective.com/browserslist" } }, + "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": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" + }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -2260,6 +2336,14 @@ "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", "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", @@ -3925,12 +4009,60 @@ "graceful-fs": "^4.1.6" } }, + "node_modules/jsonwebtoken": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", + "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", + "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": "^5.6.0" + }, + "engines": { + "node": ">=4", + "npm": ">=1.4.28" + } + }, + "node_modules/jsonwebtoken/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "bin": { + "semver": "bin/semver" + } + }, "node_modules/just-extend": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz", "integrity": "sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==", "dev": true }, + "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/lcid": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/lcid/-/lcid-3.1.1.tgz", @@ -3987,12 +4119,47 @@ "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", "dev": true }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M=" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w=" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "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": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" + }, "node_modules/lodash.truncate": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", @@ -7348,6 +7515,30 @@ "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", "dev": true }, + "@loopback/authentication": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@loopback/authentication/-/authentication-7.3.4.tgz", + "integrity": "sha512-Ck6ggwiIkTI9Vu3YjQhKHCueuSE/NWYq3ILWF5ToloPDEITaHK5Tr8q/DM3mqM2gASz3OQeTQ28zbGrq5mMzbQ==", + "requires": { + "@loopback/security": "^0.5.4", + "@types/express": "^4.17.13", + "@types/lodash": "^4.14.175", + "lodash": "^4.17.21", + "tslib": "^2.3.1" + } + }, + "@loopback/authentication-jwt": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/@loopback/authentication-jwt/-/authentication-jwt-0.9.4.tgz", + "integrity": "sha512-rjunIpqGbjY983k6NcP+K2SB1RZZoLMGbR9xqG3oAVLkWN4ss9Vhb+nylDJEtGzLT1Bvi4cJyeuaZgbZhviZ9g==", + "requires": { + "@loopback/security": "^0.5.4", + "@types/bcryptjs": "2.4.2", + "bcryptjs": "^2.4.3", + "debug": "^4.3.2", + "jsonwebtoken": "^8.5.1" + } + }, "@loopback/boot": { "version": "3.4.4", "resolved": "https://registry.npmjs.org/@loopback/boot/-/boot-3.4.4.tgz", @@ -7564,6 +7755,15 @@ "tslib": "^2.3.1" } }, + "@loopback/security": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/@loopback/security/-/security-0.5.4.tgz", + "integrity": "sha512-9pgs790AtKCQPTNa+FYQZqHZpuF22XZKTPzK9C/WmUoClF5BGvjPbXwJVOMJPYmgjZIeB0dONWiq+QlIvdcSOw==", + "requires": { + "debug": "^4.3.2", + "tslib": "^2.3.1" + } + }, "@loopback/service-proxy": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/@loopback/service-proxy/-/service-proxy-3.2.4.tgz", @@ -7663,6 +7863,11 @@ "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==", "dev": true }, + "@types/bcryptjs": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@types/bcryptjs/-/bcryptjs-2.4.2.tgz", + "integrity": "sha512-LiMQ6EOPob/4yUL66SZzu6Yh77cbzJFYll+ZfaPiPPFswtIlA/Fs1MzdKYA7JApHU49zQTbJGX3PDmCpIdDBRQ==" + }, "@types/body-parser": { "version": "1.19.1", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.1.tgz", @@ -7776,6 +7981,11 @@ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==" }, + "@types/lodash": { + "version": "4.14.177", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.177.tgz", + "integrity": "sha512-0fDwydE2clKe9MNfvXHBHF9WEahRuj+msTuQqOmAApNORFvhMYZKNGGJdCzuhheVjMps/ti0Ak/iJPACMaevvw==" + }, "@types/mime": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", @@ -8150,6 +8360,11 @@ "resolved": "https://registry.npmjs.org/bcp47/-/bcp47-1.1.2.tgz", "integrity": "sha1-NUvjMH/9CEM6ePXh4glYRfifx/4=" }, + "bcryptjs": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", + "integrity": "sha1-mrVie5PmBiH/fNrF2pczAn3x0Ms=" + }, "bignumber.js": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.0.tgz", @@ -8271,6 +8486,11 @@ "picocolors": "^1.0.0" } }, + "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": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" + }, "buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -8633,6 +8853,14 @@ "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", "dev": true }, + "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==", + "requires": { + "safe-buffer": "^5.0.1" + } + }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -9896,12 +10124,55 @@ "universalify": "^2.0.0" } }, + "jsonwebtoken": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", + "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", + "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": "^5.6.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + } + } + }, "just-extend": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz", "integrity": "sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==", "dev": true }, + "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" + } + }, "lcid": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/lcid/-/lcid-3.1.1.tgz", @@ -9946,12 +10217,47 @@ "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", "dev": true }, + "lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=" + }, + "lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=" + }, + "lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M=" + }, + "lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w=" + }, + "lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=" + }, + "lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=" + }, "lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" + }, "lodash.truncate": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", diff --git a/backend/package.json b/backend/package.json index 39802c8..6995830 100644 --- a/backend/package.json +++ b/backend/package.json @@ -41,7 +41,7 @@ "type": "git", "url": "" }, - "author": "NorbiPeti ", + "author": "Szatmári Norbert Péter ", "license": "", "files": [ "README.md", @@ -50,6 +50,8 @@ "!*/__tests__" ], "dependencies": { + "@loopback/authentication": "^7.3.4", + "@loopback/authentication-jwt": "^0.9.4", "@loopback/boot": "^3.4.4", "@loopback/core": "^2.18.0", "@loopback/repository": "^3.7.3", diff --git a/backend/src/application.ts b/backend/src/application.ts index 8b5434f..3aab82f 100644 --- a/backend/src/application.ts +++ b/backend/src/application.ts @@ -9,6 +9,9 @@ import {RestApplication} from '@loopback/rest'; import {ServiceMixin} from '@loopback/service-proxy'; import path from 'path'; import {MySequence} from './sequence'; +import {AuthenticationComponent} from '@loopback/authentication'; +import {JWTAuthenticationComponent, UserServiceBindings} from '@loopback/authentication-jwt'; +import {DatabaseDataSource} from './datasources'; export {ApplicationConfig}; @@ -30,6 +33,11 @@ export class SzakdolgozatBackendApplication extends BootMixin( }); this.component(RestExplorerComponent); + // Authentication + this.component(AuthenticationComponent); + this.component(JWTAuthenticationComponent); + this.dataSource(DatabaseDataSource, UserServiceBindings.DATASOURCE_NAME) + this.projectRoot = __dirname; // Customize @loopback/boot Booter Conventions here this.bootOptions = { diff --git a/backend/src/controllers/user.controller.ts b/backend/src/controllers/user.controller.ts index 9b4bbea..0c5c8d1 100644 --- a/backend/src/controllers/user.controller.ts +++ b/backend/src/controllers/user.controller.ts @@ -1,50 +1,107 @@ -import { - Count, - CountSchema, - Filter, - FilterExcludingWhere, - repository, - Where, -} from '@loopback/repository'; -import { - post, - param, - get, - getModelSchemaRef, - patch, - put, - del, - requestBody, - response, -} from '@loopback/rest'; +import {Count, CountSchema, Filter, FilterExcludingWhere, MODEL_PROPERTIES_KEY, repository, Where,} from '@loopback/repository'; +import {del, get, getModelSchemaRef, param, patch, post, put, requestBody, response,} from '@loopback/rest'; import {User} from '../models'; import {UserRepository} from '../repositories'; +import { + Credentials, + MyUserService, + TokenServiceBindings, + UserServiceBindings +} from '@loopback/authentication-jwt'; +import {inject, MetadataInspector} from '@loopback/core'; +import {TokenService} from '@loopback/authentication'; +import {SecurityBindings, UserProfile} from '@loopback/security'; +import {genSalt, hash} from 'bcryptjs'; +import _ from 'lodash'; +import {UserRepository as UserRepo} from '@loopback/authentication-jwt'; export class UserController { constructor( - @repository(UserRepository) - public userRepository : UserRepository, + @inject(TokenServiceBindings.TOKEN_SERVICE) + public jwtService: TokenService, + @inject(UserServiceBindings.USER_SERVICE) + public userService: MyUserService, + @inject(SecurityBindings.USER, {optional: true}) + public user: UserProfile, + @repository(UserRepo) + public userRepository : UserRepo, + @repository(UserRepository) + public userDataRepository : UserRepository, ) {} + static includeToExclude(values: (keyof User)[]): (keyof User)[] { + const metadata = MetadataInspector.getAllPropertyMetadata(MODEL_PROPERTIES_KEY, User); + if (metadata) { + return _.without(Object.keys(metadata), ...values) as (keyof User)[]; + } + else throw new Error("No property metadata found"); + } + @post('/users') @response(200, { description: 'User model instance', - content: {'application/json': {schema: getModelSchemaRef(User)}}, + content: {'application/json': {schema: getModelSchemaRef(User, {exclude: ['password']})}}, }) - async create( + async register( @requestBody({ content: { 'application/json': { schema: getModelSchemaRef(User, { - title: 'NewUser', - exclude: ['id'], + title: 'Registration request', + exclude: ['id', 'role'] }), }, }, }) - user: Omit, + request: Pick, ): Promise { - return this.userRepository.create(user); + const password = await hash(request.password, await genSalt()); + const user = { + email: request.email, + name: request.name, + password: password, + role: 'student' + } as User; + return this.userDataRepository.create(user); + } + + @post('/users/login', { + responses: { + '200': { + description: 'Token', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + token: { + type: 'string', + }, + }, + }, + }, + }, + }, + }, + }) + async login( + @requestBody({ + description: 'The input of login function', + required: true, + content: { + 'application/json': { + schema: getModelSchemaRef(User, {exclude: ['id', 'role', 'name']}) + }, + }}) credentials: Credentials, + ): Promise<{token: string}> { + // ensure the user exists, and the password is correct + const user = await this.userService.verifyCredentials(credentials); + // convert a User object into a UserProfile object (reduced set of properties) + const userProfile = this.userService.convertToUserProfile(user); + + // create a JSON Web Token based on the user profile + const token = await this.jwtService.generateToken(userProfile); + return {token}; } @get('/users/count') @@ -55,7 +112,7 @@ export class UserController { async count( @param.where(User) where?: Where, ): Promise { - return this.userRepository.count(where); + return this.userDataRepository.count(where); } @get('/users') @@ -73,7 +130,7 @@ export class UserController { async find( @param.filter(User) filter?: Filter, ): Promise { - return this.userRepository.find(filter); + return this.userDataRepository.find(filter); } @patch('/users') @@ -92,7 +149,7 @@ export class UserController { user: User, @param.where(User) where?: Where, ): Promise { - return this.userRepository.updateAll(user, where); + return this.userDataRepository.updateAll(user, where); } @get('/users/{id}') @@ -108,7 +165,7 @@ export class UserController { @param.path.number('id') id: number, @param.filter(User, {exclude: 'where'}) filter?: FilterExcludingWhere ): Promise { - return this.userRepository.findById(id, filter); + return this.userDataRepository.findById(id, filter); } @patch('/users/{id}') @@ -126,7 +183,7 @@ export class UserController { }) user: User, ): Promise { - await this.userRepository.updateById(id, user); + await this.userDataRepository.updateById(id, user); } @put('/users/{id}') @@ -137,7 +194,7 @@ export class UserController { @param.path.number('id') id: number, @requestBody() user: User, ): Promise { - await this.userRepository.replaceById(id, user); + await this.userDataRepository.replaceById(id, user); } @del('/users/{id}') @@ -145,6 +202,6 @@ export class UserController { description: 'User DELETE success', }) async deleteById(@param.path.number('id') id: number): Promise { - await this.userRepository.deleteById(id); + await this.userDataRepository.deleteById(id); } } diff --git a/backend/src/models/user.model.ts b/backend/src/models/user.model.ts index 012d4dc..a208338 100644 --- a/backend/src/models/user.model.ts +++ b/backend/src/models/user.model.ts @@ -12,26 +12,37 @@ export class User extends Entity { @property({ type: 'string', required: true, + jsonSchema: { + pattern: /[A-Za-z0-9.+_-]+@[A-Za-z.-_]*(u-szeged.hu)|(szte.hu)/.source + } }) email: string; @property({ type: 'string', required: true, + jsonSchema: { + pattern: /([A-Za-z-.]+ )+[A-Za-z-.]+/.source + } }) name: string; @property({ type: 'string', required: true, + jsonSchema: { + minLength: 8, + maxLength: 255 + }, + hidden: true }) password: string; @property({ - type: 'object', + type: 'string', required: true, }) - role: object; + role: 'admin' | 'teacher' | 'student'; constructor(data?: Partial) {