Skip to content

RPillz/laravel-feature-access

Repository files navigation

Feature access for Laravel apps

Latest Version on Packagist GitHub Tests Action Status GitHub Code Style Action Status Total Downloads

Add Plans (eg: Basic, Standard, Pro) and Feature restrictions (eg: Can Make 3 Pages, Can Upload Video) to your Laravel app.

Plans and corresponding features are hard-coded in a config file, but these defaults may be overridden with a database entry for a specific user. (ie: Your special friend who wants all the features, but, like, for free.)

Feature access can be assigned to any model (eg: User, Team) via a trait, which adds properties to use in your app logic, such as $user->canViewFeature('pictures-of-my-dog')

Installation

You can install the package via composer:

composer require rpillz/laravel-feature-access

Publish the config file. This is where your features and tiers are defined.

php artisan vendor:publish --tag=feature-access-config

Publish and run migrations. This will create a 'features' table for storing access information attached to your models.

php artisan vendor:publish --tag=feature-access-migrations
php artisan migrate

Basic Usage

1. Define Features (and Tiers) in the Config

Start by defining your app features in the feature-access.php config file.

This is an example of a feature mapped out in the config file:

return [

    'sample-feature' => [ // begin with the feature key name. This is used to request permissions.
        'name' => 'Test Feature', // Human readable name
        'read' => true, // Can read/view items in this feature
        'update' => false, // cannot edit/change/update items
        'create' => false, // cannot create new items
        'destroy' => false, // cannot destroy/delete items
        'limit' => 3, // limit on number of items allowed
        'levels' => [ // Override the base feature permissions with levels or packages (eg: basic, pro, plus)
            'pro' => [ // level/package key
                'name' => 'Extra Stuff!', // human readable name
                'update' => true, // pro level can edit/change/update items
                'create' => true, // pro level can create items
                'limit' => 5, // limit is increased
                // other permissions will default to base feature definition (above)
            ],
        ]
    ],

];

The base level of this feature will be the permission used for all users and guests unless they have been explicitly granted an upgraded access level, or override.

2. Add Trait To Model

In most cases, you would add this trait to your User model, which would allow that user to access (or not access) features.

use RPillz\FeatureAccess\Traits\HasFeatureAccess;

class User extends Authenticatable
{

    use HasFeatureAccess;

    ...

3. Add Permission Checks to your App Logic

An example, in a blade template, of adding a button to create a new item, only if the current user is allowed to create on the feature sample-feature.

@if(Auth::user()->canCreateFeature('sample-feature'))
    <button>Add New Sample Item</button>
@endif

4. Grant Higher Access To Users

You can set permission acces for your User (or any model) with the setFeatureAccess() method.

$user->setFeatureAccess('sample-feature', 'basic'); // give this user 'basic' level access to 'sample-feature' (which does not exist)

$user->setFeatureAccess('sample-feature', 'pro'); // give this user 'pro' level access to 'sample-feature'

$user->setFeatureAccess('sample-feature', 'pro', [ 'update' => false ]); // give this user 'pro' level access to 'sample-feature', but override the default setting to allow edits just for this user.

In the first example above, the user is granted basic level access to sample-feature. However, there is no basic level defined in the feature config file. Their permissions for sample-feature will default to the basic settings. These same default settings would be used if no level has been explicitly set for a user.

More Usage Options

Using Team Model

Your app may make use of Teams (a la Jetstream) in which case you may want to have the Team model using this trait, and accessing user permissions via their team.

Add the HasFeatureAccess trait to your Team model. Now any user on that team will have access to the same features, through the team.

Auth::user()->currentTeam->canReadFeature('maple-syrup');

This trait can be applied to any Model, or even multiple models if you want to be able apply permissions to both Users and Teams individually. (There is no built-in permission inheritance in such a case, just one or the other.)

Using Other Models

You can grant feature access to any model using the HasFeatureAccess trait. In most cases, that model would somehow be associated with the authenticated user. However, it could be adapted to other uses:

  • On a Tenant or Domain model, which would grant features depending upon the domain through which your app was being accessed.
  • On a particular Page or Post to change what features are displayed in the layout.

You can be using this on multiple models within the same app, so you may bend it to your will!

Methods to Check Permission On Your Model

$user->canUseFeature('feature-name', 'permission-requested');

// alias functions

$user->canCreateFeature('sample-feature'); // permission-requested = create

$user->canReadFeature('sample-feature'); // permission-requested = read
$user->canViewFeature('sample-feature'); // permission-requested = read

$user->canUpdateFeature('sample-feature'); // permission-requested = update
$user->canEditFeature('sample-feature'); // permission-requested = update

$user->canDestroyFeature('sample-feature'); // permission-requested = destroy
$user->canDeleteFeature('sample-feature'); // permission-requested = destroy

$user->withinFeatureLimit('sample-feature', $user->items->count()); // compares against feature limit
$user->withinFeatureLimit('sample-feature', $user->items->count(), 1); // is there room to add 1 more item?

// or get all the permission data for your model

$user->getFeatureData('sample-feature'); // return array

Using The Facade

You can use the Facade methods to check permissions. However, this only works for the User and/or Team models. The upside is these will work whether or not a visitor is signed in to an account, and returns base access for guests.

FeatureAccess::userCan(string $feature_name, string $permission); // check for permission to 'create', 'read', 'update', or 'destroy'

// shortcut aliases

FeatureAccess::userCanCreate('sample-feature'); // returns boolean true/false

FeatureAccess::userCanRead('sample-feature');
FeatureAccess::userCanView('sample-feature'); // alias of 'read' permission

FeatureAccess::userCanUpdate('sample-feature');
FeatureAccess::userCanEdit('sample-feature'); // alias of 'update' permission

FeatureAccess::userCanDestroy('sample-feature');
FeatureAccess::userCanDelete('sample-feature'); // alias of 'destroy' permission

// it also works for Teams! (a la Jetstream)

FeatureAccess::teamCan(string $feature_name, string $permission);

FeatureAccess::teamCanCreate('sample-feature'); // and all the other aliases as per above

If you need to get the full array of current permission access, you can use this:

FeatureAccess::userFeature('sample-feature'); // returns array
FeatureAccess::teamFeature('sample-feature'); // returns array

Integrating with Subscriptions (from Cashier, SPark, etc.)

Rather than creating/updating duplicate database rows to change a user's feature access level, there is a feature to dynamically check the active subscription level for a user, and get the feature permissions accordingly.

This feature must be activated in the feature-access.php config file.

// Check for active subscription to grant access
'subscriptions' => true,

Then you need to add a function on your User model (or Team model, or whatever is using the HasFeatureAccess trait) which will return a name corresponding to the appropriate level, as defined in your feature config.

public function getFeatureSubscriptionLevel(string $feature_name = null): ?string
    {
        // this logic needs to be customized according to your subscription set-up
        if ($subscription = $this->subscriptions->active()->first()){
            return $subscription->name; // ie: "pro"
        }

        return null;
    }

Once you have this set, the subscription will be consulted when returning a User's current feature access.

$free_user->canCreateFeature('sample-feature'); //false

$pro_user->canCreateFeature('sample-feature'); //true

In order of priority, the user's feature level is first determined by anything saved in the database (by setFeatureAccesss() for example), then checking the subscription level (if activated) and finally defaulting to the config file. This means it is possible to upgrade access above and beyond a paid subscription for your VIP users on a case-by-case basis. (technically, you could also downgrade them, you monster!)

Here's an example of the function to go along with Laravel Spark subscriptions.

public function getFeatureSubscriptionLevel(string $feature_name = null): ?string
{
    // If you're using Laravel Spark with this model as a Billable.
    // Note: The plan name from Spark is likely capitalized. Make sure it matches your feature-access config level names exactly.
    return $this->sparkPlan()->name ?: null;

}

Notice, also, that this function accepts an optional $feature_name argument, which allows you to be more granular in your subscription and logic if needed. (ie: if the subscription to access Pages is different from the subscription to access Blogs)

Super-Admin Permission

If the current model property matches anything in the array, all permission tests will return true. They get to do everything.

return [

    // grant all access to models which match...
    'super_admin_property' => 'email', // this property...
    'super_admin_access' => [ // to any of these.
        'admin@example.com',
    ],

]

Testing

composer test

Changelog

Please see CHANGELOG for more information on what has changed recently.

Contributing

Please see CONTRIBUTING for details.

Security Vulnerabilities

Please review our security policy on how to report security vulnerabilities.

Credits

License

The MIT License (MIT). Please see License File for more information.

About

Laravel package to add tiered plans and feature access for users and teams.

Resources

License

Security policy

Stars

Watchers

Forks

Sponsor this project

Packages

No packages published

Languages