How to pass data to field level access control?

default discord avatar
KasparTrlast year
9

From this doc I can read that field level access control accepts user, data, doc, siblingData and id.


https://payloadcms.com/docs/access-control/fields#field-level-access-control

When I pass doc or data to the function, it's always undefined.


What is more, is that value reactive i.e. will the access control update when the editReason changes.


 
access: {
  update: ({ req: { user }, doc }) => { if(doc) return doc.editReason === "impairment"; else return true }
  }


What I wish to achieve is a dynamic field readOnly status. As the admin field readOnly doesn't accept a function, I am attempting to use the access controls for this.

  • discord user avatar
    denolfe
    last year

    Hey @KasparTr , there are certain instances where access control is executed without reference to the document. More detail in our docs here under the Argument Availability section:

    https://payloadcms.com/docs/access-control/overview#access-control
  • default discord avatar
    KasparTrlast year

    In this case, it is impossible to build document based access control logic via access operators?



    An example use-case:

    User can only update documents they have created. This is a basic requirement in almost all systems but in PayloadCMS, it would not be possible to do as access operators don't pass document data?



    I'm getting data undefined in all access.update functions.



    Example:


    export const  isEditorOrCreatedBy: Access = ({req: { user },id,data }) => {
      const isCreatedBy = {
        createdBy: {
          equals: user.id,
        },
      }
    
      console.log("isCreatedBy: ", isCreatedBy) // json object
      console.log("data: ", data) // undefined
      console.log("id: ", id) // defined
      
      ...
    
    };


    Just watched this video again.


    It's really unclear how these queries are made without any variable declaration. Also they don't work in practice. Not sure why this item is market as answered as there are clearly issues in reality.


    https://www.youtube.com/watch?v=DoPLyXG26Dg&t=875s
  • discord user avatar
    denolfe
    last year

    In order to achieve your use-case, there would need to be some sort of reference to the user stored on the document. Would the solution in this post work for your use-case?

    https://payloadcms.com/blog/build-your-own-rbac

    ?



    The gist is to store some sort of value on the document itself, such as user id, role, or team. Then your access control would reference this value in the query constraint that is returned.



    Not sure why this item is market as answered as there are clearly issues in reality.

    We try our best to answer all inquiries. Since the last post on here was 2 months ago, and the last link was pointing to helpful information - the assumption is that the issue was resolved.

  • default discord avatar
    Jarrodlast year

    @KasparTr to do what you are looking to do, which is very common, you need to return the query constraint. You won’t have access to the variables you are talking about but the db query will constrain results based on the query you return in the access control functions

  • default discord avatar
    KasparTrlast year

    Found some code that explained how this works but not as in the video or in docs.


    Also, most collections don't have "createdBy" field added in DB so not sure how this query should magically work.



    Here is the code if anyone is trubleshooting this.



    export const canUpdateItem = (fieldToCheck: string = '<COLLECTION_FIELD_NAME>'): Access => ({ req: { user } }) => {
            return {
                or: [
                    {
                        [fieldToCheck]: {
                            in: user.id
                        }
                    }
                ]
            }
    };


    Using this in a collection:


      access: {
        create: () => true,
        read: () => true,
        update: canUpdateItem('collaborators'),
        delete: () => false
      },
  • default discord avatar
    Twoxiclast year

    I also have a usecase for accessing data to access control functions, as I described here:

    https://discord.com/channels/967097582721572934/1100757519376339004

    I agree that the query construction is a bit vague sometimes.

  • default discord avatar
    Jarrodlast year

    @KasparTr it's not magic. In the post @denolfe shared under the

    Set the createdBy Attribute Using a Hook

    section, it explains exactly what is being done to achieve this.

Star on GitHub

Star

Chat on Discord

Discord

online

Can't find what you're looking for?

Get help straight from the Payload team with an Enterprise License.