diff --git a/demo/collections/AllFields.js b/demo/collections/AllFields.js index 55ef3bbf72..5a37f2c3f0 100644 --- a/demo/collections/AllFields.js +++ b/demo/collections/AllFields.js @@ -189,6 +189,11 @@ const AllFields = { name: 'relationshipMultipleCollections', relationTo: ['localized-posts', 'conditions'], }, + { + type: 'textarea', + label: 'Textarea', + name: 'textarea', + }, ], timestamps: true, }; diff --git a/demo/collections/PublicUsers.js b/demo/collections/PublicUsers.js index 1204a4a854..da9a2f374e 100644 --- a/demo/collections/PublicUsers.js +++ b/demo/collections/PublicUsers.js @@ -12,21 +12,7 @@ module.exports = { policies: { admin: () => false, create: () => true, - read: ({ req: { user } }) => { - if (checkRole(['admin'], user)) { - return true; - } - - if (user) { - return { - id: { - equals: user.id, - }, - }; - } - - return false; - }, + read: () => true, update: ({ req: { user } }) => { if (checkRole(['admin'], user)) { return true; diff --git a/src/auth/auth.spec.js b/src/auth/auth.spec.js index 6c4a700de7..6d161a95af 100644 --- a/src/auth/auth.spec.js +++ b/src/auth/auth.spec.js @@ -51,7 +51,7 @@ describe('Users REST API', () => { it('should return a logged in user from /me', async () => { const response = await fetch(`${url}/api/admins/me`, { headers: { - Authorization: `JWT ${token}`, + Authorization: `Admin JWT ${token}`, }, }); @@ -65,7 +65,7 @@ describe('Users REST API', () => { const response = await fetch(`${url}/api/admins/refresh-token`, { method: 'post', headers: { - Authorization: `JWT ${token}`, + Authorization: `Admin JWT ${token}`, }, }); @@ -104,7 +104,7 @@ describe('Users REST API', () => { roles: ['editor'], }), headers: { - Authorization: `JWT ${token}`, + Authorization: `Admin JWT ${token}`, 'Content-Type': 'application/json', }, method: 'post', diff --git a/src/auth/operations/refresh.js b/src/auth/operations/refresh.js index d00ecf30fb..2ca58586e2 100644 --- a/src/auth/operations/refresh.js +++ b/src/auth/operations/refresh.js @@ -24,7 +24,7 @@ const refresh = async (args) => { const opts = {}; opts.expiresIn = options.collection.config.auth.tokenExpiration; - const token = options.authorization.replace('JWT ', ''); + const token = options.authorization.replace(`${options.collection.config.labels.singular} JWT `, ''); const payload = jwt.verify(token, secret, {}); delete payload.iat; delete payload.exp; diff --git a/src/auth/strategies/apiKey.js b/src/auth/strategies/apiKey.js index 311826c9dc..89d83c22ce 100644 --- a/src/auth/strategies/apiKey.js +++ b/src/auth/strategies/apiKey.js @@ -3,7 +3,7 @@ const PassportAPIKey = require('passport-headerapikey').HeaderAPIKeyStrategy; module.exports = ({ Model, config }) => { const opts = { header: 'Authorization', - prefix: 'API-Key ', + prefix: `${config.labels.singular} API-Key `, }; return new PassportAPIKey(opts, false, (apiKey, done) => { diff --git a/src/auth/strategies/jwt.js b/src/auth/strategies/jwt.js index 0aa2682e6a..8c49c919ae 100644 --- a/src/auth/strategies/jwt.js +++ b/src/auth/strategies/jwt.js @@ -3,13 +3,15 @@ const passportJwt = require('passport-jwt'); const JwtStrategy = passportJwt.Strategy; const { ExtractJwt } = passportJwt; -module.exports = (config, collection) => { +module.exports = (config, collections) => { const opts = {}; opts.jwtFromRequest = ExtractJwt.fromAuthHeaderWithScheme('JWT'); opts.secretOrKey = config.secret; return new JwtStrategy(opts, async (token, done) => { try { + const collection = collections[token.collection]; + const user = await collection.Model.findByUsername(token.email); const json = user.toJSON({ virtuals: true }); diff --git a/src/client/components/Routes.js b/src/client/components/Routes.js index ad2c852f16..027fb89b33 100644 --- a/src/client/components/Routes.js +++ b/src/client/components/Routes.js @@ -26,7 +26,7 @@ const { const Routes = () => { const [initialized, setInitialized] = useState(null); - const { user, permissions: { canAccessAdmin } } = useUser(); + const { user, permissions, permissions: { canAccessAdmin } } = useUser(); useEffect(() => { requests.get(`${routes.api}/${userSlug}/init`).then(res => res.json().then((data) => { @@ -88,77 +88,93 @@ const Routes = () => { {collections.map((collection) => { - return ( - { - return ( - - ); - }} - /> - ); + if (permissions?.[collection.slug]?.read?.permission) { + return ( + { + return ( + + ); + }} + /> + ); + } + + return null; })} {collections.map((collection) => { - return ( - { - return ( - - ); - }} - /> - ); + if (permissions?.[collection.slug]?.create?.permission) { + return ( + { + return ( + + ); + }} + /> + ); + } + + return null; })} {collections.map((collection) => { - return ( - { - return ( - - ); - }} - /> - ); + if (permissions?.[collection.slug]?.read?.permission) { + return ( + { + return ( + + ); + }} + /> + ); + } + + return null; })} {globals && globals.map((global) => { - return ( - { - return ( - - ); - }} - /> - ); + if (permissions?.[global.slug]?.read?.permission) { + return ( + { + return ( + + ); + }} + /> + ); + } + return null; })} + diff --git a/src/client/components/elements/FileDetails/index.scss b/src/client/components/elements/FileDetails/index.scss index fa10b1d632..b8ac26fe56 100644 --- a/src/client/components/elements/FileDetails/index.scss +++ b/src/client/components/elements/FileDetails/index.scss @@ -1,6 +1,8 @@ @import '../../../scss/styles.scss'; .file-details { + background-color: $color-background-gray; + header { display: flex; align-items: flex-start; diff --git a/src/client/components/forms/field-types/Auth/APIKey.js b/src/client/components/forms/field-types/Auth/APIKey.js index 88656683d8..3b2461784c 100644 --- a/src/client/components/forms/field-types/Auth/APIKey.js +++ b/src/client/components/forms/field-types/Auth/APIKey.js @@ -6,7 +6,7 @@ import Label from '../../Label'; import Button from '../../../elements/Button'; import CopyToClipboard from '../../../elements/CopyToClipboard'; import { text } from '../../../../../fields/validations'; -import useForm from '../../Form/useForm'; +import useFormFields from '../../Form/useFormFields'; import './index.scss'; @@ -19,7 +19,7 @@ const APIKey = (props) => { initialData, } = props; - const { getField } = useForm(); + const { getField } = useFormFields(); const apiKey = getField(path); @@ -36,7 +36,7 @@ const APIKey = (props) => { const fieldType = useFieldType({ path: 'apiKey', - initialData: initialData || generateAPIKey(), + initialData: initialData || uuidv4(), validate, }); diff --git a/src/client/components/forms/field-types/Auth/index.js b/src/client/components/forms/field-types/Auth/index.js index a1fd832e75..87df86e351 100644 --- a/src/client/components/forms/field-types/Auth/index.js +++ b/src/client/components/forms/field-types/Auth/index.js @@ -5,7 +5,7 @@ import Password from '../Password'; import Checkbox from '../Checkbox'; import Button from '../../../elements/Button'; import ConfirmPassword from '../ConfirmPassword'; -import useForm from '../../Form/useForm'; +import useFormFields from '../../Form/useFormFields'; import APIKey from './APIKey'; import './index.scss'; @@ -15,7 +15,7 @@ const baseClass = 'auth-fields'; const Auth = (props) => { const { initialData, useAPIKey, requirePassword } = props; const [changingPassword, setChangingPassword] = useState(requirePassword); - const { getField } = useForm(); + const { getField } = useFormFields(); const enableAPIKey = getField('enableAPIKey'); @@ -76,12 +76,14 @@ const Auth = (props) => { Auth.defaultProps = { initialData: undefined, useAPIKey: false, + requirePassword: false, }; Auth.propTypes = { fieldTypes: PropTypes.shape({}).isRequired, initialData: PropTypes.shape({}), useAPIKey: PropTypes.bool, + requirePassword: PropTypes.bool, }; export default Auth; diff --git a/src/client/components/forms/field-types/ConfirmPassword/index.js b/src/client/components/forms/field-types/ConfirmPassword/index.js index 6eed87013d..b1bc6789a5 100644 --- a/src/client/components/forms/field-types/ConfirmPassword/index.js +++ b/src/client/components/forms/field-types/ConfirmPassword/index.js @@ -2,12 +2,12 @@ import React, { useCallback } from 'react'; import useFieldType from '../../useFieldType'; import Label from '../../Label'; import Error from '../../Error'; -import useForm from '../../Form/useForm'; +import useFormFields from '../../Form/useFormFields'; import './index.scss'; const ConfirmPassword = () => { - const { getField } = useForm(); + const { getField } = useFormFields(); const password = getField('password'); const validate = useCallback((value) => { diff --git a/src/client/components/forms/field-types/Textarea/index.js b/src/client/components/forms/field-types/Textarea/index.js index ec5011c7db..96803a688c 100644 --- a/src/client/components/forms/field-types/Textarea/index.js +++ b/src/client/components/forms/field-types/Textarea/index.js @@ -23,6 +23,7 @@ const Textarea = (props) => { readOnly, minLength, maxLength, + rows, } = props; const path = pathFromProps || name; @@ -77,6 +78,7 @@ const Textarea = (props) => { placeholder={placeholder} id={path} name={path} + rows={rows} /> ); @@ -95,6 +97,7 @@ Textarea.defaultProps = { readOnly: false, minLength: undefined, maxLength: undefined, + rows: 8, }; Textarea.propTypes = { @@ -111,6 +114,7 @@ Textarea.propTypes = { readOnly: PropTypes.bool, minLength: PropTypes.number, maxLength: PropTypes.number, + rows: PropTypes.number, }; export default withCondition(Textarea); diff --git a/src/client/components/forms/field-types/Textarea/index.scss b/src/client/components/forms/field-types/Textarea/index.scss index fe79bb4abe..0ee1c018c4 100644 --- a/src/client/components/forms/field-types/Textarea/index.scss +++ b/src/client/components/forms/field-types/Textarea/index.scss @@ -5,6 +5,7 @@ textarea { @include formInput(); + height: auto; min-height: base(3); } diff --git a/src/client/components/views/Logout/index.js b/src/client/components/views/Logout/index.js index e95445109d..0e799f2fa8 100644 --- a/src/client/components/views/Logout/index.js +++ b/src/client/components/views/Logout/index.js @@ -20,7 +20,7 @@ const Logout = () => { return (
-

You have been logged out.

+

You have been logged out successfully.