Add access control (#65)
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
import axios from 'axios';
|
||||
|
||||
interface TokenResponse {
|
||||
access_token: string;
|
||||
expires_in: number;
|
||||
token_type: string;
|
||||
}
|
||||
|
||||
export default class CognitoClient {
|
||||
tokenEndpont: string;
|
||||
clientId: string;
|
||||
clientSecret: string;
|
||||
scope: string;
|
||||
expiresAt: number;
|
||||
accessToken: string;
|
||||
|
||||
constructor(
|
||||
tokenEndpoint: string,
|
||||
clientId: string,
|
||||
clientSecret: string,
|
||||
scope: string,
|
||||
) {
|
||||
this.tokenEndpont = `${tokenEndpoint}/oauth2/token`;
|
||||
this.clientId = clientId;
|
||||
this.clientSecret = clientSecret;
|
||||
this.scope = scope;
|
||||
this.accessToken = '';
|
||||
this.expiresAt = 0;
|
||||
}
|
||||
|
||||
private getPostData(): URLSearchParams {
|
||||
const params = new URLSearchParams();
|
||||
params.append('grant_type', 'client_credentials');
|
||||
params.append('client_id', this.clientId);
|
||||
params.append('scope', this.scope);
|
||||
return params;
|
||||
}
|
||||
|
||||
public async getToken(): Promise<string> {
|
||||
const now = Date.now();
|
||||
if (this.expiresAt < now) {
|
||||
await this.getNewToken();
|
||||
}
|
||||
console.log('Getting cached token');
|
||||
return this.accessToken;
|
||||
}
|
||||
|
||||
private async getNewToken(): Promise<void> {
|
||||
console.log('Getting new token');
|
||||
const authString =
|
||||
'Basic ' +
|
||||
Buffer.from(this.clientId + ':' + this.clientSecret).toString('base64');
|
||||
const res: TokenResponse = await axios
|
||||
.post(this.tokenEndpont, this.getPostData(), {
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
Authorization: authString,
|
||||
},
|
||||
})
|
||||
.then((r) => r.data);
|
||||
|
||||
this.accessToken = res.access_token;
|
||||
this.expiresAt = Date.now() + res.expires_in * 1e3;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
import axios from 'axios';
|
||||
import CognitoClient from './cognito-client';
|
||||
|
||||
const config = {
|
||||
PW_GRAPHQL_URL: 'http://docker.for.mac.localhost:4000',
|
||||
API_TOKEN_ENDPOINT:
|
||||
'https://photowall-test-admin.auth.eu-west-1.amazoncognito.com',
|
||||
API_CLIENT_ID: '',
|
||||
API_CLIENT_SECRET: '',
|
||||
};
|
||||
|
||||
async function doQuery(
|
||||
query: string,
|
||||
variables: any,
|
||||
scopes: string[],
|
||||
): Promise<any> {
|
||||
const cognitoClient = new CognitoClient(
|
||||
config.API_TOKEN_ENDPOINT,
|
||||
config.API_CLIENT_ID,
|
||||
config.API_CLIENT_SECRET,
|
||||
scopes.join(' '),
|
||||
);
|
||||
let token = '';
|
||||
token = await cognitoClient.getToken();
|
||||
|
||||
const url = config.PW_GRAPHQL_URL;
|
||||
const res = await axios({
|
||||
url: url,
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
data: JSON.stringify({
|
||||
query,
|
||||
variables,
|
||||
}),
|
||||
});
|
||||
return res;
|
||||
}
|
||||
|
||||
async function testGetOrder() {
|
||||
const query = `
|
||||
query GetOrder($id: ID!) {
|
||||
order(id:$id){
|
||||
id
|
||||
klarnaOrderId
|
||||
# paypalTransactionId
|
||||
customerType
|
||||
countryCode
|
||||
}
|
||||
}
|
||||
`;
|
||||
const variables = {
|
||||
id: '414246',
|
||||
};
|
||||
const res = await doQuery(query, variables, [
|
||||
'https://graphql.photowall.com/orders.read',
|
||||
]);
|
||||
console.log(JSON.stringify(res.data, null, 2));
|
||||
}
|
||||
|
||||
async function testGetProduct() {
|
||||
const query = `
|
||||
query GetProduct($id: Int!) {
|
||||
product(id:$id){
|
||||
id
|
||||
fields {
|
||||
artNo
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
const variables = {
|
||||
id: 52924,
|
||||
};
|
||||
const res = await doQuery(query, variables, [
|
||||
'https://graphql.photowall.com/products.read',
|
||||
]);
|
||||
console.log(JSON.stringify(res.data, null, 2));
|
||||
}
|
||||
|
||||
async function main() {
|
||||
console.log('Starting');
|
||||
try {
|
||||
await testGetOrder();
|
||||
await testGetProduct();
|
||||
} catch (e) {
|
||||
console.log(e?.response.data.errors);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
+3
-1
@@ -6,7 +6,9 @@
|
||||
"scripts": {
|
||||
"exportSQL": "cd src/__test__/db_generator/ && npm install && node index.js && cd ../../../",
|
||||
"test": "npm run exportSQL && jest --detectOpenHandles",
|
||||
"test:dev": "npm run exportSQL && jest --watch"
|
||||
"test:dev": "npm run exportSQL && jest --watch",
|
||||
"unit:dev": "jest --watch src/__test__/unit.test.ts",
|
||||
"test:manual": "cd manual-tests && ts-node test-scopes.ts"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
import { checkAccess } from '../cognito/access-control';
|
||||
import {
|
||||
AccessTokenClaim,
|
||||
ClaimVerifyResult,
|
||||
IdTokenClaim,
|
||||
} from '../cognito/cognito-client';
|
||||
|
||||
const idTokenAuth = {
|
||||
userName: 'niklas.fondberg@photowall.se',
|
||||
isValid: true,
|
||||
idToken: {
|
||||
'custom:adgroups':
|
||||
'[Domain Users, Images_Write, VPN_Users, WebAdmin, WebAdminSuperUser]',
|
||||
'cognito:username': 'photowalladfs_photowall\\niklas.fondberg',
|
||||
token_use: 'id',
|
||||
email_verified: false,
|
||||
nonce: 'Qc9kHCXrN----4st19sM',
|
||||
at_hash: '9x_RMAO3_2NW1cQYkhWEtg',
|
||||
email: 'niklas.fondberg@photowall.se',
|
||||
aud: '53og32ofjne7e6i4uf10r3bg2b',
|
||||
sub: '2646dd8b-f914-40eb-895b-c652976a65e1',
|
||||
identities: [null],
|
||||
} as IdTokenClaim,
|
||||
accessToken: null,
|
||||
} as ClaimVerifyResult;
|
||||
|
||||
const accessTokenAuth = {
|
||||
isValid: true,
|
||||
idToken: null,
|
||||
accessToken: {
|
||||
token_use: 'access',
|
||||
username: '',
|
||||
scope:
|
||||
'https://graphql.photowall.com/orders.write https://graphql.photowall.com/orders.read',
|
||||
client_id: '5vftcl43n2jkcdvd9imc270mtu',
|
||||
} as AccessTokenClaim,
|
||||
} as ClaimVerifyResult;
|
||||
|
||||
describe('GraphQL Apollo unit tests', () => {
|
||||
describe('Test auth', () => {
|
||||
it('user test is accepted', () => {
|
||||
const auth: ClaimVerifyResult = {
|
||||
userName: 'testuser',
|
||||
idToken: null,
|
||||
accessToken: null,
|
||||
isValid: true,
|
||||
};
|
||||
expect(() => checkAccess(['orders.read'], auth)).not.toThrow();
|
||||
});
|
||||
});
|
||||
describe('Test auth not ok for test', () => {
|
||||
it('user bla is accepted', () => {
|
||||
const auth: ClaimVerifyResult = {
|
||||
userName: 'bla',
|
||||
idToken: null,
|
||||
accessToken: null,
|
||||
isValid: true,
|
||||
};
|
||||
expect(() => checkAccess(['orders.read'], auth)).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Test access auth', () => {
|
||||
it('user test is accepted', () => {
|
||||
const auth = accessTokenAuth;
|
||||
expect(() =>
|
||||
checkAccess(['orders.read', 'orders.write'], auth),
|
||||
).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Test access auth not enough scope', () => {
|
||||
it('user test is accepted', () => {
|
||||
const auth = accessTokenAuth;
|
||||
expect(() =>
|
||||
checkAccess(['products.read', 'orders.read'], auth),
|
||||
).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Test id auth', () => {
|
||||
it('user test is accepted', () => {
|
||||
const auth = idTokenAuth;
|
||||
expect(() =>
|
||||
checkAccess(['orders.read', 'orders.write'], auth),
|
||||
).not.toThrow();
|
||||
});
|
||||
});
|
||||
describe('Test id auth no enough', () => {
|
||||
it('user test is accepted', () => {
|
||||
const auth = JSON.parse(JSON.stringify(idTokenAuth)) as ClaimVerifyResult;
|
||||
auth.idToken['custom:adgroups'] = '[]';
|
||||
expect(() =>
|
||||
checkAccess(['orders.read', 'orders.write'], auth),
|
||||
).toThrow();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,35 @@
|
||||
import { AuthenticationError } from 'apollo-server';
|
||||
import { ClaimVerifyResult } from './cognito-client';
|
||||
|
||||
export const RESOURCE = 'https://graphql.photowall.com';
|
||||
|
||||
export enum SCOPES {
|
||||
ORDERS_READ = 'orders.read',
|
||||
ORDERS_WRITE = 'orders.write',
|
||||
DESIGNERS_READ = 'designers.read',
|
||||
DESIGNERS_WRITE = 'designers.write',
|
||||
PRODUCTS_READ = 'products.read',
|
||||
PRODUCTS_WRITE = 'products.write',
|
||||
}
|
||||
|
||||
export function checkAccess(scopes: string[], auth: ClaimVerifyResult): void {
|
||||
if (auth.userName === 'testuser') {
|
||||
return;
|
||||
}
|
||||
if (auth.idToken) {
|
||||
if (auth.idToken['custom:adgroups'].includes('WebAdmin')) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (auth.accessToken) {
|
||||
const authScopes = auth.accessToken.scope.split(' ');
|
||||
|
||||
const requiredScoped = scopes.map((s) => `${RESOURCE}/${s}`);
|
||||
const checker = (arr: string[], target: string[]) =>
|
||||
target.every((v) => arr.includes(v));
|
||||
if (checker(authScopes, requiredScoped)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw new AuthenticationError('Not authorized for this endpoint');
|
||||
}
|
||||
@@ -49,7 +49,7 @@ interface Claim {
|
||||
iss: string;
|
||||
}
|
||||
|
||||
interface AccessTokenClaim extends Claim {
|
||||
export interface AccessTokenClaim extends Claim {
|
||||
token_use: string;
|
||||
username: string;
|
||||
client_id: string;
|
||||
@@ -57,7 +57,7 @@ interface AccessTokenClaim extends Claim {
|
||||
scope: string;
|
||||
}
|
||||
|
||||
interface IdTokenClaim extends Claim {
|
||||
export interface IdTokenClaim extends Claim {
|
||||
'custom:adgroups': string; // 'custom:adgroups': '[Domain Users, Images_Write, VPN_Users, WebAdmin, WebAdminSuperUser]',
|
||||
'cognito:username': string;
|
||||
token_use: string;
|
||||
|
||||
+10
-3
@@ -15,7 +15,7 @@ import { MarketLocaleAPI } from './datasources/market-locale-api';
|
||||
import { ImageServerApi } from './datasources/imageserver-api';
|
||||
import { InteriorAPI } from './datasources/interior-api';
|
||||
|
||||
import { verifyToken } from './cognito/cognito-client';
|
||||
import { ClaimVerifyResult, verifyToken } from './cognito/cognito-client';
|
||||
import { readFileSync } from 'fs';
|
||||
import { InteriorsLambdaAPI } from './datasources/interiors-lambda-api';
|
||||
const path = require('path');
|
||||
@@ -51,7 +51,7 @@ const dataSources = () => ({
|
||||
|
||||
const context = async ({ req }) => {
|
||||
if (process.env.NODE_ENV == 'test') {
|
||||
return { auth: 'testuser' };
|
||||
return { auth: { userName: 'testuser' } };
|
||||
}
|
||||
|
||||
const authHeader = req.headers.authorization || '';
|
||||
@@ -65,7 +65,14 @@ const context = async ({ req }) => {
|
||||
process.env.NODE_ENV === 'development' &&
|
||||
authHeader == 'Basic ZGV2OnB2N1VmYSFiZVAzeGk='
|
||||
) {
|
||||
return { auth: 'during development' };
|
||||
return {
|
||||
auth: {
|
||||
userName: 'testuser',
|
||||
isValid: true,
|
||||
accessToken: null,
|
||||
idToken: null,
|
||||
},
|
||||
};
|
||||
}
|
||||
throw new AuthenticationError('Not authorized');
|
||||
};
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { checkAccess } from '../cognito/access-control';
|
||||
import { CategoryAPI } from '../datasources/category-api';
|
||||
import { ProductAPI } from '../datasources/product-api';
|
||||
|
||||
const Category = {
|
||||
async products({ id }, _args, { dataSources }) {
|
||||
async products({ id }, _args, { dataSources, auth }) {
|
||||
checkAccess(['products.read'], auth);
|
||||
return (<ProductAPI>dataSources.productApi).getCategoryProducts(id);
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,22 +1,27 @@
|
||||
import { checkAccess } from '../cognito/access-control';
|
||||
import { DesignerAPI } from '../datasources/designer-api';
|
||||
import { OrderAPI } from '../datasources/order-api';
|
||||
import { ProductAPI } from '../datasources/product-api';
|
||||
import { GeneralInput } from '../types/types';
|
||||
|
||||
const Designer = {
|
||||
async orderRows({ id }, input: GeneralInput, { dataSources }) {
|
||||
async orderRows({ id }, input: GeneralInput, { dataSources, auth }) {
|
||||
checkAccess(['orders.read'], auth);
|
||||
return (<OrderAPI>dataSources.orderApi).getOrderRowsByDesignerId(id, input);
|
||||
},
|
||||
async products({ id }, _args, { dataSources }) {
|
||||
async products({ id }, _args, { dataSources, auth }) {
|
||||
checkAccess(['products.read'], auth);
|
||||
return (<ProductAPI>dataSources.productApi).getDesignerProducts(id);
|
||||
},
|
||||
};
|
||||
|
||||
async function getDesigners(_, { limit }, { dataSources }) {
|
||||
async function getDesigners(_, { limit }, { dataSources, auth }) {
|
||||
checkAccess(['designers.read'], auth);
|
||||
return (<DesignerAPI>dataSources.designerApi).getDesigners(limit);
|
||||
}
|
||||
|
||||
async function getDesigner(parent, { id }, { dataSources }) {
|
||||
async function getDesigner(_, { id }, { dataSources, auth }) {
|
||||
checkAccess(['designers.read'], auth);
|
||||
return (<DesignerAPI>dataSources.designerApi).getDesignerById(id);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { checkAccess } from '../cognito/access-control';
|
||||
import { KeywordAPI } from '../datasources/keyword-api';
|
||||
import { ProductAPI } from '../datasources/product-api';
|
||||
|
||||
const Keyword = {
|
||||
async products({ id }, _args, { dataSources }) {
|
||||
async products({ id }, _args, { dataSources, auth }) {
|
||||
checkAccess(['products.read'], auth);
|
||||
return (<ProductAPI>dataSources.productApi).getKeywordProducts(id);
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { checkAccess } from '../cognito/access-control';
|
||||
import { MarketLocaleAPI } from '../datasources/market-locale-api';
|
||||
import { OrderAPI } from '../datasources/order-api';
|
||||
import { OrdersResult } from '../types/order-types';
|
||||
@@ -27,7 +28,8 @@ const Order = {
|
||||
};
|
||||
|
||||
const OrderRow = {
|
||||
async order(parent, _args, { dataSources }) {
|
||||
async order(parent, _args, { dataSources, auth }) {
|
||||
checkAccess(['orders.read'], auth);
|
||||
return (<OrderAPI>dataSources.orderApi).getOrderById(parent.orderId);
|
||||
},
|
||||
};
|
||||
@@ -35,8 +37,9 @@ const OrderRow = {
|
||||
async function getOrdersResult(
|
||||
_,
|
||||
input: GeneralInput,
|
||||
{ dataSources },
|
||||
{ dataSources, auth },
|
||||
): Promise<OrdersResult> {
|
||||
checkAccess(['orders.read'], auth);
|
||||
const total = (<OrderAPI>dataSources.orderApi).getOrdersTotal(input);
|
||||
const items = (<OrderAPI>dataSources.orderApi).getOrders(input);
|
||||
return {
|
||||
@@ -49,7 +52,8 @@ async function getOrdersResult(
|
||||
};
|
||||
}
|
||||
|
||||
async function getOrder(_, { id }, { dataSources }) {
|
||||
async function getOrder(_, { id }, { dataSources, auth }) {
|
||||
checkAccess(['orders.read'], auth);
|
||||
return (<OrderAPI>dataSources.orderApi).getOrderById(id);
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ import { InteriorsLambdaAPI } from '../datasources/interiors-lambda-api';
|
||||
import { IdNumberResult } from '../types/types';
|
||||
import { moveS3File } from '../s3';
|
||||
import { DesignerAPI } from '../datasources/designer-api';
|
||||
import { checkAccess } from '../cognito/access-control';
|
||||
|
||||
const ProductBlacklist = {
|
||||
async market(parent, _args, { dataSources }) {
|
||||
@@ -30,7 +31,8 @@ const Product = {
|
||||
async categories({ id }, _args, { dataSources }): Promise<Array<Category>> {
|
||||
return (<CategoryAPI>dataSources.categoryApi).getProductCategories(id);
|
||||
},
|
||||
async designer({ designerId }, _args, { dataSources }) {
|
||||
async designer({ designerId }, _args, { dataSources, auth }) {
|
||||
checkAccess(['designers.read'], auth);
|
||||
if (!designerId) {
|
||||
return null;
|
||||
}
|
||||
@@ -53,8 +55,9 @@ const PrintProduct = {
|
||||
async function getProductsResult(
|
||||
_,
|
||||
input: ProductsFilterInput,
|
||||
{ dataSources },
|
||||
{ dataSources, auth },
|
||||
): Promise<ProductListResult> {
|
||||
checkAccess(['products.read'], auth);
|
||||
const total = (<ProductAPI>dataSources.productApi).getProductsTotal(input);
|
||||
const items = (<ProductAPI>dataSources.productApi).getProducts(input);
|
||||
|
||||
@@ -71,8 +74,9 @@ async function getProductsResult(
|
||||
async function getProductsSearchResult(
|
||||
_,
|
||||
{ q },
|
||||
{ dataSources },
|
||||
{ dataSources, auth },
|
||||
): Promise<ProductsSearchResult> {
|
||||
checkAccess(['products.read', 'designers.read'], auth);
|
||||
const prodApi = <ProductAPI>dataSources.productApi;
|
||||
const catApi = <CategoryAPI>dataSources.categoryApi;
|
||||
const keywApi = <KeywordAPI>dataSources.keywordApi;
|
||||
@@ -92,7 +96,7 @@ async function getProductsSearchResult(
|
||||
};
|
||||
}
|
||||
|
||||
async function getProduct(_, { id }, { dataSources }): Promise<Product> {
|
||||
async function getProduct(_, { id }, { dataSources, auth }): Promise<Product> {
|
||||
return (<ProductAPI>dataSources.productApi).getProduct(id);
|
||||
}
|
||||
|
||||
@@ -111,14 +115,16 @@ export const productQueryTypeDefs = {
|
||||
///////////////////
|
||||
// Mutations below
|
||||
export const productMutationTypeDefs = {
|
||||
async addProduct(_, { name, batch }, { dataSources }) {
|
||||
async addProduct(_, { name, batch }, { dataSources, auth }) {
|
||||
checkAccess(['products.write'], auth);
|
||||
const id = await (<ProductAPI>dataSources.productApi).addProduct(
|
||||
name,
|
||||
batch,
|
||||
);
|
||||
return (<ProductAPI>dataSources.productApi).getProduct(id);
|
||||
},
|
||||
async productInfo(_, { productId, info }, { dataSources }) {
|
||||
async productInfo(_, { productId, info }, { dataSources, auth }) {
|
||||
checkAccess(['products.write'], auth);
|
||||
await (<ProductAPI>dataSources.productApi).updateProductInfo(
|
||||
productId,
|
||||
info,
|
||||
@@ -128,8 +134,9 @@ export const productMutationTypeDefs = {
|
||||
async productFocusPoint(
|
||||
_,
|
||||
{ productId, focusXpoint2, focusYpoint2 },
|
||||
{ dataSources },
|
||||
{ dataSources, auth },
|
||||
) {
|
||||
checkAccess(['products.write'], auth);
|
||||
await (<ProductAPI>dataSources.productApi).updateFocusPoint(
|
||||
productId,
|
||||
focusXpoint2,
|
||||
@@ -145,14 +152,16 @@ export const productMutationTypeDefs = {
|
||||
)).generateNewInteriors(product);
|
||||
return (<ProductAPI>dataSources.productApi).getProduct(productId);
|
||||
},
|
||||
async productBlacklisting(_, { productId, markets }, { dataSources }) {
|
||||
async productBlacklisting(_, { productId, markets }, { dataSources, auth }) {
|
||||
checkAccess(['products.write'], auth);
|
||||
await (<ProductAPI>dataSources.productApi).updateBlacklisting(
|
||||
productId,
|
||||
markets,
|
||||
);
|
||||
return (<ProductAPI>dataSources.productApi).getProduct(productId);
|
||||
},
|
||||
async productGroup(_, { productId, groupIds }, { dataSources }) {
|
||||
async productGroup(_, { productId, groupIds }, { dataSources, auth }) {
|
||||
checkAccess(['products.write'], auth);
|
||||
await (<ProductAPI>dataSources.productApi).updateGroups(
|
||||
productId,
|
||||
groupIds,
|
||||
@@ -168,8 +177,9 @@ export const productMutationTypeDefs = {
|
||||
async productGroupInteriors(
|
||||
_,
|
||||
{ productId, groupId, uris },
|
||||
{ dataSources },
|
||||
{ dataSources, auth },
|
||||
) {
|
||||
checkAccess(['products.write'], auth);
|
||||
await (<ProductAPI>dataSources.productApi).addInteriorsToProductGroup(
|
||||
productId,
|
||||
groupId,
|
||||
@@ -177,7 +187,8 @@ export const productMutationTypeDefs = {
|
||||
);
|
||||
return (<ProductAPI>dataSources.productApi).getProduct(productId);
|
||||
},
|
||||
async productKeywords(_, { productId, keywordIds }, { dataSources }) {
|
||||
async productKeywords(_, { productId, keywordIds }, { dataSources, auth }) {
|
||||
checkAccess(['products.write'], auth);
|
||||
await (<KeywordAPI>dataSources.keywordApi).setProductKeywords(
|
||||
productId,
|
||||
keywordIds,
|
||||
@@ -187,8 +198,9 @@ export const productMutationTypeDefs = {
|
||||
async productProportionsWarning(
|
||||
_,
|
||||
{ productId, proportions },
|
||||
{ dataSources },
|
||||
{ dataSources, auth },
|
||||
) {
|
||||
checkAccess(['products.write'], auth);
|
||||
await (<ProductAPI>dataSources.productApi).setProportionsWarning(
|
||||
productId,
|
||||
proportions,
|
||||
@@ -198,8 +210,9 @@ export const productMutationTypeDefs = {
|
||||
async addOwnInteriorToPrintProduct(
|
||||
_,
|
||||
{ printId, uploadedS3Key },
|
||||
{ dataSources },
|
||||
{ dataSources, auth },
|
||||
): Promise<IdNumberResult> {
|
||||
checkAccess(['products.write'], auth);
|
||||
return (<InteriorAPI>dataSources.interiorApi).addOwnUploadToPrintId(
|
||||
printId,
|
||||
uploadedS3Key,
|
||||
@@ -208,8 +221,9 @@ export const productMutationTypeDefs = {
|
||||
async productWallpaperType(
|
||||
_,
|
||||
{ productId, wallpaperTypes },
|
||||
{ dataSources },
|
||||
{ dataSources, auth },
|
||||
) {
|
||||
checkAccess(['products.write'], auth);
|
||||
await (<ProductAPI>dataSources.productApi).setWallpaperTypes(
|
||||
productId,
|
||||
wallpaperTypes,
|
||||
@@ -219,8 +233,9 @@ export const productMutationTypeDefs = {
|
||||
async updateProductImage(
|
||||
_,
|
||||
{ productId, uploadedS3Key, width, height },
|
||||
{ dataSources },
|
||||
{ dataSources, auth },
|
||||
) {
|
||||
checkAccess(['products.write'], auth);
|
||||
await moveS3File(
|
||||
CONFIG.uploadBucket,
|
||||
uploadedS3Key,
|
||||
@@ -246,7 +261,12 @@ export const productMutationTypeDefs = {
|
||||
)).generateNewInteriors(product);
|
||||
return 'success';
|
||||
},
|
||||
async addRelatedProducts(_, { productId, articleNumbers }, { dataSources }) {
|
||||
async addRelatedProducts(
|
||||
_,
|
||||
{ productId, articleNumbers },
|
||||
{ dataSources, auth },
|
||||
) {
|
||||
checkAccess(['products.write'], auth);
|
||||
await (<ProductAPI>dataSources.productApi).addRelatedProducts(
|
||||
productId,
|
||||
articleNumbers,
|
||||
@@ -256,8 +276,9 @@ export const productMutationTypeDefs = {
|
||||
async removeRelatedProducts(
|
||||
_,
|
||||
{ productId, relatedProductIds },
|
||||
{ dataSources },
|
||||
{ dataSources, auth },
|
||||
) {
|
||||
checkAccess(['products.write'], auth);
|
||||
await (<ProductAPI>dataSources.productApi).removeRelatedProducts(
|
||||
productId,
|
||||
relatedProductIds,
|
||||
@@ -265,7 +286,12 @@ export const productMutationTypeDefs = {
|
||||
return (<ProductAPI>dataSources.productApi).getProduct(productId);
|
||||
},
|
||||
|
||||
async addCategoriesToProduct(_, { productId, categoryIds }, { dataSources }) {
|
||||
async addCategoriesToProduct(
|
||||
_,
|
||||
{ productId, categoryIds },
|
||||
{ dataSources, auth },
|
||||
) {
|
||||
checkAccess(['products.write'], auth);
|
||||
await (<ProductAPI>dataSources.productApi).addCategoriesToProduct(
|
||||
productId,
|
||||
categoryIds,
|
||||
@@ -276,15 +302,21 @@ export const productMutationTypeDefs = {
|
||||
async removeCategoriesFromProduct(
|
||||
_,
|
||||
{ productId, categoryIds },
|
||||
{ dataSources },
|
||||
{ dataSources, auth },
|
||||
) {
|
||||
checkAccess(['products.write'], auth);
|
||||
await (<ProductAPI>dataSources.productApi).removeCategoriesFromProduct(
|
||||
productId,
|
||||
categoryIds,
|
||||
);
|
||||
return (<ProductAPI>dataSources.productApi).getProduct(productId);
|
||||
},
|
||||
async updateProductComments(_, { productId, comments }, { dataSources }) {
|
||||
async updateProductComments(
|
||||
_,
|
||||
{ productId, comments },
|
||||
{ dataSources, auth },
|
||||
) {
|
||||
checkAccess(['products.write'], auth);
|
||||
await (<ProductAPI>dataSources.productApi).updateComments(
|
||||
productId,
|
||||
comments,
|
||||
|
||||
Reference in New Issue
Block a user