From 64d81e9894be260e0bae3a9db155eb799aa9ff36 Mon Sep 17 00:00:00 2001 From: vigdorov Date: Sat, 8 Aug 2020 19:21:05 +0300 Subject: [PATCH] =?UTF-8?q?HM-111.=20=D0=90=D0=B2=D1=82=D0=BE=D1=80=20api?= =?UTF-8?q?=20=D0=BF=D1=80=D0=B8=D1=81=D0=B2=D0=B0=D0=B8=D0=B2=D0=B0=D0=B5?= =?UTF-8?q?=D1=82=D1=81=D1=8F=20=D0=B8=D1=81=D1=85=D0=BE=D0=B4=D1=8F=20?= =?UTF-8?q?=D0=B8=D0=B7=20=D1=82=D0=BE=D0=BA=D0=B5=D0=BD=D0=B0=20=D0=B8=20?= =?UTF-8?q?=D0=BD=D0=B0=D0=BB=D0=B8=D1=87=D0=B8=D1=8F=20=D0=BC=D0=B5=D1=82?= =?UTF-8?q?=D0=BA=D0=B8=20=D1=83=20=D0=BF=D0=BE=D0=BB=D1=8C=D0=B7=D0=BE?= =?UTF-8?q?=D0=B2=D0=B0=D1=82=D0=B5=D0=BB=D1=8F=20is=5Fadmin?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- http/store.http | 5 +- http/users.http | 4 +- package-lock.json | 100 ++++++++++++++++++++++++++++++++++ package.json | 2 + src/store/store.controller.ts | 2 +- src/store/store.service.ts | 56 ++++++++++++++----- 6 files changed, 152 insertions(+), 17 deletions(-) diff --git a/http/store.http b/http/store.http index f0ae021..76b6b30 100644 --- a/http/store.http +++ b/http/store.http @@ -10,9 +10,10 @@ Api-Name: store-service-test POST http://localhost:4001/store HTTP/1.1 content-type: application/json Api-Name: store-service-test +Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2dpbiI6InN0cmluZyIsImFnZW50IjoidnNjb2RlLXJlc3RjbGllbnQiLCJpYXQiOjE1OTY5MDM0NTQsImV4cCI6MTU5NjkwMzc1NH0.Uos0Ei9Fes1euZ72uh_6E9ixG1orBu79bFYjzzmWRR8 { - "key": "teFDfd4_fd-g53", + "key": "testAp", "value": { }, @@ -37,7 +38,7 @@ Api-Name: store-service-test } ### -DELETE http://localhost:4001/store/testApi-433 HTTP/1.1 +DELETE http://localhost:4001/store/testAp HTTP/1.1 Api-Name: store-service-test ### diff --git a/http/users.http b/http/users.http index 9550f7c..0b04653 100644 --- a/http/users.http +++ b/http/users.http @@ -1,8 +1,10 @@ ### Список всех пользователей GET http://api.auth.vigdorov.ru/users HTTP/1.1 +Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2dpbiI6InN0cmluZyIsImFnZW50IjoidnNjb2RlLXJlc3RjbGllbnQiLCJpYXQiOjE1OTY5MDIxNjAsImV4cCI6MTU5NjkwMjQ2MH0.wYty3CwisCckk3wUbZ4FT8WmQJlgowzf5csbxPdPqWU ### Получить одного пользователя -GET http://api.auth.vigdorov.ru/users/admin HTTP/1.1 +GET http://api.auth.vigdorov.ru/users/search/vigdorov2 HTTP/1.1 +Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2dpbiI6InN0cmluZyIsImFnZW50IjoidnNjb2RlLXJlc3RjbGllbnQiLCJpYXQiOjE1OTY5MDIxNjAsImV4cCI6MTU5NjkwMjQ2MH0.wYty3CwisCckk3wUbZ4FT8WmQJlgowzf5csbxPdPqWU ### Создать пользователя POST http://api.auth.vigdorov.ru/users HTTP/1.1 diff --git a/package-lock.json b/package-lock.json index e7f5f48..5d28f34 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1956,6 +1956,15 @@ "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", "dev": true }, + "@types/jsonwebtoken": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-8.5.0.tgz", + "integrity": "sha512-9bVao7LvyorRGZCw0VmH/dr7Og+NdjYSsKAxB43OQoComFbBgsEpoR9JW6+qSq/ogwVBg8GI2MfAlk4SYI4OLg==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/mime": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.2.tgz", @@ -3217,6 +3226,11 @@ } } }, + "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.1", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", @@ -4149,6 +4163,14 @@ "safer-buffer": "^2.1.0" } }, + "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", @@ -8674,6 +8696,30 @@ "universalify": "^1.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": { + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, "jsprim": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", @@ -8686,6 +8732,25 @@ "verror": "1.10.0" } }, + "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" + } + }, "kareem": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.3.1.tgz", @@ -8786,12 +8851,47 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" }, + "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.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", "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.sortby": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", diff --git a/package.json b/package.json index 9295edf..e8ceeea 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "@nestjs/mongoose": "^7.0.2", "@nestjs/platform-express": "^7.0.0", "@nestjs/swagger": "^4.5.12", + "jsonwebtoken": "^8.5.1", "moment": "^2.27.0", "mongoose": "^5.9.25", "reflect-metadata": "^0.1.13", @@ -39,6 +40,7 @@ "@nestjs/testing": "^7.0.0", "@types/express": "^4.17.3", "@types/jest": "25.2.3", + "@types/jsonwebtoken": "^8.5.0", "@types/moment": "^2.13.0", "@types/mongoose": "^5.7.29", "@types/node": "^13.9.1", diff --git a/src/store/store.controller.ts b/src/store/store.controller.ts index 049837c..1c67d26 100644 --- a/src/store/store.controller.ts +++ b/src/store/store.controller.ts @@ -80,7 +80,7 @@ export class StoreController { }) async create(@Req() request: Request): Promise { const api = makeApiHeader(request); - const store = await this.storeService.create(api, request.body); + const store = await this.storeService.create(api, request.body, request.headers.authorization); return prepareStoreToStoreRequest(store); } diff --git a/src/store/store.service.ts b/src/store/store.service.ts index a39522c..0d647cc 100644 --- a/src/store/store.service.ts +++ b/src/store/store.service.ts @@ -1,8 +1,16 @@ import {Model, Connection} from 'mongoose'; -import {Injectable, NotFoundException, BadGatewayException, ConflictException, BadRequestException} from '@nestjs/common'; +import {Injectable, NotFoundException, BadGatewayException, ConflictException, BadRequestException, HttpException, HttpService} from '@nestjs/common'; import {InjectConnection} from '@nestjs/mongoose'; import {Store, StoreRequest, StoreSchema} from './store.schema'; import {DB_TEST_NAME, DB_NAME, COLLECTION_STORE} from 'src/consts'; +import * as jwt from 'jsonwebtoken'; + +interface Token { + login: string; + agent: string; + iat: number; + exp: number; +} const validateModel = async (store: Store) => { try { @@ -17,6 +25,7 @@ export class StoreService { constructor( @InjectConnection(DB_NAME) private dbConnection: Connection, @InjectConnection(DB_TEST_NAME) private dbTestConnection: Connection, + private http: HttpService, ) {} storeModel(api: string): Model { @@ -30,30 +39,51 @@ export class StoreService { return this.storeModel(api).find().exec(); } - async create(api: string, store: StoreRequest): Promise { + async create(api: string, store: StoreRequest, access_token: string): Promise { const searchStore = await this.storeModel(api).findOne({key: store.key}); if (searchStore) { throw new ConflictException(`Api key "${store.key}" is already taken`); } - const createdStore = new (this.storeModel(api))(store); + const {login, agent} = jwt.decode(access_token) as Token; + + const apiPath = 'http://api.auth.vigdorov.ru/users/search/'; + const headers = { + Authorization: access_token, + 'user-agent': agent + }; try { - await validateModel(createdStore); - } catch (e) { - if (e?.message?.includes('validation failed')) { - throw new BadRequestException(e.message); + const {data: currentUser} = await this.http.get(`${apiPath}${login}`, {headers}).toPromise(); + await this.http.get(`${apiPath}${store.author}`, {headers}).toPromise(); + const author = currentUser.is_admin ? store.author : login; + const createdStore = new (this.storeModel(api))({ + ...store, + author, + }); + + try { + await validateModel(createdStore); + } catch (e) { + if (e?.message?.includes('validation failed')) { + throw new BadRequestException(e.message); + } + throw e; } - throw e; - } - const savedStore = await createdStore.save(); - if (!Object.keys(savedStore.value).length) { - await savedStore.updateOne(store); + const savedStore = await createdStore.save(); + if (!Object.keys(savedStore.value).length) { + await savedStore.updateOne({value: {}}); + } + return savedStore; + } catch (e) { + if (e?.response?.data?.statusCode === 404) { + throw new NotFoundException(e.response.data.message); + } + throw new BadRequestException(e.message); } - return savedStore; } async update(api: string, {author, ...omitProps}: StoreRequest): Promise {