How to keep /api/users endpoint off limits, but still allow filtering content by user?

System Information
  • Strapi Version:
  • Operating System:
  • Database:
  • Node Version:
  • NPM Version:
  • Yarn Version:

So I tried to understand why I can’t filter outgoing data in multiple API endpoints by the user (I'm following examples from the web and I can't understand why they don't work for me. Please help). In the end I found this: Strapi quirks you should know about - DEV Community and gave my user role the permissions to see user data. But that opened a can of worms - now /api/users are accessible to them too, and that is definitely an undesired effect.

So what are other ways of doing this? Can I manually query the database in my API controllers and do the filtering there? What other things there are I could do to fix this?

Thanks in advance,
Alan

The ‘user’ (created_by or updated_by) is not populated by default, but you can turn that on by adding a field to the schema.json for a given collection (Populate and Select | Strapi Documentation). I haven’t had to update the collection’s controller as the above documentation suggests, but since the User is a relation, I did need to request population of relations in the API request (see ‘Population’, further up on the same page). I used populate=* but you could request just the user relation.

Once you have the fields populated, you can also use them for querying (Filters, Locale, and Publication State | Strapi Documentation).

Be aware that the above exposes user names on the API … You should leave authentication turned on for any content type where you allow user details to be populated.

Hey Steve, and thanks for your time.

You missed the point of my question a tiny bit, though. I figured out that in order to get my “owner” field to show up when using populate=* I need to enable “find” permission on the user role. That’s what got it working.

But that also exposed all the users in /api/users endpoint, which was an undesired side-effect. But I sort of got around that also by extending user-permissions by adding strapi-server.js and only returning an empty object from the endpoint there. More about it here:

Alan

Agreed, relation population and find API permissions should be handled separately but aren’t right now.

What I did was I created a custom Policy:

// /src/policies/prevent-direct-access.js
const { PolicyError } = require('@strapi/utils/lib/errors');

module.exports = (ctx /*, config, { strapi }*/) => {
  throw new PolicyError(`Direct access to ${ctx.routerPath} is forbidden`, {
    policy: 'prevent-direct-access',
  });
};

And if there is a find route that I want to prevent from being accessed directly, but need to enable for population, I configure it like so:

// /src/api/category/routes/category.js
const { createCoreRouter } = require('@strapi/strapi').factories;

module.exports = createCoreRouter('api::category.category', {
  only: ['find'],
  config: {
    find: {
      policies: ['global::prevent-direct-access'],
    },
  },
});

Documentation:

Awesome! Thank you @GregorSondermeier!

But how would I use this to do the same on the user level? I guess there I can’t use createCoreRouter, as It would override all the existing `/user’ endpoints, right?

You could extend the users-permission plugin (docs: Plugins extension | Strapi Documentation) and add the policy to the one route you want to change.

OK. That sounds a bit better, indeed, albeit a more complex solution. Thank you! I’ll look into it.