Permits, Permissions, Roles, Troles?

35 views
Skip to first unread message

A L

unread,
Apr 12, 2012, 5:48:40 PM4/12/12
to cantango
Hi,

Before I begin I just want to thank you for creating such an awesome
gem.

I know it's probably something trivial I'm missing, but for the last
week I just have not been able to figure out how to adapt Cantango to
perform the following architecture:

1. Users login with their own credentials (email & password)
2. Users can have one or more accounts
3. There are two types of accounts (lead account and support account)
4. Users can access specific projects base on the accounts they have
5. Lead accounts and support accounts are specific to Projects

Example:
User1 has LeadAccountA for ProjectA
User2 has SupportAccountB for ProjectB
User3 has LeadAccountA for ProjectA and SupportAccountB for ProjectB
User4 has SupportAccountA for ProjectA and LeadAccountB for ProjectB
a user can manage project x if they have lead account x
a user can read-only project x if they have support account x

I'm not sure if the architecture needs to be changed, but I'm
basically trying to control access to read/write abilities on projects
based on a users role for each individual project. And a user can
have different roles on different projects.

Can someone please offer some advice on how to implement this?

So far I've set up:
users to be authenticated with devise
users as tango_user and has many support accounts
support accounts as tango_user_account and belongs to users

I haven't set up the lead accounts yet since I'm still trying to get
the support accounts working.

Thus far I've been stuck on:
a. how to link support accounts to users
b. how to link support accounts to projects
c. whether permits, permissions, roles, or troles are what I should be
using

Later on, I'll be adding another type of user, admin, who will have
access to everything.

Am I on the right track? Or have I totally missed how to use
cantango?

Thanks so much for your time,
A L

Kristian Mandrup

unread,
Apr 13, 2012, 3:50:49 AM4/13/12
to cant...@googlegroups.com
Hi,

On the main CanTango wiki there should be a page on how to set up a model with a User having multiple Accounts.

> 2. There are two types of accounts (lead account and support account)
> 3. Users can access specific projects base on the accounts they have

Set up the USer to have multiple accounts via a User.has_many :accounts, Account.belongs_to :user
Cantango will looks for a #current_account method on the User object (the account which the user session is currently on.
If this method call returns an (Account) object it will use this account for authorization (policies).

> 1. Users login with their own credentials (email & password)


The authentication can get the User credentials from the User, no problem. This is just normal Devise or whatever you like.

> 4. Users can access specific projects base on the accounts they have

This is just normal CanTango (CanCan) Authorization policies using current_user from User to get the current Account instance to use to determine authorization policies.

> Lead accounts and support accounts are specific to Projects

I guess this is just a modeling fact. Account.belongs_to :project, Project.has_one :account ?

So the question is… if the role is directly based on the current Account the user is on, or more granular? A combination of Account and role of that account?
In principle you could have a #role on the Account and dynamically change this role and use this attribute for Authorization. You could then set up both a AccountPermits, fx
SupportAccountPermit and LeadAccountPermit and role permits, fx AdminRolePermit, BasicRolePermit.

I also suggest you look into using the cantango-permits gem instead of the cantango 0.9.4.7 version, which is unfortunately not entirely stable.

For the roles part, consider using my old roles gem, fx role_active_record or similar. Not sure the last remaining issues on troles were fixed. Or perhaps use an alternative roles solution and use the cantango config to configure it correctly into cantango.

Hope this helps!

Kristian

A L

unread,
Apr 13, 2012, 7:33:10 PM4/13/12
to cantango
Hi Kristian,

Thanks for the quick response with all that information.

Something really caught my eye from your post above and led me to take
a step back and rethink some things:
"I guess this is just a modeling fact. Account.belongs_to :project,
Project.has_one :account ? "

This led me to wonder:
What's the difference from implementing CRUD with something like:
current_user.project
versus implementing Cantango with something like:
can :edit, Project

Of course I would need to modify the second option to only show
Projects accessible by the current user (maybe something like
"can :edit, Project.find(current_user.project)"; totally not sure
about that). Which is something I'm at a lost at right now too.

Is something along the lines of the second option more secure? Or did
I totally miss Cantango's objective?

***************
Assuming the above questions don't lead to the fact that I'm mis-using
Cantango, then I have the following questions/responses to your reply:

"Cantango will looks for a #current_account method on the User object
(the account which the user session is currently on. If this method
call returns an (Account) object it will use this account for
authorization (policies). "
Is the #current_account method typically determined by a session
parameter or database value? Do you happen to a few sample methods I
can look at to better understand how to do this?

"So the question is… if the role is directly based on the current
Account the user is on, or more granular? A combination of Account and
role of that account? "
The role (I'm guessing you mean read/write access to projects?) is
based on the account type, whether it is a lead account or support
account. Lead accounts have read/write access to whichever projects
it has access to. (I'm guessing that would be a "has_many" and
"belongs_to" declaration in combination with something similar to
"can :edit, Project.find(current_user.project)"?) Support accounts
only have read access to (all) projects (this declaration seems to be
a simple "can :view, Project").

Lastly, in regards to roles, where would I use them? I'm thinking
access base on account types and which Projects are linked to that
particular account should be sufficient to determine whether or not
the user has access. Or is there something I'm missing? In my mind
I'm picturing something like this:
User --> Account --> (account_type? && is_account_linked_to_project? ?
allow_access : deny_access) --> Project

Also, I see "fx" a lot. What does that mean?

I apologize in advance for asking dumb questions. I'm still trying to
figure out where model declaration and methods end and Cantango kicks
in. And if there's some concept that I'm obviously not understanding,
then please feel free to point that out so I can readjust some of my
assumptions.

Thank you again for your work on this Gem and responding to my
questions. I really appreciate you helping me out.

Take care!
A L

A L

unread,
Apr 13, 2012, 9:42:51 PM4/13/12
to cantango
Hi Kristian,

I think I've found the missing piece:
can :read, Project, :active => true, :user_id => user.id

This was from CanCan.
https://github.com/ryanb/cancan/wiki/Defining-Abilities
But since CanTango is based off of CanCan, I think that's what I need
to understand what's going on.

I'll give this a try and see how things go. Thanks for talking me
through. Your comment about defining models really got me thinking in
the right direction (or so I hope is the right direction). Anyways,
thanks again for your help!

Take care and keep up the good work!
A L

Kristian Mandrup

unread,
Apr 14, 2012, 11:33:55 AM4/14/12
to cant...@googlegroups.com

Good to hear :) If you get it all configured and working let me know and put it up on github as a working example demo app. Cheers!

A L

unread,
Apr 17, 2012, 5:18:12 PM4/17/12
to cantango
Hi Kristian,

I was only to get it partially working. Here's what I did:

/models/user.rb
class User < ActiveRecord::Base
devise ...
attr_accessible ...
def self.guest
@guest ||= Guest.new
end
tango_user
attr_accessor :account

has_one :lead_account
end

/models/lead_account.rb
class LeadAccount < ActiveRecord::Base
tango_user_account

belongs_to :user
has_many :projects
end

/models/project.rb
class Project < ActiveRecord::Base
belongs_to :lead_account
end

With, I'm able to restrict guest to certain things, have users log in,
and show accounts related to the logged in user.

Here's the parts (defining and checking abilities) that I'm trying to
figure out:

# I've been trying to use Account permits as well, but still no luck
/permits/user_permit.rb
class UserPermit < CanTango::UserPermit
def initialize ability
super
end

protected

# I want to define something like this, but don't know how. The
closest I've seen is: https://github.com/ryanb/cancan/wiki/Abilities-in-Database
and (account scope/session API) https://github.com/kristianmandrup/cantango/wiki/CanTango-APIs
def permit_rules(current_user)
can :manage, Project do |project|
project.lead_account.id == current_user.lead_account.id
end
end
end

The other part is checking the ability. I want to use something like
the following, but can't find documentation on the best way to
accomplish something like it:
current_user_can?(:create, Project)
current_user.lead_account.can?(:create, Project)
user_ability(current_user).can?(:create, Project)

I'll definitely put the relevant files on GitHub...if I ever get it to
work...

Thanks again for your kind advice!
A L

On Apr 14, 5:33 am, Kristian Mandrup <kmand...@gmail.com> wrote:
> Good to hear :) If you get it all configured and working let me know and
> put it up on github as a working example demo app. Cheers!

Kristian Mandrup

