Answers.Body: <p>The reason was: </p>
  
  <p><strong>"hasCpSection": true</strong> property was missing from the plugin's composer.json and I had to (composer) remove and require it again in order to see the permission checkmark. </p>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2020-01-11T15:24:55.437Z
Answers.LastActivityDate: 2020-01-11T15:24:55.437Z
Answers.UserDisplayName: peet86
Answers.Body: <p>If your plugin has a CP section (specifically, the main Plugin class <a href="https://docs.craftcms.com/v3/extend/cp-section.html#plugin-sections" rel="nofollow noreferrer">declares a <code>$hasCpSection</code> property</a>, and it’s set to <code>true</code>), you should get an “Access My Plugin Name” option near the top of the permissions list—this might be enough to grant access to your plugin's controllers. See how this is enforced, <a href="https://github.com/craftcms/cms/blob/dd841844b65bd94c17aed618f395ad790f769068/src/web/Application.php#L272-L274" rel="nofollow noreferrer">here</a>.</p>
  
  <p>If you want to use more granular permissions, you'll have to check that box, plus anything you register via the <code>UserPermissions::EVENT_REGISTER_PERMISSIONS</code> event. Here's an example of that being done in a stripped-down main plugin class:</p>
  
  <pre class="lang-php prettyprint-override"><code>
  &lt;?php
  
  use Craft;
  
  use craft\base\Plugin as BasePlugin;
  use craft\events\RegisterUserPermissionsEvent;
  use craft\services\UserPermissions;
  
  use yii\base\Event;
  
  class MyPlugin extends BasePlugin {
      // ...
  
      public function init()
      {
          Event::on(
              UserPermissions::class,
              UserPermissions::EVENT_REGISTER_PERMISSIONS,
              function (RegisterUserPermissionsEvent $event) {
                  // Each "group" is just a key in the Permissions array, and you can have 
                  $event-&gt;permissions[Craft::t('my-plugin', 'My Plugin Features')] = [
                      // You might have discrete feature permissions, or different ones for `access` vs `modify`:
                      'my-plugin-access-first-feature' =&gt; [
                          'label' =&gt; Craft::t('my-plugin', 'Can Access Feature A')
                          // You can also use the `nested` key here to group permissions!
                      ],
                      'my-plugin-access-second-feature' =&gt; [
                          'label' =&gt; Craft::t('my-plugin', 'Can Access Feature B')
                      ]
                  ];
              });
      }
  
      // ...
  }
  </code></pre>
  
  <p>In a controller action, you can call <code>$this-&gt;requirePermission('my-plugin-access-first-feature');</code> to lock down access.</p>
  
  <blockquote>
    <p><strong>Also worth noting:</strong> Some plugins (like Commerce) use a few abstract controller definitions (i.e. <a href="https://github.com/craftcms/commerce/blob/develop/src/controllers/BaseController.php" rel="nofollow noreferrer"><code>BaseController</code></a>, <a href="https://github.com/craftcms/commerce/blob/develop/src/controllers/BaseCpController.php" rel="nofollow noreferrer"><code>BaseCpController</code></a>, <a href="https://github.com/craftcms/commerce/blob/develop/src/controllers/BaseAdminController.php" rel="nofollow noreferrer"><code>BaseAdminController</code></a>) to lock down entire bundles of functionality. If you're using this paradigm, you won't be able to easily relax permissions—only tighten—enforced by the parent Controller.</p>
  </blockquote>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2020-01-08T07:16:57.687Z
Answers.Text: Thanks for your answer. I've tried both actually. $hasCpSection doesn't change anything, the plugin still doesn't show up in permissions. I've checked other plugins, they work as expected without this property.. 
  
  
  Custom granular permissions are working when I declare them the same way like in your example but that doesn't solve the issue that non-admin users are not able to access at all.
Answers.UserDisplayName: peet86
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2020-01-09T03:27:05.207Z
Answers.Text: Wild, I can't think why this would be.  I'll keep this in the back of my mind and ping you if anything else occurs to me!
Answers.UserDisplayName: August Miller
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2020-05-01T16:23:44.997Z
Answers.Text: You can either set `hasCpSection` in the Composer file or in the main plugin class (not both). However, re-installing the plugin via Composer is required if you set `hasCpSection` to `true` in hindsight.
Answers.UserDisplayName: Johannes
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2020-01-07T21:38:39.043Z
Answers.LastActivityDate: 2020-01-07T22:02:00.700Z
Answers.LastEditDate: 2020-01-07T22:02:00.700Z
Answers.UserDisplayName: August Miller
Body: <p>Non-admin users are not able to access my plugin in the CP. With admin rights, everything is fine—the templates are accessible, but all the other users are getting 403.</p>
  
  <p>I've tried to build the plugin based on Craft 3 best practices and used:</p>
  
  <p><code>EVENT_REGISTER_CP_NAV_ITEMS</code> - to register nav items</p>
  
  <p><code>EVENT_REGISTER_CP_URL_RULES</code> - to register url rules like this: </p>
  
  <p><code>$event-&gt;rules['plugin-name'] =  ['template' =&gt; 'plugin-handle/index'];</code></p>
  
  <p>What am I missing? </p>
  
  <p>Should I add / register something else in the plugin's <code>init()</code>? </p>
  
  <p>The permission checkbox for the plugin doesn't even show up in User(Group) Permissions.</p>
  
  <p>I'm not using controllers because routing to templates are working fine and seems to be enough. Is that a problem?</p>
  
