Non Database Permissions (Plus CLI Generation!)

Photo by Rubén Bagüés

Keeping permissions stored in the database is a common way to deal with them, but considering your application is usually the decider of what permissions should be available is it really the best approach?

For example, in most cases, a permission might work the following way:

- A user visits a certain page
- A button on the page requires the permission “view_button”
- We check if the user either has that permission directly (perhaps in a table that looks like user_permissions), or if the user is linked to a “role” that is linked to that permission
- We allow or deny the display or action based on the result

So far no real issues but behind the scenes there are a few ways the permission may be added to the user. We can assume an administrator can login and toggle the “view_button” permission on or off for this role/user. Because of this we may have a “permissions” table, where we store all the available application permissions that can be attached.

The problem with this method is we’re depending on the database for something that should be relatively static to our application. When we add new features to our application we need to make sure a new table row is added for this permission, using seeders or some other method to make sure the fields are populated in each instance of our app. It can be a bit of a pain to manage, which is why for a few of my projects I’ve started thinking of permissions as file based classes that can then be referenced in a relationship by the roles/users table. Of course this won’t work in all situations, but I think it’s a pretty clean solution and ends up being much easier to manage, take a look!

Getting setup

(Note, I’m using Laravel in this example but there’s no reason this concept wouldn’t apply to other frameworks as well)

To start, I’m creating a directory within my /app folder called “Permissions” to store all this fun stuff. I’ll most likely be extracting this out later as a package but for now it’s a quick way to mess around with how I want this to flow.

Within “Permissions”, I’m going to create a BaseClass that each Permission will extend, we can go ahead and call it “BasePermission.php” and it would look something like so:

<?php
namespace App\Permissions;
abstract class BasePermission{
 protected $id;
protected $label;
}

Pretty basic so far, I only added the protected $label/$id variables to give us a way to name or set a label for each permission (ie for when an admin is viewing it).

For each individual permission I prefer to keep these in their own sub-directory. I’ll make a folder called “Available” to keep each of these permissions. I want each permission to be as easy to create as possible, so inside of app/Permissions/Available, I will create our first permission:

<?php
namespace App\Permissions\Available;
use App\Permissions\BasePermission;
class ManageUsers extends BasePermission {
}

Now, you’ll notice I didn’t fill in our $label variable in this class, I’ll get to that in a minute. What I want to do first though is setup a way for each permission to be obtainable.

Imagine you are setting up the administration page where we need to list out all permissions, and let the admin setup which permission each user has. Maybe we want to call a class like PermissionRepository::all() to simply return an array or collection of each Permission class:

<?php
namespace App\Permissions;
class PermissionRepository{
public static function all(){
$permissions = collect();
$permissions->push(App\Permissions\Available\ManageUsers::class);
return $permissions;
}
}

Simple enough, but when we need to add a permission a few weeks from now it’s unlikely we’ll remember where to add new permissions. Maybe this would work better in a config file? We could add permission classes to our configuration (similar to how we register new providers).

In your /config directory, create a new file called: “permissions.php” with the following:

<?php
return [
'available' => [
App\Permissions\Available\ManageUsers::class,
],
];

I feel like this is a lot nicer, so we can just add new permissions to our app configuration as they come! (Further along I’ll speed this process up even more).

Let’s go back to our PermissionRepository and change that “all()” method, we can still use the repository which will just pull from our config:

<?php
namespace App\Permissions;
class PermissionRepository{
 public static function all(){
$permissions = collect();
$permissionClasses = config(‘permissions.available’);
if(is_array($permissionClasses)){
foreach($permissionClasses as $permissionClass){
$object = new $permissionClass;
$permissions->push($object);
}
}
return $permissions;
}
}

So while I’m sure I’ll want to go back to this and clean it up, we can at least see what’s going on. We pull our permissions from the config file, loop though each and push new instances of each permission class to our collection and then return the collection.

Administering Permissions

Now if we’re trying to think of what we want the output to eventually look like, we would want the administration of these permissions to maybe look something like this:


<div class=”form-group”>
<label class=”control-label”>Permissions</label>
@foreach(\App\Permissions\PermissionRepository::get() as $permission)
<div class=”checkbox”>
<label>
<input type=”checkbox” name=”permissions[]” value=”{{ $permission->getId() }}”> {{ $permission->getLabel() }}
</label>
</div>
@endforeach
</div>

We need some way to show the label of each class, and we also need an Id to identify this class when it gets stored in something like a “user_permissions” table.

Of course we could set these up manually for each permission, but that’s even more work every time we create a permission. Instead, I want to simply determine the label and id based on the class name of the permission. In cases where the name just doesn’t work, I should still be able to override the id or label just by updating the classes $id or $label property.

After updating our BasePermission class, I end up with the following:

<?php
namespace App\Permissions;
abstract class BasePermission{
protected $id;
protected $label;
public function getLabel(){
if($this->label){
return $this->label;
}
return $this->labelFromClassName();
}
public function getId(){
if($this->id){
return $this->id;
}
return $this->idFromClassName();
}
private function idFromClassName(){
$reflect = new \ReflectionClass(get_class($this));
$name = $reflect->getShortName();
return $name;
}
private function labelFromClassName(){
$reflect = new \ReflectionClass(get_class($this));
$name = $reflect->getShortName();
$name = $this->formatClassNameToLabel($name);
return $name;
}
private function formatClassNameToLabel($className){
$label = $className;
$label = $this->formatUppercaseToSpaceUppercase($label);
$label = ucwords($label);
return $label;
}
private function formatUppercaseToSpaceUppercase($string){
$pieces = preg_split('/(?=[A-Z])/',$string);
return implode('',$pieces);
}
}

