In an app where ordinary users are limited to viewing and editing their
own “stuff”, but someone with admin privs can view and edit anybody’s
stuff, what’s the right strategy for routing?
At first blush, I’d think that an ordinary user (e.g. with id 565)
should see something like:
… where the controller assumes @current_user has been established by
authlogic or whatnot. But if you’re logged in as an admin, you could
get at that same user’s stuff via:
and you could list and administer all the users via:
Does this sound like the right approach? If so, what are the patterns
for the routes and controllers? If not, what’s the accepted DRY,
Check out acl9 for access control http://github.com/be9/acl9
With acl9 you control what users have access to which specific methods
and assign users roles on specific objects. You could set it up in the
create method of stuffs_controller so the current_user is the “owner” of
that stuff and only allow owners to edit that specific object, then
admin has access to all.
In that approach it’s all handled in the models and controllers so
routes are set up however you want.
I’m using Cancan, which works really well and is pretty cleanly
implemented. Check out Ryan B. Railscast here
The urls you have up there end in .html, which doesn’t really happen
all that often in rails. With routes, what you’re more likely going to
…where ### is the user_id (or whatever). This goes to the show
action for user ###. If the user_id matches the current_user or if
current_user is an admin, they get to access to that model. If not,
they’ll get redirected. Furthermore, if current_user is an admin, they
would also get access to:
…which goes to the index action to list all of the users. Other (non-
admin) users will get a nasty flash message and redirected elsewhere.
Hope I’m answering the right question.
Yes, you’re answering the right question. My addition of the .html
suffixes (suffixen?) was a brain bubble and should be ignored.
I wonder if exposing the user id in the url is useful or prudent. I
agree with most RESTful philosophy, but even if authorization code is in
place to prevent the user 123 from accessing the account of user 142,
exposing the db-level user ID in the URL doesn’t feel right.
That’s why I was asking about having two styles of routing: one where
the user ID is implicit (derived from session and authentication
credentials) for ordinary users, and one where the user ID is manifest
in the URL (for the administrator).
But: CanCan looks like a sensible adjunct to Authlogic (even though I’ve
already written a Role model). I’ll just go with the flow and use that
as it was intended.
Thanks for the pointer.
I see your point, but the user_id is just an arbitrary number used for
the lookup of the correct user record. You could use anything, like
user name (User.find_first_by_username(params[:username]) if you have
the correct route set up. Or you could do the same thing with a
randomly generated unique id that identifies the user. This all
assumes you are concerned with user_id being a sequential integer. At
any rate, it’s tried and true.
You should be able to use your implementation of role models with
CanCan. It doesn’t provide roles, just authorization based on your
implementation of “roles”. You could, for instance, use CanCan to
control access based on something as arbitrary as which UserAgent
their browser is reports. Just remember to restrict access in both the
View and the Controller (especially).
Good luck on your project.
Oh, and one other thought. If you are still concerned about the id in
the url, in UserController#Index you could also check if the user is
an admin, and if not then set params[:user_id] = current_user and then
render :action => show instead of the index action. I haven’t tried it
in a while, but as I remember this would give non-admins a url that
looks like index (e.g. http://example.com/users) but would be doing
the the show action (e.g. same as http://example.com/users/####). You
wouldn’t have to play with the routing at all.
This is only a partial solution, because while it would work with the
show action, the all of the other actions would still require the id
in the url (i.e. http://example.com/users/####/edit). There might be
something you could do in a routing block to hide it, or maybe
something like inserting the id through rack, but I think all of those
are going to be a lot of work and not much bang for the buck.