Keep original request and add properties to it, instead of copy

This commit is contained in:
dsod
2022-08-20 01:33:29 +02:00
parent b21a56fdf7
commit bc97d3d6f2
11 changed files with 128 additions and 73 deletions

View File

@@ -19,15 +19,12 @@ async function localForgotPassword(payload: Payload, options: Options): Promise<
data, data,
expiration, expiration,
disableEmail, disableEmail,
req: incomingReq = {}, req = {} as PayloadRequest,
} = options; } = options;
const collection = payload.collections[collectionSlug]; const collection = payload.collections[collectionSlug];
const req = { req.payloadAPI = 'local';
...incomingReq,
payloadAPI: 'local',
} as PayloadRequest;
if (!req.payloadDataLoader) req.payloadDataLoader = getDataLoader(req); if (!req.payloadDataLoader) req.payloadDataLoader = getDataLoader(req);

View File

@@ -23,7 +23,7 @@ export type Options = {
async function localLogin<T extends TypeWithID = any>(payload: Payload, options: Options): Promise<Result & { user: T}> { async function localLogin<T extends TypeWithID = any>(payload: Payload, options: Options): Promise<Result & { user: T}> {
const { const {
collection: collectionSlug, collection: collectionSlug,
req: incomingReq = {}, req = {} as PayloadRequest,
res, res,
depth, depth,
locale, locale,
@@ -35,13 +35,10 @@ async function localLogin<T extends TypeWithID = any>(payload: Payload, options:
const collection = payload.collections[collectionSlug]; const collection = payload.collections[collectionSlug];
const req = { req.payloadAPI = 'local';
...incomingReq, req.payload = payload;
payloadAPI: 'local', req.locale = undefined;
payload, req.fallbackLocale = undefined;
locale: undefined,
fallbackLocale: undefined,
} as PayloadRequest;
if (!req.payloadDataLoader) req.payloadDataLoader = getDataLoader(req); if (!req.payloadDataLoader) req.payloadDataLoader = getDataLoader(req);

View File

@@ -18,16 +18,13 @@ async function localResetPassword(payload: Payload, options: Options): Promise<R
collection: collectionSlug, collection: collectionSlug,
data, data,
overrideAccess, overrideAccess,
req: incomingReq = {}, req = {} as PayloadRequest,
} = options; } = options;
const collection = payload.collections[collectionSlug]; const collection = payload.collections[collectionSlug];
const req = { req.payload = payload;
...incomingReq, req.payloadAPI = 'local';
payload,
payloadAPI: 'local',
} as PayloadRequest;
if (!req.payloadDataLoader) req.payloadDataLoader = getDataLoader(req); if (!req.payloadDataLoader) req.payloadDataLoader = getDataLoader(req);

View File

@@ -17,16 +17,13 @@ async function localUnlock(payload: Payload, options: Options): Promise<boolean>
collection: collectionSlug, collection: collectionSlug,
data, data,
overrideAccess = true, overrideAccess = true,
req: incomingReq = {}, req = {} as PayloadRequest,
} = options; } = options;
const collection = payload.collections[collectionSlug]; const collection = payload.collections[collectionSlug];
const req = { req.payload = payload;
...incomingReq, req.payloadAPI = 'local';
payload,
payloadAPI: 'local',
} as PayloadRequest;
if (!req.payloadDataLoader) req.payloadDataLoader = getDataLoader(req); if (!req.payloadDataLoader) req.payloadDataLoader = getDataLoader(req);

View File

@@ -1,9 +1,9 @@
import { UploadedFile } from 'express-fileupload';
import { Payload } from '../../..'; import { Payload } from '../../..';
import { PayloadRequest } from '../../../express/types'; import { PayloadRequest } from '../../../express/types';
import { Document } from '../../../types'; import { Document } from '../../../types';
import getFileByPath from '../../../uploads/getFileByPath'; import getFileByPath from '../../../uploads/getFileByPath';
import create from '../create'; import create from '../create';
import { File } from '../../../uploads/types';
import { getDataLoader } from '../../dataloader'; import { getDataLoader } from '../../dataloader';
@@ -18,7 +18,7 @@ export type Options<T> = {
disableVerificationEmail?: boolean disableVerificationEmail?: boolean
showHiddenFields?: boolean showHiddenFields?: boolean
filePath?: string filePath?: string
file?: File file?: UploadedFile
overwriteExistingFiles?: boolean overwriteExistingFiles?: boolean
req?: PayloadRequest req?: PayloadRequest
draft?: boolean draft?: boolean
@@ -38,23 +38,21 @@ export default async function createLocal<T = any>(payload: Payload, options: Op
filePath, filePath,
file, file,
overwriteExistingFiles = false, overwriteExistingFiles = false,
req: incomingReq, req = {} as PayloadRequest,
draft, draft,
} = options; } = options;
const collection = payload.collections[collectionSlug]; const collection = payload.collections[collectionSlug];
const req = { req.payloadAPI = 'local';
...incomingReq || {}, req.locale = locale || req?.locale || (payload?.config?.localization ? payload?.config?.localization?.defaultLocale : null);
user, req.fallbackLocale = fallbackLocale || req?.fallbackLocale || null;
payloadAPI: 'local', req.payload = payload;
locale: locale || incomingReq?.locale || (payload?.config?.localization ? payload?.config?.localization?.defaultLocale : null), req.files = {
fallbackLocale: fallbackLocale || incomingReq?.fallbackLocale || null, file: (file as UploadedFile) ?? (getFileByPath(filePath) as UploadedFile),
payload, };
files: {
file: file ?? getFileByPath(filePath), if (typeof user !== 'undefined') req.user = user;
},
} as PayloadRequest;
if (!req.payloadDataLoader) req.payloadDataLoader = getDataLoader(req); if (!req.payloadDataLoader) req.payloadDataLoader = getDataLoader(req);

View File

@@ -43,19 +43,15 @@ export default async function findLocal<T extends TypeWithID = any>(payload: Pay
sort, sort,
draft = false, draft = false,
pagination = true, pagination = true,
req: incomingReq, req = {} as PayloadRequest,
} = options; } = options;
const collection = payload.collections[collectionSlug]; const collection = payload.collections[collectionSlug];
const req = { req.payloadAPI = 'local';
user: undefined, req.locale = locale || req?.locale || (payload?.config?.localization ? payload?.config?.localization?.defaultLocale : null);
...incomingReq || {}, req.fallbackLocale = fallbackLocale || req?.fallbackLocale || null;
payloadAPI: 'local', req.payload = payload;
locale: locale || incomingReq?.locale || (payload?.config?.localization ? payload?.config?.localization?.defaultLocale : null),
fallbackLocale: fallbackLocale || incomingReq?.fallbackLocale || null,
payload,
} as PayloadRequest;
if (!req.payloadDataLoader) req.payloadDataLoader = getDataLoader(req); if (!req.payloadDataLoader) req.payloadDataLoader = getDataLoader(req);

View File

@@ -33,20 +33,16 @@ export default async function findByIDLocal<T extends TypeWithID = any>(payload:
overrideAccess = true, overrideAccess = true,
disableErrors = false, disableErrors = false,
showHiddenFields, showHiddenFields,
req: incomingReq, req = {} as PayloadRequest,
draft = false, draft = false,
} = options; } = options;
const collection = payload.collections[collectionSlug]; const collection = payload.collections[collectionSlug];
const req = { req.payloadAPI = 'local';
user: undefined, req.locale = locale || req?.locale || (payload?.config?.localization ? payload?.config?.localization?.defaultLocale : null);
...incomingReq || {}, req.fallbackLocale = fallbackLocale || req?.fallbackLocale || null;
payloadAPI: 'local', req.payload = payload;
locale: locale || incomingReq?.locale || (payload?.config?.localization ? payload?.config?.localization?.defaultLocale : null),
fallbackLocale: fallbackLocale || incomingReq?.fallbackLocale || null,
payload,
} as PayloadRequest;
if (typeof user !== 'undefined') req.user = user; if (typeof user !== 'undefined') req.user = user;