unread,
Apr 17, 2012, 5:30:00 PM4/17/12
to cant...@googlegroups.com
Hi A. L,

You should check out the Ability specs and Ability namespace of the repository
CanTango was designed in order to specifically allow Ability checks outside of the CanCan limitation of doing it on current_user only. Fx to allow rules for multiple devise users and also accounts.

See the examples on the wiki and in the specs and the two dummy apps included.

I think you can do sth like:

admin_can?
admin_account_can?

For you it would be

lead_acount_can?

But you must register the LeadAccount with CanTango for this to work (using tango_account macro)

I also think you can use the CanCan Ability directly (this is what those generated methods do under the covers).

> can :manage, Project do |project|
> project.lead_account.id == current_user.lead_account.id
> end

I don't think you need to specify current_user here. Within the permit, both #user and #permit are available and many other helper method as well (see docs and examples)
There is also a Rules DSL in order to facilitate this kind of complex logic. Can't remember the syntax just now. But you are def. on the right track :)
Sorry, is very late now (midnight) and I have been coding since 8:30 this morning

Good luck!

Kris

A L

unread,
Apr 18, 2012, 3:14:41 PM4/18/12
to cantango
Hi Kris,

Thanks again for the advice. I think I've read a lot of the material
you were referring to, but I can't be certain since there's no link.
But mostly everything that's linked from the GitHub page, I should
have ran through, including that ActiveAdmin demo app.

There were a lot of variables to tweak to figure out what's going on,
but your recommendations really helped me focus in on certain
aspects. With that said, here's where I'm at (maybe you have
encountered something similar before?):

So I followed your advice to use "lead_account_can?" and
"lead_account_cannot?". And I redefined the ability to be simpler to
isolate the problem first, so in LeadAccountPermit class, I only have:
def permit_rules
can :read, Project
end

I've added the following code to my view to figure out what's going
on:
(in HAML)
= 'lead_account_can?' if try(:lead_account_can?, :read, Project)
= 'lead_account_cannot?' if try(:lead_account_cannot?, :read, Project)

Now here's the extremely funny part:
Only one of the statement executes as true, as one would expect.
However, the statement that's executed as true DOESN'T change whenever
I change the permit rule from "can" to "cannot" or vice-versa. The
statement that's executed as true DOES change when I modify the macro
in the lead account model (/app/models/lead_account.rb).

This is the results I get when I toggle the macros:
macro "tango_user_account" returns "true" for "lead_account_cannot?"
regardless of whether lead accounts was set to "can" or "cannot" in
the model
macro "tango_user" returns "true" for "lead_account_can?" regardless
of whether lead accounts was set to "can" or "cannot" in the model
macro "tango_account" returns "true" for "lead_account_cannot?"
regardless of whether lead accounts was set to "can" or "cannot" in
the model

Any ideas on why this funny behavior occurs?

I've also been having to restart the server each time to see the
change, even though I turned caching off.

Also in the IRB:
LeadAccount.first.cannot?(:read,Project) returns true regardless of
the macro and permit ability
LeadAccount.first.can?(:read,Project) returns false regardless of the
macro and permit ability

I'm so confused...

But at least it's not returning:
No permissions for #<LeadAccount:0x0000...> found for #all_permissions
call
like it was before.

Thank you again so much for helping me out with this even though
you're coding the whole day.

Take care!
A L
> > and (account scope/session API)https://github.com/kristianmandrup/cantango/wiki/CanTango-APIs
> ...
>
> read more »

Kristian Mandrup

unread,
Apr 19, 2012, 3:49:55 AM4/19/12
to cant...@googlegroups.com
I'm afraid I have no idea why that happens. I think I heard or experiences something similar at one point, but is almost one year ago, so…
I recommend that you reference the CanTango repo using:

gem 'cantango', :path => 'local path to cantango'

And then start debugging using puts or whatever to see what the hell is going on!

Unfortunately you really have to "dive in", in order to get it working for your solution I think.
Good luck!

Kristian

PS: I think you might have a better chance with the newer cantango-permits gem. It is much better tested, well-designed and should be easier to work with. Much of the old documentation still applies in general, however the namespaces have been cleaned up a bit. Go through the specs and see… I'm still working on the store and cache solution for that version however, but they can be added later as plugins.

Reply all
Reply to author
Forward
0 new messages