Latest commit

History

26 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

NOTE

Ensure you don't use the RoutAwareModel for your Authenticatable User model - it becomes a circular operation when applying the global scopes and you'll get a bad gateway (502) error.

What's it all about?

The purpose of the project was to create a syntactically simple way to implement context-based user access control. What does that mean, exactly? Good question...

Context-based access control

I wanted to start with the idea that I could use a really straight-forward syntax for my "things" (whatever they might be). The first concept I came up with was Check::can('post.edit'). Because I was a fan of naming my routes, this made good sense from a flow point-of-view. Because I have my routes named, I figured I'd be able to implement middleware that would also leverage the access control system.

Adding context to the access control wasn't a trivial task. Each model will have its own context. Say in a Post model, "owning" a post might mean that there is a user_id field on the Post that is equal to the current user, but in a User model, "owning" might mean that users are in the same company as you. So, how do I have a simple syntax for implementing and checking permissions, but also giving context when the need arises?

Using the Jeffrey Way school of thought, I started with how I wanted to define things... I really wanted my Role classes to be so simple it's almost stupid.

$permissions = [
'post' => [
'index', 'create', 'store', 'view', 'edit:own', 'update:own',
]
];

After starting with those two ideas, I set to work and actually managed to implement them. What we have is, I think, a simple, fluent way of managing user access.

Route Aware Models

If you have, say, a listing page for your users where they can see all posts, but can only edit their own, you'd simply have to do the following.

Register the service provider config/app.php

'providers' => [
...
Jellis\Check\Providers\CheckServiceProvider::class,
...
],

Register the facade in config/app.php

'aliases' => [
...
'Check' => Jellis\Check\Facades\Check::class,
...
],

Name the route and assign the middleware

Route::get('post', ['uses' => 'PostController@index', 'as' => 'post.index', 'middleware' => 'check']);

Create a role (assuming "member" for this user)

<?phpnamespaceApp\Roles;
useJellis\Check\Roles\Base;
class Member extends Base {
protected$permissions = [
'post' => [
'index', 'view', 'create', 'store', 'view', 'edit:own', 'update:own',
],
];
}

Configure the model to do its thing

namespaceApp\Models;
useJellis\Check\RouteAwareModel;
class Post extends RouteAwareModel
{
protected$table = 'posts';
.../** * This is to check against a given model */
public function allowOwnOnly()
{
return$this->user_id == \Auth::id();
}
/** * This is to restrict things coming out of the database */publicfunctionrestrictOwnOnly(Builder$builder)
{
$builder->where('user_id', Auth::id());
}
}

You need to implement the getRole() method on the user model

class User extends Model {
...
publicfunctiongetRole()
{
return$this->role; // Or however you determine what a user's role is right now
}
...
}

Register the middleware in Kernel.php

protected$routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'check' => \Jellis\Check\Middleware\Checker::class,
];

Retrieve some records in your controller

class PostController extends Controller {
publicfunctionindex()
{
// You could check stuff here if you need to$myThing = Check::can('my.thing');
// Or you can do a contextual check, say, on a post$post = Post::find(1);
if (Check::can('post.edit', $post)) {
// Do some thing
}
// In this instance, let's pass it to the view$posts = Post::all();
returnview('post.index', compact('posts'));
}
}

And in the view you can do things like

@foreach($posts as $post)
<p>{{ $post->title }}@check('post.edit', $post)<strong>You can edit</strong>@endcheck</p>
@endforeach

So you're a super admin??

Who really wants to be putting all of those routes in for super admin? Not me.

When defining your SuperAdmin role, just override the can() method

class SuperAdmin extends Base
{
/** * Can do all the things all the time * * @param string $action * @param Model $model * @return bool */publicfunctioncheck($action, Model$model = null)
{
returntrue;
}
}

TODO

  1. Implement ability to define a permission for multiple contexts edit:own|company
  2. Implement multiple contexts on the scope for checking access rights
  3. Implement multiple contexts on the scope for pulling records from the model
  4. Allow ability to wildcard a thing post.*, whilst still retaining scope ability post.*:own

About

A very easy-to-implement user access control package designed for use with Laravel and Eloquent

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Latest commit

