@@ -11,7 +11,9 @@ describe('requireAuth directive', () => {
|
|||||||
it('requireAuth has stub implementation. Should not throw when current user', () => {
|
it('requireAuth has stub implementation. Should not throw when current user', () => {
|
||||||
// If you want to set values in context, pass it through e.g.
|
// If you want to set values in context, pass it through e.g.
|
||||||
// mockRedwoodDirective(requireAuth, { context: { currentUser: { id: 1, name: 'Lebron McGretzky' } }})
|
// mockRedwoodDirective(requireAuth, { context: { currentUser: { id: 1, name: 'Lebron McGretzky' } }})
|
||||||
const mockExecution = mockRedwoodDirective(requireAuth, { context: {} })
|
const mockExecution = mockRedwoodDirective(requireAuth, {
|
||||||
|
context: { currentUser: { id: 1, name: 'Bob McBobFace' } },
|
||||||
|
})
|
||||||
|
|
||||||
expect(mockExecution).not.toThrowError()
|
expect(mockExecution).not.toThrowError()
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,15 +1,13 @@
|
|||||||
import {
|
import { parseJWT } from '@redwoodjs/api'
|
||||||
AuthenticationError,
|
import { AuthenticationError, ForbiddenError } from '@redwoodjs/graphql-server'
|
||||||
ForbiddenError,
|
|
||||||
parseJWT,
|
|
||||||
} from '@redwoodjs/graphql-server'
|
|
||||||
import { logger } from 'src/lib/logger'
|
import { logger } from 'src/lib/logger'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* getCurrentUser returns the user information together with
|
* getCurrentUser returns the user information together with
|
||||||
* an optional collection of roles used by requireAuth() to check
|
* an optional collection of roles used by requireAuth() to check
|
||||||
* if the user is authenticated or has role-based access
|
* if the user is authenticated or has role-based access
|
||||||
*
|
*
|
||||||
* @param decoded - The decoded access token containing user info and JWT claims like `sub`
|
* @param decoded - The decoded access token containing user info and JWT claims like `sub`. Note could be null.
|
||||||
* @param { token, SupportedAuthTypes type } - The access token itself as well as the auth provider type
|
* @param { token, SupportedAuthTypes type } - The access token itself as well as the auth provider type
|
||||||
* @param { APIGatewayEvent event, Context context } - An object which contains information from the invoker
|
* @param { APIGatewayEvent event, Context context } - An object which contains information from the invoker
|
||||||
* such as headers and cookies, and the context information about the invocation such as IP Address
|
* such as headers and cookies, and the context information about the invocation such as IP Address
|
||||||
@@ -44,35 +42,38 @@ export const isAuthenticated = () => {
|
|||||||
return !!context.currentUser
|
return !!context.currentUser
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When checking role membership, roles can be a single value, a list, or none.
|
||||||
|
* You can use Prisma enums too (if you're using them for roles), just import your enum type from `@prisma/client`
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if the currentUser is authenticated (and assigned one of the given roles)
|
* Checks if the currentUser is authenticated (and assigned one of the given roles)
|
||||||
*
|
*
|
||||||
* @param {string= | string[]=} roles - A single role or list of roles to check if the user belongs to
|
* @param roles: AllowedRoles - Checks if the currentUser is assigned one of these roles
|
||||||
*
|
*
|
||||||
* @returns {boolean} - Returns true if the currentUser is authenticated (and assigned one of the given roles)
|
* @returns {boolean} - Returns true if the currentUser is logged in and assigned one of the given roles,
|
||||||
|
* or when no roles are provided to check against. Otherwise returns false.
|
||||||
*/
|
*/
|
||||||
export const hasRole = ({ roles }) => {
|
export const hasRole = ({ roles }) => {
|
||||||
if (!isAuthenticated()) {
|
if (!isAuthenticated()) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (roles) {
|
||||||
typeof roles !== 'undefined' &&
|
if (Array.isArray(roles)) {
|
||||||
typeof roles === 'string' &&
|
return context.currentUser.roles?.some((r) => roles.includes(r))
|
||||||
context.currentUser.roles?.includes(roles)
|
}
|
||||||
) {
|
|
||||||
return true
|
if (typeof roles === 'string') {
|
||||||
|
return context.currentUser.roles?.includes(roles)
|
||||||
|
}
|
||||||
|
|
||||||
|
// roles not found
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
return true
|
||||||
typeof roles !== 'undefined' &&
|
|
||||||
Array.isArray(roles) &&
|
|
||||||
context.currentUser.roles?.some((r) => roles.includes(r))
|
|
||||||
) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -80,7 +81,7 @@ export const hasRole = ({ roles }) => {
|
|||||||
* whether or not they are assigned a role, and optionally raise an
|
* whether or not they are assigned a role, and optionally raise an
|
||||||
* error if they're not.
|
* error if they're not.
|
||||||
*
|
*
|
||||||
* @param {string= | string[]=} roles - A single role or list of roles to check if the user belongs to
|
* @param roles: AllowedRoles - When checking role membership, these roles grant access.
|
||||||
*
|
*
|
||||||
* @returns - If the currentUser is authenticated (and assigned one of the given roles)
|
* @returns - If the currentUser is authenticated (and assigned one of the given roles)
|
||||||
*
|
*
|
||||||
@@ -89,8 +90,8 @@ export const hasRole = ({ roles }) => {
|
|||||||
*
|
*
|
||||||
* @see https://github.com/redwoodjs/redwood/tree/main/packages/auth for examples
|
* @see https://github.com/redwoodjs/redwood/tree/main/packages/auth for examples
|
||||||
*/
|
*/
|
||||||
export const requireAuth = ({ roles } = {}) => {
|
export const requireAuth = ({ roles }) => {
|
||||||
if (!isAuthenticated) {
|
if (!isAuthenticated()) {
|
||||||
throw new AuthenticationError("You don't have permission to do that.")
|
throw new AuthenticationError("You don't have permission to do that.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
"@redwoodjs/forms": "^0.37.4",
|
"@redwoodjs/forms": "^0.37.4",
|
||||||
"@redwoodjs/router": "^0.37.4",
|
"@redwoodjs/router": "^0.37.4",
|
||||||
"@redwoodjs/web": "^0.37.4",
|
"@redwoodjs/web": "^0.37.4",
|
||||||
"netlify-identity-widget": "^1.9.1",
|
"netlify-identity-widget": "^1.9.2",
|
||||||
"prop-types": "^15.7.2",
|
"prop-types": "^15.7.2",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-dom": "^17.0.2"
|
"react-dom": "^17.0.2"
|
||||||
|
|||||||
@@ -6,10 +6,10 @@ import {
|
|||||||
FieldError,
|
FieldError,
|
||||||
Label,
|
Label,
|
||||||
FormError,
|
FormError,
|
||||||
|
useForm,
|
||||||
} from '@redwoodjs/forms'
|
} from '@redwoodjs/forms'
|
||||||
import { useMutation } from '@redwoodjs/web'
|
import { useMutation } from '@redwoodjs/web'
|
||||||
import { toast, Toaster } from '@redwoodjs/web/toast'
|
import { toast, Toaster } from '@redwoodjs/web/toast'
|
||||||
import { useForm } from 'react-hook-form'
|
|
||||||
|
|
||||||
const CREATE_CONTACT = gql`
|
const CREATE_CONTACT = gql`
|
||||||
mutation CreateContactMutation($input: CreateContactInput!) {
|
mutation CreateContactMutation($input: CreateContactInput!) {
|
||||||
@@ -34,75 +34,81 @@ const ContactPage = () => {
|
|||||||
console.log(data)
|
console.log(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
return <>
|
return (
|
||||||
<Toaster />
|
<>
|
||||||
<Form
|
<Toaster />
|
||||||
onSubmit={onSubmit}
|
<Form
|
||||||
config={{ mode: 'onBlur' }}
|
onSubmit={onSubmit}
|
||||||
error={error}
|
config={{ mode: 'onBlur' }}
|
||||||
formMethods={formMethods}
|
|
||||||
>
|
|
||||||
<FormError
|
|
||||||
error={error}
|
error={error}
|
||||||
wrapperClassName="py-4 px-6 rounded-lg bg-red-100 text-red-700"
|
formMethods={formMethods}
|
||||||
listClassName="list-disc ml-4"
|
|
||||||
listItemClassName=""
|
|
||||||
/>
|
|
||||||
<Label
|
|
||||||
name="name"
|
|
||||||
className="block text-gray-700 uppercase text-sm"
|
|
||||||
errorClassName="block uppercase text-sm text-red-700"
|
|
||||||
>
|
>
|
||||||
Name
|
<FormError
|
||||||
</Label>
|
error={error}
|
||||||
<TextField
|
wrapperClassName="py-4 px-6 rounded-lg bg-red-100 text-red-700"
|
||||||
name="name"
|
listClassName="list-disc ml-4"
|
||||||
validation={{ required: true }}
|
listItemClassName=""
|
||||||
className="border rounded-sm px-2 py-1 outline-none"
|
/>
|
||||||
errorClassName="border rounded-sm px-2 py-1 border-red-700 outline-none"
|
<Label
|
||||||
/>
|
name="name"
|
||||||
<FieldError name="name" className="block text-red-700" />
|
className="block text-gray-700 uppercase text-sm"
|
||||||
|
errorClassName="block uppercase text-sm text-red-700"
|
||||||
|
>
|
||||||
|
Name
|
||||||
|
</Label>
|
||||||
|
<TextField
|
||||||
|
name="name"
|
||||||
|
validation={{ required: true }}
|
||||||
|
className="border rounded-sm px-2 py-1 outline-none"
|
||||||
|
errorClassName="border rounded-sm px-2 py-1 border-red-700 outline-none"
|
||||||
|
/>
|
||||||
|
<FieldError name="name" className="block text-red-700" />
|
||||||
|
|
||||||
<Label
|
<Label
|
||||||
name="name"
|
name="name"
|
||||||
className="block mt-8 text-gray-700 uppercase text-sm"
|
className="block mt-8 text-gray-700 uppercase text-sm"
|
||||||
errorClassName="block mt-8 text-red-700 uppercase text-sm"
|
errorClassName="block mt-8 text-red-700 uppercase text-sm"
|
||||||
>
|
>
|
||||||
Email
|
Email
|
||||||
</Label>
|
</Label>
|
||||||
<TextField
|
<TextField
|
||||||
name="email"
|
name="email"
|
||||||
validation={{
|
validation={{
|
||||||
required: true,
|
required: true,
|
||||||
}}
|
pattern: {
|
||||||
className="border rounded-sm px-2 py-1"
|
value: /[^@]+@[^.]+\..+/,
|
||||||
errorClassName="border rounded-sm px-2 py-1 border-red-700 outline-none"
|
message: 'Please enter a valid email address',
|
||||||
/>
|
},
|
||||||
<FieldError name="email" className="block text-red-700" />
|
}}
|
||||||
|
className="border rounded-sm px-2 py-1"
|
||||||
|
errorClassName="border rounded-sm px-2 py-1 border-red-700 outline-none"
|
||||||
|
/>
|
||||||
|
<FieldError name="email" className="block text-red-700" />
|
||||||
|
|
||||||
<Label
|
<Label
|
||||||
name="name"
|
name="name"
|
||||||
className="block mt-8 text-gray-700 uppercase text-sm"
|
className="block mt-8 text-gray-700 uppercase text-sm"
|
||||||
errorClassName="block mt-8 text-red-700 uppercase text-sm"
|
errorClassName="block mt-8 text-red-700 uppercase text-sm"
|
||||||
>
|
>
|
||||||
Message
|
Message
|
||||||
</Label>
|
</Label>
|
||||||
<TextAreaField
|
<TextAreaField
|
||||||
name="message"
|
name="message"
|
||||||
validation={{ required: true }}
|
validation={{ required: true }}
|
||||||
className="block border rounded-sm px-2 py-1"
|
className="block border rounded-sm px-2 py-1"
|
||||||
errorClassName="block border rounded-sm px-2 py-1 border-red-700 outline-none"
|
errorClassName="block border rounded-sm px-2 py-1 border-red-700 outline-none"
|
||||||
/>
|
/>
|
||||||
<FieldError name="message" className="block text-red-700" />
|
<FieldError name="message" className="block text-red-700" />
|
||||||
|
|
||||||
<Submit
|
<Submit
|
||||||
className="block bg-blue-700 text-white mt-8 px-4 py-2 rounded"
|
className="block bg-blue-700 text-white mt-8 px-4 py-2 rounded"
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
>
|
>
|
||||||
Save
|
Save
|
||||||
</Submit>
|
</Submit>
|
||||||
</Form>
|
</Form>
|
||||||
</>;
|
</>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ContactPage
|
export default ContactPage
|
||||||
|
|||||||
@@ -13764,10 +13764,10 @@ nested-error-stacks@^2.0.0, nested-error-stacks@^2.1.0:
|
|||||||
resolved "https://registry.yarnpkg.com/nested-error-stacks/-/nested-error-stacks-2.1.0.tgz#0fbdcf3e13fe4994781280524f8b96b0cdff9c61"
|
resolved "https://registry.yarnpkg.com/nested-error-stacks/-/nested-error-stacks-2.1.0.tgz#0fbdcf3e13fe4994781280524f8b96b0cdff9c61"
|
||||||
integrity sha512-AO81vsIO1k1sM4Zrd6Hu7regmJN1NSiAja10gc4bX3F0wd+9rQmcuHQaHVQCYIEC8iFXnE+mavh23GOt7wBgug==
|
integrity sha512-AO81vsIO1k1sM4Zrd6Hu7regmJN1NSiAja10gc4bX3F0wd+9rQmcuHQaHVQCYIEC8iFXnE+mavh23GOt7wBgug==
|
||||||
|
|
||||||
netlify-identity-widget@^1.9.1:
|
netlify-identity-widget@^1.9.2:
|
||||||
version "1.9.1"
|
version "1.9.2"
|
||||||
resolved "https://registry.yarnpkg.com/netlify-identity-widget/-/netlify-identity-widget-1.9.1.tgz#9e716c4b92b9f0cc041074eb86fc962f35295b46"
|
resolved "https://registry.yarnpkg.com/netlify-identity-widget/-/netlify-identity-widget-1.9.2.tgz#4339c9155fc4c2570ae3ddd61825d952b574b02e"
|
||||||
integrity sha512-9oIWjwUSdRk3SkREcZNjZaVuDDx9T/wSIXZNQsQeY4qoXic/FiXVEGgu2RU3IuA4OI3L2652xY1o+PpS03Ugaw==
|
integrity sha512-IbS1JHhs7BflCCvp3C9f6tmNSZqbyBhZ4Gs5+Qxt4IlPybTOVv0PqJ4TAsA7uxh1R+oXOAmk0OOMAkEaPYeCtA==
|
||||||
|
|
||||||
new-github-issue-url@0.2.1:
|
new-github-issue-url@0.2.1:
|
||||||
version "0.2.1"
|
version "0.2.1"
|
||||||
|
|||||||
Reference in New Issue
Block a user