Treat sanitizedquery as frozen

This commit is contained in:
rijkvanzanten
2020-09-08 18:54:18 -04:00
parent b19ffd960b
commit a8552abc23
14 changed files with 80 additions and 12 deletions

View File

@@ -6,7 +6,7 @@ let cache: Keyv | null = null;
if (env.CACHE_ENABLED === true) {
validateEnv(['CACHE_NAMESPACE', 'CACHE_TTL', 'CACHE_STORE']);
cache = new Keyv({ namespace: process.env.CACHE_NAMESPACE });
cache = new Keyv({ namespace: env.CACHE_NAMESPACE, ttl: env.CACHE_TTL });
}
export default cache;

View File

@@ -30,10 +30,10 @@ STORAGE_LOCAL_ROOT="./uploads"
{{ security }}
ACCESS_TOKEN_TTL="15m",
REFRESH_TOKEN_TTL="7d",
REFRESH_TOKEN_COOKIE_SECURE=false,
REFRESH_TOKEN_COOKIE_SAME_SITE="lax",
ACCESS_TOKEN_TTL="15m"
REFRESH_TOKEN_TTL="7d"
REFRESH_TOKEN_COOKIE_SECURE=false
REFRESH_TOKEN_COOKIE_SAME_SITE="lax"
####################################################################################################
## SSO (OAuth) Providers

View File