History

26 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

NOTE

Ensure you don't use the RoutAwareModel for your Authenticatable User model - it becomes a circular operation when applying the global scopes and you'll get a bad gateway (502) error.

What's it all about?

The purpose of the project was to create a syntactically simple way to implement context-based user access control. What does that mean, exactly? Good question...

Context-based access control

I wanted to start with the idea that I could use a really straight-forward syntax for my "things" (whatever they might be). The first concept I came up with was Check::can('post.edit'). Because I was a fan of naming my routes, this made good sense from a flow point-of-view. Because I have my routes named, I figured I'd be able to implement middleware that would also leverage the access control system.

Adding context to the access control wasn't a trivial task. Each model will have its own context. Say in a Post model, "owning" a post might mean that there is a user_id field on the Post that is equal to the current user, but in a User model, "owning" might mean that users are in the same company as you. So, how do I have a simple syntax for implementing and checking permissions, but also giving context when the need arises?

Using the Jeffrey Way school of thought, I started with how I wanted to define things... I really wanted my Role classes to be so simple it's almost stupid.

$permissions = [
'post' => [
'index', 'create', 'store', 'view', 'edit:own', 'update:own',
]
];

After starting with those two ideas, I set to work and actually managed to implement them. What we have is, I think, a simple, fluent way of managing user access.

Route Aware Models

If you have, say, a listing page for your users where they can see all posts, but can only edit their own, you'd simply have to do the following.

Register the service provider config/app.php

'providers' => [
...
Jellis\Check\Providers\CheckServiceProvider::class,
...
],

Register the facade in config/app.php

'aliases' => [
...
'Check' => Jellis\Check\Facades\Check::class,
...
],

Name the route and assign the middleware

Route::get('post', ['uses' => 'PostController@index', 'as' => 'post.index', 'middleware' => 'check']);

Create a role (assuming "member" for this user)

<?phpnamespaceApp\Roles;
useJellis\Check\Roles\Base;
class Member extends Base {
protected$permissions = [
'post' => [
'index', 'view', 'create', 'store', 'view', 'edit:own', 'update:own',
],
];
}

Configure the model to do its thing

namespaceApp\Models;
useJellis\Check\RouteAwareModel;
class Post extends RouteAwareModel
{
protected$table = 'posts';
.../** * This is to check against a given model */
public function allowOwnOnly()
{
return$this->user_id == \Auth::id();
}
/** * This is to restrict things coming out of the database */publicfunctionrestrictOwnOnly(Builder$builder)
{
$builder->where('user_id', Auth::id());
}
}

You need to implement the getRole() method on the user model

class User extends Model {
...
publicfunctiongetRole()
{
return$this->role; // Or however you determine what a user's role is right now
}
...
}

Register the middleware in Kernel.php

protected$routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'check' => \Jellis\Check\Middleware\Checker::class,
];

Retrieve some records in your controller

class PostController extends Controller {
publicfunctionindex()
{
// You could check stuff here if you need to$myThing = Check::can('my.thing');
// Or you can do a contextual check, say, on a post$post = Post::find(1);
if (Check::can('post.edit', $post)) {
// Do some thing
}
// In this instance, let's pass it to the view$posts = Post::all();
returnview('post.index', compact('posts'));
}
}

And in the view you can do things like

@foreach($posts as $post)
<p>{{ $post->title }}@check('post.edit', $post)<strong>You can edit</strong>@endcheck</p>
@endforeach

So you're a super admin??

Who really wants to be putting all of those routes in for super admin? Not me.

When defining your SuperAdmin role, just override the can() method

class SuperAdmin extends Base
{
/** * Can do all the things all the time * * @param string $action * @param Model $model * @return bool */publicfunctioncheck($action, Model$model = null)
{
returntrue;
}
}

TODO

  1. Implement ability to define a permission for multiple contexts edit:own|company
  2. Implement multiple contexts on the scope for checking access rights
  3. Implement multiple contexts on the scope for pulling records from the model
  4. Allow ability to wildcard a thing post.*, whilst still retaining scope ability post.*:own

About

A very easy-to-implement user access control package designed for use with Laravel and Eloquent

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

