PermissionHandler.php 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. <?php
  2. namespace Drupal\user;
  3. use Drupal\Core\Discovery\YamlDiscovery;
  4. use Drupal\Core\Controller\ControllerResolverInterface;
  5. use Drupal\Core\Extension\ModuleHandlerInterface;
  6. use Drupal\Core\StringTranslation\StringTranslationTrait;
  7. use Drupal\Core\StringTranslation\TranslationInterface;
  8. /**
  9. * Provides the available permissions based on yml files.
  10. *
  11. * To define permissions you can use a $module.permissions.yml file. This file
  12. * defines machine names, human-readable names, restrict access (if required for
  13. * security warning), and optionally descriptions for each permission type. The
  14. * machine names are the canonical way to refer to permissions for access
  15. * checking.
  16. *
  17. * If your module needs to define dynamic permissions you can use the
  18. * permission_callbacks key to declare a callable that will return an array of
  19. * permissions, keyed by machine name. Each item in the array can contain the
  20. * same keys as an entry in $module.permissions.yml.
  21. *
  22. * Here is an example from the core filter module (comments have been added):
  23. * @code
  24. * # The key is the permission machine name, and is required.
  25. * administer filters:
  26. * # (required) Human readable name of the permission used in the UI.
  27. * title: 'Administer text formats and filters'
  28. * # (optional) Additional description fo the permission used in the UI.
  29. * description: 'Define how text is handled by combining filters into text formats.'
  30. * # (optional) Boolean, when set to true a warning about site security will
  31. * # be displayed on the Permissions page. Defaults to false.
  32. * restrict access: false
  33. *
  34. * # An array of callables used to generate dynamic permissions.
  35. * permission_callbacks:
  36. * # Each item in the array should return an associative array with one or
  37. * # more permissions following the same keys as the permission defined above.
  38. * - Drupal\filter\FilterPermissions::permissions
  39. * @endcode
  40. *
  41. * @see filter.permissions.yml
  42. * @see \Drupal\filter\FilterPermissions
  43. * @see user_api
  44. */
  45. class PermissionHandler implements PermissionHandlerInterface {
  46. use StringTranslationTrait;
  47. /**
  48. * The module handler.
  49. *
  50. * @var \Drupal\Core\Extension\ModuleHandlerInterface
  51. */
  52. protected $moduleHandler;
  53. /**
  54. * The YAML discovery class to find all .permissions.yml files.
  55. *
  56. * @var \Drupal\Core\Discovery\YamlDiscovery
  57. */
  58. protected $yamlDiscovery;
  59. /**
  60. * The controller resolver.
  61. *
  62. * @var \Drupal\Core\Controller\ControllerResolverInterface
  63. */
  64. protected $controllerResolver;
  65. /**
  66. * Constructs a new PermissionHandler.
  67. *
  68. * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
  69. * The module handler.
  70. * @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
  71. * The string translation.
  72. * @param \Drupal\Core\Controller\ControllerResolverInterface $controller_resolver
  73. * The controller resolver.
  74. */
  75. public function __construct(ModuleHandlerInterface $module_handler, TranslationInterface $string_translation, ControllerResolverInterface $controller_resolver) {
  76. // @todo It would be nice if you could pull all module directories from the
  77. // container.
  78. $this->moduleHandler = $module_handler;
  79. $this->stringTranslation = $string_translation;
  80. $this->controllerResolver = $controller_resolver;
  81. }
  82. /**
  83. * Gets the YAML discovery.
  84. *
  85. * @return \Drupal\Core\Discovery\YamlDiscovery
  86. * The YAML discovery.
  87. */
  88. protected function getYamlDiscovery() {
  89. if (!isset($this->yamlDiscovery)) {
  90. $this->yamlDiscovery = new YamlDiscovery('permissions', $this->moduleHandler->getModuleDirectories());
  91. }
  92. return $this->yamlDiscovery;
  93. }
  94. /**
  95. * {@inheritdoc}
  96. */
  97. public function getPermissions() {
  98. $all_permissions = $this->buildPermissionsYaml();
  99. return $this->sortPermissions($all_permissions);
  100. }
  101. /**
  102. * {@inheritdoc}
  103. */
  104. public function moduleProvidesPermissions($module_name) {
  105. // @TODO Static cache this information, see
  106. // https://www.drupal.org/node/2339487
  107. $permissions = $this->getPermissions();
  108. foreach ($permissions as $permission) {
  109. if ($permission['provider'] == $module_name) {
  110. return TRUE;
  111. }
  112. }
  113. return FALSE;
  114. }
  115. /**
  116. * Builds all permissions provided by .permissions.yml files.
  117. *
  118. * @return array[]
  119. * Each return permission is an array with the following keys:
  120. * - title: The title of the permission.
  121. * - description: The description of the permission, defaults to NULL.
  122. * - provider: The provider of the permission.
  123. */
  124. protected function buildPermissionsYaml() {
  125. $all_permissions = [];
  126. $all_callback_permissions = [];
  127. foreach ($this->getYamlDiscovery()->findAll() as $provider => $permissions) {
  128. // The top-level 'permissions_callback' is a list of methods in controller
  129. // syntax, see \Drupal\Core\Controller\ControllerResolver. These methods
  130. // should return an array of permissions in the same structure.
  131. if (isset($permissions['permission_callbacks'])) {
  132. foreach ($permissions['permission_callbacks'] as $permission_callback) {
  133. $callback = $this->controllerResolver->getControllerFromDefinition($permission_callback);
  134. if ($callback_permissions = call_user_func($callback)) {
  135. // Add any callback permissions to the array of permissions. Any
  136. // defaults can then get processed below.
  137. foreach ($callback_permissions as $name => $callback_permission) {
  138. if (!is_array($callback_permission)) {
  139. $callback_permission = [
  140. 'title' => $callback_permission,
  141. ];
  142. }
  143. $callback_permission += [
  144. 'description' => NULL,
  145. 'provider' => $provider,
  146. ];
  147. $all_callback_permissions[$name] = $callback_permission;
  148. }
  149. }
  150. }
  151. unset($permissions['permission_callbacks']);
  152. }
  153. foreach ($permissions as &$permission) {
  154. if (!is_array($permission)) {
  155. $permission = [
  156. 'title' => $permission,
  157. ];
  158. }
  159. $permission['title'] = $this->t($permission['title']);
  160. $permission['description'] = isset($permission['description']) ? $this->t($permission['description']) : NULL;
  161. $permission['provider'] = !empty($permission['provider']) ? $permission['provider'] : $provider;
  162. }
  163. $all_permissions += $permissions;
  164. }
  165. return $all_permissions + $all_callback_permissions;
  166. }
  167. /**
  168. * Sorts the given permissions by provider name and title.
  169. *
  170. * @param array $all_permissions
  171. * The permissions to be sorted.
  172. *
  173. * @return array[]
  174. * Each return permission is an array with the following keys:
  175. * - title: The title of the permission.
  176. * - description: The description of the permission, defaults to NULL.
  177. * - provider: The provider of the permission.
  178. */
  179. protected function sortPermissions(array $all_permissions = []) {
  180. // Get a list of all the modules providing permissions and sort by
  181. // display name.
  182. $modules = $this->getModuleNames();
  183. uasort($all_permissions, function (array $permission_a, array $permission_b) use ($modules) {
  184. if ($modules[$permission_a['provider']] == $modules[$permission_b['provider']]) {
  185. return $permission_a['title'] > $permission_b['title'];
  186. }
  187. else {
  188. return $modules[$permission_a['provider']] > $modules[$permission_b['provider']];
  189. }
  190. });
  191. return $all_permissions;
  192. }
  193. /**
  194. * Returns all module names.
  195. *
  196. * @return string[]
  197. * Returns the human readable names of all modules keyed by machine name.
  198. */
  199. protected function getModuleNames() {
  200. $modules = [];
  201. foreach (array_keys($this->moduleHandler->getModuleList()) as $module) {
  202. $modules[$module] = $this->moduleHandler->getName($module);
  203. }
  204. asort($modules);
  205. return $modules;
  206. }
  207. /**
  208. * Wraps system_rebuild_module_data()
  209. *
  210. * @return \Drupal\Core\Extension\Extension[]
  211. */
  212. protected function systemRebuildModuleData() {
  213. return system_rebuild_module_data();
  214. }
  215. }