diff --git a/api/openapi.yaml b/api/openapi.yaml index cd84bda23a1d7afed46a815f9fd00f25a9b2d149..e9523a52d2ff2b891f9dcc81f5bc70279f9de7f3 100644 --- a/api/openapi.yaml +++ b/api/openapi.yaml @@ -40,6 +40,8 @@ paths: application/json: schema: $ref: '#/components/schemas/Ad' + security: + - bearerAuth: [] responses: "200": description: Successful operation @@ -49,10 +51,182 @@ paths: $ref: '#/components/schemas/Ad' "405": description: Invalid input + x-swagger-router-controller: Ad + /update/{id}: + put: + tags: + - ad + summary: Mettre à jour une annonce + description: Met à jour une annonce. + operationId: updateAd + parameters: + - name: id + in: path + description: ID de l'annonce à mettre à jour + required: true + schema: + type: string + requestBody: + description: Annonce à mettre à jour + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateAd' + security: + - bearerAuth: [] + responses: + '200': + description: Succès. Renvoie les détails de l'annonce mise à jour. + '403': + description: Accès interdit. L'utilisateur n'est pas autorisé à effectuer cette action. + '404': + description: Non trouvé. L'annonce avec cet ID n'a pas été trouvée. + x-swagger-router-controller: Ad + /announcements/delete/{id}: + post: + tags: + - ad + summary: Supprimer une annonce + description: Supprime une annonce + operationId: deleteAd + parameters: + - name: id + in: path + description: ID de l'annonce à supprimer + required: true + schema: + type: string + security: + - bearerAuth: [] + responses: + '200': + description: Succès. L'annonce a été supprimée avec succès. + '404': + description: Annonce non trouvée. + '403': + description: Utilisateur non autorisé. + '401': + description: + Authentification requise. + '500': + description: Erreur serveur interne. Veuillez réessayer plus tard. + x-swagger-router-controller: Ad + /announcements: + get: + tags: + - ad + summary: Obtenir la liste des annonces + description: Récupère la liste des annonces + operationId: getAds + security: + - bearerAuth: [] + responses: + "200": + description: Succès. Liste des annonces récupérée avec succès. + content: + application/json: + schema: + $ref: '#/components/schemas/AnnouncementList' + "500": + description: Erreur serveur interne. Veuillez réessayer plus tard. + x-swagger-router-controller: Ad + /announcements/{id}: + get: + tags: + - ad + summary: Obtenir les détails d'une annonce + description: Récupère les détails d'une annonce + operationId: getAdById + parameters: + - name: id + in: path + description: ID de l'annonce à récupérer + required: true + schema: + type: string security: - - real-estate-ads_auth: - - write:ads - - read:ads + - bearerAuth: [] + responses: + "200": + description: Succès. Détails de l'annonce récupérés avec succès. + content: + application/json: + schema: + $ref: '#/components/schemas/Ad' + "403": + description: Accès interdit. L'utilisateur n'est pas autorisé à accéder à cette annonce. + "404": + description: Non trouvé. L'annonce avec l'ID spécifié n'a pas été trouvée ou n'est pas disponible. + "500": + description: Erreur serveur interne. Veuillez réessayer plus tard. + x-swagger-router-controller: Ad + /announcements/{id}/ask: + put: + tags: + - ad + summary: Poser une question sur une annonce + description: Pose une question sur une annonce + operationId: askQuestion + parameters: + - name: id + in: path + description: ID de l'annonce sur laquelle poser la question + required: true + schema: + type: string + requestBody: + description: Question à poser sur l'annonce + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/askQuestion' + security: + - bearerAuth: [] + responses: + '200': + description: Succès. La question a été posée avec succès. + '500': + description: Erreur serveur interne. Veuillez réessayer plus tard. + x-swagger-router-controller: Ad + /announcements/{id}/question/{questionId}/answer: + put: + tags: + - ad + summary: Répondre à une question sur une annonce + description: Répond à une question sur une annonce + operationId: answerQuestion + parameters: + - name: id + in: path + description: ID de l'annonce sur laquelle répondre à la question + required: true + schema: + type: string + - name: questionId + in: path + description: ID de la question à laquelle répondre + required: true + schema: + type: string + requestBody: + description: Réponse à la question + required: true + content: + application/json: + schema: + type: object + properties: + answer: + type: string + security: + - bearerAuth: [] + responses: + '200': + description: Succès. La réponse a été enregistrée avec succès. + '500': + description: Erreur serveur interne. Veuillez réessayer plus tard. x-swagger-router-controller: Ad /update/{id}: put: @@ -237,6 +411,10 @@ paths: application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/User' + security: + - oAuthSample: + - write_pets + - read_pets responses: default: description: successful operation @@ -305,11 +483,14 @@ paths: description: "" operationId: logoutUser parameters: [] + security: + - bearerAuth: [] responses: default: description: successful operation x-swagger-router-controller: User components: + schemas: Ad: type: object @@ -341,8 +522,6 @@ components: type: string format: date-time example: "2023-11-03T14:30:00Z" - userName: - type: string photos: type: array items: @@ -357,7 +536,6 @@ components: - description - price - availabilityDate - - userName UpdateAd: type: object properties: @@ -467,6 +645,18 @@ components: items: $ref: '#/components/schemas/User' securitySchemes: + bearerAuth: + type: http + scheme: bearer + oAuthSample: + type: oauth2 + description: This API uses OAuth 2 with the implicit grant flow. + flows: + implicit: + authorizationUrl: https://api.example.com/oauth2/authorize + scopes: + read_pets: read your pets + write_pets: modify pets in your account real-estate-ads_auth: type: oauth2 flows: diff --git a/controllers/Ad.js b/controllers/Ad.js index 4f834814abb546b9a6602b49051b6a5079c95493..8ca510270b330a699abaa02f43f24fd490537bc4 100644 --- a/controllers/Ad.js +++ b/controllers/Ad.js @@ -5,7 +5,8 @@ var Ad = require('../service/AdService'); // Assurez-vous d'importer le service var User = require('../service/UserService'); module.exports.addAd = function addAd (req, res, next, body) { - Ad.addAd(body) + const token = req.headers.authorization.split(' ')[1]; + Ad.addAd(body, token) .then(function (response) { utils.writeJson(res, response); }) @@ -15,7 +16,8 @@ module.exports.addAd = function addAd (req, res, next, body) { }; module.exports.updateAd = function updateAd (req, res, next) { - Ad.updateAd(req.openapi.pathParams.id, req.body) + const token = req.headers.authorization.split(' ')[1]; + Ad.updateAd(req.openapi.pathParams.id, req.body, token) .then(function (response) { utils.writeJson(res, response); }) @@ -25,7 +27,8 @@ module.exports.updateAd = function updateAd (req, res, next) { }; module.exports.deleteAd = function deleteAd(req, res, next, announcementId) { - Ad.deleteAd(announcementId) + const token = req.headers.authorization.split(' ')[1]; + Ad.deleteAd(announcementId, token) .then(function (response) { utils.writeJson(res, response); }) @@ -35,8 +38,9 @@ module.exports.deleteAd = function deleteAd(req, res, next, announcementId) { }; module.exports.getAds = function getAnnouncements(req, res) { - const user = req.user; - Ad.getAds(user) + const token = req.headers.authorization.split(' ')[1]; + + Ad.getAds(token) .then(function (response) { utils.writeJson(res, response); }) @@ -47,8 +51,9 @@ module.exports.getAds = function getAnnouncements(req, res) { module.exports.getAdById = function getAnnouncementById(req, res) { const announcementId = req.openapi.pathParams.id; - const user = req.user; - Ad.getAdById(announcementId, user) + const token = req.headers.authorization.split(' ')[1]; + + Ad.getAdById(announcementId, token) .then(function (response) { utils.writeJson(res, response); }) @@ -60,9 +65,9 @@ module.exports.getAdById = function getAnnouncementById(req, res) { exports.askQuestion = async function(req, res) { const announcementId = req.openapi.pathParams.id; const { question } = req.body; - const user = req.user; + const token = req.headers.authorization.split(' ')[1]; - Ad.askQuestion(announcementId, question, user) + Ad.askQuestion(announcementId, question, token) .then(function (response) { utils.writeJson(res, response); }) @@ -75,9 +80,9 @@ exports.answerQuestion = async function(req, res) { const announcementId = req.openapi.pathParams.id; const questionId = req.openapi.pathParams.questionId; const { answer } = req.body; - const user = req.user; + const token = req.headers.authorization.split(' ')[1]; - Ad.answerQuestion(announcementId, questionId, answer, user) + Ad.answerQuestion(announcementId, questionId, answer, token) .then(function (response) { utils.writeJson(res, response); }) diff --git a/controllers/User.js b/controllers/User.js index c659931ff841dd8818013e2ac55c2701a93df363..6218f4818bca2b0f31705a6438e9dd560c8f562e 100644 --- a/controllers/User.js +++ b/controllers/User.js @@ -5,6 +5,10 @@ var User = require('../service/UserService'); var passport = require('../passeport-config'); var UserModel = require('../models/User'); +const jwt = require('jsonwebtoken'); +const { extractUserFromToken } = require( '../service/UserService'); +const secretKey = 'secretKey'; + // passport.use(UserModel.createStrategy()); module.exports.createUser = async function(req, res) { try { @@ -25,6 +29,7 @@ module.exports.loginUser = async function loginUser(req, res, next) { if (!user) { return res.status(401).send('Invalid username or password.'); // Non autorisé } + const token = jwt.sign({ username: user.username }, 'secretKey', { expiresIn: '1h' }); // User.loginUser() // .then(function (response) { // utils.writeJson(res, response); @@ -32,7 +37,7 @@ module.exports.loginUser = async function loginUser(req, res, next) { // .catch(function (response) { // utils.writeJson(res, response); // }); - return res.status(200).send('Authentication successful.'); // Succès + return res.status(200).json({ token }); })(req, res, next); } catch (error) { console.error("Erreur lors de la tentative de connexion :", error); @@ -40,7 +45,7 @@ module.exports.loginUser = async function loginUser(req, res, next) { } } -module.exports.logoutUser = function(req, res, next) { +module.exports.logoutUser = async function(req, res, next) { try { /* req.logout(function(err) { diff --git a/package-lock.json b/package-lock.json index b9fa18ea54217485aa2916df92b261280de47a9b..0fdce2eee53bfecc1c5f91151b53c52b12aab4fe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "dotenv": "^16.3.1", "express-session": "^1.17.3", "js-yaml": "^3.3.0", + "jsonwebtoken": "^9.0.2", "mongoose": "^7.6.3", "oas3-tools": "^2.2.3", "passport": "^0.6.0", @@ -338,6 +339,11 @@ "node": ">=14.20.1" } }, + "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", @@ -584,6 +590,14 @@ "url": "https://github.com/motdotla/dotenv?sponsor=1" } }, + "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", @@ -1204,6 +1218,51 @@ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" }, + "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/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "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/kareem": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.5.1.tgz", @@ -1227,6 +1286,41 @@ "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==" }, + "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.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" + }, "node_modules/lodash.uniq": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", @@ -1241,7 +1335,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, "dependencies": { "yallist": "^4.0.0" }, @@ -1869,7 +1962,6 @@ "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, "dependencies": { "lru-cache": "^6.0.0" }, @@ -2253,8 +2345,7 @@ "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" } } } diff --git a/package.json b/package.json index 4d148fa852dc3d1dc0754b06af12078131ac5cfc..a2d30d0ba57dbc5ebf1e39e36ec1054dc2749b5c 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "dotenv": "^16.3.1", "express-session": "^1.17.3", "js-yaml": "^3.3.0", + "jsonwebtoken": "^9.0.2", "mongoose": "^7.6.3", "oas3-tools": "^2.2.3", "passport": "^0.6.0", diff --git a/service/AdService.js b/service/AdService.js index 9e908dd344551ff98c74bb694aaf8a15a21b7a03..bc4b09d1d9313ce6f08fe113492726b96933c590 100644 --- a/service/AdService.js +++ b/service/AdService.js @@ -1,22 +1,53 @@ const Ad = require('../models/Ad'); +const User = require('../models/User'); -// Méthode pour ajouter une nouvelle annonce -exports.addAd = function (body) { - return new Promise(async (resolve, reject) => { - try { - const newAd = new Ad(body); - const savedAd = await newAd.save(); - resolve(savedAd); - } catch (error) { - reject(error); - } - }); +const {extractUserFromToken} = require("./UserService"); + +const secretKey = 'secretKey'; + +exports.addAd = function (body, token) { + return new Promise(async (resolve, reject) => { + + try { + const { title, propertyType, publicationStatus, propertyStatus, description, price, availabilityDate} = body; + const photos = []; + /* + req.files.forEach(file => { + photos.push(file.filename); + }); + */ + const decoded = await extractUserFromToken(token, secretKey); + const user = await User.findOne({ username: decoded.username }); + + if (user.isAgent === false) { + return resolve({ status: 403, message: 'User non autorisée.' }); + } + const newAnnouncement = new Ad({ + title, + propertyType, + publicationStatus, + propertyStatus, + description, + price, + availabilityDate, + photos, + userName: user.username + }); + + await newAnnouncement.save(); + //res.status(200).json(newAnnouncement); + resolve({ status: 200, message: 'Annonce Crée avec succes' }); + } catch (error) { + reject(error); + } + }); } // Méthode pour mettre à jour une annonce -exports.updateAd = function (announcementId, reqBody) { +exports.updateAd = function (announcementId, reqBody, token) { return new Promise(async (resolve, reject) => { try { + const user = await extractUserFromToken(token, secretKey); const { title, propertyType, publicationStatus, propertyStatus, description, price, availabilityDate } = reqBody; /* let photos = []; @@ -41,14 +72,13 @@ exports.updateAd = function (announcementId, reqBody) { //photos, } ); - if (!updatedAnnouncement) - return resolve({ status: 404, message: 'annonce non trouvée' }); - - /* + if (!updatedAnnouncement) { + return resolve({status: 404, message: 'annonce non trouvée'}); + } if (user.username !== updatedAnnouncement.userName) { - throw new Error('Utilisateur non autorisé'); + return resolve({status: 401, message: 'Utilisateur non autorisé'}); } - */ + resolve({ status: 200, message: 'Annonce Modifié avec succès' }); } catch (error) { reject(error); @@ -56,20 +86,21 @@ exports.updateAd = function (announcementId, reqBody) { }); } -exports.deleteAd = function (announcementId) { +exports.deleteAd = function (announcementId, token) { return new Promise(async (resolve, reject) => { try { + const user = await extractUserFromToken(token, secretKey); + const Announcement = await Ad.findById(announcementId); + if (user.username !== Announcement.userName) { + return resolve({ status: 403, message: 'User non autorisé' }); + } const deletedAnnouncement = await Ad.findByIdAndRemove(announcementId); if (!deletedAnnouncement) { return resolve({ status: 404, message: 'annonce non trouvée' }); } - /* - if (username !== deletedAnnouncement.userName) { - return resolve({ status: 403, message: 'User non autorisé' }); - } - */ + /* deletedAnnouncement.photos.forEach((photo) => { const filePath = path.join('public', 'uploads', photo); @@ -85,44 +116,46 @@ exports.deleteAd = function (announcementId) { }); }; -exports.getAds = function (user) { +exports.getAds = function (token) { return new Promise(async (resolve, reject) => { try { - let announcements; - /* - if (user && user.isAgent) { - announcements = await Ad.find({ userName: user.username }); - } else { - announcements = await Ad.find({ publicationStatus: 'Publiée' }); - } - */ - announcements = await Ad.find({ publicationStatus: 'Publiée' }); - resolve(announcements); + const decoded = await extractUserFromToken(token, secretKey); + let announcements; + const user = await User.findOne({ username: decoded.username }); + if (user && user.isAgent) { + announcements = await Ad.find({ userName: user.username }); + } else { + announcements = await Ad.find({ publicationStatus: 'Publiée' }); + } + resolve(announcements); } catch (error) { reject(error); } }); }; -exports.getAdById = function (announcementId, user) { +exports.getAdById = function (announcementId, token) { return new Promise(async (resolve, reject) => { try { - const announcementDetails = await Ad.findById(announcementId); - /* - if ( + const announcementDetails = await Ad.findById(announcementId); + const decoded = await extractUserFromToken(token, secretKey); + const user = await User.findOne({ username: decoded.username }); + + + if ( (user && user.username !== announcementDetails.userName || !user) && announcementDetails.publicationStatus !== 'Publiée' - ) { - return resolve({ status: 404, message: 'Cette annonce est introuvable ou non disponible.' }); - } - if (user && user.username !== announcementDetails.userName && user.isAgent) { - return resolve({ status: 403, message: 'User non autorisée.' }); - } - */ - if( !announcementDetails ) { - return resolve({ status: 404, message: 'Cette annonce est introuvable ou non disponible.' }); - } - resolve(announcementDetails); + ) { + return resolve({ status: 404, message: 'Cette annonce est introuvable ou non disponible.' }); + } + if (user && user.username !== announcementDetails.userName && user.isAgent) { + return resolve({ status: 403, message: 'User non autorisée.' }); + } + + if( !announcementDetails ) { + return resolve({ status: 404, message: 'Cette annonce est introuvable ou non disponible.' }); + } + resolve(announcementDetails); } catch (error) { reject(error); } @@ -130,10 +163,13 @@ exports.getAdById = function (announcementId, user) { }; // Service pour poser une question sur une annonce -exports.askQuestion = async function(announcementId, question, user) { +exports.askQuestion = async function(announcementId, question, token) { return new Promise(async (resolve, reject) => { try { const announcement = await Ad.findById(announcementId); + const decoded = await extractUserFromToken(token, secretKey); + const user = await User.findOne({ username: decoded.username }); + if (!announcement) { return resolve({ status: 404, message: 'Annonce non trouvée.' }); } @@ -141,7 +177,7 @@ exports.askQuestion = async function(announcementId, question, user) { return resolve({ status: 404, message: 'Vous ne pouvez poser une question que sur les annonces non disponibles.' }); } announcement.questions.push({ - user: "fifi", + user: user.username, question }); await announcement.save(); @@ -153,23 +189,26 @@ exports.askQuestion = async function(announcementId, question, user) { }; // Service pour répondre à une question sur une annonce -exports.answerQuestion = async function(announcementId, questionId, answer, user) { +exports.answerQuestion = async function(announcementId, questionId, answer, token) { return new Promise(async (resolve, reject) => { try { const announcement = await Ad.findById(announcementId); + const decoded = await extractUserFromToken(token, secretKey); + const user = await User.findOne({ username: decoded.username }); + if (!announcement) { return resolve({ status: 404, message: 'Annonce non trouvée.' }); } - /* + if (user.username !== announcement.userName) { - throw new Error('Utilisateur non autorisé.'); + return resolve({ status: 403, message: 'User non autorisée.' }); } - */ + const question = announcement.questions.id(questionId); if (!question) { return resolve({ status: 404, message: 'Question non trouvée.' }); } - question.answers.push({ user: "hajar", answer }); + question.answers.push({ user: user.username, answer }); await announcement.save(); resolve({ status: 200, message: 'Réponse ajoutée avec succès.' }); } catch (error) { diff --git a/service/UserService.js b/service/UserService.js index 8e297999ab4f594a72ac44ab64d738523fea8ec1..15690fd4b0d94dc5b33853c8c6e554aa0905bbf0 100644 --- a/service/UserService.js +++ b/service/UserService.js @@ -3,6 +3,8 @@ const passport = require('../passeport-config'); const User = require('../models/User'); +const jwt = require('jsonwebtoken'); + passport.use(User.createStrategy()); /** @@ -36,3 +38,26 @@ exports.isAuthenticated = function (req, res, next) { res.status(401).json({ message: 'Authentification requise' }); } +exports.extractUserFromToken = function(token, secretKey) { + return new Promise(async (resolve, reject) => { + try { + const decoded = await jwt.verify(token, secretKey); + // Vérifier la date d'expiration + const currentTimestamp = Math.floor(Date.now() / 1000); + if (decoded.exp && decoded.exp < currentTimestamp) { + reject(new Error('Token expiré.')); + return; + } + + const user = await User.findOne({ username: decoded.username }); + + if (!user) { + reject(new Error('Utilisateur non trouvé.')); + } else { + resolve(decoded); + } + } catch (err) { + reject(err); + } + }); +};