26 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

NOTE

Ensure you don't use the RoutAwareModel for your Authenticatable User model - it becomes a circular operation when applying the global scopes and you'll get a bad gateway (502) error.

What's it all about?

The purpose of the project was to create a syntactically simple way to implement context-based user access control. What does that mean, exactly? Good question...

Context-based access control

I wanted to start with the idea that I could use a really straight-forward syntax for my "things" (whatever they might be). The first concept I came up with was Check::can('post.edit'). Because I was a fan of naming my routes, this made good sense from a flow point-of-view. Because I have my routes named, I figured I'd be able to implement middleware that would also leverage the access control system.

Adding context to the access control wasn't a trivial task. Each model will have its own context. Say in a Post model, "owning" a post might mean that there is a user_id field on the Post that is equal to the current user, but in a User model, "owning" might mean that users are in the same company as you. So, how do I have a simple syntax for implementing and checking permissions, but also giving context when the need arises?

Using the Jeffrey Way school of thought, I started with how I wanted to define things... I really wanted my Role classes to be so simple it's almost stupid.

$permissions = [
'post' => [
'index', 'create', 'store', 'view', 'edit:own', 'update:own',
]
];

After starting with those two ideas, I set to work and actually managed to implement them. What we have is, I think, a simple, fluent way of managing user access.

Route Aware Models

If you have, say, a listing page for your users where they can see all posts, but can only edit their own, you'd simply have to do the following.

Register the service provider config/app.php

'providers' => [
...
Jellis\Check\Providers\CheckServiceProvider::class,
...
],

Register the facade in config/app.php

'aliases' => [
...
'Check' => Jellis\Check\Facades\Check::class,
...
],

Name the route and assign the middleware

Route::get('post', ['uses' => 'PostController@index', 'as' => 'post.index', 'middleware' => 'check']);

Create a role (assuming "member" for this user)

<?phpnamespaceApp\Roles;
useJellis\Check\Roles\Base;
class Member extends Base {
protected$permissions = [
'post' => [
'index', 'view', 'create', 'store', 'view', 'edit:own', 'update:own',
],
];
}

Configure the model to do its thing

namespaceApp\Models;
useJellis\Check\RouteAwareModel;
class Post extends RouteAwareModel
{
protected$table = 'posts';
.../** * This is to check against a given model */
public function allowOwnOnly()
{
return$this->user_id == \Auth::id();
}
/** * This is to restrict things coming out of the database */publicfunctionrestrictOwnOnly(Builder$builder)
{
$builder->where('user_id', Auth::id());
}
}

You need to implement the getRole() method on the user model

class User extends Model {
...
publicfunctiongetRole()
{
return$this->role; // Or however you determine what a user's role is right now
}
...
}

Register the middleware in Kernel.php

protected$routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'check' => \Jellis\Check\Middleware\Checker::class,
];

Retrieve some records in your controller

class PostController extends Controller {
publicfunctionindex()
{
// You could check stuff here if you need to$myThing = Check::can('my.thing');
// Or you can do a contextual check, say, on a post$post = Post::find(1);
if (Check::can('post.edit', $post)) {
// Do some thing
}
// In this instance, let's pass it to the view$posts = Post::all();
returnview('post.index', compact('posts'));
}
}

And in the view you can do things like

@foreach($posts as $post)
<p>{{ $post->title }}@check('post.edit', $post)<strong>You can edit</strong>@endcheck</p>
@endforeach

So you're a super admin??

Who really wants to be putting all of those routes in for super admin? Not me.

When defining your SuperAdmin role, just override the can() method

class SuperAdmin extends Base
{
/** * Can do all the things all the time * * @param string $action * @param Model $model * @return bool */publicfunctioncheck($action, Model$model = null)
{
returntrue;
}
}

TODO

  1. Implement ability to define a permission for multiple contexts edit:own|company
  2. Implement multiple contexts on the scope for checking access rights
  3. Implement multiple contexts on the scope for pulling records from the model
  4. Allow ability to wildcard a thing post.*, whilst still retaining scope ability post.*:own

About

A very easy-to-implement user access control package designed for use with Laravel and Eloquent

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

