HM-114. Добавление правил работы с хранилищами
This commit is contained in:
@ -1,10 +1,12 @@
|
|||||||
###
|
###
|
||||||
GET http://localhost:4001/store HTTP/1.1
|
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
|
GET http://localhost:4001/store/rgfdfff HTTP/1.1
|
||||||
Api-Name: store-service-test
|
Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2dpbiI6InZpZ2Rvcm92IiwiYWdlbnQiOiJ2c2NvZGUtcmVzdGNsaWVudCIsImlhdCI6MTU5OTQ5OTYyMSwiZXhwIjoxNTk5NDk5OTIxfQ.Ym4xOpoe8UvO626Vxp-XqxspoJSL7JpjZ1rlSW0vIWs
|
||||||
|
|
||||||
|
|
||||||
###
|
###
|
||||||
GET http://localhost:4001/store/rgfdfff?hook=sdf6sdfhs99-sdf HTTP/1.1
|
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
|
POST http://localhost:4001/store HTTP/1.1
|
||||||
content-type: application/json
|
content-type: application/json
|
||||||
Api-Name: store-service-test
|
Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2dpbiI6InZpZ2Rvcm92IiwiYWdlbnQiOiJ2c2NvZGUtcmVzdGNsaWVudCIsImlhdCI6MTU5OTQ5NzU5NCwiZXhwIjoxNTk5NDk3ODk0fQ.j8_bKayAiHgVLL-SfiHAH4SnijJLi8dfGWlLx30u8Kc
|
||||||
Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2dpbiI6InN0cmluZyIsImFnZW50IjoidnNjb2RlLXJlc3RjbGllbnQiLCJpYXQiOjE1OTY5MDM0NTQsImV4cCI6MTU5NjkwMzc1NH0.Uos0Ei9Fes1euZ72uh_6E9ixG1orBu79bFYjzzmWRR8
|
|
||||||
|
|
||||||
{
|
{
|
||||||
"key": "testAp",
|
"key": "testAp2",
|
||||||
"value": {
|
"value": {
|
||||||
|
|
||||||
},
|
},
|
||||||
"description": "тестовое апи",
|
"description": "тестовое апи",
|
||||||
"service_name": "test-api",
|
"service_name": "test-api",
|
||||||
"author": "vigdorov"
|
"author": "vigdorov2",
|
||||||
|
"hide": true
|
||||||
}
|
}
|
||||||
|
|
||||||
### Update Request
|
### Update Request
|
||||||
PUT http://localhost:4001/store HTTP/1.1
|
PUT http://localhost:4001/store HTTP/1.1
|
||||||
content-type: application/json
|
content-type: application/json
|
||||||
Api-Name: store-service-test
|
Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2dpbiI6ImFkbWluIiwiYWdlbnQiOiJ2c2NvZGUtcmVzdGNsaWVudCIsImlhdCI6MTU5OTQ5OTAxNCwiZXhwIjoxNTk5NDk5MzE0fQ.kpW1KPVGQvI7iZ1-EhyeO5MAGbo4clpQ0J-ategIf2w
|
||||||
|
|
||||||
{
|
{
|
||||||
"key": "testApi-4234222",
|
"key": "testAp",
|
||||||
"value": {
|
"value": {
|
||||||
|
|
||||||
},
|
},
|
||||||
"description": "тестовое апи22",
|
"description": "тестовое апи22",
|
||||||
"service_name": "test-api",
|
"service_name": "test-api",
|
||||||
"author": "vigdorov23422"
|
"author": "vigdorov23422",
|
||||||
|
"hide": true
|
||||||
}
|
}
|
||||||
|
|
||||||
###
|
###
|
||||||
DELETE http://localhost:4001/store/testAp HTTP/1.1
|
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
|
GET http://localhost:4001/logs/client HTTP/1.1
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import {StoreService} from './store.service';
|
|||||||
import {Store, StoreRequest} from './store.schema';
|
import {Store, StoreRequest} from './store.schema';
|
||||||
import {ApiResponse, ApiTags, ApiParam, ApiBody, ApiBearerAuth, ApiSecurity, ApiQuery} from '@nestjs/swagger';
|
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 {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 {LoggingInterceptor} from 'src/logs/logging.interceptor';
|
||||||
import {
|
import {
|
||||||
FIND_ALL_SUCCESS,
|
FIND_ALL_SUCCESS,
|
||||||
@ -22,9 +22,9 @@ import {AuthService} from 'src/services/auth.service';
|
|||||||
import {makeApiHeader} from './utils';
|
import {makeApiHeader} from './utils';
|
||||||
|
|
||||||
const prepareStoreToStoreRequest = ({
|
const prepareStoreToStoreRequest = ({
|
||||||
key, value, description, service_name, author
|
key, value, description, service_name, author, hide
|
||||||
}: Store): StoreRequest => ({
|
}: Store): StoreRequest => ({
|
||||||
key, value, description, service_name, author,
|
key, value, description, service_name, author, hide: !!hide
|
||||||
});
|
});
|
||||||
|
|
||||||
@ApiSecurity('apiKey')
|
@ApiSecurity('apiKey')
|
||||||
@ -45,7 +45,7 @@ export class StoreController {
|
|||||||
await this.authService.checkRequest(request);
|
await this.authService.checkRequest(request);
|
||||||
|
|
||||||
const api = makeApiHeader(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);
|
return storeList.map(prepareStoreToStoreRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,7 +71,7 @@ export class StoreController {
|
|||||||
await this.authService.checkRequest(request);
|
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);
|
return prepareStoreToStoreRequest(store);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,7 +113,7 @@ export class StoreController {
|
|||||||
await this.authService.checkRequest(request);
|
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);
|
return prepareStoreToStoreRequest(store);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,7 +128,7 @@ export class StoreController {
|
|||||||
async removeOne(@Req() request: Request<{key: string}>): Promise<StoreRequest> {
|
async removeOne(@Req() request: Request<{key: string}>): Promise<StoreRequest> {
|
||||||
const {key} = request.params;
|
const {key} = request.params;
|
||||||
const api = makeApiHeader(request);
|
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);
|
return prepareStoreToStoreRequest(store);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -62,6 +62,9 @@ export class StoreRequest {
|
|||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
author: string;
|
author: string;
|
||||||
|
|
||||||
|
@ApiProperty()
|
||||||
|
hide: boolean;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
hook_tokens?: HookTokenResponse[];
|
hook_tokens?: HookTokenResponse[];
|
||||||
}
|
}
|
||||||
@ -134,6 +137,11 @@ export class Store extends Document {
|
|||||||
})
|
})
|
||||||
author: string;
|
author: string;
|
||||||
|
|
||||||
|
@Prop({
|
||||||
|
type: Boolean,
|
||||||
|
})
|
||||||
|
hide: boolean;
|
||||||
|
|
||||||
@Prop({
|
@Prop({
|
||||||
type: [HookTokenSchema]
|
type: [HookTokenSchema]
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import {Model, Connection} from 'mongoose';
|
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 {InjectConnection} from '@nestjs/mongoose';
|
||||||
import {Store, StoreRequest, StoreSchema, HookToken, HookTokenResponse, HookTokenMap, RightType} from './store.schema';
|
import {Store, StoreRequest, StoreSchema, HookToken, HookTokenResponse, HookTokenMap, RightType} 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';
|
||||||
@ -12,6 +12,12 @@ interface Token {
|
|||||||
exp: number;
|
exp: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface User {
|
||||||
|
login: string;
|
||||||
|
avatar: string;
|
||||||
|
is_admin: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
const prepareHook = (baseHook: HookToken): HookTokenResponse => ({
|
const prepareHook = (baseHook: HookToken): HookTokenResponse => ({
|
||||||
holder: baseHook.holder,
|
holder: baseHook.holder,
|
||||||
description: baseHook.description,
|
description: baseHook.description,
|
||||||
@ -27,6 +33,7 @@ const prepareStore = (baseStore: Store): StoreRequest => ({
|
|||||||
author: baseStore.author,
|
author: baseStore.author,
|
||||||
service_name: baseStore.service_name,
|
service_name: baseStore.service_name,
|
||||||
hook_tokens: baseStore.hook_tokens?.map(prepareHook),
|
hook_tokens: baseStore.hook_tokens?.map(prepareHook),
|
||||||
|
hide: !!baseStore.hide,
|
||||||
})
|
})
|
||||||
|
|
||||||
const validateModel = async (store: Store) => {
|
const validateModel = async (store: Store) => {
|
||||||
@ -52,8 +59,11 @@ export class StoreService {
|
|||||||
return this.dbConnection.model<Store>(COLLECTION_STORE, StoreSchema);
|
return this.dbConnection.model<Store>(COLLECTION_STORE, StoreSchema);
|
||||||
}
|
}
|
||||||
|
|
||||||
async findAll(api: string): Promise<Store[]> {
|
async findAll(api: string, access_token: string): Promise<Store[]> {
|
||||||
return this.storeModel(api).find().exec();
|
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<Store> {
|
async create(api: string, store: StoreRequest, access_token: string): Promise<Store> {
|
||||||
@ -72,9 +82,10 @@ export class StoreService {
|
|||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const {data: currentUser} = await this.http.get(`${apiPath}${login}`, {headers}).toPromise();
|
const user = await this.loadUserByToken(access_token);
|
||||||
await this.http.get(`${apiPath}${store.author}`, {headers}).toPromise();
|
const author = user.is_admin ? store.author : login;
|
||||||
const author = currentUser.is_admin ? store.author : login;
|
await this.http.get(`${apiPath}${author}`, {headers}).toPromise();
|
||||||
|
|
||||||
const createdStore = new (this.storeModel(api))({
|
const createdStore = new (this.storeModel(api))({
|
||||||
...store,
|
...store,
|
||||||
author,
|
author,
|
||||||
@ -103,10 +114,35 @@ export class StoreService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async update(api: string, {author, ...omitProps}: StoreRequest): Promise<Store> {
|
async loadUserByToken (access_token: string): Promise<User> {
|
||||||
|
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<User>(`${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<Store> {
|
||||||
const searchStore = await this.findOne(api, omitProps.key);
|
const searchStore = await this.findOne(api, omitProps.key);
|
||||||
|
|
||||||
if (searchStore) {
|
if (searchStore) {
|
||||||
|
const user = await this.loadUserByToken(access_token);
|
||||||
|
|
||||||
|
if (!user.is_admin && user.login !== searchStore.author) {
|
||||||
|
throw new UnauthorizedException('Доступ запрещен');
|
||||||
|
}
|
||||||
|
|
||||||
const store = {
|
const store = {
|
||||||
...omitProps,
|
...omitProps,
|
||||||
author: searchStore.author,
|
author: searchStore.author,
|
||||||
@ -140,10 +176,25 @@ export class StoreService {
|
|||||||
return searchStore;
|
return searchStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
async removeOne(api: string, key: string): Promise<Store> {
|
async findOneStore(api: string, key: string, access_token: string): Promise<Store> {
|
||||||
|
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<Store> {
|
||||||
const searchStore = await this.findOne(api, key);
|
const searchStore = await this.findOne(api, key);
|
||||||
|
|
||||||
if (searchStore) {
|
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});
|
await this.storeModel(api).deleteOne({key});
|
||||||
|
|
||||||
return searchStore;
|
return searchStore;
|
||||||
|
|||||||
Reference in New Issue
Block a user