changes access permissions structure to be more typescript-friendly
This commit is contained in:
@@ -121,7 +121,7 @@ const Routes = () => {
|
||||
path={`${match.url}/collections/${collection.slug}`}
|
||||
exact
|
||||
render={(routeProps) => {
|
||||
if (permissions?.[collection.slug]?.read?.permission) {
|
||||
if (permissions?.collections?.[collection.slug]?.read?.permission) {
|
||||
return (
|
||||
<List
|
||||
{...routeProps}
|
||||
@@ -141,7 +141,7 @@ const Routes = () => {
|
||||
path={`${match.url}/collections/${collection.slug}/create`}
|
||||
exact
|
||||
render={(routeProps) => {
|
||||
if (permissions?.[collection.slug]?.create?.permission) {
|
||||
if (permissions?.collections?.[collection.slug]?.create?.permission) {
|
||||
return (
|
||||
<Edit
|
||||
{...routeProps}
|
||||
@@ -161,7 +161,7 @@ const Routes = () => {
|
||||
path={`${match.url}/collections/${collection.slug}/:id`}
|
||||
exact
|
||||
render={(routeProps) => {
|
||||
if (permissions?.[collection.slug]?.read?.permission) {
|
||||
if (permissions?.collections?.[collection.slug]?.read?.permission) {
|
||||
return (
|
||||
<Edit
|
||||
isEditing
|
||||
@@ -182,7 +182,7 @@ const Routes = () => {
|
||||
path={`${match.url}/globals/${global.slug}`}
|
||||
exact
|
||||
render={(routeProps) => {
|
||||
if (permissions?.[global.slug]?.read?.permission) {
|
||||
if (permissions?.globals?.[global.slug]?.read?.permission) {
|
||||
return (
|
||||
<EditGlobal
|
||||
{...routeProps}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { NavLink, Link, useHistory } from 'react-router-dom';
|
||||
import { useConfig } from '@payloadcms/config-provider';
|
||||
import { useAuth } from '@payloadcms/config-provider';
|
||||
import { useConfig, useAuth } from '@payloadcms/config-provider';
|
||||
|
||||
import RenderCustomComponent from '../../utilities/RenderCustomComponent';
|
||||
import Chevron from '../../icons/Chevron';
|
||||
import LogOut from '../../icons/LogOut';
|
||||
@@ -65,7 +65,7 @@ const DefaultNav = () => {
|
||||
{collections && collections.map((collection, i) => {
|
||||
const href = `${admin}/collections/${collection.slug}`;
|
||||
|
||||
if (permissions?.[collection.slug]?.read.permission) {
|
||||
if (permissions?.collections?.[collection.slug]?.read.permission) {
|
||||
return (
|
||||
<NavLink
|
||||
activeClassName="active"
|
||||
@@ -88,7 +88,7 @@ const DefaultNav = () => {
|
||||
{globals.map((global, i) => {
|
||||
const href = `${admin}/globals/${global.slug}`;
|
||||
|
||||
if (permissions?.[global.slug].read.permission) {
|
||||
if (permissions?.globals?.[global.slug].read.permission) {
|
||||
return (
|
||||
<NavLink
|
||||
activeClassName="active"
|
||||
|
||||
@@ -7,7 +7,7 @@ import Select from '../../../../../Select';
|
||||
|
||||
|
||||
const createOptions = (collections, permissions) => collections.reduce((options, collection) => {
|
||||
if (permissions[collection.slug]?.read?.permission && collection?.admin?.enableRichTextRelationship) {
|
||||
if (permissions?.collections?.[collection.slug]?.read?.permission && collection?.admin?.enableRichTextRelationship) {
|
||||
return [
|
||||
...options,
|
||||
{
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { useConfig } from '@payloadcms/config-provider';
|
||||
import { useConfig, useAuth } from '@payloadcms/config-provider';
|
||||
import { useStepNav } from '../../elements/StepNav';
|
||||
import { useAuth } from '@payloadcms/config-provider';
|
||||
|
||||
import usePayloadAPI from '../../../hooks/usePayloadAPI';
|
||||
import { useLocale } from '../../utilities/Locale';
|
||||
import DefaultAccount from './Default';
|
||||
@@ -31,7 +31,7 @@ const AccountView = () => {
|
||||
|
||||
const { fields } = collection;
|
||||
|
||||
const collectionPermissions = permissions?.[user?.collection];
|
||||
const collectionPermissions = permissions?.collections?.[user?.collection];
|
||||
|
||||
const [{ data, isLoading }] = usePayloadAPI(
|
||||
`${serverURL}${api}/${collection?.slug}/${user?.id}?depth=0`,
|
||||
|
||||
@@ -33,7 +33,7 @@ const Dashboard = (props) => {
|
||||
<h3 className={`${baseClass}__label`}>Collections</h3>
|
||||
<ul className={`${baseClass}__card-list`}>
|
||||
{collections.map((collection) => {
|
||||
const hasCreatePermission = permissions?.[collection.slug]?.create?.permission;
|
||||
const hasCreatePermission = permissions?.collections?.[collection.slug]?.create?.permission;
|
||||
|
||||
return (
|
||||
<li key={collection.slug}>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useConfig } from '@payloadcms/config-provider';
|
||||
import { useAuth } from '@payloadcms/config-provider';
|
||||
import { useConfig, useAuth } from '@payloadcms/config-provider';
|
||||
|
||||
import { useStepNav } from '../../elements/StepNav';
|
||||
import RenderCustomComponent from '../../utilities/RenderCustomComponent';
|
||||
import DefaultDashboard from './Default';
|
||||
@@ -22,7 +22,7 @@ const Dashboard = () => {
|
||||
|
||||
useEffect(() => {
|
||||
setFilteredGlobals(
|
||||
globals.filter((global) => permissions?.[global.slug]?.read?.permission),
|
||||
globals.filter((global) => permissions?.globals?.[global.slug]?.read?.permission),
|
||||
);
|
||||
}, [permissions, globals]);
|
||||
|
||||
@@ -36,7 +36,7 @@ const Dashboard = () => {
|
||||
CustomComponent={CustomDashboard}
|
||||
componentProps={{
|
||||
globals: filteredGlobals,
|
||||
collections: collections.filter((collection) => permissions?.[collection.slug]?.read?.permission),
|
||||
collections: collections.filter((collection) => permissions?.collections?.[collection.slug]?.read?.permission),
|
||||
permissions,
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useHistory, useLocation } from 'react-router-dom';
|
||||
import { useConfig } from '@payloadcms/config-provider';
|
||||
import { useConfig, useAuth } from '@payloadcms/config-provider';
|
||||
import { useStepNav } from '../../elements/StepNav';
|
||||
import usePayloadAPI from '../../../hooks/usePayloadAPI';
|
||||
import { useAuth } from '@payloadcms/config-provider';
|
||||
|
||||
import { useLocale } from '../../utilities/Locale';
|
||||
|
||||
import RenderCustomComponent from '../../utilities/RenderCustomComponent';
|
||||
@@ -77,7 +77,7 @@ const GlobalView = (props) => {
|
||||
awaitInitialState();
|
||||
}, [dataToRender, fields]);
|
||||
|
||||
const globalPermissions = permissions?.[slug];
|
||||
const globalPermissions = permissions?.globals?.[slug];
|
||||
|
||||
return (
|
||||
<NegativeFieldGutterProvider allow>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Redirect, useRouteMatch, useHistory, useLocation } from 'react-router-dom';
|
||||
import { useConfig } from '@payloadcms/config-provider';
|
||||
import { useConfig, useAuth } from '@payloadcms/config-provider';
|
||||
import { useStepNav } from '../../../elements/StepNav';
|
||||
import usePayloadAPI from '../../../../hooks/usePayloadAPI';
|
||||
import { useAuth } from '@payloadcms/config-provider';
|
||||
|
||||
|
||||
import RenderCustomComponent from '../../../utilities/RenderCustomComponent';
|
||||
import DefaultEdit from './Default';
|
||||
@@ -91,7 +91,7 @@ const EditView = (props) => {
|
||||
);
|
||||
}
|
||||
|
||||
const collectionPermissions = permissions?.[slug];
|
||||
const collectionPermissions = permissions?.collections?.[slug];
|
||||
|
||||
const apiURL = `${serverURL}${api}/${slug}/${id}`;
|
||||
const action = `${serverURL}${api}/${slug}${isEditing ? `/${id}` : ''}?locale=${locale}&depth=0`;
|
||||
|
||||
@@ -2,8 +2,8 @@ import React, { useEffect, useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import queryString from 'qs';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { useConfig } from '@payloadcms/config-provider';
|
||||
import { useAuth } from '@payloadcms/config-provider';
|
||||
import { useConfig, useAuth } from '@payloadcms/config-provider';
|
||||
|
||||
import usePayloadAPI from '../../../../hooks/usePayloadAPI';
|
||||
import DefaultList from './Default';
|
||||
import RenderCustomComponent from '../../../utilities/RenderCustomComponent';
|
||||
@@ -37,7 +37,7 @@ const ListView = (props) => {
|
||||
const [columns, setColumns] = useState([]);
|
||||
const [sort, setSort] = useState(null);
|
||||
|
||||
const collectionPermissions = permissions?.[slug];
|
||||
const collectionPermissions = permissions?.collections?.[slug];
|
||||
const hasCreatePermission = collectionPermissions?.create?.permission;
|
||||
|
||||
const { page } = queryString.parse(location.search, { ignoreQueryPrefix: true });
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore - need to do this because this file doesn't actually exist
|
||||
import config from 'payload/config';
|
||||
import React from 'react';
|
||||
import { render } from 'react-dom';
|
||||
import { BrowserRouter as Router } from 'react-router-dom';
|
||||
@@ -6,7 +9,6 @@ import { WindowInfoProvider } from '@faceless-ui/window-info';
|
||||
import { ModalProvider, ModalContainer } from '@faceless-ui/modal';
|
||||
import { ToastContainer, Slide } from 'react-toastify';
|
||||
import { ConfigProvider, AuthProvider } from '@payloadcms/config-provider';
|
||||
import unsanitizedConfig from 'payload/unsanitizedConfig';
|
||||
import { SearchParamsProvider } from './components/utilities/SearchParams';
|
||||
import { LocaleProvider } from './components/utilities/Locale';
|
||||
import Routes from './components/Routes';
|
||||
@@ -16,7 +18,7 @@ import './scss/app.scss';
|
||||
|
||||
const Index = () => (
|
||||
<React.Fragment>
|
||||
<ConfigProvider config={unsanitizedConfig}>
|
||||
<ConfigProvider config={config}>
|
||||
<WindowInfoProvider breakpoints={{
|
||||
xs: parseInt(getCSSVariable('breakpoint-xs-width').replace('px', ''), 10),
|
||||
s: parseInt(getCSSVariable('breakpoint-s-width').replace('px', ''), 10),
|
||||
|
||||
@@ -67,18 +67,20 @@ async function accessOperation(args) {
|
||||
});
|
||||
};
|
||||
|
||||
const executeEntityPolicies = (entity, operations) => {
|
||||
results[entity.slug] = {
|
||||
const executeEntityPolicies = (entity, operations, type) => {
|
||||
if (!results[type]) results[type] = {};
|
||||
|
||||
results[type][entity.slug] = {
|
||||
fields: {},
|
||||
};
|
||||
|
||||
operations.forEach((operation) => {
|
||||
executeFieldPolicies(results[entity.slug].fields, entity.fields, operation);
|
||||
executeFieldPolicies(results[type][entity.slug].fields, entity.fields, operation);
|
||||
|
||||
if (typeof entity.access[operation] === 'function') {
|
||||
promises.push(createAccessPromise(results[entity.slug], entity.access[operation], operation));
|
||||
promises.push(createAccessPromise(results[type][entity.slug], entity.access[operation], operation));
|
||||
} else {
|
||||
results[entity.slug][operation] = {
|
||||
results[type][entity.slug][operation] = {
|
||||
permission: isLoggedIn,
|
||||
};
|
||||
}
|
||||
@@ -87,17 +89,17 @@ async function accessOperation(args) {
|
||||
|
||||
if (userCollectionConfig) {
|
||||
results.canAccessAdmin = userCollectionConfig.access.admin ? userCollectionConfig.access.admin(args) : isLoggedIn;
|
||||
if (results.canAccessAdmin) results.license = config.license;
|
||||
if (results.canAccessAdmin) results.license = this.license;
|
||||
} else {
|
||||
results.canAccessAdmin = false;
|
||||
}
|
||||
|
||||
config.collections.forEach((collection) => {
|
||||
executeEntityPolicies(collection, allOperations);
|
||||
executeEntityPolicies(collection, allOperations, 'collection');
|
||||
});
|
||||
|
||||
config.globals.forEach((global) => {
|
||||
executeEntityPolicies(global, ['read', 'update']);
|
||||
executeEntityPolicies(global, ['read', 'update'], 'global');
|
||||
});
|
||||
|
||||
await Promise.all(promises);
|
||||
|
||||
@@ -3,31 +3,51 @@ export type Permission = {
|
||||
where?: Record<string, unknown>
|
||||
}
|
||||
|
||||
export type Permissions = {
|
||||
[key: string]: boolean | {
|
||||
create: Permission
|
||||
read: Permission
|
||||
update: Permission
|
||||
delete: Permission
|
||||
fields: {
|
||||
[field: string]: {
|
||||
create: {
|
||||
permission: boolean
|
||||
}
|
||||
read: {
|
||||
permission: boolean
|
||||
}
|
||||
update: {
|
||||
permission: boolean
|
||||
}
|
||||
delete: {
|
||||
permission: boolean
|
||||
}
|
||||
export type CollectionPermission = {
|
||||
create: Permission
|
||||
read: Permission
|
||||
update: Permission
|
||||
delete: Permission
|
||||
fields: {
|
||||
[field: string]: {
|
||||
create: {
|
||||
permission: boolean
|
||||
}
|
||||
read: {
|
||||
permission: boolean
|
||||
}
|
||||
update: {
|
||||
permission: boolean
|
||||
}
|
||||
delete: {
|
||||
permission: boolean
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export type GlobalPermission = {
|
||||
read: Permission
|
||||
update: Permission
|
||||
fields: {
|
||||
[field: string]: {
|
||||
read: {
|
||||
permission: boolean
|
||||
}
|
||||
update: {
|
||||
permission: boolean
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export type Permissions = {
|
||||
canAccessAdmin: boolean
|
||||
license?: string
|
||||
collections: CollectionPermission[]
|
||||
globals?: GlobalPermission[]
|
||||
}
|
||||
|
||||
export type User = {
|
||||
id: string
|
||||
email: string
|
||||
|
||||
@@ -66,7 +66,6 @@ const sanitizeConfig = (config: Config): Config => {
|
||||
|
||||
sanitizedConfig.components = { ...(config.components || {}) };
|
||||
sanitizedConfig.hooks = { ...(config.hooks || {}) };
|
||||
sanitizedConfig.admin = { ...(config.admin || {}) };
|
||||
|
||||
return sanitizedConfig as DeepRequired<Config>;
|
||||
};
|
||||
|
||||
@@ -3,7 +3,8 @@ import { Response, NextFunction } from 'express';
|
||||
import formatErrorResponse from '../responses/formatError';
|
||||
import { PayloadRequest } from '../types/payloadRequest';
|
||||
|
||||
const errorHandler = (config, logger) => async (err, req: PayloadRequest, res: Response): Promise<void> => {
|
||||
// NextFunction must be passed for Express to use this middleware as error handler
|
||||
const errorHandler = (config, logger) => async (err, req: PayloadRequest, res: Response, next: NextFunction): Promise<void> => {
|
||||
const data = formatErrorResponse(err);
|
||||
let response;
|
||||
let status = err.status || httpStatus.INTERNAL_SERVER_ERROR;
|
||||
|
||||
@@ -318,4 +318,7 @@ export class Payload {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new Payload();
|
||||
const payload = new Payload();
|
||||
|
||||
export default payload;
|
||||
module.exports = payload;
|
||||
|
||||
@@ -78,7 +78,7 @@ export default (config: Config): Configuration => {
|
||||
},
|
||||
modules: ['node_modules', path.resolve(__dirname, '../../node_modules')],
|
||||
alias: {
|
||||
'payload/unsanitizedConfig': config.paths.config,
|
||||
'payload/config': config.paths.config,
|
||||
'@payloadcms/payload$': mockModulePath,
|
||||
},
|
||||
extensions: ['.ts', '.tsx', '.js', '.json'],
|
||||
|
||||
@@ -103,7 +103,7 @@ export default (config: Config): Configuration => {
|
||||
},
|
||||
modules: ['node_modules', path.resolve(__dirname, '../../node_modules')],
|
||||
alias: {
|
||||
'payload/unsanitizedConfig': config.paths.config,
|
||||
'payload/config': config.paths.config,
|
||||
'@payloadcms/payload$': mockModulePath,
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user