26 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

NOTE

Ensure you don't use the RoutAwareModel for your Authenticatable User model - it becomes a circular operation when applying the global scopes and you'll get a bad gateway (502) error.

What's it all about?

The purpose of the project was to create a syntactically simple way to implement context-based user access control. What does that mean, exactly? Good question...

Context-based access control

I wanted to start with the idea that I could use a really straight-forward syntax for my "things" (whatever they might be). The first concept I came up with was Check::can('post.edit'). Because I was a fan of naming my routes, this made good sense from a flow point-of-view. Because I have my routes named, I figured I'd be able to implement middleware that would also leverage the access control system.

Adding context to the access control wasn't a trivial task. Each model will have its own context. Say in a Post model, "owning" a post might mean that there is a user_id field on the Post that is equal to the current user, but in a User model, "owning" might mean that users are in the same company as you. So, how do I have a simple syntax for implementing and checking permissions, but also giving context when the need arises?

Using the Jeffrey Way school of thought, I started with how I wanted to define things... I really wanted my Role classes to be so simple it's almost stupid.

$permissions = [
'post' => [
'index', 'create', 'store', 'view', 'edit:own', 'update:own',
]
];

After starting with those two ideas, I set to work and actually managed to implement them. What we have is, I think, a simple, fluent way of managing user access.

Route Aware Models

If you have, say, a listing page for your users where they can see all posts, but can only edit their own, you'd simply have to do the following.

Register the service provider config/app.php

'providers' => [
...
Jellis\Check\Providers\CheckServiceProvider::class,
...
],

Register the facade in config/app.php

'aliases' => [
...
'Check' => Jellis\Check\Facades\Check::class,
...
],

Name the route and assign the middleware

Route::get('post', ['uses' => 'PostController@index', 'as' => 'post.index', 'middleware' => 'check']);

Create a role (assuming "member" for this user)

<?phpnamespaceApp\Roles;
useJellis\Check\Roles\Base;
class Member extends Base {
protected$permissions = [
'post' => [
'index', 'view', 'create', 'store', 'view', 'edit:own', 'update:own',
],
];
}

Configure the model to do its thing

namespaceApp\Models;
useJellis\Check\RouteAwareModel;
class Post extends RouteAwareModel
{
protected$table = 'posts';
.../** * This is to check against a given model */
public function allowOwnOnly()
{
return$this->user_id == \Auth::id();
}
/** * This is to restrict things coming out of the database */publicfunctionrestrictOwnOnly(Builder$builder)
{
$builder->where('user_id', Auth::id());
}
}

You need to implement the getRole() method on the user model

class User extends Model {
...
publicfunctiongetRole()
{
return$this->role; // Or however you determine what a user's role is right now
}
...
}

Register the middleware in Kernel.php

protected$routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'check' => \Jellis\Check\Middleware\Checker::class,
];

Retrieve some records in your controller

class PostController extends Controller {
publicfunctionindex()
{
// You could check stuff here if you need to$myThing = Check::can('my.thing');
// Or you can do a contextual check, say, on a post$post = Post::find(1);
if (Check::can('post.edit', $post)) {
// Do some thing
}
// In this instance, let's pass it to the view$posts = Post::all();
returnview('post.index', compact('posts'));
}
}

And in the view you can do things like

@foreach($posts as $post)
<p>{{ $post->title }}@check('post.edit', $post)<strong>You can edit</strong>@endcheck</p>
@endforeach

So you're a super admin??

Who really wants to be putting all of those routes in for super admin? Not me.

When defining your SuperAdmin role, just override the can() method

class SuperAdmin extends Base
{
/** * Can do all the things all the time * * @param string $action * @param Model $model * @return bool */publicfunctioncheck($action, Model$model = null)
{
returntrue;
}
}

TODO

  1. Implement ability to define a permission for multiple contexts edit:own|company
  2. Implement multiple contexts on the scope for checking access rights
  3. Implement multiple contexts on the scope for pulling records from the model
  4. Allow ability to wildcard a thing post.*, whilst still retaining scope ability post.*:own

About

A very easy-to-implement user access control package designed for use with Laravel and Eloquent

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Latest commit

