I'm wondering how to implement some permission logic in a Rails 2.3.8 app.
For example, a user can only edit tasks the user created (aka "owns").
Should I do something like this:
user.can_edit_task(task)
or this:
task.can_be_edited_by?(user)
#Method needs to be passed in a user object (from controller). But then how
#can I access the current user in a "before_save" filter as below :
.
def User
before_save: check_permissions!
def check_permissions
#this way?
raise some_exception if task.can_be_edited_by?(user)
#or this way?
raise some_exception if self.can_edit_task?(task)
end
end
Any tips on how to go about this?
Please try to adopt some of existing plugins like cancan
It will save your life.
Related
So if I have a User, and he can create/update his Service, but he cannot :publish(bool) them, what would be the best solution to skip this field from the update_params hash?
I found this similar question, but it links to CanCan 2.0 which never got released, and I was wondering if there is a way to solve this problem using CanCanCan, but could not find anything in their documentation. Many thanks!
As far as I know you can't. I would have a seperate publish action. So I'd omit publish from the form and the service_params entirely.
Then I'd do something like this:
Class ServicesController
def publish
#service = Service.find(params[:id])
#service.update_column(:publish, true)
end
end
Then in ability
can :manage, Service
cannot :publish, Service
Alternatively you could do this (assuming you have a current user and something defining them as an admin)
def service_params
if current_user.admin?
params.require(:service).permit(:my_field, :publish)
else
params.require(:service).permit(:my_field)
end
end
So you'd be omitting it from the parameters if they're not an admin. In this case you'd probably want to hide the fields in the view dependent on whether or not they can change the field.
I'm currently trying to implement simple audit for users (just for destroy method). This way I know if the user has been deleted by an admin or user deleted itself. I wanted to add deleted_by_id column to my model.
I was thinking to use before_destroy, and to retrieve the user info like described in this post :
http://www.zorched.net/2007/05/29/making-session-data-available-to-models-in-ruby-on-rails/
module UserInfo
def current_user
Thread.current[:user]
end
def self.current_user=(user)
Thread.current[:user] = user
end
end
But this article is from 2007, I'm not sure will this work in multithreaded and is there something more up to date on this topic, has anyone done something like this lately to pass on the experience?
Using that technique would certainly work, but will violate the principle that wants the Model unaware of the controller state.
If you need to know who is responsible for a deletion, the correct approach is to pass such information as parameter.
Instead of using callbacks and threads (both represents unnecessary complexity in this case) simply define a new method in your model
class User
def delete_user(actor)
self.deleted_by_id = actor.id
# do what you need to do with the record
# such as .destroy or whatever
end
end
Then in your controller simply call
#user.delete_user(current_user)
This approach:
respects the MVC pattern
can be easily tested in isolation with minimal dependencies (it's a model method)
expose a custom API instead of coupling your app to ActiveRecord API
You can use paranoia gem to make soft deletes. And then I suggest destroying users through some kind of service. Check, really basic example below:
class UserDestroyService
def initialize(user, destroyer)
#user = user
#destroyer = destroyer
end
def perform
#user.deleted_by_id = #destroyer.id
#user.destroy
end
end
UserDestroyService.new(user, current_user).perform
How do I execute a particular function after a user has signed up.
(I wanted to add to one of my associations, I already have it coded in a non-devise rails but now I need it here)
Device has provided helper action 'after_sign_in_path_for' you can override it within your application controller.
def after_sign_in_path_for(resource_or_scope)
.... #write your customize code or call any method
end
For sign up it would look like:
def after_sign_up_path_for(resource_or_scope)
if resource_or_scope.is_a? User # and perhaps other conditions
#... do something, go somewhere
else
super
end
end
Ofc. Assuming that your Devise user model is called User.
You can use the after_create callback in your User model.
The following guide has tons of examples: http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html
Hi I have a rails App which displays always the company name on each page.
Since a logged in user can have multiple companies she belongs to.
User and companies are stored in the db.
I use authlogic for the user management.
Now I do not want to hit the database on every postback or page change
What would be best practise to chache/store the company until the logged in users changes or the user selects a different company? Something like global instance vars for a given user.
I started with this in my application_controller
def current_company
return #current_company if defined?(#current_company)
#current_company = Account.includes(:users).where(:users =>current_company)
end
and I realized that I am still hitting the db...
Is the session the recommended way or what would be best practice for this...
Thanks for the help in advance
||= way:
def current_company
#current_company ||= Account.includes(:users).where(:users =>current_company)
end
memoize way:
def current_company
Account.includes(:users).where(:users =>current_company)
end
memoize :current_company
Differences between this method and normal memoization with ||=
http://apidock.com/rails/ActiveSupport/memoize#447-Differences-between-normal-or-assign-operator
#tadman, you are right but from my point of view depends how complex its the method that you are trying to "cache". For simple cases I prefer ||=
I think this is what you're looking for
https://github.com/nkallen/cache-money
I need to find the current logged in user in my model.
I defined cattr_accessor current_logged_in in User model.
Now, when a user logs in I set User.current_logged_in = current_user.id.
Later, in other models I access the variable using User.current_logged_in_user. As of now it works.
Is it the right way to implement this?
A good way to implement it, unless you're just doing your code as a learning exercise is to use a plugin like devise.
That said, you should avoid accessing the current_user in models. current_user is a session thing and should not be tied to the model. Instead pass in the current_user as a parameter to methods in the model. Something like:
def can_delete_item(user)
if user.is_admin?
....
else
.....
end
.....
end