Restricting Write to currentIdentity in Role for User

I would like to prevent a user from from writing to another users ID.

I have the following predicate function that get’s permissions denied.

This is on my user collection, which is in the membership and is logged in.

I’ve tried this

Lambda(
  ["oldData", "newData", "ref"],
  Equals(Identity(), Select(["ref"], Get(Var("ref"))))
)

and I have tried this

Lambda(
  ["oldData", "newData", "ref"],
  Equals(Identity(), Var("ref"))
)

The only thing that works is if I give full write permissions which I don’t want to do.

How do I get the ref of the User that is being accessed from this query so it matches currentIdentity and returns true in the lambda

mutation{
  updateUser(id:"123",data:{name:"Anders Kitson"}){
    name
  }
}

Hi @anders! Can you share your full role? It might help to debug to see the whole thing.


ptpaterson

I am not quite sure what you mean by whole Role, here is a screenshot.

Could you elaborate on what would help me explain better?

I meant the entire definition. That is, what is the result of Get(Role('Knight')). You shared a portion, but it could be important to review the whole thing to effectively debug with you.

In general, the write predicate you have appears saved correctly. That means that there would be something else going on.

I’ve put on write full permissions for the user role for testing.

Get(Role('Knight'))

{
  ref: Role("Knight"),
  ts: 1627697799010000,
  name: "Knight",
  privileges: [
    {
      resource: Collection("User"),
      actions: {
        read: true,
        write: true,
        create: false,
        delete: false,
        history_read: false,
        history_write: false,
        unrestricted_read: false
      }
    },
    {
      resource: Collection("Quest"),
      actions: {
        read: true,
        write: Query(
          Lambda(
            ["oldData", "newData"],
            And(
              Equals(Identity(), Select(["data", "owner"], Var("oldData"))),
              Equals(
                Select(["data", "owner"], Var("oldData")),
                Select(["data", "owner"], Var("newData"))
              )
            )
          )
        ),
        create: Query(
          Lambda(
            "values",
            And(
              Equals(Identity(), Select(["data", "owner"], Var("values"))),
              Equals(false, Select(["data", "isAccepted"], Var("values"))),
              Equals(false, Select(["data", "isBeingReviewed"], Var("values")))
            )
          )
        ),
        delete: false,
        history_read: false,
        history_write: false,
        unrestricted_read: false
      }
    },
    {
      resource: Collection("Knight"),
      actions: {
        read: true,
        write: Query(
          Lambda(
            ["oldData", "newData"],
            And(
              Equals(Identity(), Select(["data", "owner"], Var("oldData"))),
              Equals(
                Select(["data", "owner"], Var("oldData")),
                Select(["data", "owner"], Var("newData"))
              )
            )
          )
        ),
        create: Query(
          Lambda(
            "values",
            Equals(Identity(), Select(["data", "owner"], Var("values")))
          )
        ),
        delete: false,
        history_read: false,
        history_write: false,
        unrestricted_read: false
      }
    },
    {
      resource: Collection("Hero"),
      actions: {
        read: true,
        write: true,
        create: Query(
          Lambda(
            "values",
            And(
              Equals(false, Select(["data", "isAccepted"], Var("values"))),
              Equals(false, Select(["data", "isBeingReviewed"], Var("values")))
            )
          )
        ),
        delete: false,
        history_read: false,
        history_write: false,
        unrestricted_read: false
      }
    },
    {
      resource: Index("quest_knights_by_quest"),
      actions: {
        unrestricted_read: false,
        read: true
      }
    },
    {
      resource: Index("unique_User_email"),
      actions: {
        unrestricted_read: false,
        read: true
      }
    },
    {
      resource: Index("quest_heros_by_quest"),
      actions: {
        unrestricted_read: false,
        read: true
      }
    },
    {
      resource: Index("user_jobs_by_user"),
      actions: {
        unrestricted_read: false,
        read: true
      }
    },
    {
      resource: Index("user_quests_by_user"),
      actions: {
        unrestricted_read: false,
        read: true
      }
    },
    {
      resource: Index("getQuests"),
      actions: {
        unrestricted_read: false,
        read: true
      }
    },
    {
      resource: Index("getHeros"),
      actions: {
        unrestricted_read: false,
        read: true
      }
    },
    {
      resource: Index("user_knights_by_user"),
      actions: {
        unrestricted_read: false,
        read: true
      }
    },
    {
      resource: Index("getKnights"),
      actions: {
        unrestricted_read: false,
        read: true
      }
    }
  ],
  membership: [
    {
      resource: Collection("User"),
      predicate: Query(
        Lambda(
          "userRef",
          Equals(Select(["data", "role"], Get(Var("userRef"))), "KNIGHT")
        )
      )
    }
  ]
}

Here is a minimal role that I believe does what you want: Read-only if CurrentIdentity matches, write only if CurrentIdentity matches.

Also minimal because of no membership predicate. Presumably, there is nothing wrong with the membership predicate since full read/write permissions work when given.

CreateRole({
  name: "user_read_write",
  privileges: [
    {
      resource: Collection("User"),
      actions: {
        read: Query(Lambda("ref", Equals(CurrentIdentity(), Var("ref")))),
        write: Query(
          Lambda(
            ["oldData", "newData", "ref"],
            Equals(CurrentIdentity(), Var("ref"))
          )
        )
      }
    }
  ],
  membership: [
    {
      resource: Collection("User")
    }
  ]
})

The same predicate that you started with should work. Even using Identity() instead of CurrentIdentity() for the time being.

I can confirm this works as intended by logging in with a user, then performing the GraphQL queries with the secret.

If the predicate is not working for you, then something else must be off. Are you sure that your GraphQL mutation is updating the same user for which you have a token?