Skip to content

Instantly share code, notes, and snippets.

@mooror
Last active January 3, 2019 01:19
Show Gist options
  • Save mooror/ea2137356ffd392ea0c455d6d5bb1919 to your computer and use it in GitHub Desktop.
Save mooror/ea2137356ffd392ea0c455d6d5bb1919 to your computer and use it in GitHub Desktop.
My Silverstripe 4 code for auto-generating access permissions for DataObjects
<?php
namespace Sitelease\Core\Traits;
use ReflectionClass;
use SilverStripe\Security\Permission;
/**
* Adds functionality to automatically create model level access permissions
* for a DataObject
*
* The code requires a boolean configuration property called
* "auto_create_permissions" to function. We recommend that
* you add this property to a DataExtension applied to the base
* "DataObject" class if you want to all models to have permissions
* defined be default
*
* Note: This trait is made to be used in classes that
* extend the PermissionsProvider interface.
*/
trait AccessPermissionsProvider
{
/**
* Will attempt to get the suffix from the configuration
* property, but if that is empty it will generate one using the
* class name.
*
* @author Benjamin Blake (sitelease.ca)
*
* @return string
*/
public function getPermissionsSuffix()
{
$suffix = $this->config()->get("permissions_suffix");
if (!$suffix) {
$namespace = get_called_class();
$reflect = new ReflectionClass($namespace);
$className = $reflect->getShortName();
if (substr($className, 0, strlen("SL")) == "SL") {
$suffix = strtoupper($className);
} else {
$suffix = "SL".strtoupper($className);
}
}
$this->extend('updatePermissionsSuffix', $suffix);
return $suffix;
}
/**
* Will attempt to get the descriptor from the configuration
* property, but if that is empty it will generate one using the
* singular_name() method
*
* @author Benjamin Blake (sitelease.ca)
*
* @return string
*/
public function getPermissionsDescriptor()
{
$descriptor = $this->config()->get("permissions_descriptor");
if (!$descriptor) {
$readableClass = $this->singular_name();
$descriptor = strtolower($readableClass);
}
$this->extend('updatePermissionsDescriptor', $descriptor);
return $descriptor;
}
/**
* Will attempt to get the category from the configuration
* property, but if that is empty it will generate one using the
* plural_name() method
*
* @author Benjamin Blake (sitelease.ca)
*
* @return string
*/
public function getPermissionsCategory()
{
$category = $this->config()->get("permissions_category");
if (!$category) {
$readableClass = $this->plural_name();
$category = ucfirst($readableClass." (Sitelease)");
}
$this->extend('updatePermissionsCategory', $category);
return $category;
}
/**
* Used to let classes provide new permission codes.
* Every implementor of PermissionProvider is accessed and providePermissions()
* called to get the full list of permission codes.
*
* @author Benjamin Blake (sitelease.ca)
*
* @return array
*/
public function providePermissions()
{
$autoCreatePermissions = $this->config()->get("auto_create_permissions");
if ($autoCreatePermissions) {
$permissionsArray = $this->permissionsArray;
$suffix = $this->getPermissionsSuffix();
$category = $this->getPermissionsCategory();
$descriptor = $this->getPermissionsDescriptor();
$actions = $this->config()->get("permissions_config_array");
if ($actions) {
$iterations = 0;
foreach ($actions as $actionName => $actionTitle) {
// If the title is empty or falsey, create one
if (empty($actionTitle) || !$actionTitle) {
$actionTitle = ucfirst($actionName);
$actionTitle .= " - Allow user to $actionName $descriptor objects";
}
$codeActionName = str_replace("-", "_", $actionName);
$codeActionName = str_replace(" ", "_", $codeActionName);
$permissionCode = strtoupper($codeActionName) . '_' . $suffix;
$permissionsArray[$permissionCode] = array(
'name' => $actionTitle,
'help' => 'Permission Code: '.$permissionCode,
'category' => $category,
'sort' => 99-$iterations
);
$iterations++;
}
}
$this->extend('updateProvidePermissions', $permissionsArray);
return $permissionsArray;
} else {
return false;
}
}
/**
* @author Benjamin Blake (sitelease.ca)
*
* @param Member $member
* @return boolean
*/
public function canView($member = null)
{
$autoCreatePermissions = $this->config()->get("auto_create_permissions");
if ($autoCreatePermissions) {
return array(Permission::check('VIEW_' . $this->getPermissionsSuffix(), 'any', $member));
} else {
$accessGranted = Permission::check('ADMIN', 'any', $member);
$this->extend('updateCanView', $accessGranted);
return $accessGranted;
}
}
/**
* @author Benjamin Blake (sitelease.ca)
*
* @param Member $member
* @return boolean
*/
public function canEdit($member = null)
{
$autoCreatePermissions = $this->config()->get("auto_create_permissions");
if ($autoCreatePermissions) {
return array(Permission::check('EDIT_' . $this->getPermissionsSuffix(), 'any', $member));
} else {
$accessGranted = Permission::check('ADMIN', 'any', $member);
$this->extend('updateCanEdit', $accessGranted);
return $accessGranted;
}
}
/**
* @author Benjamin Blake (sitelease.ca)
*
* @param Member $member
* @return boolean
*/
public function canDelete($member = null)
{
$autoCreatePermissions = $this->config()->get("auto_create_permissions");
if ($autoCreatePermissions) {
return array(Permission::check('DELETE_' . $this->getPermissionsSuffix(), 'any', $member));
} else {
$accessGranted = Permission::check('ADMIN', 'any', $member);
$this->extend('updateCanDelete', $accessGranted);
return $accessGranted;
}
}
/**
* @author Benjamin Blake (sitelease.ca)
*
* @param Member $member
* @param array $context Additional context-specific data which might
* affect whether (or where) this object could be created.
* @return boolean
*/
public function canCreate($member = null, $context = array())
{
$autoCreatePermissions = $this->config()->get("auto_create_permissions");
if ($autoCreatePermissions) {
return array(Permission::check('CREATE_' . $this->getPermissionsSuffix(), 'any', $member));
} else {
$accessGranted = Permission::check('ADMIN', 'any', $member);
$this->extend('updateCanCreate', $accessGranted);
return $accessGranted;
}
}
/**
* @author Benjamin Blake (sitelease.ca)
*
* @param Member $member
* @return boolean
*/
public function canAccessExperimental($member = null)
{
$autoCreatePermissions = $this->config()->get("auto_create_permissions");
if ($autoCreatePermissions) {
return array(Permission::check('EXPERIMENTAL_ACCESS_' . $this->getPermissionsSuffix(), 'any', $member));
} else {
$accessGranted = Permission::check('ADMIN', 'any', $member);
$this->extend('updateCanAccessExperimental', $accessGranted);
return $accessGranted;
}
}
}
<?php
namespace Sitelease\Core\Extensions;
use SilverStripe\ORM\DataExtension;
class AccessPermissionsProviderExtension extends DataExtension
{
/**
* @var boolean
* @config
*/
private static $auto_create_permissions = true;
/**
* A string that will be appended to the action name
* for the permission code.
*
* Should start with "SL", be uppercase, and contain
* the model (DataObject) name
*
* @var boolean|string
* @config
*/
private static $permissions_suffix = false;
/**
* A string that will be used in the generation of the permission
* description for items that don't have the discription explicitly set
*
* Usually will be set to a fully lowercase version of your object name.
*
* Example: A DataObject called "Products" you might use "products"
*
* @var boolean|string
* @config
*/
private static $permissions_descriptor = false;
/**
* A string that will be used as the heading that all of the
* permission checkboxes will be displayed under (on permissions tabs)
*
* Usually set to a human readable version of the DataObject name,
* followed by the name of the composer vendor in brackets.
*
* Example: For a DataObject called "Products" in a module
* created by the "Sitelease" vendor you would use "Products (Sitelease)"
*
* @var boolean|string
* @config
*/
private static $permissions_category = false;
/**
* An array that contains a key value pair for each action that you
* want the providePermissions() method to auto create for this DataObject
*
* The item key must contain the action/permission name while the key can
* either contain a custom description (that will be displayed on permission
* tabs) or false.
*
* If you leave the item value empty (or set it to false) a description will be
* generated for you.
*
* @var boolean|array
* @config
*/
private static $permissions_config_array = array(
'view' => "",
'edit' => "",
'delete' => "",
'create' => "",
"experimental-access" => "Access Experimental Features - Allow user to use experimental fields and settings"
);
/**
* A variable that will be updated and ultimatly returned
* by the providePermissions() method
*
* You can manually add extra action permissions to this
* DataObject by adding items to this array using the following syntax:
* ```
* "ACTIONNAME_SLSUFFIX" => array(
* "name" => "Action Name - Permission description",
* "help" => "Permission Code: ACTIONNAME_SLSUFFIX",
* "category" => "Category Title (Sitelease)",
* "sort" => 99
* )
*```
* @var array
*/
public $permissionsArray = array();
}
---
Name: coreconfig
---
# Add relationship abstraction methods to the DataObject via a extension
SilverStripe\ORM\DataObject:
extensions:
- Sitelease\Core\Extensions\AccessPermissionsProviderExtension
<?php
use SilverStripe\ORM\DataObject;
use SilverStripe\Security\PermissionProvider;
use Sitelease\Core\Traits\AccessPermissionsProvider;
class ExampleModel extends DataObject implements PermissionProvider
{
// Create model level permissions for this class
use AccessPermissionsProvider;
private static $table_name = 'ExampleModel';
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment