From 71539faa32d7869fdda71bc411db7cf2af407654 Mon Sep 17 00:00:00 2001 From: Belle Aerni Date: Tue, 12 Sep 2023 16:14:50 -0700 Subject: [PATCH] Added permissions docs into our "creating a module" page --- .../guides/creating-a-module.mdx | 54 ++++++++++++++++++ public/img/docs/can-always-access.png | Bin 0 -> 6820 bytes 2 files changed, 54 insertions(+) create mode 100644 public/img/docs/can-always-access.png diff --git a/pages/docs/contribution-handbook/guides/creating-a-module.mdx b/pages/docs/contribution-handbook/guides/creating-a-module.mdx index 8a5ee64..8d39497 100644 --- a/pages/docs/contribution-handbook/guides/creating-a-module.mdx +++ b/pages/docs/contribution-handbook/guides/creating-a-module.mdx @@ -58,3 +58,57 @@ public static function onBeforeAdminCronRun(\Box_Event $event): void error_log('Cron was called!'); } ``` + +## Adding permissions to your module + +FOSSBilling offers the ability for modules to define new permission keys, meaning even modules that aren't built by the FOSSBilling team may still offer granular and flexible permissions in whatever way best suits their specific needs. + +### Considerations + +- FOSSBilling automatically adds a "module access" permission key to all modules, you shouldn't define one yourself + - Outside of the generic "module access" permission, FOSSBilling won't do anything to check permissions for you. It is the module's responsibility to check for permissions at the correct moment. + - Module permissions are specific to staff members, there is no permissions system for clients at the moment. + - Permission keys are specific to your own module, so you don't need to worry about a possible naming conflict. + +### Defining your permissions + +All modules which want to implement custom permissions need to define a `getModulePermissions` function. FOSSBilling will call this function whenever it wants to get a list of the available functions for a given module. Below is an example of one of these functions: + +```PHP +public function getModulePermissions(): array +{ + return [ + 'delete_something' => [ + 'type' => 'bool', + 'display_name' => __trans('Delete something'), + 'description' => __trans('Allows the staff member to delete "something"'), + ], + 'can_always_access' => true, + 'manage_settings', + ]; +} +``` +Because this function isn't static and FOSSBilling sets the `DI` before calling it, you may potentially take advantage of this to dynamically generate the permissions, although we don't have an example of such an implementation. + +If you add `can_always_access` and set it to be `true` in your permissions, this means that it is impossible to prevent access to this module for a staff member. This doesn't mean that they can perform all actions, it simply means that will always have the minimum permissions in the form of access to it. Most modules won't need this. +![can always access](/img/docs/can-always-access.png) + +Adding `manage_settings` to your permissions list tells FOSSBilling that you want to be able to restrict access to your modules settings (`/admin/extension/settings/email` as an example). Due to how some modules use this "settings" page for purposes other than settings, this specific permission key is opt-in. Adding `manage_settings` is all you need to do to opt-in to using the permission key and FOSSBilling will then automatically populate the permission key with the correct description and display name as well as checking for permission when someone attempts to access the module settings. + +### Checking for permission + +Obviously having the ability to define permissions is only useful if you also have a way to check those permissions, which is handled by calling a function within the `staff` module. Here's an example below using our `delete_something` permission key: + +```PHP +$staff_service = $this->di['mod_service']('Staff'); +if (!$staff_service->hasPermission(null, 'example', 'delete_something')) { + throw new \Box_Exception('You do not have permission to perform this action', [], 403); +} +``` +Let's break it down line-by-line: +1. We create an instance of the staff module's service class, as this holds the `hasPermission` function. +2. We call on the `hasPermission` providing it the following parameters: + - By passing `null` to the first parameter we tell the function to use the ID for the currently authenticated staff member. + - The second parameter represents the module ID that we are checking the permissions for, in this case we put `example`, but you should put the ID of your module. + - The third parameter is going to be the permission key you are checking, in this example it's `delete_something`. This should match the key as you set it in `getModulePermissions` +3. Finally in this example we throw an except to prevent any further code from being executed. If you do use an exception, please use "You do not have permission to perform this action" as the message as this will be translated. The error code should also be `403` diff --git a/public/img/docs/can-always-access.png b/public/img/docs/can-always-access.png new file mode 100644 index 0000000000000000000000000000000000000000..1cbbf78e431ccbff418f9c3a17b5ed53c59518ee GIT binary patch literal 6820 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 r8X6PKe<+dvZ+MXZMZ{F#-BNdt@yV