RBAC custom condition with relation in V4

System Information
  • Strapi Version: 4.0.2 (Enterprise)
  • Operating System: local: MacOS 12.1 / server: Ubuntu 18.04.6 LTS
  • Database: sqlite
  • Node Version: 16.13.1
  • NPM Version:
  • Yarn Version: 1.22.17

What I want to achieve:

I have two content-types (collections) News and Company with a one way relation news → company (News has one Company).
For the sake of simplicity company consists only of a text field called Name. Also the user-role I’m aiming for now gets full permissions for the company collection.

Let’s say we have two news and two companies:
NewsA → CompanyA
NewsB → CompanyB

I want to build a custom role for the backend which can see only the news belonging to companyA (so in the example can only see newsA).

Approach in V3.5.4:

I’ve build a POC a couple of months ago which seems to work fine.
In the bootstrap.js file, I’ve added something like:

//bootstrap.js
const conditions = [
  {
    displayName: "Filter-Company-A",
    name: "filter-company-a",
    handler: {
      "Company.Name": { $eq: "CompanyA" },
    },
  },
];

module.exports = async () => {

  await strapi.admin.services.permission.conditionProvider.registerMany(
    conditions
  );
};

This worked fine in V3.5.4 but not in V4.0.1. I can apply the condition (in ./src/index.js and then on the specific role) but the user with the role doesn’t see any news.

So the main question is: how to get this functionality in V4?

Approaches I took so far:

Adjust the query
The REST API for news in V3.5.4 exposed something like:

// part of response of the /news API
[
  {
    "id": 2,
    "Title": "NewsA",
    "Content": null,
    "Company": {
      "id": 4,
      "Name": "CompanyA",
      "published_at": "2021-11-30T06:09:08.671Z",
      "created_at": "2021-11-30T06:09:07.338Z",
      "updated_at": "2021-12-08T14:26:20.319Z"
    },
]

where the V4.0.1 endpoint looks like this:

// part of response of the /api/news?populate=* API
{
  "data": [
    {
      "id": 3,
      "attributes": {
        "Title": "NewsA",
        "Content": null,
        "Company": {
          "data": {
            "id": 2,
            "attributes": {
              "Name": "CompanyA",
              "createdAt": "2022-01-04T12:01:33.618Z",
              "updatedAt": "2022-01-04T12:01:34.474Z",
              "publishedAt": "2022-01-04T12:01:34.471Z"
            }
          }
        },
      }
    }
  ],
}

Because the REST API looks different now. I tried adjusting the query to point to the value of attributes.Company.data.attributes.Name (and some other combinations) without success. I’m a bit lost here because I don’t now which endpoint exactly gets called and how the response looks like (eg. if relations get populated internally).

Customize the news route
Because the REST API in V4.0.1 doesn’t populate relations on default, I customized the route to always populate the relation. Again, I don’t know if this even is the right endpoint or if this issue is related to how data gets exposed in the REST API.

If I’m missing some information or need to clarify just reach out.
I’m sorry if this is already explained somewhere but I spent quite some time screening all the information I could find.
Thanks in advance for nudging me into the right direction or solving the problem.

Leave this here for everyone who has a similar problem:

The RBAC API is considered unstable for now, see: Role-Based Access Control (RBAC) - Strapi Developer Docs.
I missed this the last time I’ve read the docs and it indicates there are changes coming!

My solution:
Changing the handler to be a function. It was at simple as that.

In my example:

const conditions = [
  {
    displayName: "Filter-Company-A",
    name: "filter-company-a",
    handler: {
      "Company.Name": { $eq: "CompanyA" },
    },
  },
];

becomes

const conditions = [
  {
    displayName: "Filter-Company-A",
    name: "filter-company-a",
    handler() {
          return { "Company.Name": { $eq: "CompanyA" } };
        },
  },
];

In my tests the handler property has to be a function no matter what value the query aims for (nested or not nested). This surely will be fixed in an upcoming release. Gonna have a look into that making an PR for the docs later if there isn’t already one.

1 Like

Hi, do you know how you’d write the “query” in case your Company relation contained multiple different entities (companies)?

Hi, do you know how you’d write the “query” in case your Company relation contained multiple different entities (companies)?

You could use $elemMatch:
e.g.:

{ 
  companies: {
    $elemMatch: {
      name: {
        $eq: "CompanyA",
      }
    }
  }
}