W aplikacji, którą piszę mam model User zawierający wszystkich użytkowników systemu. Użytkownicy ci mają przydzielone role aplikacyjne. Część z nich to “obsługa” a część to klienci.
W akcji Users#index chcę wyświetlić listę użytkowników systemu ale z ograniczeniami, “reseller” może widzieć i aktualizować siebie oraz widzieć i zarządzać swoimi użytkownikami.
Trochę kodu dla wyjaśnienia:
class User < ActiveRecord::Base
has_secure_password
# for resellers and more
has_many :cs, class_name: "Client", foreign_key: :customer_service_id
# for client and user
has_one :client
include RoleModel
# 1 - root
# 2 - admin
# 4 - manager
# 8 - seller
# 16 - reseller
# 32 - client
# 64 - user
roles :root, :admin, :manager, :seller, :reseller, :client, :user
(...)
end
class Client < ActiveRecord::Base
belongs_to :user, -> { where 'roles_mask = 32' }
belongs_to :customer_service, -> { where 'roles_mask < 32' }, class_name: 'User'
(...)
end
class Ability
include CanCan::Ability
def initialize(user)
@user = user
@user ||= User.new # guest user (not logged in)
if @user.root?
can :manage, :all
end
case
when @user.admin?
admin_ability
when @user.manager?
manager_ability
when @user.seller?
seller_ability
when @user.reseller?
reseller_ability
when @user.client?
client_ability
when @user.user?
@user_ability
else
guest_ability
end
end
(...)
def reseller_ability
can :read, User, id: @user.id
can :read, User, client: { customer_service_id: @user.id }
can :update, User, id: @user.id
can :update, User, client: { customer_service_id: @user.id }
can [:create, :destroy], User, client: { customer_service_id: @user.id }
cannot :destroy, User, id: @user.id
end
(...)
end
Niby wygląda OK bo pierwsze dwa warunki powinnu byś złożone przez OR. Tylko, że to nie działa bo CanCan tworzy zapytanie:
SELECT "users".* FROM "users"
INNER JOIN "clients" ON "clients"."user_id" = "users"."id"
WHERE (roles_mask > 1)
AND (("clients"."customer_service_id" = 402741401) OR ("users"."id" = 402741401))
W efekcie otrzymuję listę klientów “resellera” ale nie mam informacji o samym “resellerze” bo “INNER JOIN” eliminuje go z wyników.
Jak sobie z tym poradzić?