@@ -2,6 +2,7 @@ import express from 'express';
import asyncHandler from 'express-async-handler';
import PermissionsService from '../services/permissions';
import MetaService from '../services/meta';
import { clone } from 'lodash';
import { InvalidCredentialsException } from '../exceptions';
const router = express.Router();
@@ -40,7 +41,7 @@ router.get(
}
const service = new PermissionsService();
const query = req.sanitizedQuery || {};
const query = clone(req.sanitizedQuery || {});
query.filter = {
...(query.filter || {}),
@@ -59,6 +60,7 @@ router.get(
router.get(
'/:pk',
asyncHandler(async (req, res, next) => {
if (req.path.endsWith('me')) return next();
const service = new PermissionsService({ accountability: req.accountability });
const record = await service.readByKey(Number(req.params.pk), req.sanitizedQuery);

View File

@@ -51,6 +51,7 @@ router.get(
router.get(
'/:pk',
asyncHandler(async (req, res, next) => {
if (req.path.endsWith('me')) return next();
const service = new UsersService({ accountability: req.accountability });
const items = await service.readByKey(req.params.pk, req.sanitizedQuery);
res.locals.payload = { data: items || null };

View File

@@ -13,8 +13,6 @@ const checkCacheMiddleware: RequestHandler = asyncHandler(async (req, res, next)
const key = getCacheKey(req);
const cachedData = await cache.get(key);
console.log(key);
if (cachedData) {
return res.json(cachedData);
} else {

View File

@@ -5,10 +5,9 @@ import { getCacheKey } from "../utils/get-cache-key";
import cache from '../cache';
export const respond: RequestHandler = asyncHandler(async (req, res) => {
if (env.CACHE_ENABLED === true && cache) {
if (req.method.toLowerCase() === 'get' && env.CACHE_ENABLED === true && cache) {
const key = getCacheKey(req);
await cache.set(key, res.locals.payload);
console.log(key);
}
return res.json(res.locals.payload);

View File

@@ -57,6 +57,7 @@ const sanitizeQuery: RequestHandler = (req, res, next) => {
}
req.sanitizedQuery = query;
Object.freeze(req.sanitizedQuery);
return next();
};

View File

@@ -6,6 +6,7 @@ import SchemaInspector from 'knex-schema-inspector';
import FieldsService from '../services/fields';
import { omit } from 'lodash';
import ItemsService from '../services/items';
import cache from '../cache';
export default class CollectionsService {
knex: Knex;
@@ -85,6 +86,10 @@ export default class CollectionsService {
}
});
if (cache) {
await cache.clear();
}
return Array.isArray(data) ? createdCollections : createdCollections[0];
}
@@ -233,6 +238,10 @@ export default class CollectionsService {
await collectionItemsService.update(collectionUpdates);
if (cache) {
await cache.clear();
}
return key!;
}
@@ -296,6 +305,10 @@ export default class CollectionsService {
await this.knex.schema.dropTable(collectionKey);
}
if (cache) {
await cache.clear();
}
return collection;
}
}

View File

@@ -5,10 +5,11 @@ import ItemsService from '../services/items';
import { ColumnBuilder } from 'knex';
import getLocalType from '../utils/get-local-type';
import { types } from '../types';
import { FieldNotFoundException, ForbiddenException } from '../exceptions';
import { ForbiddenException } from '../exceptions';
import Knex, { CreateTableBuilder } from 'knex';
import PayloadService from '../services/payload';
import getDefaultValue from '../utils/get-default-value';
import cache from '../cache';
type RawField = Partial<Field> & { field: string; type: typeof types[number] };
@@ -205,6 +206,10 @@ export default class FieldsService {
field: field.field,
});
}
if (cache) {
await cache.clear();
}
}
/** @todo research how to make this happen in SQLite / Redshift */
@@ -272,6 +277,10 @@ export default class FieldsService {
}
}
if (cache) {
await cache.clear();
}
return field.field;
}
@@ -309,6 +318,10 @@ export default class FieldsService {
.where({ one_collection: collection, one_field: field });
}
}
if (cache) {
await cache.clear();
}
}
public addColumnToTable(table: CreateTableBuilder, field: Field) {

View File

@@ -7,6 +7,7 @@ import parseIPTC from '../utils/parse-iptc';
import path from 'path';
import { AbstractServiceOptions, File, PrimaryKey } from '../types';
import { clone } from 'lodash';
import cache from '../cache';
export default class FilesService extends ItemsService {
constructor(options?: AbstractServiceOptions) {
@@ -77,6 +78,10 @@ export default class FilesService extends ItemsService {
const sudoService = new ItemsService('directus_files');
await sudoService.update(payload, primaryKey);
if (cache) {
await cache.clear();
}
return primaryKey;
}
@@ -97,6 +102,10 @@ export default class FilesService extends ItemsService {
await super.delete(keys);
if (cache) {
await cache.clear();
}
return key;
}
}

View File

@@ -13,6 +13,7 @@ import {
AbstractServiceOptions,
} from '../types';
import Knex from 'knex';
import cache from '../cache';
import PayloadService from './payload';
import AuthorizationService from './authorization';
@@ -145,6 +146,10 @@ export default class ItemsService implements AbstractService {
await trx.insert(revisionRecords).into('directus_revisions');
}
if (cache) {
await cache.clear();
}
return primaryKeys;
});
@@ -172,6 +177,7 @@ export default class ItemsService implements AbstractService {
query: Query = {},
action: PermissionsAction = 'read'
): Promise<Item | Item[]> {
query = clone(query);
const schemaInspector = SchemaInspector(this.knex);
const primaryKeyField = await schemaInspector.primary(this.collection);
const keys = Array.isArray(key) ? key : [key];
@@ -301,6 +307,10 @@ export default class ItemsService implements AbstractService {
}
});
if (cache) {
await cache.clear();
}
return key;
}
@@ -359,10 +369,15 @@ export default class ItemsService implements AbstractService {
}
});
if (cache) {
await cache.clear();
}
return key;
}
async readSingleton(query: Query) {
query = clone(query);
const schemaInspector = SchemaInspector(this.knex);
query.limit = 1;

View File

@@ -8,6 +8,7 @@ import { InvalidPayloadException, ForbiddenException } from '../exceptions';
import { Accountability, PrimaryKey, Item, AbstractServiceOptions } from '../types';
import Knex from 'knex';
import env from '../env';
import cache from '../cache';
export default class UsersService extends ItemsService {
knex: Knex;
@@ -42,6 +43,10 @@ export default class UsersService extends ItemsService {
}
}
if (cache) {
await cache.clear();
}
return this.service.update(data, key as any);
}
@@ -78,6 +83,10 @@ export default class UsersService extends ItemsService {
await this.knex('directus_users')
.update({ password: passwordHashed, status: 'active' })
.where({ id: user.id });
if (cache) {
await cache.clear();
}
}
async requestPasswordReset(email: string) {
@@ -114,6 +123,10 @@ export default class UsersService extends ItemsService {
await this.knex('directus_users')
.update({ password: passwordHashed, status: 'active' })
.where({ id: user.id });
if (cache) {
await cache.clear();
}
}
async enableTFA(pk: string) {

View File

@@ -12,6 +12,7 @@ import {
Accountability,
} from '../types';
import database from '../database';
import { clone } from 'lodash';
export default async function getASTFromQuery(
collection: string,
@@ -19,6 +20,7 @@ export default async function getASTFromQuery(
accountability?: Accountability | null,
action?: PermissionsAction
): Promise<AST> {
query = clone(query);
/**
* we might not need al this info at all times, but it's easier to fetch it all once, than trying to fetch it for every
* requested field. @todo look into utilizing graphql/dataloader for this purpose

View File

@@ -1,6 +1,8 @@
import { Request } from "express";
import url from 'url';
export function getCacheKey(req: Request) {
const key = `${req.accountability?.user || 'null'}-${req.originalUrl}-${JSON.stringify(req.sanitizedQuery)}`;
const path = url.parse(req.originalUrl).pathname;
const key = `${req.accountability?.user || 'null'}-${path}-${JSON.stringify(req.sanitizedQuery)}`;
return key;
}