Set permissions programmatically? #6294

This discussion has been migrated from our Github Discussion #6294


laggingreflex216d ago

I have a collection type “Content

The normal API route to get all contents is GET /contents

But by default it’s inaccessible, and gives a 403 Forbidden

You have to goto the Admin Panel > Plugins > Roles & Permissions > Permissions > Application > Content then select all and hit Save

Is there a way to do this programmatically?

2 Likes

Responses to the discussion on Github


Mcastres216d ago

Collaborator

Hello @laggingreflex

What you can do is writing this code inside your config/functions/bootstrap.js

"use strict";
/**
 * An asynchronous bootstrap function that runs before
 * your application gets started.
 *
 * This gives you an opportunity to set up your data model,
 * run jobs, or perform some special logic.
 *
 * See more details here: https://strapi.io/documentation/3.0.0-beta.x/concepts/configurations.html#bootstrap
 */
const findPublicRole = async () => {
  const result = await strapi
    .query("role", "users-permissions")
    .findOne({ type: "public" });
  return result;
};

const setDefaultPermissions = async () => {
  const role = await findPublicRole();
  const permissions = await strapi
    .query("permission", "users-permissions")
    .find({ type: "application", role: role.id });
  await Promise.all(
    permissions.map(p =>
      strapi
        .query("permission", "users-permissions")
        .update({ id: p.id }, { enabled: true })
    )
  );
};

const isFirstRun = async () => {
  const pluginStore = strapi.store({
    environment: strapi.config.environment,
    type: "type",
    name: "setup"
  });
  const initHasRun = await pluginStore.get({ key: "initHasRun" });
  await pluginStore.set({ key: "initHasRun", value: true });
  return !initHasRun;
};

module.exports = async () => {
  const shouldSetDefaultPermissions = await isFirstRun();
  if (shouldSetDefaultPermissions) {
    await setDefaultPermissions();
  }
};

It will allow every permissions on all your collection types during the first run of your application.

1 Like

Thank you!
It would have been great to have something related on the Strapi documentation :slight_smile:
Cheers!

@piwi something we could possibly add to the dev docs, really though, something like this would be for an example after we document the internal API.

1 Like

Hello !

Thanks for this post, I need something similar !
Though, for some reasons long to explain, I don’t need to set it at first run, but second one, because I already use first run to bootstrap the default database rows, then I import the data in a second time using SQL.

Is there a way to launch this function at the second run ?

Thanks for your attention

first run you could set a key in the database (like an incremental counter) then when it gets to 1 or 2 (depends if you start your counter on 0 or not) then run the code to inject permissions.

1 Like

Thanks for your answer @DMehaffy,
Sounds like a smart solution, will go for something like this !

1 Like

Hi @DMehaffy , I just updated strapi v3 to v4 and this code is not working. With v4 strapi how can I do?

Hi @datle,

What do you want to achieve with this in v4?

I’ve had a similar problem and I’ve managed to hack this together which will give public find perrmissions to every api content-type;

const boostrapPermissions = async () => {
  const roles = await strapi
    .service("plugin::users-permissions.role")
    .getRoles();
  const _public = await strapi
    .service("plugin::users-permissions.role")
    .getRole(roles.filter((role) => role.type === "public")[0].id);
  for (const permission of Object.keys(_public.permissions)) {
    if (permission.startsWith("api")) {
      for (const controller of Object.keys(
        _public.permissions[permission].controllers
      )) {
        _public.permissions[permission].controllers[
          controller
        ].find.enabled = true;
      }
    }
  }
  await strapi
    .service("plugin::users-permissions.role")
    .updateRole(_public.id, _public);
};
2 Likes

Hi @chrisallmark

Just wanted to suggest another way of going about this by using strapi-plugin-config-sync.

You can manage your permissions with JSON files. Importing and exporting them across environments. Manually, or programatically.

Hi,

Thanks for this. But how did you manage to do this? How did you know, for example, that you can access the roles with the .getRoles method or that each role has a .permissions attribute? I’m new to Strapi and I’d like to come up with these little hacks sometimes, but it’s been difficult to do so.

it is giving an error
‘error: strapi.service(…).getRoles is not a function’

I was getting this too.

I found this commit in the strapi repository which shows getRoles and getRole were renamed
commit 567816591a521630f16facb01caa86447c1039fd

Changing getRoles for find and getRole for findOne worked for me

Here is an updated verison of the function above.
Call it in the ./src/index.js bootstrap method and pass the strapi value

This should work after v4.1.10

const boostrapPermissions = async (strapi) => {
  const roles = await strapi
    .service("plugin::users-permissions.role")
    .find();

  const _public = await strapi
    .service("plugin::users-permissions.role")
    .findOne(roles.filter((role) => role.type === "public")[0].id);

  // Iterate over all api content-types
  Object.keys(_public.permissions)
    .filter(permission => permission.startsWith('api'))
    .forEach(permission => {
      const controller = Object.keys(_public.permissions[permission].controllers)[0];

      // Enable find
      _public.permissions[permission].controllers[controller].find.enabled = true;

      // Enable findOne if exists
      if (_public.permissions[permission].controllers[controller].findOne) 
        _public.permissions[permission].controllers[controller].findOne.enabled = true;
      
    });

  await strapi
    .service("plugin::users-permissions.role")
    .updateRole(_public.id, _public);
};
1 Like

Thanks for this worked well

Hi there - for anyone that bumps into this problem, I have developed a plugin with an approach similar to the ones discussed above:

It updates the database during bootstrap, and should be easy to configure via ./config/plugins.js. Hope it helps some people.

2 Likes

Great plugin good job @BretCameron !
Would be even better if we can use the same config approach for other roles than public

This version only works for “find” and “findOne” (and breaks if there is no “find” like in a custom API), so I update it to set all endpoints to true. This is useful for unit testing.

function setAllEndpointsToEnebleTrue(obj) {
  for (let key in obj) {
    if (obj[key] && typeof obj[key] === "object" && "enabled" in obj[key]) {
      obj[key].enabled = true;
    }
  }
}

const makeAllRoutesPublic = async (strapi) => {
  const roles = await strapi.service("plugin::users-permissions.role").find();

  const _public = await strapi
    .service("plugin::users-permissions.role")
    .findOne(roles.filter((role) => role.type === "public")[0].id);

  // Iterate over all api content-types
  Object.keys(_public.permissions)
    .filter((permission) => permission.startsWith("api"))
    .forEach((permission) => {
      const controller = Object.keys(
        _public.permissions[permission].controllers
      )[0];

      setAllEndpointsToEneblaeTrue(
        _public.permissions[permission].controllers[controller]
      );
    });

  await strapi
    .service("plugin::users-permissions.role")
    .updateRole(_public.id, _public);
};