History

26 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

NOTE

Ensure you don't use the RoutAwareModel for your Authenticatable User model - it becomes a circular operation when applying the global scopes and you'll get a bad gateway (502) error.

What's it all about?

The purpose of the project was to create a syntactically simple way to implement context-based user access control. What does that mean, exactly? Good question...

Context-based access control

I wanted to start with the idea that I could use a really straight-forward syntax for my "things" (whatever they might be). The first concept I came up with was Check::can('post.edit'). Because I was a fan of naming my routes, this made good sense from a flow point-of-view. Because I have my routes named, I figured I'd be able to implement middleware that would also leverage the access control system.

Adding context to the access control wasn't a trivial task. Each model will have its own context. Say in a Post model, "owning" a post might mean that there is a user_id field on the Post that is equal to the current user, but in a User model, "owning" might mean that users are in the same company as you. So, how do I have a simple syntax for implementing and checking permissions, but also giving context when the need arises?

Using the Jeffrey Way school of thought, I started with how I wanted to define things... I really wanted my Role classes to be so simple it's almost stupid.

$permissions = [
'post' => [
'index', 'create', 'store', 'view', 'edit:own', 'update:own',
]
];

After starting with those two ideas, I set to work and actually managed to implement them. What we have is, I think, a simple, fluent way of managing user access.

Route Aware Models

If you have, say, a listing page for your users where they can see all posts, but can only edit their own, you'd simply have to do the following.

Register the service provider config/app.php

'providers' => [
...
Jellis\Check\Providers\CheckServiceProvider::class,
...
],

Register the facade in config/app.php

'aliases' => [
...
'Check' => Jellis\Check\Facades\Check::class,
...
],

Name the route and assign the middleware

Route::get('post', ['uses' => 'PostController@index', 'as' => 'post.index', 'middleware' => 'check']);

Create a role (assuming "member" for this user)

<?phpnamespaceApp\Roles;
useJellis\Check\Roles\Base;
class Member extends Base {
protected$permissions = [
'post' => [
'index', 'view', 'create', 'store', 'view', 'edit:own', 'update:own',
],
];
}

Configure the model to do its thing

namespaceApp\Models;
useJellis\Check\RouteAwareModel;
class Post extends RouteAwareModel
{
protected$table = 'posts';
.../** * This is to check against a given model */
public function allowOwnOnly()
{
return$this->user_id == \Auth::id();
}
/** * This is to restrict things coming out of the database */publicfunctionrestrictOwnOnly(Builder$builder)
{
$builder->where('user_id', Auth::id());
}
}

You need to implement the getRole() method on the user model

class User extends Model {
...
publicfunctiongetRole()
{
return$this->role; // Or however you determine what a user's role is right now
}
...
}

Register the middleware in Kernel.php

protected$routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'check' => \Jellis\Check\Middleware\Checker::class,
];

Retrieve some records in your controller

class PostController extends Controller {
publicfunctionindex()
{
// You could check stuff here if you need to$myThing = Check::can('my.thing');
// Or you can do a contextual check, say, on a post$post = Post::find(1);
if (Check::can('post.edit', $post)) {
// Do some thing
}
// In this instance, let's pass it to the view$posts = Post::all();
returnview('post.index', compact('posts'));
}
}

And in the view you can do things like

@foreach($posts as $post)
<p>{{ $post->title }}@check('post.edit', $post)<strong>You can edit</strong>@endcheck</p>
@endforeach

So you're a super admin??

Who really wants to be putting all of those routes in for super admin? Not me.

When defining your SuperAdmin role, just override the can() method

class SuperAdmin extends Base
{
/** * Can do all the things all the time * * @param string $action * @param Model $model * @return bool */publicfunctioncheck($action, Model$model = null)
{
returntrue;
}
}

TODO

  1. Implement ability to define a permission for multiple contexts edit:own|company
  2. Implement multiple contexts on the scope for checking access rights
  3. Implement multiple contexts on the scope for pulling records from the model
  4. Allow ability to wildcard a thing post.*, whilst still retaining scope ability post.*:own

About

A very easy-to-implement user access control package designed for use with Laravel and Eloquent

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

