I'm trying to define user created roles that select permissions from a list of permissions. I want a permission like this:
def initialize(user)
user.projects_users.each do |project_user|
project_user.role.privileges do |privilege|
can :create, ProjectsUser, :project_id => project_user.project_id
end
end
end
But I'm trying to save privileges in a database in such a way that I can do this to get the outcome above
def initialize(user)
user.projects_users.each do |project_user|
project_user.role.privileges do |privilege|
can privilege.action.to_sym, privilege.subject_class.constantize, privilege.conditions
end
end
end
The problem lies in the 'privilege.conditions' part. I cannot store a condition that must be executed only in the Ability.rb file. If I try to store:
{ :project_id => project_user.project_id }
It would say there is no variable named 'project_user'. I could save it as a string and in my Ability file do eval(privilege.condition), however I would need to do this only on the values. I tried something like this:
def initialize(user)
user.projects_users.each do |project_user|
project_user.role.privileges do |privilege|
can privilege.action.to_sym, privilege.subject_class.constantize, privilege.conditions.each do |subject, id|
subject => eval(id)
end
end
end
end
The error I'm getting is 'syntax error, unexpected =>, expecting keyword_end' for the 'subject =>' piece.
Not sure how to do this exactly...
I'm using this line of commands to test it:
@user_id = 4
@role = Role.create(name: "Tester", project_id: 4)
@priv = Privilege.create(:action => :create, :subject_class => 'ProjectsUser', :conditions => { :project_id => 'project_user.project_id' })
@role.privileges << @priv
@project_user = ProjectsUser.create(:user_id => @user_id, :role_id => @role.id, :project_id => @role.project_id)
@a = Ability.new(User.find(@user_id))
@a.can?(:create, ProjectsUser.new(:user_id => @user_id + 1, :role_id => @role.id, :project_id => @role.project_id))
Any advice?
Ok So I found a really easy work around. Essentially the do block on the conditions was not being evaluated correctly. Here's the working code:
user.projects_users.each do |project_user|
project_user.role.privileges.each do |privilege|
can privilege.action.to_sym, privilege.subject_class.constantize, Hash[privilege.conditions.map {|subject, condition| [subject, eval(condition)] }]
end
end
Notice the Hash[privilege.conditions.map {|subject, condition| [subject, eval(condition)] }]
What this is doing is taking a symbol as the key in conditions such as :subject_id and mapping it to the evaluated condition, which evaluated to a particular id.
In my model I have
class Privilege < ActiveRecord::Base
has_and_belongs_to_many :roles
serialize :conditions, Hash
end
And an example model is:
Privilege.create(
:action => :create,
:subject_class => 'ProjectsUser',
:conditions => { :project_id => 'project_user.project_id' }
)
Update
This method only works for conditions that are one level deep. A condition like this would NOT work. You will get a: TypeError: no implicit conversion of Hash into String
:conditions => {
:project => {
:location_id => 'project_user.project.location_id'
}
}
This is not the best solution, but a work around this is
:conditions => {
:project => "{
:location_id => eval(\"project_user.project.location_id'\")
}"
}