Keep original request and add properties to it, instead of copy
This commit is contained in:
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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({
|
||||||
|
|||||||
@@ -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 ?? {},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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".
|
||||||
|
|||||||
Reference in New Issue
Block a user