Tags: <control-panel><permissions>
Title: “Access Plugin” doesn't appear in Permissions list
ThreadText: # “Access Plugin” doesn't appear in Permissions list
  
  (Asked by: username_0 on 2020-01-07)
  
  Non-admin users are not able to access my plugin in the CP. With admin rights, everything is fine—the templates are accessible, but all the other users are getting 403.
  
  I've tried to build the plugin based on Craft 3 best practices and used:
  
  `EVENT_REGISTER_CP_NAV_ITEMS` - to register nav items
  
  `EVENT_REGISTER_CP_URL_RULES` - to register url rules like this:
  
  `$event->rules['plugin-name'] = ['template' => 'plugin-handle/index'];`
  
  What am I missing?
  
  Should I add / register something else in the plugin's `init()`?
  
  The permission checkbox for the plugin doesn't even show up in User(Group) Permissions.
  
  I'm not using controllers because routing to templates are working fine and seems to be enough. Is that a problem?
  
  **Tags:** control-panel, permissions
  
  
  ---
  
  ## 2 answers
  
  ### Answer by username_0 on 2020-01-11. Score: 2
  
  The reason was:
  
  **"hasCpSection": true** property was missing from the plugin's composer.json and I had to (composer) remove and require it again in order to see the permission checkmark.
  
  
  ---
  
  ### Answer by username_1 on 2020-01-07. Score: 2
  
  If your plugin has a CP section (specifically, the main Plugin class [declares a `$hasCpSection` property](https://docs.craftcms.com/v3/extend/cp-section.html#plugin-sections), and it’s set to `true`), you should get an “Access My Plugin Name” option near the top of the permissions list—this might be enough to grant access to your plugin's controllers. See how this is enforced, [here](https://github.com/craftcms/cms/blob/dd841844b65bd94c17aed618f395ad790f769068/src/web/Application.php#L272-L274).
  
  If you want to use more granular permissions, you'll have to check that box, plus anything you register via the `UserPermissions::EVENT_REGISTER_PERMISSIONS` event. Here's an example of that being done in a stripped-down main plugin class:
  
  ```php
  
  <?php
  
  use Craft;
  
  use craft\base\Plugin as BasePlugin;
  use craft\events\RegisterUserPermissionsEvent;
  use craft\services\UserPermissions;
  
  use yii\base\Event;
  
  class MyPlugin extends BasePlugin {
      // ...
  
      public function init()
      {
          Event::on(
              UserPermissions::class,
              UserPermissions::EVENT_REGISTER_PERMISSIONS,
              function (RegisterUserPermissionsEvent $event) {
                  // Each "group" is just a key in the Permissions array, and you can have 
                  $event->permissions[Craft::t('my-plugin', 'My Plugin Features')] = [
                      // You might have discrete feature permissions, or different ones for `access` vs `modify`:
                      'my-plugin-access-first-feature' => [
                          'label' => Craft::t('my-plugin', 'Can Access Feature A')
                          // You can also use the `nested` key here to group permissions!
                      ],
                      'my-plugin-access-second-feature' => [
                          'label' => Craft::t('my-plugin', 'Can Access Feature B')
                      ]
                  ];
              });
      }
  
      // ...
  }
  
  ```
  
  In a controller action, you can call `$this->requirePermission('my-plugin-access-first-feature');` to lock down access.
  
  > **Also worth noting:** Some plugins (like Commerce) use a few abstract controller definitions (i.e. [`BaseController`](https://github.com/craftcms/commerce/blob/develop/src/controllers/BaseController.php), [`BaseCpController`](https://github.com/craftcms/commerce/blob/develop/src/controllers/BaseCpController.php), [`BaseAdminController`](https://github.com/craftcms/commerce/blob/develop/src/controllers/BaseAdminController.php)) to lock down entire bundles of functionality. If you're using this paradigm, you won't be able to easily relax permissions—only tighten—enforced by the parent Controller.
  
  > **Comments:**
  > **username_0**: Thanks for your answer. I've tried both actually. $hasCpSection doesn't change anything, the plugin still doesn't show up in permissions. I've checked other plugins, they work as expected without this property..    Custom granular permissions are working when I declare them the same way like in your example but that doesn't solve the issue that non-admin users are not able to access at all.  
  > **username_1**: Wild, I can't think why this would be.  I'll keep this in the back of my mind and ping you if anything else occurs to me!  
  > **username_2**: You can either set `hasCpSection` in the Composer file or in the main plugin class (not both). However, re-installing the plugin via Composer is required if you set `hasCpSection` to `true` in hindsight.  
  
  ---
  
  