26 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

NOTE

Ensure you don't use the RoutAwareModel for your Authenticatable User model - it becomes a circular operation when applying the global scopes and you'll get a bad gateway (502) error.

What's it all about?

The purpose of the project was to create a syntactically simple way to implement context-based user access control. What does that mean, exactly? Good question...

Context-based access control

I wanted to start with the idea that I could use a really straight-forward syntax for my "things" (whatever they might be). The first concept I came up with was Check::can('post.edit'). Because I was a fan of naming my routes, this made good sense from a flow point-of-view. Because I have my routes named, I figured I'd be able to implement middleware that would also leverage the access control system.

Adding context to the access control wasn't a trivial task. Each model will have its own context. Say in a Post model, "owning" a post might mean that there is a user_id field on the Post that is equal to the current user, but in a User model, "owning" might mean that users are in the same company as you. So, how do I have a simple syntax for implementing and checking permissions, but also giving context when the need arises?

Using the Jeffrey Way school of thought, I started with how I wanted to define things... I really wanted my Role classes to be so simple it's almost stupid.

$permissions = [
'post' => [
'index', 'create', 'store', 'view', 'edit:own', 'update:own',
]
];

After starting with those two ideas, I set to work and actually managed to implement them. What we have is, I think, a simple, fluent way of managing user access.

Route Aware Models

If you have, say, a listing page for your users where they can see all posts, but can only edit their own, you'd simply have to do the following.

Register the service provider config/app.php

'providers' => [
...
Jellis\Check\Providers\CheckServiceProvider::class,
...
],

Register the facade in config/app.php

'aliases' => [
...
'Check' => Jellis\Check\Facades\Check::class,
...
],

Name the route and assign the middleware

Route::get('post', ['uses' => 'PostController@index', 'as' => 'post.index', 'middleware' => 'check']);

Create a role (assuming "member" for this user)

<?phpnamespaceApp\Roles;
useJellis\Check\Roles\Base;
class Member extends Base {
protected$permissions = [
'post' => [
'index', 'view', 'create', 'store', 'view', 'edit:own', 'update:own',
],
];
}

Configure the model to do its thing

namespaceApp\Models;
useJellis\Check\RouteAwareModel;
class Post extends RouteAwareModel
{
protected$table = 'posts';
.../** * This is to check against a given model */
public function allowOwnOnly()
{
return$this->user_id == \Auth::id();
}
/** * This is to restrict things coming out of the database */publicfunctionrestrictOwnOnly(Builder$builder)
{
$builder->where('user_id', Auth::id());
}
}

You need to implement the getRole() method on the user model

class User extends Model {
...
publicfunctiongetRole()
{
return$this->role; // Or however you determine what a user's role is right now
}
...
}

Register the middleware in Kernel.php

protected$routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'check' => \Jellis\Check\Middleware\Checker::class,
];

Retrieve some records in your controller

class PostController extends Controller {
publicfunctionindex()
{
// You could check stuff here if you need to$myThing = Check::can('my.thing');
// Or you can do a contextual check, say, on a post$post = Post::find(1);
if (Check::can('post.edit', $post)) {
// Do some thing
}
// In this instance, let's pass it to the view$posts = Post::all();
returnview('post.index', compact('posts'));
}
}

And in the view you can do things like

@foreach($posts as $post)
<p>{{ $post->title }}@check('post.edit', $post)<strong>You can edit</strong>@endcheck</p>
@endforeach

So you're a super admin??

Who really wants to be putting all of those routes in for super admin? Not me.

When defining your SuperAdmin role, just override the can() method

class SuperAdmin extends Base
{
/** * Can do all the things all the time * * @param string $action * @param Model $model * @return bool */publicfunctioncheck($action, Model$model = null)
{
returntrue;
}
}

TODO

  1. Implement ability to define a permission for multiple contexts edit:own|company
  2. Implement multiple contexts on the scope for checking access rights
  3. Implement multiple contexts on the scope for pulling records from the model
  4. Allow ability to wildcard a thing post.*, whilst still retaining scope ability post.*:own

About

A very easy-to-implement user access control package designed for use with Laravel and Eloquent

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

