7

The use case is a development module that you would only have installed on a local environment.

My thought was to use $config in settings.php to do this:

$config['user.role.anonymous']['permissions'][] = 'use browsersync';
$config['user.role.authenticated']['permissions'][] = 'use browsersync';

When doing this everything looks good in the permissions UI but in practice the permissions seem to be clobbered and permissions like 'access content' are no longer enabled for these users.

7
  • You can create a module that is only installed on the development environment. That module would add these permissions to the time configuration entities and save them.
    – Eyal
    Commented Dec 2, 2015 at 5:44
  • *the reason you can't use the config array is that roles are configuration entities that are saved in the database.
    – Eyal
    Commented Dec 2, 2015 at 5:45
  • Forgive my very basic understanding of CMI but isn't everything in the $config array a config entity of some kind? If not then how do you tell the difference to know what can and cannot be overridden via $config?
    – star-szr
    Commented Dec 2, 2015 at 10:32
  • Correction: as per the drupal 8 documentation it is possible to override configuration via the $config array in settings.php. I will post a detailed answer with the recommended way to achieve required functionality.
    – Eyal
    Commented Dec 2, 2015 at 18:55
  • Your suggestion seems to be the correct way to go about this. If it doesn't work for you then it could be a bug in drupal core.
    – Eyal
    Commented Dec 2, 2015 at 20:53

3 Answers 3

7

Permissions are clobbered because Drupal uses NestedArray::mergeDeepArray() to merge overrides with existing config.

For example, if we have this permissions

$ drush cget user.role.anonymous permissions
'user.role.anonymous:permissions':
  - 'access content'
  - 'access comments'

and then add

$config['user.role.anonymous']['permissions'][] = 'use browsersync';

to settings.php, the result will be

$ drush cget user.role.anonymous permissions
'user.role.anonymous:permissions':
  - 'use browsersync'
  - 'access comments'

An easy way to workaround this would be

$config['user.role.anonymous']['permissions'][9999] = 'use browsersync';

which will result in

$ drush cget user.role.anonymous permissions
'user.role.anonymous:permissions':
  0:'access content'
  1:'access comments'
  9999: 'use browsersync'

But if you need to remove a permission via settings.php, then I honestly don't know how to do it, even with module config overrides system (which has an ability to load original config before providing the override). Maybe overriding the unwanted permission with NULL may work, but I haven't tried.

2
  • Maybe overriding the unwanted permission with NULL may work - This doesn't work in my Drupal instance. The 9999 addtion does work however. :-) Commented Sep 25, 2020 at 0:07
  • Edit: actually both work - but you have to the array key correct. Commented Sep 25, 2020 at 0:13
0

I'm not sure I completely understand your use case, but think https://www.drupal.org/project/secure_permissions will work for you. It doesn't manage permissions in the settings.php. Instead it allows you to export permission to a hook that can be implemented across several modules. We have a module called cu_debug_admin_bundle that enables devel, disables a few caching related modules. and sets a number of variables for local testing. The module also sets the permission so that devel output is visible to users with specific roles. Ideally that module isn't even deployed to production.

 /**
 * Define site permissions in code.
 *
 * Create a secure_permissions_data module directory and place this function
 * in secure_permissions_data.module.
 *
 * @param $role
 *   The role for which the permissions are being requested.
 *
 * @return
 *   An array defining all the permissions for the site.
 */

function cu_debug_admin_bundle_secure_permissions($role) {
  $permissions = array(
    'developer' => array(
      'access devel information',
      'access security review list',
      'run security checks',
    ),
  );
  if (isset($permissions[$role])) {
    return $permissions[$role];
  }
}
2
  • Thanks this is for D8 though, and I don't see a D8 version of that module. Was hoping to accomplish this with core functionality in CMI.
    – star-szr
    Commented Dec 2, 2015 at 2:15
  • Yikes. I wasn't planning on updating secure_permissions to D8 because I thought this type of use case COULD be handled by CMI.
    – kreynen
    Commented Dec 2, 2015 at 20:46
0

Following on form @Leksat's comment Maybe overriding the unwanted permission with NULL may work, this does work but you have to override the specific array key - see mock code below.

// Too early to use the File Storage class - so manually parse the yml file.
$user_role_anonymous = \Symfony\Component\Yaml\Yaml::parse(file_get_contents($settings['config_sync_directory'] . '/user.role.anonymous.yml'));
$permissions_to_remove = [
  'access devel information',
  'access kint',
];
foreach ($permissions_to_remove as $permission) {
  if (isset($user_role_anonymous['permissions'])) {
    if (($key = array_search($permission, $user_role_anonymous['permissions'])) !== FALSE) {
      $config['user.role.anonymous']['permissions'][$key] = NULL;
    }
  }
}

EDIT: better method using anonymous function for less variable pollution.

$config = isset($config) ? $config : [];
$config_directories = isset($config_directories) ? $config_directories : [];
$settings = isset($settings) ? $settings : [];

// Add or remove perrissons as needed.
$config = call_user_func(
  function ($config, $config_directories, $settings) {
    // Get sync folder.
    // Same as Drupal\Core\Site\Settings::initialize().
    if (!isset($settings['config_sync_directory']) && !empty($config_directories['sync'])) {
      $settings['config_sync_directory'] = $config_directories['sync'];
    }
    elseif (isset($settings['config_sync_directory'])) {
      $config_directories['sync'] = $settings['config_sync_directory'];
    }

    // Too early to use the FileStorage class - so manually parse the yml file.
    $user_roles_configs = [
      'user.role.anonymous' => \Symfony\Component\Yaml\Yaml::parse(file_get_contents($settings['config_sync_directory'] . '/user.role.anonymous.yml')),
      'user.role.authenticated' => \Symfony\Component\Yaml\Yaml::parse(file_get_contents($settings['config_sync_directory'] . '/user.role.authenticated.yml')),
    ];
    $permissions_to_remove = [
      'access devel information',
      'access kint',
    ];
    foreach ($user_roles_configs as $user_role_config_name => $user_roles_config) {
      foreach ($permissions_to_remove as $permission) {
        if (isset($user_roles_config['permissions'])) {
          if (($key = array_search($permission, $user_roles_config['permissions'])) !== FALSE) {
            $config[$user_role_config_name]['permissions'][$key] = NULL;
          }
        }
      }
    }
    return $config;
  },
  $config,
  $config_directories,
  $settings
);

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.