Roadmap/#1379 admin ui doc level access (#1624)
* feat: adds document level access endpoints so admin ui can now accurately reflect document level access control * chore(docs): new doc access callout, updates useDocumentInfo props from change
This commit is contained in:
@@ -10,6 +10,7 @@ export const restrictedSlug = 'restricted';
|
||||
export const restrictedVersionsSlug = 'restricted-versions';
|
||||
export const siblingDataSlug = 'sibling-data';
|
||||
export const relyOnRequestHeadersSlug = 'rely-on-request-headers';
|
||||
export const docLevelAccessSlug = 'doc-level-access';
|
||||
|
||||
const openAccess = {
|
||||
create: () => true,
|
||||
@@ -32,7 +33,7 @@ const UseRequestHeadersAccess: FieldAccess = ({ req: { headers } }) => {
|
||||
|
||||
export default buildConfig({
|
||||
admin: {
|
||||
user: 'users'
|
||||
user: 'users',
|
||||
},
|
||||
collections: [
|
||||
{
|
||||
@@ -45,7 +46,7 @@ export default buildConfig({
|
||||
setTimeout(resolve, 50, true); // set to 'true' or 'false' here to simulate the response
|
||||
}),
|
||||
},
|
||||
fields: []
|
||||
fields: [],
|
||||
},
|
||||
{
|
||||
slug,
|
||||
@@ -195,6 +196,52 @@ export default buildConfig({
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
slug: docLevelAccessSlug,
|
||||
labels: {
|
||||
singular: 'Doc Level Access',
|
||||
plural: 'Doc Level Access',
|
||||
},
|
||||
access: {
|
||||
delete: () => ({
|
||||
and: [
|
||||
{
|
||||
approvedForRemoval: {
|
||||
equals: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'approvedForRemoval',
|
||||
type: 'checkbox',
|
||||
defaultValue: false,
|
||||
admin: {
|
||||
position: 'sidebar',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'approvedTitle',
|
||||
type: 'text',
|
||||
localized: true,
|
||||
access: {
|
||||
update: (args) => {
|
||||
if (args?.doc?.lockTitle) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'lockTitle',
|
||||
type: 'checkbox',
|
||||
defaultValue: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
onInit: async (payload) => {
|
||||
await payload.create({
|
||||
|
||||
@@ -4,7 +4,7 @@ import payload from '../../src';
|
||||
import { AdminUrlUtil } from '../helpers/adminUrlUtil';
|
||||
import { initPayloadE2E } from '../helpers/configHelpers';
|
||||
import { login } from '../helpers';
|
||||
import { restrictedVersionsSlug, readOnlySlug, restrictedSlug, slug } from './config';
|
||||
import { restrictedVersionsSlug, readOnlySlug, restrictedSlug, slug, docLevelAccessSlug } from './config';
|
||||
import type { ReadOnlyCollection, RestrictedVersion } from './payload-types';
|
||||
|
||||
/**
|
||||
@@ -17,16 +17,17 @@ import type { ReadOnlyCollection, RestrictedVersion } from './payload-types';
|
||||
*/
|
||||
|
||||
const { beforeAll, describe } = test;
|
||||
|
||||
describe('access control', () => {
|
||||
let page: Page;
|
||||
let url: AdminUrlUtil;
|
||||
let restrictedUrl: AdminUrlUtil;
|
||||
let readOnlyUrl: AdminUrlUtil;
|
||||
let restrictedVersionsUrl: AdminUrlUtil;
|
||||
let serverURL: string;
|
||||
|
||||
beforeAll(async ({ browser }) => {
|
||||
const { serverURL } = await initPayloadE2E(__dirname);
|
||||
const config = await initPayloadE2E(__dirname);
|
||||
serverURL = config.serverURL;
|
||||
|
||||
url = new AdminUrlUtil(serverURL, slug);
|
||||
restrictedUrl = new AdminUrlUtil(serverURL, restrictedSlug);
|
||||
@@ -174,11 +175,51 @@ describe('access control', () => {
|
||||
await expect(page.locator('.versions-count')).not.toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
describe('doc level access', () => {
|
||||
let existingDoc: ReadOnlyCollection;
|
||||
let docLevelAccessURL;
|
||||
|
||||
beforeAll(async () => {
|
||||
docLevelAccessURL = new AdminUrlUtil(serverURL, docLevelAccessSlug);
|
||||
|
||||
existingDoc = await payload.create<any>({
|
||||
collection: docLevelAccessSlug,
|
||||
data: {
|
||||
approvedTitle: 'Title',
|
||||
lockTitle: true,
|
||||
approvedForRemoval: false,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('disable field based on document data', async () => {
|
||||
await page.goto(docLevelAccessURL.edit(existingDoc.id));
|
||||
|
||||
// validate that the text input is disabled because the field is "locked"
|
||||
const isDisabled = await page.locator('#field-approvedTitle').isDisabled();
|
||||
expect(isDisabled).toBe(true);
|
||||
});
|
||||
|
||||
test('disable operation based on document data', async () => {
|
||||
await page.goto(docLevelAccessURL.edit(existingDoc.id));
|
||||
|
||||
// validate that the delete action is not displayed
|
||||
const duplicateAction = page.locator('.collection-edit__collection-actions >> li').last();
|
||||
await expect(duplicateAction).toContainText('Duplicate');
|
||||
|
||||
await page.locator('#field-approvedForRemoval + button').click();
|
||||
await page.locator('#action-save').click();
|
||||
|
||||
const deleteAction = page.locator('.collection-edit__collection-actions >> li').last();
|
||||
await expect(deleteAction).toContainText('Delete');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
async function createDoc(data: any): Promise<{ id: string }> {
|
||||
return payload.create({
|
||||
collection: slug,
|
||||
collection: docLevelAccessSlug,
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user