payload icon indicating copy to clipboard operation
payload copied to clipboard

Version publishing and draft permission weirdness

Open LrsChrSch opened this issue 1 year ago • 5 comments

Link to reproduction

No response

Environment Info

Binaries:
  Node: 20.11.1
  npm: N/A
  Yarn: N/A
  pnpm: N/A
Relevant Packages:
  payload: 3.0.0-beta.108
  next: 15.0.0-canary.160
  @payloadcms/db-postgres: 3.0.0-beta.108
  @payloadcms/email-nodemailer: 3.0.0-beta.108
  @payloadcms/graphql: 3.0.0-beta.108
  @payloadcms/next/utilities: 3.0.0-beta.108
  @payloadcms/plugin-cloud: 3.0.0-beta.108
  @payloadcms/richtext-lexical: 3.0.0-beta.108
  @payloadcms/translations: 3.0.0-beta.108
  @payloadcms/ui/shared: 3.0.0-beta.108
  react: 19.0.0-rc-5dcb0097-20240918
  react-dom: 19.0.0-rc-5dcb0097-20240918
Operating System:
  Platform: win32
  Arch: x64
  Version: Windows 11 Pro
  Available memory (MB): 49099
  Available CPU cores: 12

Describe the Bug

This is somewhat related to #6580

In my case I have a global with versions and drafts enabled. Sort of like this:

{
  slug: 'frontpage',
  fields: [
    ... // fields like title, etc.
  ],
  access: {
    read: publishedAccess, // basically _status === 'published'
    update: adminsAndModeratorsCanPublish, // see below
    readVersions: editorAccess // pretty much any logged in user
  },
  versions: {
    max: 10,
    drafts: {
      validate: true
    }
  }
}

adminsCanPublish looks like this (yes, very ugly but this will have to do for now):

export const adminsCanPublish: Access = ({ req: { user }, data }) => {
    if (user) {
        if (user.role === 'Admin') return true // admins can do whatever they want

        if (data?._status === 'published') return false // others will get blocked if they try to publish something

        return true // if they don't want to publish anything, let them through
    }

    return false;
}

This leads to following behaviour:

  • admins can create drafts and publish them (as expected)
  • editors can't publish anything (as expected)
  • editors can create new drafts ONLY when the current document is not published (i.e. the admin has created a draft based on the published version beforehand). You would expect those editors to be able to save a new draft based on the published document
  • however editors can create a new draft by going into the version list view and reverting an old version since that would create a new draft which they can then edit and save again
  • other non-logged in users can't do anything (as expected)

This behaviour is very confusing and probably not what was intended (except I did something wrong lol. in which case sorry for opening this issue and thanks for correcting my mistake :D )

I would rather expect the following behaviour:

  • admins can create drafts and publish them (both buttons visible)
  • editors can always create drafts (including editing the published version which should just create a new draft as usual) but not publish them (only draft button visible)
  • editors can work on old versions by reverting them thus creating a newer draft
  • any other person can't do anything

I was able to somewhat bring this functionality back by adding a hook that just sets the _status to draft. This ofc breaks publishing completely, so it isn't a viable solution.

Afaik those publishing and draft buttons are visible based on an api test call. To fix this I'd propose modifying the canPublish api test call to use _status = 'draft' or doing one for the published status and for the draft status. Or something along those lines at least. Maybe a more finegrained access control with readVersions, canPublish and canDraft would be preferable in the future as that makes setting this up way easier to the user.

Thank you so much in advance! Maybe this isn't even a bug on the payload side but rather a setup mistake on mine. In any case, thanks for any help on this :)