We’ve got our two public methods here for obtaining the value and id. With both of these we use reflection to get the name of the current permission class and grab it’s name. With the label we do an additional step to add spaces in between each capital letter.

Using our ManageUsers permission we would end up with the following:

$permission->getId(); // “ManageUsers”
$permission->getValue(); // “Manage Users”

It’s not sexy, and those formatter methods should certainly be moved to another class but it’s working as we would expect and we can refactor all the yuck out later.

Setup your relationship in your model like you would normally, where something like $user->permissions would return a collection of all the permission ids (strings) that are saved to that model.

Verifying Permissions

There are many different ways you can continue handling these permissions from here, it just depends on how you’d like to actually check if this permission is set for the user.

I think it would be pretty neat to do something like:

if(\Auth::user()->hasPermissionId(\App\Permissions\Available\ManageUsers::id())){
//Do a thing
}

Since we’re pointing directly to the class, I never have to wonder if I named my permission correctly. If there is any issue, my IDE will toss an error at me. I can also easily jump to any places in my code where this permission is in use.

It’s perhaps a bit verbose, maybe later if we wanted to we could create a helper function like

if(permission(\App\Permissions\Available\ManageUsers::id())){
//Do a thing
}

But we can hold off on that for now.

So to get this working, the first thing you’ll see is we need to make a new static method on the BasePermission class to return the generated id of the permission:

public static function id(){
$instance = new static;
return $instance->getId();
}

Then (assuming permissions are tied directly to users) our User class might just have something like below to give us our relationship and a few helpers to check if a permission exists on the model:

public function permissions(){
return $this->hasMany(\App\UserPermission::class, ‘role_id’);
}
public function permissionById($permissionId){
return $this->permissions()->where(‘permission’,$permissionId)->first();
}
public function hasPermissionId($permissionId){
if($this->permissionById($permissionId)){
return true;
}
return false;
}

Bonus! Generate permissions via the command line

I kept repeating how simple I wanted it to be to make your own permissions. So why not make a generator for it?

Let’s make the command “php artisan make:permission ManageAccounts” do two things:

  • Generate a new class file and store it in our Permissions/Active directory
  • Update our permissions.php config with the new

To start, make the new command by typing:

php artisan make:command MakePermission

and then open up the newly generated app/Console/Commands/MakePermission.php file and add these lines to the “handle()” method:

$creator = new \App\Permissions\Creator\PermissionCreator();
$creator->setInterface($this);
$creator->setClassName($this->argument('className'));
$creator->create();

Rather than add all the creation code in this command, I’d prefer to pass it off to it’s own class in case I plan to extend it or use it elsewhere later. So we’ll need to create this new “PermissionCreator” class.

As you can see I’m also setting 2 properties:

  • The first is a reference to the command itself, this will let us still send output notifications/errors as we move along the process
  • Next is simply the class name parameter “MakePermission”

Then go ahead and make our /app/Permissions/Creator/PermissionCreator.php file. Here’s the full code:

<?php
namespace App\Permissions\Creator;
class PermissionCreator{
protected $interface;
protected $className;
public function setInterface($interface){
$this->interface = $interface;
}
public function setClassName($className){
$this->className = $className;
}
public function createPermissionsFile(){
$this->interface->info('Creating Permissions File');
$putFilePath = app_path().'/Permissions/Available/'.$this->className.'.php';
if(file_exists($putFilePath)){
$this->interface->error('File Already Exists: '.$putFilePath);
}else{
$templateFilePath = app_path().'/Permissions/Creator/PermissionClassTemplate.php.txt';
$templateFile = file_get_contents($templateFilePath);
$fileContents = str_replace('[CLASSNAME]',$this->className,$templateFile);
\File::put($putFilePath,$fileContents);
}
}
public function addPermissionToPermissionsConfig(){
$this->interface->info('Adding Permission To Permissions Config');
$permissionsFilePath = config_path().'/permissions.php';
$permissionsContents = file_get_contents($permissionsFilePath);
$lineSearch = "'available' => [".PHP_EOL;
$newLine = 'App\Permissions\Available\\'.$this->className.'::class,'.PHP_EOL;
if(strstr($permissionsContents,$newLine)){
$this->interface->error('Permission already in Config');
}else{
$lineReplace = $lineSearch.$newLine;
$permissionsContents = str_replace($lineSearch,$lineReplace,$permissionsContents);
\File::put($permissionsFilePath,$permissionsContents);
}
}
public function create(){
$this->createPermissionsFile();
$this->addPermissionToPermissionsConfig();
$this->interface->info('Done!');
}
}

So when calling the create method we just run through two steps:

  • Create the permissions file: Using the class name property we generate a file based on a template (shown below), and save this to our Permissions/Available directory assuming it does not exist
  • Add this class to the permissions config file, assuming we do not see it there already.

You’ll see in the CreatePermissionsFile method we look for a template file which is the template for our class, add this code to a new file located at /Permissions/Creator/PermissionClassTemplate.php.txt

<?php

namespace App\Permissions\Available;

use App\Permissions\BasePermission;

class [CLASSNAME] extends BasePermission {

}

Easy as pie, after running this command you can create a new permission and then use it right away.

I’ve still only started using this process, so I’d love to hear some feedback or how you might handle permissions differently 🤘