How can I bootstrap Public role to have create permissions?

I saw this thread on StackOverflow about how to adjust Public role:

But that code appears to be out of date as the queries do not work.

I couldn’t find any issues on GitHub or here about granting permissions to Public role via bootstrap.js.

You can use something similar to the following code in:

Path: 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();
  }
};
3 Likes

Hey, thanks for the fast reply. I tried the isFirstRun() with the strapi/strapi:3.2.5-node12-alpine image but it appears that the result of:

  const initHasRun = await pluginStore.get({ key: "initHasRun" });

On first service startup is just null. Doesn’t seem right.

Also, the setDefaultPermissions() function fails with:

app | [2020-11-10T13:17:18.176Z] error Error: The model permission can't be found.
app |     at DatabaseManager.query (/srv/app/node_modules/strapi-database/lib/database-manager.js:75:13)
app |     at Strapi.query (/srv/app/node_modules/strapi/lib/Strapi.js:436:20)
app |     at /srv/app/config/functions/bootstrap.js:60:10
app |     at Array.map (<anonymous>)
app |     at setDefaultPermissions (/srv/app/config/functions/bootstrap.js:58:17)
app |     at async module.exports (/srv/app/config/functions/bootstrap.js:83:3)
app |     at async Strapi.runBootstrapFunctions (/srv/app/node_modules/strapi/lib/Strapi.js:407:5)
app |     at async Strapi.load (/srv/app/node_modules/strapi/lib/Strapi.js:336:5)
app |     at async Strapi.start (/srv/app/node_modules/strapi/lib/Strapi.js:190:9)

This is most likely due to the permissions table not existing in the database when the bootstrap is ran, it may require a first startup to generate the permissions table then you can add it in (bootstrap is ran before the users-permissions plugin init process).

I believe in that example the initHasRun is a custom value just for the bootstrap example provided above.

So what you’re saying is that it’s actually impossible to bootstrap roles or users at first app startup in Strapi?

Then what’s the point of bootstrap.js?

I just spun up a fresh project and used yarn strapi generate:api test test:string (didn’t autostart).

And set that bootstrap code example in the ./config/functions/bootstrap.js and it properly set the permissions:

1 Like

Keep in mind this filter is looking for application permissions, aka content-types and a default fresh project has none (only plugin models)

And yeah the core_store code does set a custom key, meaning it will only set those permissions once:

Okay, I made a completely fresh bootstrap.js with just your script and it seems to have worked:

Though it is a bit aggressive. What if I want just the create?

I think I have to modify:

  const permissions = await strapi
    .query("permission", "users-permissions")
    .find({ type: "application", role: role.id });

But not sure how. Am I supposed to add another attribute in find()?

Oh, I think I see in your screenshot, it’s action, right?

Exactly yes you can add the action to it. That script is on there as an example, you will probably need to tweak it to fit your needs. (Not that most of it isn’t inside the module.exports = {} it’s outside of it.)

1 Like

Awesome, thanks for helping with that.

I have one more related question tho. What about bootstrapping super admin?
I tried using this script:

const createSuperAdminUser = async () => {
  const params = {
    username:   process.env.ADMIN_USER,
    password:   process.env.ADMIN_PASS,
    firstname:  process.env.ADMIN_USER,
    lastname:   process.env.ADMIN_USER,
    email:      process.env.ADMIN_EMAIL,
    blocked:    false,
    isActive:   true,
  }

  const admins = await strapi.query('user', 'admin').find({ _limit: 1 })
  if (admins.length) {
    console.error(`Admin user already exists: ${admins[0].email}`)
    return
  }

  try {
    // in the admin under services you can find the super admin role
    const superAdminRole = await strapi.admin.services.role.getSuperAdmin()
    // Hash password before storing in the database
    params.roles = [superAdminRole.id]
    params.password = await strapi.admin.services.auth.hashPassword(params.password)
    // Create admin account
    const admin = await strapi.query('user', 'admin').create({...params})
    console.info('Admin account created:', admin)
  } catch (error) {
    console.error(error)
  }
}

But when it gets to

params.roles = [superAdminRole.id]

It fails with:

app | TypeError: Cannot read property 'id' of null
app |     at createSuperAdminUser (/srv/app/config/functions/bootstrap.js:35:36)
app |     at async module.exports (/srv/app/config/functions/bootstrap.js:83:5)
app |     at async Strapi.runBootstrapFunctions (/srv/app/node_modules/strapi/lib/Strapi.js:407:5)
app |     at async Strapi.load (/srv/app/node_modules/strapi/lib/Strapi.js:336:5)
app |     at async Strapi.start (/srv/app/node_modules/strapi/lib/Strapi.js:190:9)

Because:

    const superAdminRole = await strapi.admin.services.role.getSuperAdmin()

Returns null at first startup. Is that normal?

:thinking: Let me do some testing locally, I know that relation between admin users and admin roles is handled differently as we allow multiple roles per user.

I had the same question about bootstraping the strapi admin and created a post for it:

1 Like

Yes, this is normal, the strapi roles are verified after bootstrap

Check my code which solves this problem for the first run (creates role if it doesn’t exist, then creates the super admin user):

2 Likes

Awesome, thanks for all the help!

1 Like