I'm using Catalyst with Catalyst::Plugin::Authentication
and
Catalyst::Plugin::Authorization::Roles
and am wondering if there is a better
approach to adding an attribute to a model that I'm not seeing.
Each user is permitted to access one or more companies, but there is
always one primary (current) company at a time. The permitted list is
stored in the database, and database access is primarily through DBIC
.
My first inclination is to say that it's the user that has a current
company, and thus put it as part of the user model: give the user
package a "sub company { … }
" to get/set the user's current company. The
database check is fairly easy; just use "$self->search_related
" (a DBIC
method, inherited by the user model).
The problems I run in to are:
root
, that allows you to act as
any company, ignoring the list in the database. Checking this role
can be done through the database, but everywhere else in the app uses
$c->assert_user_role
and friends.I've heard its best to keep the models as Catalyst-independent as
possible. It also seems pretty weird to have a model manipulating
$c->session
.
Of course, I could move those checks to the controllers, and have the model accept whatever the controller sends, but that's violating DRY pretty heavily, and just begging for a security issue if I forget one of the checks somewhere.
Any suggestions? Or do I just shrug and go ahead and do it in the model?
Thanks, and apologies for the title, I couldn't come up with a good one.
The key is to create an instance of the model class for each request, and then pass in the parts of the request you need. In this case, you probably want to pass in a base resultset. Your model will make all the database calls via $self->resultset->...
, and it will "just work" for the current user. (If the current user is root, then you just pass in $schema->resultset("Foo")
. If the current user is someone else, then pass in $schema->resultset("Foo")->stuff_that_can_be_seen_by($c->user)
. Your model then no longer cares.)
I have some slides about this, but they are very outdated:
http://www.jrock.us/doqueue-grr/slide95c.html#end
(See the stuff immediately before and after, also.)
Note that restricted resultsets and web ACLs are orthogonal. You want to make the model as tight as possible (so that your app can't accidentally do something you don't want it to, even if the code says to), but various web-only details will still need to be encoded in ACLs. ("You are not allowed to view this page." is different from "You can only delete your own objects, not everyone's". The ACL handles the first case, the restricted resultset handles the second. Even if you write $rs->delete
, since the resultset is restricted, you didn't delete everything in the database. You only deleted the things that you have permission to delete. Convenient!)