26 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

NOTE

Ensure you don't use the RoutAwareModel for your Authenticatable User model - it becomes a circular operation when applying the global scopes and you'll get a bad gateway (502) error.

What's it all about?

The purpose of the project was to create a syntactically simple way to implement context-based user access control. What does that mean, exactly? Good question...

Context-based access control

I wanted to start with the idea that I could use a really straight-forward syntax for my "things" (whatever they might be). The first concept I came up with was Check::can('post.edit'). Because I was a fan of naming my routes, this made good sense from a flow point-of-view. Because I have my routes named, I figured I'd be able to implement middleware that would also leverage the access control system.

Adding context to the access control wasn't a trivial task. Each model will have its own context. Say in a Post model, "owning" a post might mean that there is a user_id field on the Post that is equal to the current user, but in a User model, "owning" might mean that users are in the same company as you. So, how do I have a simple syntax for implementing and checking permissions, but also giving context when the need arises?

Using the Jeffrey Way school of thought, I started with how I wanted to define things... I really wanted my Role classes to be so simple it's almost stupid.

$permissions = [
'post' => [
'index', 'create', 'store', 'view', 'edit:own', 'update:own',
]
];

After starting with those two ideas, I set to work and actually managed to implement them. What we have is, I think, a simple, fluent way of managing user access.

Route Aware Models

If you have, say, a listing page for your users where they can see all posts, but can only edit their own, you'd simply have to do the following.

Register the service provider config/app.php

'providers' => [
...
Jellis\Check\Providers\CheckServiceProvider::class,
...
],

Register the facade in config/app.php

'aliases' => [
...
'Check' => Jellis\Check\Facades\Check::class,
...
],

Name the route and assign the middleware

Route::get('post', ['uses' => 'PostController@index', 'as' => 'post.index', 'middleware' => 'check']);

Create a role (assuming "member" for this user)

<?phpnamespaceApp\Roles;
useJellis\Check\Roles\Base;
class Member extends Base {
protected$permissions = [
'post' => [
'index', 'view', 'create', 'store', 'view', 'edit:own', 'update:own',
],
];
}

Configure the model to do its thing

namespaceApp\Models;
useJellis\Check\RouteAwareModel;
class Post extends RouteAwareModel
{
protected$table = 'posts';
.../** * This is to check against a given model */
public function allowOwnOnly()
{
return$this->user_id == \Auth::id();
}
/** * This is to restrict things coming out of the database */publicfunctionrestrictOwnOnly(Builder$builder)
{
$builder->where('user_id', Auth::id());
}
}

You need to implement the getRole() method on the user model

class User extends Model {
...
publicfunctiongetRole()
{
return$this->role; // Or however you determine what a user's role is right now
}
...
}

Register the middleware in Kernel.php

protected$routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'check' => \Jellis\Check\Middleware\Checker::class,
];

Retrieve some records in your controller

class PostController extends Controller {
publicfunctionindex()
{
// You could check stuff here if you need to$myThing = Check::can('my.thing');
// Or you can do a contextual check, say, on a post$post = Post::find(1);
if (Check::can('post.edit', $post)) {
// Do some thing
}
// In this instance, let's pass it to the view$posts = Post::all();
returnview('post.index', compact('posts'));
}
}

And in the view you can do things like

@foreach($posts as $post)
<p>{{ $post->title }}@check('post.edit', $post)<strong>You can edit</strong>@endcheck</p>
@endforeach

So you're a super admin??

Who really wants to be putting all of those routes in for super admin? Not me.

When defining your SuperAdmin role, just override the can() method

class SuperAdmin extends Base
{
/** * Can do all the things all the time * * @param string $action * @param Model $model * @return bool */publicfunctioncheck($action, Model$model = null)
{
returntrue;
}
}

TODO

  1. Implement ability to define a permission for multiple contexts edit:own|company
  2. Implement multiple contexts on the scope for checking access rights
  3. Implement multiple contexts on the scope for pulling records from the model
  4. Allow ability to wildcard a thing post.*, whilst still retaining scope ability post.*:own

About

A very easy-to-implement user access control package designed for use with Laravel and Eloquent

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Latest commit

