I'm pretty new to Mojolicious and so far I love it. I'm writing a REST API and I was wondering what is the best way to restrict/allow CRUD operations on specific resources based on different user roles.
Let's say I have a role: 'A'.
I want to give read/write access to users with role 'A' to:
So far I do not think there is any great majik to be gained from MOJO
(or any restful server)
So far I am using the old stand by 'Role' and 'Privilege' pattern
ie
1) Each user is given a Role
2) Each Role is given a set of 'Privileges'
So in your case Role A would have the 'Privs'
resource1-CRUD
resource2-CRUD
resource3-R
and no 'privs' for 'resource4'
Implementation could be as simple as
package Bla::Resource4;
use strict;
use warnings;
use v5.10;
use base 'Mojolicious::Controller';
sub show {
my $self = shift;
my $profile = Bla::User->get($self->session('id'));
$self->redirect_to('Not_Allowed');
unless($profile->priv('resource4-R'));
...
sub delete {
my $self = shift;
my $profile = Bla::User->get($self->session('id'));
$self->redirect_to('Not_Allowed');
unless($profile->priv('resource4-CRUD'));
...
You can make this a fine grained as you like and even have it on a
user by user basis.
I myself prefer only restrictive 'privs' so I have fewer to deal
with.
Hope this helps
On Feb 29, 9:25 pm, Chuck Finley <cfmwes...@gmail.com> wrote:
> I'm pretty new to Mojolicious and so far I love it. I'm writing a REST API
> and I was wondering what is the best way to restrict/allow CRUD operations
> on specific resources based on different user roles.
> Let's say I have a role: 'A'.
> I want to give read/write access to users with role 'A' to:
I found something for ruby that does this here: https://github.com/stffn/declarative_authorization and thought there was something similar for Mojolicious (it would be very nice). If someone else doesn't have other ideas on how to do this I will probably do what byterock suggested.
Thank you for the suggestion, I appreciate it very much. On Mar 1, 2012 9:03 AM, "byterock" <byter...@hotmail.com> wrote:
> So far I do not think there is any great majik to be gained from MOJO > (or any restful server)
> So far I am using the old stand by 'Role' and 'Privilege' pattern
> ie > 1) Each user is given a Role > 2) Each Role is given a set of 'Privileges'
> So in your case Role A would have the 'Privs'
> resource1-CRUD > resource2-CRUD > resource3-R
> and no 'privs' for 'resource4'
> Implementation could be as simple as
> package Bla::Resource4;
> use strict; > use warnings; > use v5.10;
> use base 'Mojolicious::Controller';
> sub show { > my $self = shift; > my $profile = Bla::User->get($self->session('id')); > $self->redirect_to('Not_Allowed'); > unless($profile->priv('resource4-R')); > ...
> sub delete { > my $self = shift; > my $profile = Bla::User->get($self->session('id')); > $self->redirect_to('Not_Allowed'); > unless($profile->priv('resource4-CRUD')); > ...
> You can make this a fine grained as you like and even have it on a > user by user basis.
> I myself prefer only restrictive 'privs' so I have fewer to deal > with.
> Hope this helps
> On Feb 29, 9:25 pm, Chuck Finley <cfmwes...@gmail.com> wrote: > > Hi all.
> > I'm pretty new to Mojolicious and so far I love it. I'm writing a REST > API > > and I was wondering what is the best way to restrict/allow CRUD > operations > > on specific resources based on different user roles.
> > Let's say I have a role: 'A'.
> > I want to give read/write access to users with role 'A' to:
> > /api/resource1 > > /api/resource2
> > But read-only access to:
> > /api/resource3
> > And restrict any access to:
> > /api/resource4
> > Thanks in advance!
> -- > You received this message because you are subscribed to the Google Groups > "Mojolicious" group. > To post to this group, send email to mojolicious@googlegroups.com. > To unsubscribe from this group, send email to > mojolicious+unsubscribe@googlegroups.com. > For more options, visit this group at > http://groups.google.com/group/mojolicious?hl=en.
At 2012-02-29 21:25:04 -0500, cfmwes...@gmail.com wrote:
> I'm pretty new to Mojolicious and so far I love it. I'm writing a REST > API and I was wondering what is the best way to restrict/allow CRUD > operations on specific resources based on different user roles.
I try to keep my authorization code in one place as far as possible, instead of sprinkling «if ($user->can('…')) …» checks across my code base. (Sometimes I can't avoid adding a specific check to an action, but it's an exception.)
To this end, I have routing shortcuts like 'allow_roles' and 'allow_if', which I use to construct authorisation bridges when defining routes. For example,
my $auth = $r->bridge->to('auth#allow_users');
my $accounts = $auth->allow_roles([qw/admin acctadmin acctmaint/]); my $a = $accounts->waypoint('/accounts')->to('accounts#index'); $a->get('/list')->to('accounts#list'); $a->post('/import')->to('accounts#import');
("admin", "acctadmin", etc. are roles assigned to a user.)
This can make it harder to define and think about your routes (because you want to minimise the number of bridges), but it's easier to review the code and ensure that you're doing the right thing, and you don't have to worry about forgetting to check permissions in some action.
I know some people don't get along with this sort of thing, but I'm quite happy with it.
-- ams
P.S. Here's a longer, unedited example from one of my projects:
# Bid-related routes, open to any customer and (bid) administrator.
# The following are available only to internal users (i.e. any sort # of administrator), but the last couple of actions are not open to # "bidmaint"
my $bidmaint = $bids->allow_if(sub {$_[1]->is_internal}); $bidmaint->get('/overview')->to('bids#overview'); $bidmaint->waypoint('/calendar')->to('bids-calendar#index') ->route('/list')->to('bids-calendar#list'); $bidmaint->get('/mov')->to('bids#mov'); $bidmaint->get('/volumes')->to('rpo#details'); $bidmaint->get('/breakdown')->to('bids#breakdown'); my $bidadmin = $bidmaint->allow_roles([qw/admin bidadmin/]); $bidadmin->post('/export')->to('bids-export#index'); $bidadmin->post('/internal_approve')->to('bids#internal_approve');
…and here's the corresponding output from «myapp routes»:
+/ * +/bids * bids +/ * +/list GET list +/list_submitted GET list_submitted +/create GET create +/create POST create +/archive GET archive +/list_archived GET list_archived +/no-bids * nobids +/list * list +/save-no-bids POST savenobids +/ * +/ * +/approve POST approve +/:bid_id * bid_id +/update GET update +/update POST update +/delete POST delete +/ * +/ * +/overview GET overview +/calendar * calendar +/list * list +/mov GET mov +/volumes GET volumes +/breakdown GET breakdown +/ * +/export POST export +/internal_approve POST internal_approve
> At 2012-02-29 21:25:04 -0500, cfmwes...@gmail.com wrote:
> > I'm pretty new to Mojolicious and so far I love it. I'm writing a REST
> > API and I was wondering what is the best way to restrict/allow CRUD
> > operations on specific resources based on different user roles.
> I try to keep my authorization code in one place as far as possible,
> instead of sprinkling «if ($user->can('…')) …» checks across my code
> base. (Sometimes I can't avoid adding a specific check to an action,
> but it's an exception.)
> To this end, I have routing shortcuts like 'allow_roles' and 'allow_if',
> which I use to construct authorisation bridges when defining routes. For
> example,
> my $auth = $r->bridge->to('auth#allow_users');
> my $accounts = $auth->allow_roles([qw/admin acctadmin acctmaint/]);
> my $a = $accounts->waypoint('/accounts')->to('accounts#index');
> $a->get('/list')->to('accounts#list');
> $a->post('/import')->to('accounts#import');
> ("admin", "acctadmin", etc. are roles assigned to a user.)
> This can make it harder to define and think about your routes (because
> you want to minimise the number of bridges), but it's easier to review
> the code and ensure that you're doing the right thing, and you don't
> have to worry about forgetting to check permissions in some action.
> I know some people don't get along with this sort of thing, but I'm
> quite happy with it.
> -- ams
> P.S. Here's a longer, unedited example from one of my projects:
> # Bid-related routes, open to any customer and (bid) administrator.
> # The following are available only to internal users (i.e. any sort
> # of administrator), but the last couple of actions are not open to
> # "bidmaint"
I actually thought about implementing Mojolicious::Plugin::Authorization which should provide something like allow_roles (and/or allow_privilege if using privileges per role).
From my experience it is mostly ok to limit authorization per route endpoint, but on some occassions mure subtle control is required. This requires to have a helper for that too (like assert_roles, assert_privileges). That should allow most of the scenarios for authorization. Of course callbacks would be used, to actually get roles/privileges from the logged in user object.
### For the routing my $auth = $r->bridge->to('auth#allow_users'); my $authorized = $auth->allow_roles('catfriend'); $authorized->get('/cats/show')->to('cats#show_cats');
### In your action handler sub show_cats { ... ### Assert will throw unauthorized exception $self->assert_privileges(['show','cats']); $self->render(text => 'We have these secret cats: lady, bunny, foobar');
}
Packaging it up in a plugin might help mojo to give some guidance on how to handle that. If I get something together I'll post it for review.
Cheers
+rl -- Roland Lammel QuikIT - IT Lösungen - flexibel und schnell Web: http://www.quikit.at Phone: +43 (676) 9737845 Email: r...@quikit.at
"Enjoy your job, make lots of money, work within the law. Choose any two."
On Fri, Mar 2, 2012 at 12:33, byterock <byter...@hotmail.com> wrote: > I like it.
> A good way to implement role~priv pattern.
> My only comment would be how well does this scale up to say 500+ pages > and say 6 routes per page?
> I guess you could break it up into different route controllers?
> cheers > John
> On Mar 1, 6:21 pm, Abhijit Menon-Sen <a...@toroid.org> wrote: > > At 2012-02-29 21:25:04 -0500, cfmwes...@gmail.com wrote:
> > > I'm pretty new to Mojolicious and so far I love it. I'm writing a REST > > > API and I was wondering what is the best way to restrict/allow CRUD > > > operations on specific resources based on different user roles.
> > I try to keep my authorization code in one place as far as possible, > > instead of sprinkling «if ($user->can('…')) …» checks across my code > > base. (Sometimes I can't avoid adding a specific check to an action, > > but it's an exception.)
> > To this end, I have routing shortcuts like 'allow_roles' and 'allow_if', > > which I use to construct authorisation bridges when defining routes. For > > example,
> > my $auth = $r->bridge->to('auth#allow_users');
> > my $accounts = $auth->allow_roles([qw/admin acctadmin acctmaint/]); > > my $a = $accounts->waypoint('/accounts')->to('accounts#index'); > > $a->get('/list')->to('accounts#list'); > > $a->post('/import')->to('accounts#import');
> > ("admin", "acctadmin", etc. are roles assigned to a user.)
> > This can make it harder to define and think about your routes (because > > you want to minimise the number of bridges), but it's easier to review > > the code and ensure that you're doing the right thing, and you don't > > have to worry about forgetting to check permissions in some action.
> > I know some people don't get along with this sort of thing, but I'm > > quite happy with it.
> > -- ams
> > P.S. Here's a longer, unedited example from one of my projects:
> > # Bid-related routes, open to any customer and (bid) administrator.
> > # The following are available only to internal users (i.e. any sort > > # of administrator), but the last couple of actions are not open to > > # "bidmaint"
> > …and here's the corresponding output from «myapp routes»:
> > +/ * > > +/bids * bids > > +/ * > > +/list GET list > > +/list_submitted GET list_submitted > > +/create GET create > > +/create POST create > > +/archive GET archive > > +/list_archived GET list_archived > > +/no-bids * nobids > > +/list * list > > +/save-no-bids POST savenobids > > +/ * > > +/ * > > +/approve POST approve > > +/:bid_id * bid_id > > +/update GET update > > +/update POST update > > +/delete POST delete > > +/ * > > +/ * > > +/overview GET overview > > +/calendar * calendar > > +/list * list > > +/mov GET mov > > +/volumes GET volumes > > +/breakdown GET breakdown > > +/ * > > +/export POST export > > +/internal_approve POST internal_approve
> -- > You received this message because you are subscribed to the Google Groups > "Mojolicious" group. > To post to this group, send email to mojolicious@googlegroups.com. > To unsubscribe from this group, send email to > mojolicious+unsubscribe@googlegroups.com. > For more options, visit this group at > http://groups.google.com/group/mojolicious?hl=en.
Thank you all for your suggestions. You've given me a lot to think about. I really like the idea of the Authorization Mojolicious plugin. It would not only help me, but also the entire community. I'm looking forward to see if that solution is implemented. Thank you all!
They both vary in whether they put the auth into the model or a separate auth model. However, I like the basic idea of security being a function of the user object and the model record (obviously only where that level of granularity is needed...)
I tried to have easy to grasp words also for the conditions (authorize_role, authorize_roles) and also provide helpers for assertion in code (assert_role, assert_roles), same should be there for privileges.
As some apps won't need privileges (roles surely suffice for simple apps), you just use the roles part ad provide a sub for check_user_role (to get the role for a user).
Don't know if shorter names would work too but I think "has" and "is" are a little to generic as condition names.
Nice work btw.
Cheers
+rl -- Roland Lammel QuikIT - IT Lösungen - flexibel und schnell Web: http://www.quikit.at Phone: +43 (676) 9737845 Email: r...@quikit.at
"Enjoy your job, make lots of money, work within the law. Choose any two."
> It is an empty shell type api that you must supply 4 sub to that you > then use to 'Authorize' the current session.
> Any comments would be great
> -- > You received this message because you are subscribed to the Google Groups > "Mojolicious" group. > To post to this group, send email to mojolicious@googlegroups.com. > To unsubscribe from this group, send email to > mojolicious+unsubscribe@googlegroups.com. > For more options, visit this group at > http://groups.google.com/group/mojolicious?hl=en.
> They both vary in whether they put the auth into the model or a separate > auth model. However, I like the basic idea of security being a function of > the user object and the model record (obviously only where that level of > granularity is needed...)
> Good luck
> Ed W
-- Ben van Staveren phone: +62 81 70777529 email: benvanstave...@gmail.com
I like this one, especially since I was considering rolling something like this into the Authentication module (where it really has no place since it's not about authentication but authorization), and I didn't have time for it.. thank god! At least now there's a proper plugin that does it :)
As Ed mentioned he put in a pull request that I merged and then totally forgot about (been too busy with work and my kid), but it's been merged and a new version of the plugin has been released to CPAN.
A few things changed that might break existing code but I've tried to put in some warnings about deprecated options and helpers that will be around until the next release. Enjoy :)
> They both vary in whether they put the auth into the model or a separate > auth model. However, I like the basic idea of security being a function of > the user object and the model record (obviously only where that level of > granularity is needed...)
> Good luck
> Ed W
-- Ben van Staveren phone: +62 81 70777529 email: benvanstave...@gmail.com
Mine is of course a little more generic, (well dumb really as I leave
all the guts up to the user) than you invocation.
The main reason for the sort names was it makes one's code very easy
to read in a natural way.
Looking at some of the other comments I think I will make it required
only one or both of the 'has_priv' and 'is_role' code stubs. That way
one could use it with or without roles.
I should be able to get it up an CPAN later this week if you have no
objections Rolan.
cheers John
On Mar 29, 7:16 pm, Roland Lammel <r...@quikit.at> wrote:
> I tried to have easy to grasp words also for the conditions
> (authorize_role, authorize_roles) and also provide helpers for assertion in
> code (assert_role, assert_roles), same should be there for privileges.
> As some apps won't need privileges (roles surely suffice for simple apps),
> you just use the roles part ad provide a sub for check_user_role (to get
> the role for a user).
> Don't know if shorter names would work too but I think "has" and "is" are a
> little to generic as condition names.
> Nice work btw.
> Cheers
> +rl
> --
> Roland Lammel
> QuikIT - IT Lösungen - flexibel und schnell
> Web:http://www.quikit.at > Phone: +43 (676) 9737845
> Email: r...@quikit.at
> "Enjoy your job, make lots of money, work within the law. Choose any two."
> On Thu, Mar 29, 2012 at 19:25, byterock <byter...@hotmail.com> wrote:
> > Well after playing around a bunch and pulling out what little hair I
> > have left I finally got something that works really well for me at
> > least
> > It is an empty shell type api that you must supply 4 sub to that you
> > then use to 'Authorize' the current session.
> > Any comments would be great
> > --
> > You received this message because you are subscribed to the Google Groups
> > "Mojolicious" group.
> > To post to this group, send email to mojolicious@googlegroups.com.
> > To unsubscribe from this group, send email to
> > mojolicious+unsubscribe@googlegroups.com.
> > For more options, visit this group at
> >http://groups.google.com/group/mojolicious?hl=en.
On Mon, Apr 2, 2012 at 21:18, byterock <byter...@hotmail.com> wrote: > Hi Rolan sorry I did not see your code on github.
> Mine is of course a little more generic, (well dumb really as I leave > all the guts up to the user) than you invocation.
> The main reason for the sort names was it makes one's code very easy > to read in a natural way.
> Looking at some of the other comments I think I will make it required > only one or both of the 'has_priv' and 'is_role' code stubs. That way > one could use it with or without roles.
> I should be able to get it up an CPAN later this week if you have no > objections Rolan.
> cheers John
> On Mar 29, 7:16 pm, Roland Lammel <r...@quikit.at> wrote: > > Hi there,
> > Funny thing is I started exactly the same some weeks ago, but was > > distracted with other things soon after.
> > I tried to have easy to grasp words also for the conditions > > (authorize_role, authorize_roles) and also provide helpers for assertion > in > > code (assert_role, assert_roles), same should be there for privileges.
> > As some apps won't need privileges (roles surely suffice for simple > apps), > > you just use the roles part ad provide a sub for check_user_role (to get > > the role for a user).
> > Don't know if shorter names would work too but I think "has" and "is" > are a > > little to generic as condition names.
> > "Enjoy your job, make lots of money, work within the law. Choose any > two."
> > On Thu, Mar 29, 2012 at 19:25, byterock <byter...@hotmail.com> wrote:
> > > Well after playing around a bunch and pulling out what little hair I > > > have left I finally got something that works really well for me at > > > least
> > > It is an empty shell type api that you must supply 4 sub to that you > > > then use to 'Authorize' the current session.
> > > Any comments would be great
> > > -- > > > You received this message because you are subscribed to the Google > Groups > > > "Mojolicious" group. > > > To post to this group, send email to mojolicious@googlegroups.com. > > > To unsubscribe from this group, send email to > > > mojolicious+unsubscribe@googlegroups.com. > > > For more options, visit this group at > > >http://groups.google.com/group/mojolicious?hl=en.
> -- > You received this message because you are subscribed to the Google Groups > "Mojolicious" group. > To post to this group, send email to mojolicious@googlegroups.com. > To unsubscribe from this group, send email to > mojolicious+unsubscribe@googlegroups.com. > For more options, visit this group at > http://groups.google.com/group/mojolicious?hl=en.