View File

@@ -28,18 +28,15 @@ export default async function findVersionByIDLocal<T extends TypeWithVersion<T>
overrideAccess = true, overrideAccess = true,
disableErrors = false, disableErrors = false,
showHiddenFields, showHiddenFields,
req: incomingReq, req = {} as PayloadRequest,
} = options; } = options;
const collection = payload.collections[collectionSlug]; const collection = payload.collections[collectionSlug];
const req = { req.payloadAPI = 'local';
...incomingReq || {}, req.locale = locale || req?.locale || this?.config?.localization?.defaultLocale;
payloadAPI: 'local', req.fallbackLocale = fallbackLocale || req?.fallbackLocale || null;
locale: locale || incomingReq?.locale || this?.config?.localization?.defaultLocale, req.payload = payload;
fallbackLocale: fallbackLocale || incomingReq?.fallbackLocale || null,
payload,
} as PayloadRequest;
if (!req.payloadDataLoader) req.payloadDataLoader = getDataLoader(req); if (!req.payloadDataLoader) req.payloadDataLoader = getDataLoader(req);

View File

@@ -9,6 +9,7 @@ export const readOnlySlug = 'read-only-collection';
export const restrictedSlug = 'restricted'; export const restrictedSlug = 'restricted';
export const restrictedVersionsSlug = 'restricted-versions'; export const restrictedVersionsSlug = 'restricted-versions';
export const siblingDataSlug = 'sibling-data'; export const siblingDataSlug = 'sibling-data';
export const relyOnRequestHeadersSlug = 'rely-on-request-headers';
const openAccess = { const openAccess = {
create: () => true, create: () => true,
@@ -24,6 +25,11 @@ const PublicReadabilityAccess: FieldAccess = ({ req: { user }, siblingData }) =>
return false; return false;
}; };
export const requestHeaders = {authorization: 'Bearer testBearerToken'};
const UseRequestHeadersAccess: FieldAccess = ({ req: { headers } }) => {
return !!headers && headers.authorization === requestHeaders.authorization;
};
export default buildConfig({ export default buildConfig({
collections: [ collections: [
{ {
@@ -115,6 +121,21 @@ export default buildConfig({
}, },
], ],
}, },
{
slug: relyOnRequestHeadersSlug,
access: {
create: UseRequestHeadersAccess,
read: UseRequestHeadersAccess,
update: UseRequestHeadersAccess,
delete: UseRequestHeadersAccess,
},
fields: [
{
name: 'name',
type: 'text',
},
],
},
], ],
onInit: async (payload) => { onInit: async (payload) => {
await payload.create({ await payload.create({

View File

@@ -1,9 +1,11 @@
import mongoose from 'mongoose'; import mongoose from 'mongoose';
import payload from '../../src'; import payload from '../../src';
import type { Options as CreateOptions } from '../../src/collections/operations/local/create';
import { Forbidden } from '../../src/errors'; import { Forbidden } from '../../src/errors';
import type { PayloadRequest } from '../../src/types';
import { initPayloadTest } from '../helpers/configHelpers'; import { initPayloadTest } from '../helpers/configHelpers';
import { restrictedSlug, siblingDataSlug, slug } from './config'; import { relyOnRequestHeadersSlug, requestHeaders, restrictedSlug, siblingDataSlug, slug } from './config';
import type { Restricted, Post, SiblingDatum } from './payload-types'; import type { Restricted, Post, SiblingDatum, RelyOnRequestHeader } from './payload-types';
import { firstArrayText, secondArrayText } from './shared'; import { firstArrayText, secondArrayText } from './shared';
describe('Access Control', () => { describe('Access Control', () => {
@@ -74,7 +76,7 @@ describe('Access Control', () => {
describe('Collections', () => { describe('Collections', () => {
describe('restricted collection', () => { describe('restricted collection', () => {
it('field without read access should not show', async () => { it('field without read access should not show', async () => {
const { id } = await createDoc({ restrictedField: 'restricted' }); const { id } = await createDoc<Post>({ restrictedField: 'restricted' });
const retrievedDoc = await payload.findByID({ collection: slug, id, overrideAccess: false }); const retrievedDoc = await payload.findByID({ collection: slug, id, overrideAccess: false });
@@ -82,7 +84,7 @@ describe('Access Control', () => {
}); });
it('field without read access should not show when overrideAccess: true', async () => { it('field without read access should not show when overrideAccess: true', async () => {
const { id, restrictedField } = await createDoc({ restrictedField: 'restricted' }); const { id, restrictedField } = await createDoc<Post>({ restrictedField: 'restricted' });
const retrievedDoc = await payload.findByID({ collection: slug, id, overrideAccess: true }); const retrievedDoc = await payload.findByID({ collection: slug, id, overrideAccess: true });
@@ -90,13 +92,59 @@ describe('Access Control', () => {
}); });
it('field without read access should not show when overrideAccess default', async () => { it('field without read access should not show when overrideAccess default', async () => {
const { id, restrictedField } = await createDoc({ restrictedField: 'restricted' }); const { id, restrictedField } = await createDoc<Post>({ restrictedField: 'restricted' });
const retrievedDoc = await payload.findByID({ collection: slug, id }); const retrievedDoc = await payload.findByID({ collection: slug, id });
expect(retrievedDoc.restrictedField).toEqual(restrictedField); expect(retrievedDoc.restrictedField).toEqual(restrictedField);
}); });
}); });
describe('non-enumerated request properties passed to access control', () => {
it('access control ok when passing request headers', async () => {
const req = Object.defineProperty({}, 'headers', {
value: requestHeaders,
enumerable: false,
}) as PayloadRequest;
const name = 'name';
const overrideAccess = false;
const { id } = await createDoc<RelyOnRequestHeader>({ name }, relyOnRequestHeadersSlug, { req, overrideAccess });
const docById = await payload.findByID({ collection: relyOnRequestHeadersSlug, id, req, overrideAccess });
const { docs: docsByName } = await payload.find({
collection: relyOnRequestHeadersSlug,
where: {
name: {
equals: name,
},
},
req,
overrideAccess,
});
expect(docById).not.toBeUndefined();
expect(docsByName.length).toBeGreaterThan(0);
});
it('access control fails when omitting request headers', async () => {
const name = 'name';
const overrideAccess = false;
await expect(() => createDoc<RelyOnRequestHeader>({ name }, relyOnRequestHeadersSlug, { overrideAccess })).rejects.toThrow(Forbidden);
const { id } = await createDoc<RelyOnRequestHeader>({ name }, relyOnRequestHeadersSlug);
await expect(() => payload.findByID({ collection: relyOnRequestHeadersSlug, id, overrideAccess })).rejects.toThrow(Forbidden);
await expect(() => payload.find({
collection: relyOnRequestHeadersSlug,
where: {
name: {
equals: name,
},
},
overrideAccess,
})).rejects.toThrow(Forbidden);
});
});
}); });
describe('Override Access', () => { describe('Override Access', () => {
@@ -172,9 +220,10 @@ describe('Access Control', () => {
}); });
}); });
async function createDoc(data: Partial<Post>): Promise<Post> { async function createDoc<Collection>(data: Partial<Collection>, overrideSlug = slug, options?: Partial<CreateOptions<Collection>>): Promise<Collection> {
return payload.create({ return payload.create<Collection>({
collection: slug, ...options,
collection: overrideSlug,
data: data ?? {}, data: data ?? {},
}); });
} }

View File

@@ -60,6 +60,16 @@ export interface SiblingDatum {
createdAt: string; createdAt: string;
updatedAt: string; updatedAt: string;
} }
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "rely-on-request-headers".
*/
export interface RelyOnRequestHeader {
id: string;
name?: string;
createdAt: string;
updatedAt: string;
}
/** /**
* This interface was referenced by `Config`'s JSON-Schema * This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "users". * via the `definition` "users".