-
Notifications
You must be signed in to change notification settings - Fork 17
Account User scenario
In some cases you might have Accounts set up for different sub domains of the site. A User might then have certain permissions when looked into some parts of the site and other permissions in other parts of the site.
“The scenario I’m talking about is for applications where users create an account (usually with a subdomain) with their own members like lighthouse or basecamp. So a user would be an admin in the account they created but maybe a guest or an editor under someone else’s account. Systems like ACL9 account for this by allowing you to add role assignments to objects. So User A can be an admin on Account A and a guest on Account B.” – aaronchi
One way to set this up would look like this:
class Account < ActiveRecord::Base
has_many :roles
has_many :users, :through => :roles
end
class User < ActiveRecord::Base
has_many :roles
has_many :accounts, :through => :roles
end
class Role < ActiveRecord::Base
belongs_to :account
belongs_to :user
end
As I see it, what is implied is that the role should not be retrieved directly from the user, but should go through the account of the user. In cream, the default is to apply the role logic directly on the #current_user returned by #role_subject.
We could change the #role_subject method like this:
def role_subject
current_account.roles_of(current_user)
end
Which would retrieve the roles (from joint Role table) for the current user for the account he is currently operating in.
In order to achieve this, you will have to customize the #role_subject and likely also the #post_signin and #post_signout methods, currently all defined in user_control.rb in the Cream source. You would likely have to assign and store the current account in the session and add a custom #current_account method that uses this session variable to retrieve the Account. This might be implemented something like the following:
# Sign in an user that already was authenticated. This helper is useful for logging
# users in after sign up.
#
# Examples:
#
# sign_in :user, @user # sign_in(scope, resource)
# sign_in @user # sign_in(resource)
# sign_in @user, :account => :forum # sign_in(resource, options)
#
def sign_in(resource_or_scope, *args)
...
end
def post_signin resource, options = {}
session[:user_id] = resource.id
session[:user_class_name] = resource.class.name
# custom account logic
session[:account_name] = options[:account]
end
def post_signout
session[:user_id] = nil
@current_user = nil
end
def current_account
Account.by_name(account_name)
end
protected
def account_name
session[:account_name]
end
For sign_in you could use the options argument to set which Account the User signed in on and then store this info in the session and/or set on the User. Then the method #role_subject should return the Role instance retrieved by calling
current_account.roles_of(current_user)
Add a #has_role? method to the Role class and ensure that any cream methods such as #can? and #cannot? work on the #role_subject and not the #current_user. This should mostly do the trick I think (Not yet tested, May 10 – 2011).
class Role < ...
...
def has_role? role_name
self.name.to_sym == role_name.to_sym
end
end
Note that if the #roles_of call returns a collection of Roles, this collection must have the #has_role? and similar methods implemented (see the https://github.com/kristianmandrup/roles_generic gem)
Note that the #current_ability method is used for applying CanCan logic uses the #role_subject method.
Ability controller in Cream (ability.rb)
module Cream::Controller
module Ability
def current_ability
@current_ability ||= Permits::Ability.new(role_subject, request)
end
end
end