Reproduction Steps

  • create a new payloadcms beta project
  • add a role selection to the users table (in case you want to test with the admin role)
  • add a global or collection with a text field or other data (shouldn't really matter for this, just need something to update)
  • add the access permissions as described above
  • go into the admin UI and set up an admin user and a regular user
  • add some data to the global or collection and publish it as the admin user
  • switch to the other user
  • try to create a new draft based on the currently published document (shouldn't work, button is missing)
  • switch back to admin user
  • create a new draft based on the currently published document (should work since admin can do everything)
  • switch back to other user
  • this time you should be able to create a new draft
  • you should also be able to revert any previous versions. Drafts become new drafts and previous published versions become new published ones (circumventing the access control rule entirely)

Adapters and Plugins

db-postgres

LrsChrSch avatar Sep 21 '24 14:09 LrsChrSch

Hey, so I took your functions and simplified them a bit to explain it here, but I think this is an access control issue if I understand your problem correctly, correct me if I'm wrong.

access: {
  read: () => true,
  // Any user can create
  create: ({ req: { user } }) => {
    if (user) {
      return true
    }

    return false
  },
  update: ({ req: { user }, data }) => {
    if (user) {
      if (user.role === 'admin') {
        return true
      } // admins can do whatever they want

      if (data?._status === 'published') {
        return false
      } // others will get blocked if they try to publish something

      return true // if they don't want to publish anything, let them through
    }

    return false
  },

  // Only admins can delete
  delete: ({ req: { user } }) => {
    if (user) {
      if (user.role === 'admin') {
        return true
      }
    }

    return false
  },
},

So with these functions, I tested the following workflow:

  • An admin created a piece of content as a draft
  • An editor could then edit it
  • An admin could then publish it
  • Editor can no longer edit it, BUT the editor can go in the versions history and choose "restore this version as a draft" for the published version which creates a new draft version
  • Editor can continue to edit drafts and the status of the document is as follows: image

So editors can continue editing copies of the published document but they cannot edit the actual published version if that makes sense.

In my code I did allow creation for any user, not sure if that was missing from your access control or not.

Does this address your issue here?

paulpopus avatar Sep 23 '24 05:09 paulpopus

Hi! Thanks for your reply!

Right, the editors can't edit the published version (which in itself is fine). The problem is that as soon as a version is published when an editor visits the page, all the fields will be greyed out like you said. So editors can't make changes without going into the versions first and reverting a version.

And that's what confused me a bit. The editors should be able to edit any versions, but they should only be able to save them as drafts. That would mean the fields should stay editable and the "save Draft" button should also be visible even when a version was recently published.

Hope that made sense, but just in case I added a few screenshots at the bottom.

For example, this is the first version that was created and published by the admin:

Screenshot 2024-09-23 154049

The editors view looks like this (note that they can't publish anything new which is good. but they also can't save a new draft):

Screenshot 2024-09-23 154056

This is after the admin has created a new draft (version 3 in this case):

Screenshot 2024-09-23 154226

And this is what the editor view looks like when that draft is the most recent document. They can't publish anything, but they are suddenly allowed to save the draft again (if they made any changes ofc).

Screenshot 2024-09-23 154232

What editors can also do is to revert a published version (ignore the rest, version ID 8 is a reverted version of version 2.). That means editors are allowed to make a new version with _status = 'published'. That ofc only creates a copy of it so it at least isn't a security concern.

Screenshot 2024-09-23 154727

They can however edit this new copy all of a sudden (even though it's technically published) and they can save it as a draft.

Screenshot 2024-09-23 154741

Hope that helped with clarifying my initial confusing post a bit. Again: Thank you so much for your help!

LrsChrSch avatar Sep 23 '24 13:09 LrsChrSch

They can however edit this new copy all of a sudden (even though it's technically published) and they can save it as a draft.

Yeah that's intentional since drafts don't have status published but editors will always be able to edit or create drafts even from the published document.

If you need additional ways to lock down content you can use a checkbox or a custom status field as well that you can check against to ensure that editors can never edit or create more draft versions of it further

paulpopus avatar Sep 23 '24 15:09 paulpopus

Ah, okay. But then why would the "Save Draft" button be missing in the second screenshot? I get that editors shouldn't be able to edit the published version directly. But why not always show the Button and create a new draft with the edits made to the published version?

That would keep users from going into the versions tab, manually creating a new version and editing that.

LrsChrSch avatar Sep 23 '24 15:09 LrsChrSch

This issue has been marked as stale due to lack of activity.

To keep this issue open, please indicate that it is still relevant in a comment below.

github-actions[bot] avatar Dec 13 '24 05:12 github-actions[bot]

This issue was automatically closed due to lack of activity.

github-actions[bot] avatar Dec 21 '24 05:12 github-actions[bot]

@LrsChrSch I am going to close this, I just tested with latest (3.47.0) and using a simple access control:

access: {
  update: ({ data }) => {
    return data?._status === 'draft'
  }
}

allowed me to save drafts but not publish - which I think is what you were looking for. Can you test on latest?

JarrodMFlesch avatar Jul 14 '25 15:07 JarrodMFlesch

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

github-actions[bot] avatar Jul 22 '25 05:07 github-actions[bot]