HM-111. Автор api присваивается исходя из токена и наличия метки у пользователя is_admin

This commit is contained in:
vigdorov
2020-08-08 19:21:05 +03:00
parent b1ffc9a8a5
commit 64d81e9894
6 changed files with 152 additions and 17 deletions

View File

@ -10,9 +10,10 @@ Api-Name: store-service-test
POST http://localhost:4001/store HTTP/1.1 POST http://localhost:4001/store HTTP/1.1
content-type: application/json content-type: application/json
Api-Name: store-service-test Api-Name: store-service-test
Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2dpbiI6InN0cmluZyIsImFnZW50IjoidnNjb2RlLXJlc3RjbGllbnQiLCJpYXQiOjE1OTY5MDM0NTQsImV4cCI6MTU5NjkwMzc1NH0.Uos0Ei9Fes1euZ72uh_6E9ixG1orBu79bFYjzzmWRR8
{ {
"key": "teFDfd4_fd-g53", "key": "testAp",
"value": { "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 Api-Name: store-service-test
### ###

View File

@ -1,8 +1,10 @@
### Список всех пользователей ### Список всех пользователей
GET http://api.auth.vigdorov.ru/users HTTP/1.1 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 POST http://api.auth.vigdorov.ru/users HTTP/1.1

100
package-lock.json generated
View File

@ -1956,6 +1956,15 @@
"integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=",
"dev": true "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": { "@types/mime": {
"version": "2.0.2", "version": "2.0.2",
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.2.tgz", "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": { "buffer-from": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
@ -4149,6 +4163,14 @@
"safer-buffer": "^2.1.0" "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": { "ee-first": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
@ -8674,6 +8696,30 @@
"universalify": "^1.0.0" "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": { "jsprim": {
"version": "1.4.1", "version": "1.4.1",
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
@ -8686,6 +8732,25 @@
"verror": "1.10.0" "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": { "kareem": {
"version": "2.3.1", "version": "2.3.1",
"resolved": "https://registry.npmjs.org/kareem/-/kareem-2.3.1.tgz", "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", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" "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": { "lodash.memoize": {
"version": "4.1.2", "version": "4.1.2",
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
"integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=",
"dev": true "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": { "lodash.sortby": {
"version": "4.7.0", "version": "4.7.0",
"resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz",

View File

@ -26,6 +26,7 @@
"@nestjs/mongoose": "^7.0.2", "@nestjs/mongoose": "^7.0.2",
"@nestjs/platform-express": "^7.0.0", "@nestjs/platform-express": "^7.0.0",
"@nestjs/swagger": "^4.5.12", "@nestjs/swagger": "^4.5.12",
"jsonwebtoken": "^8.5.1",
"moment": "^2.27.0", "moment": "^2.27.0",
"mongoose": "^5.9.25", "mongoose": "^5.9.25",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
@ -39,6 +40,7 @@
"@nestjs/testing": "^7.0.0", "@nestjs/testing": "^7.0.0",
"@types/express": "^4.17.3", "@types/express": "^4.17.3",
"@types/jest": "25.2.3", "@types/jest": "25.2.3",
"@types/jsonwebtoken": "^8.5.0",
"@types/moment": "^2.13.0", "@types/moment": "^2.13.0",
"@types/mongoose": "^5.7.29", "@types/mongoose": "^5.7.29",
"@types/node": "^13.9.1", "@types/node": "^13.9.1",

View File

@ -80,7 +80,7 @@ export class StoreController {
}) })
async create(@Req() request: Request<null, StoreRequest>): Promise<StoreRequest> { async create(@Req() request: Request<null, StoreRequest>): Promise<StoreRequest> {
const api = makeApiHeader(request); 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); return prepareStoreToStoreRequest(store);
} }

View File

@ -1,8 +1,16 @@
import {Model, Connection} from 'mongoose'; 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 {InjectConnection} from '@nestjs/mongoose';
import {Store, StoreRequest, StoreSchema} from './store.schema'; import {Store, StoreRequest, StoreSchema} from './store.schema';
import {DB_TEST_NAME, DB_NAME, COLLECTION_STORE} from 'src/consts'; 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) => { const validateModel = async (store: Store) => {
try { try {
@ -17,6 +25,7 @@ export class StoreService {
constructor( constructor(
@InjectConnection(DB_NAME) private dbConnection: Connection, @InjectConnection(DB_NAME) private dbConnection: Connection,
@InjectConnection(DB_TEST_NAME) private dbTestConnection: Connection, @InjectConnection(DB_TEST_NAME) private dbTestConnection: Connection,
private http: HttpService,
) {} ) {}
storeModel(api: string): Model<Store> { storeModel(api: string): Model<Store> {
@ -30,14 +39,29 @@ export class StoreService {
return this.storeModel(api).find().exec(); return this.storeModel(api).find().exec();
} }
async create(api: string, store: StoreRequest): Promise<Store> { async create(api: string, store: StoreRequest, access_token: string): Promise<Store> {
const searchStore = await this.storeModel(api).findOne({key: store.key}); const searchStore = await this.storeModel(api).findOne({key: store.key});
if (searchStore) { if (searchStore) {
throw new ConflictException(`Api key "${store.key}" is already taken`); 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 {
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 { try {
await validateModel(createdStore); await validateModel(createdStore);
@ -51,9 +75,15 @@ export class StoreService {
const savedStore = await createdStore.save(); const savedStore = await createdStore.save();
if (!Object.keys(savedStore.value).length) { if (!Object.keys(savedStore.value).length) {
await savedStore.updateOne(store); await savedStore.updateOne({value: {}});
} }
return savedStore; return savedStore;
} catch (e) {
if (e?.response?.data?.statusCode === 404) {
throw new NotFoundException(e.response.data.message);
}
throw new BadRequestException(e.message);
}
} }
async update(api: string, {author, ...omitProps}: StoreRequest): Promise<Store> { async update(api: string, {author, ...omitProps}: StoreRequest): Promise<Store> {