History

26 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

NOTE

Ensure you don't use the RoutAwareModel for your Authenticatable User model - it becomes a circular operation when applying the global scopes and you'll get a bad gateway (502) error.

What's it all about?

The purpose of the project was to create a syntactically simple way to implement context-based user access control. What does that mean, exactly? Good question...

Context-based access control

I wanted to start with the idea that I could use a really straight-forward syntax for my "things" (whatever they might be). The first concept I came up with was Check::can('post.edit'). Because I was a fan of naming my routes, this made good sense from a flow point-of-view. Because I have my routes named, I figured I'd be able to implement middleware that would also leverage the access control system.

Adding context to the access control wasn't a trivial task. Each model will have its own context. Say in a Post model, "owning" a post might mean that there is a user_id field on the Post that is equal to the current user, but in a User model, "owning" might mean that users are in the same company as you. So, how do I have a simple syntax for implementing and checking permissions, but also giving context when the need arises?

Using the Jeffrey Way school of thought, I started with how I wanted to define things... I really wanted my Role classes to be so simple it's almost stupid.

$permissions = [
'post' => [
'index', 'create', 'store', 'view', 'edit:own', 'update:own',
]
];

After starting with those two ideas, I set to work and actually managed to implement them. What we have is, I think, a simple, fluent way of managing user access.

Route Aware Models

If you have, say, a listing page for your users where they can see all posts, but can only edit their own, you'd simply have to do the following.

Register the service provider config/app.php

'providers' => [
...
Jellis\Check\Providers\CheckServiceProvider::class,
...
],

Register the facade in config/app.php

'aliases' => [
...
'Check' => Jellis\Check\Facades\Check::class,
...
],

Name the route and assign the middleware

Route::get('post', ['uses' => 'PostController@index', 'as' => 'post.index', 'middleware' => 'check']);

Create a role (assuming "member" for this user)

<?phpnamespaceApp\Roles;
useJellis\Check\Roles\Base;
class Member extends Base {
protected$permissions = [
'post' => [
'index', 'view', 'create', 'store', 'view', 'edit:own', 'update:own',
],
];
}

Configure the model to do its thing

namespaceApp\Models;
useJellis\Check\RouteAwareModel;
class Post extends RouteAwareModel
{
protected$table = 'posts';
.../** * This is to check against a given model */
public function allowOwnOnly()
{
return$this->user_id == \Auth::id();
}
/** * This is to restrict things coming out of the database */publicfunctionrestrictOwnOnly(Builder$builder)
{
$builder->where('user_id', Auth::id());
}
}

You need to implement the getRole() method on the user model

class User extends Model {
...
publicfunctiongetRole()
{
return$this->role; // Or however you determine what a user's role is right now
}
...
}

Register the middleware in Kernel.php

protected$routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'check' => \Jellis\Check\Middleware\Checker::class,
];

Retrieve some records in your controller

class PostController extends Controller {
publicfunctionindex()
{
// You could check stuff here if you need to$myThing = Check::can('my.thing');
// Or you can do a contextual check, say, on a post$post = Post::find(1);
if (Check::can('post.edit', $post)) {
// Do some thing
}
// In this instance, let's pass it to the view$posts = Post::all();
returnview('post.index', compact('posts'));
}
}

And in the view you can do things like

@foreach($posts as $post)
<p>{{ $post->title }}@check('post.edit', $post)<strong>You can edit</strong>@endcheck</p>
@endforeach

So you're a super admin??

Who really wants to be putting all of those routes in for super admin? Not me.

When defining your SuperAdmin role, just override the can() method

class SuperAdmin extends Base
{
/** * Can do all the things all the time * * @param string $action * @param Model $model * @return bool */publicfunctioncheck($action, Model$model = null)
{
returntrue;
}
}

TODO

  1. Implement ability to define a permission for multiple contexts edit:own|company
  2. Implement multiple contexts on the scope for checking access rights
  3. Implement multiple contexts on the scope for pulling records from the model
  4. Allow ability to wildcard a thing post.*, whilst still retaining scope ability post.*:own

About

A very easy-to-implement user access control package designed for use with Laravel and Eloquent

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages