payloadcms / payload

Payload is the open-source, fullstack Next.js framework, giving you instant backend superpowers. Get a full TypeScript backend and admin panel instantly. Use Payload as a headless CMS or for building powerful applications.
https://payloadcms.com
MIT License
25.74k stars 1.64k forks source link

Custom endpoints in collection are allow anonymous and not following the Access Rules #8304

Closed ArcaneTSGK closed 2 months ago

ArcaneTSGK commented 2 months ago

Link to reproduction

No response

Environment Info

"@payloadcms/next": "3.0.0-beta.102"
"pnpm": "9.8.0"
"node": ">=20.9.0"

My collection

import type { CollectionConfig } from 'payload';
import { hasCollectionAccess } from '@/utils/access';
import { Department } from '@/enums/department';
import { AllDepartments, AllRoles } from '@/utils/access/groups';
import { Role } from '@/enums/role';
import { author } from '@/fields/author';
import { CollectionGroup } from '@/enums/collectionGroup';

export const GameMedia: CollectionConfig = {
  slug: 'game-media',
  access: {
    read: hasCollectionAccess(AllDepartments, AllRoles),
    create: hasCollectionAccess([Department.Development], [Role.SuperAdmin, Role.Api]),
    update: hasCollectionAccess([Department.Development], [Role.SuperAdmin, Role.Api]),
    delete: hasCollectionAccess([Department.Development], [Role.SuperAdmin, Role.Api])
  },
  admin: {
    group: CollectionGroup.GameImages
  },
  endpoints: [
    {
      path: '/files',
      method: 'get',
      handler: async req => {
        const imagesResponse = await req.payload.find({
          collection: 'game-media',
          limit: req.query.limit as number || 1000,
          page: req.query.page as number || 1,
        });

        const images = {
          items: imagesResponse.docs.map(image => {
            return {
              id: image.id,
              filename: image.filename
            };
          }),
          page: imagesResponse.page,
          pageSize: imagesResponse.limit,
          total: imagesResponse.docs.length,
          hasNextPage: imagesResponse.hasNextPage
        };
        return Response.json(images);
      }
    },
    {
      path: '/files/:filename',
      method: 'get',
      handler: async req => {
        const imageResponse = await req.payload.find({
          collection: 'game-media',
          where: {
            filename: {
              equals: req.routeParams!.filename
            }
          }
        });

        if (imageResponse.docs.length === 0) {
          return Response.json({}, { status: 404, statusText: 'Image Not Found' })
        }

        const image = imageResponse.docs.map(file => {
          return {
            id: file.id,
            filename: file.filename
          };
        })[0];
        return Response.json(image);
      }
    }
  ],
  fields: [
    {
      name: 'alt',
      type: 'text',
      required: true
    },
    author
  ],
  upload: true
};


### Describe the Bug

When requesting /api/game-media without a JWT token or Payload Cookie I get the expected 'You cannot perform this action'.

When requesting it with a token obtained from api/users/login with my API user I have created, I get the expected result.

If I use my custom endpoints, /api/game-media/files and /api/game-media/files/:filename then regardless of having a bearer token it allows the request through and returns a result, which is incorrect.

Is this a bug or do I need to manually include some auth logic in custom endpoints, the documentation doesn't indicate that this is needed.

Thanks

### Reproduction Steps

Create a custom endpoint on a collection that has some access logic for a user.

The custom endpoint will be accessible regardless of being logged in or not. 

### Adapters and Plugins

db-postgres
akhrarovsaid commented 2 months ago

Hey ArcaneTSGK,

I believe you're correct in your assumption that you need to manually include your own auth checks. This is by design, since Payload has no way to know your intention for these endpoints in advance.

If you need help manually checking auth, I'd check the website templates, specifically the checkRoles() function and the access folder.

r1tsuu commented 2 months ago

Hey, @akhrarovsaid is right, in your own endpoints you need to do checks for a user by yourself. Another way could be to utilize usage of overrideAccess: false https://payloadcms.com/docs/access-control/overview#default-settings and passing req.

paulpopus commented 1 month ago

I've updated the docs to mention this detail with a warning https://github.com/payloadcms/payload/pull/8321

github-actions[bot] commented 1 month ago

This issue has been automatically locked. Please open a new issue if this issue persists with any additional detail.