james2m / canard

Makes role based authorization in Rails really simple. Wraps CanCan and RoleModel up with a smattering of syntactic sugar, some generators and scopes.
MIT License
125 stars 28 forks source link

How to use cancard with separate context? #5

Closed swistaczek closed 9 years ago

swistaczek commented 12 years ago

Hello, First of all I would like to say thank you for great and well tested open source project. I have trouble with getting out of my problem so I decided to ask for help. Currently I am trying to make system with couple separate interfaces but with one User model. There is Admin, Partner and User interface. User could have many global roles (cancard fit there very good) but User could have many roles as PartnerUser (model which contains relation user_id, partner_id, role). I dont know how to set up before_filter in my Partner base namespace to add this separate context (role of user for selected partner).

Please checkout my relations:

class PartnerUser < ActiveRecord::Base
  ROLES = {
    owner: 0,
    employee: 1 
  }

  belongs_to :user
  belongs_to :partner

  has_many :transactions, class_name: "Transaction"

  validate :role, inclussion: { in: ROLES }
class Partner < ActiveRecord::Base

  has_many :partner_users, include: :user
  has_many :users, through: :partner_users
class User < ActiveRecord::Base
  ALLOWED_ROLES = [:manager, :admin, :reporter]
  acts_as_user roles: ALLOWED_ROLES

  # Partner
  has_many :partner_users
  has_many :partners, through: :partner_users

Partner base controller

# -*- encoding : utf-8 -*-
class Partner::BaseController < InheritedResources::Base
  layout 'admin/index'

  before_filter :authenticate_user!
  before_filter :authorize_for_subdomain!

  helper_method :current_user_role
  helper_method :current_partner

  rescue_from ActiveRecord::RecordNotFound, with: :partner_not_found

  add_crumb(I18n.t('partner.dashboard.singular')) { |instance| instance.send :partner_root_path }

  def authorize_for_subdomain!
    @subdomain = request.subdomain.to_s.split('.').first
    @current_partner = Partner.find_by_url(@subdomain)

    raise ActiveRecord::RecordNotFound unless @current_partner.present?
    @current_partner_user = @current_partner.partner_users.where(user_id: @current_user.id).try(:first)

    raise CanCan::AccessDenied unless @current_partner_user.present?

    @current_user_role = @current_partner_user.current_role || :employee
    current_user
  end

  private

  def current_user_role
    authorize_for_subdomain! unless @current_user_role.present?
    @current_user_role
  end

  def current_partner
    authorize_for_subdomain! unless @current_partner.present?
    @current_partner
  end

  protected

  def partner_not_found
    flash[:error] = "Poszukiwana strona nie istnieje"
    redirect_to root_path
  end

  def begin_of_association_chain
    current_partner
  end

  def set_current_user_for_acl
    @current_ability ||= Ability.new(current_admin)
  end

end

Thanks in advantage for any suggestions :). Have a nice day!

james2m commented 12 years ago

@swistaczek I'm not sure what you are trying to achieve, could you re-state your question?

swistaczek commented 12 years ago

@james2m in my app User could have role like 'admin' or 'moderator', this role match users action privalages for whole application, but I have also separate context (partner section and namespace) where user could have role like 'owner' or 'employee' and for example I would like to restrict User that is Employee for Partner to only view (action show) clients.

I store User-Partner relation in join model (id, user_id, partner_id, role [employee/owner]). I would like to add this separate context to before_filter for my Partner namespace base controller (other partners controllers inherit from base). Using only cancan I think I would define Ability controller with initialize method accepting 2 params (user model and partner role) and then judge what user should be able to do or what not. How to achive this with cancard?

james2m commented 11 years ago

@swistaczek firstly, sorry it's been a long wait. I've been on the road.

From what you describe everything you are trying to achieve is possible with straight cancan by referencing the join model attributes.

I assume this is all working now, but let me know if you need anything clarifying. If I don't hear anything I'll assume I'm good to close this.