diff --git a/http/store.http b/http/store.http index ed1793d..8e47ba2 100644 --- a/http/store.http +++ b/http/store.http @@ -1,10 +1,12 @@ ### GET http://localhost:4001/store HTTP/1.1 -Api-Name: store-service-test +Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2dpbiI6InZpZ2Rvcm92IiwiYWdlbnQiOiJ2c2NvZGUtcmVzdGNsaWVudCIsImlhdCI6MTU5OTQ5OTYyMSwiZXhwIjoxNTk5NDk5OTIxfQ.Ym4xOpoe8UvO626Vxp-XqxspoJSL7JpjZ1rlSW0vIWs + ### -GET http://localhost:4001/store/testApi-4345345345 HTTP/1.1 -Api-Name: store-service-test +GET http://localhost:4001/store/rgfdfff HTTP/1.1 +Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2dpbiI6InZpZ2Rvcm92IiwiYWdlbnQiOiJ2c2NvZGUtcmVzdGNsaWVudCIsImlhdCI6MTU5OTQ5OTYyMSwiZXhwIjoxNTk5NDk5OTIxfQ.Ym4xOpoe8UvO626Vxp-XqxspoJSL7JpjZ1rlSW0vIWs + ### GET http://localhost:4001/store/rgfdfff?hook=sdf6sdfhs99-sdf HTTP/1.1 @@ -12,37 +14,39 @@ GET http://localhost:4001/store/rgfdfff?hook=sdf6sdfhs99-sdf HTTP/1.1 ### POST http://localhost:4001/store HTTP/1.1 content-type: application/json -Api-Name: store-service-test -Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2dpbiI6InN0cmluZyIsImFnZW50IjoidnNjb2RlLXJlc3RjbGllbnQiLCJpYXQiOjE1OTY5MDM0NTQsImV4cCI6MTU5NjkwMzc1NH0.Uos0Ei9Fes1euZ72uh_6E9ixG1orBu79bFYjzzmWRR8 +Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2dpbiI6InZpZ2Rvcm92IiwiYWdlbnQiOiJ2c2NvZGUtcmVzdGNsaWVudCIsImlhdCI6MTU5OTQ5NzU5NCwiZXhwIjoxNTk5NDk3ODk0fQ.j8_bKayAiHgVLL-SfiHAH4SnijJLi8dfGWlLx30u8Kc { - "key": "testAp", + "key": "testAp2", "value": { }, "description": "тестовое апи", "service_name": "test-api", - "author": "vigdorov" + "author": "vigdorov2", + "hide": true } ### Update Request PUT http://localhost:4001/store HTTP/1.1 content-type: application/json -Api-Name: store-service-test +Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2dpbiI6ImFkbWluIiwiYWdlbnQiOiJ2c2NvZGUtcmVzdGNsaWVudCIsImlhdCI6MTU5OTQ5OTAxNCwiZXhwIjoxNTk5NDk5MzE0fQ.kpW1KPVGQvI7iZ1-EhyeO5MAGbo4clpQ0J-ategIf2w { - "key": "testApi-4234222", + "key": "testAp", "value": { }, "description": "тестовое апи22", "service_name": "test-api", - "author": "vigdorov23422" + "author": "vigdorov23422", + "hide": true } ### DELETE http://localhost:4001/store/testAp HTTP/1.1 -Api-Name: store-service-test +Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2dpbiI6InZpZ2Rvcm92IiwiYWdlbnQiOiJ2c2NvZGUtcmVzdGNsaWVudCIsImlhdCI6MTU5OTQ5OTA1MywiZXhwIjoxNTk5NDk5MzUzfQ.xBWvh867VmZb2cREvoul5vQwNA5z9nq6bSXx9ZH5lhc + ### GET http://localhost:4001/logs/client HTTP/1.1 diff --git a/src/store/store.controller.ts b/src/store/store.controller.ts index 693706d..f216175 100644 --- a/src/store/store.controller.ts +++ b/src/store/store.controller.ts @@ -3,7 +3,7 @@ import {StoreService} from './store.service'; import {Store, StoreRequest} from './store.schema'; import {ApiResponse, ApiTags, ApiParam, ApiBody, ApiBearerAuth, ApiSecurity, ApiQuery} from '@nestjs/swagger'; import {ALLOW_ORIGIN_ALL, ALLOW_METHOD, ALLOW_CREDENTIALS, CONTENT_LENGTH, ALLOW_HEADERS, COLLECTION_STORE} from 'src/consts'; -import {Request} from 'express'; +import {Request, response} from 'express'; import {LoggingInterceptor} from 'src/logs/logging.interceptor'; import { FIND_ALL_SUCCESS, @@ -22,9 +22,9 @@ import {AuthService} from 'src/services/auth.service'; import {makeApiHeader} from './utils'; const prepareStoreToStoreRequest = ({ - key, value, description, service_name, author + key, value, description, service_name, author, hide }: Store): StoreRequest => ({ - key, value, description, service_name, author, + key, value, description, service_name, author, hide: !!hide }); @ApiSecurity('apiKey') @@ -45,7 +45,7 @@ export class StoreController { await this.authService.checkRequest(request); const api = makeApiHeader(request); - const storeList = await this.storeService.findAll(api); + const storeList = await this.storeService.findAll(api, request.headers.authorization); return storeList.map(prepareStoreToStoreRequest); } @@ -71,7 +71,7 @@ export class StoreController { await this.authService.checkRequest(request); } - const store = await this.storeService.findOne(api, key); + const store = await this.storeService.findOneStore(api, key, request.headers.authorization); return prepareStoreToStoreRequest(store); } @@ -113,7 +113,7 @@ export class StoreController { await this.authService.checkRequest(request); } - const store = await this.storeService.update(api, request.body); + const store = await this.storeService.update(api, request.body, request.headers.authorization); return prepareStoreToStoreRequest(store); } @@ -128,7 +128,7 @@ export class StoreController { async removeOne(@Req() request: Request<{key: string}>): Promise { const {key} = request.params; const api = makeApiHeader(request); - const store = await this.storeService.removeOne(api, key); + const store = await this.storeService.removeOne(api, key, request.headers.authorization); return prepareStoreToStoreRequest(store); } diff --git a/src/store/store.schema.ts b/src/store/store.schema.ts index fb01e9c..3619cdd 100644 --- a/src/store/store.schema.ts +++ b/src/store/store.schema.ts @@ -62,6 +62,9 @@ export class StoreRequest { @ApiProperty() author: string; + @ApiProperty() + hide: boolean; + @ApiProperty() hook_tokens?: HookTokenResponse[]; } @@ -134,6 +137,11 @@ export class Store extends Document { }) author: string; + @Prop({ + type: Boolean, + }) + hide: boolean; + @Prop({ type: [HookTokenSchema] }) diff --git a/src/store/store.service.ts b/src/store/store.service.ts index 7538447..1a65e1d 100644 --- a/src/store/store.service.ts +++ b/src/store/store.service.ts @@ -1,5 +1,5 @@ import {Model, Connection} from 'mongoose'; -import {Injectable, NotFoundException, BadGatewayException, ConflictException, BadRequestException, HttpException, HttpService} from '@nestjs/common'; +import {Injectable, NotFoundException, BadGatewayException, ConflictException, BadRequestException, HttpException, HttpService, UnauthorizedException} from '@nestjs/common'; import {InjectConnection} from '@nestjs/mongoose'; import {Store, StoreRequest, StoreSchema, HookToken, HookTokenResponse, HookTokenMap, RightType} from './store.schema'; import {DB_TEST_NAME, DB_NAME, COLLECTION_STORE} from 'src/consts'; @@ -12,6 +12,12 @@ interface Token { exp: number; } +interface User { + login: string; + avatar: string; + is_admin: boolean; +} + const prepareHook = (baseHook: HookToken): HookTokenResponse => ({ holder: baseHook.holder, description: baseHook.description, @@ -27,6 +33,7 @@ const prepareStore = (baseStore: Store): StoreRequest => ({ author: baseStore.author, service_name: baseStore.service_name, hook_tokens: baseStore.hook_tokens?.map(prepareHook), + hide: !!baseStore.hide, }) const validateModel = async (store: Store) => { @@ -52,8 +59,11 @@ export class StoreService { return this.dbConnection.model(COLLECTION_STORE, StoreSchema); } - async findAll(api: string): Promise { - return this.storeModel(api).find().exec(); + async findAll(api: string, access_token: string): Promise { + const user = await this.loadUserByToken(access_token); + return (await this.storeModel(api).find().exec()).filter(store => { + return user.is_admin || store.author === user.login || !store.hide; + }); } async create(api: string, store: StoreRequest, access_token: string): Promise { @@ -72,9 +82,10 @@ export class StoreService { }; 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 user = await this.loadUserByToken(access_token); + const author = user.is_admin ? store.author : login; + await this.http.get(`${apiPath}${author}`, {headers}).toPromise(); + const createdStore = new (this.storeModel(api))({ ...store, author, @@ -103,10 +114,35 @@ export class StoreService { } } - async update(api: string, {author, ...omitProps}: StoreRequest): Promise { + async loadUserByToken (access_token: string): Promise { + 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(); + return currentUser; + } catch (e) { + if (e?.response?.status === 401) { + throw new UnauthorizedException('Доступ запрещен'); + } + throw new BadRequestException(e.message); + } + + } + + async update(api: string, {author, ...omitProps}: StoreRequest, access_token: string): Promise { const searchStore = await this.findOne(api, omitProps.key); if (searchStore) { + const user = await this.loadUserByToken(access_token); + + if (!user.is_admin && user.login !== searchStore.author) { + throw new UnauthorizedException('Доступ запрещен'); + } + const store = { ...omitProps, author: searchStore.author, @@ -140,10 +176,25 @@ export class StoreService { return searchStore; } - async removeOne(api: string, key: string): Promise { + async findOneStore(api: string, key: string, access_token: string): Promise { + const store = await this.findOne(api, key); + const user = await this.loadUserByToken(access_token); + if (user.is_admin || user.login === store.author || !store.hide) { + return store; + } + throw new UnauthorizedException('Доступ запрещен'); + } + + async removeOne(api: string, key: string, access_token: string): Promise { const searchStore = await this.findOne(api, key); if (searchStore) { + const user = await this.loadUserByToken(access_token); + + if (!user.is_admin && user.login !== searchStore.author) { + throw new UnauthorizedException('Доступ запрещен'); + } + await this.storeModel(api).deleteOne({key}); return searchStore;