Rails authorization? check in Model vs controller - ruby-on-rails-3

I am doing a check on a user model to determine whether s/he has one or more task_list, if she has more than one task_list only then she is allowed to delete it, otherwise an exception is thrown. I basically have an method called delete_list in the user model to allow for short hand deletions such as user1.delete_list(list1)
I am debating whether to put the check in CanCan where it would be apply as a before filter on the controller or whether to have it in the user model as well . What is the recommended practice?

I think a good DRY approach to this would be to create a method in your model that tests whether a delete is allowed. Then use that method from your controller or from ability.rb. IMHO I think having complicated permission/business logic decoupled from CanCan is better when there is a chance you might change to a different permission system in the future.
In your model:
def can_destroy_list(list)
... Do check here ....
end
In ability.rb
can :destroy, List do |list|
user.can_destroy_list(list)
end
Your controller and views can then also use can_destroy_list directly on the model instance if nessary or use: if can? :destroy, #list

Related

Is there a Rails convention for how to structure nested models using FactoryGirl?

There is a part of BDD that I am really confused about. I have all kinds of different request specs to test. With the following structure:
User has_many Products
Product has_many PriceLevels
PriceLevel has_many Prices
I am confused about how to go about setting up factories using FactoryGirl. I am familiar with the concept of associations and traits. I am familiar with the concepts of build and create. But I am unsure in what situations to use these.
The best way I can communicate what I am confused about is to give some examples.
I have a request spec to test User registration. I don't use a factory here for obvious reasons. This I understand.
I have a request spec to test a User creating new Products. I create a user factory. I assume I need to use create instead of build here, because the CRUD methods in the ProductsController do an AR find on the id that is passed via the URL. Correct me if I'm wrong here.
I have a request spec to test adding different PriceLevels for a Product. Here I am using two factories: User and Product.
I have a request spec to test adding different Prices for a User's Product's PriceLevel. Now I'm up to 3 factories: User, Product, PriceLevel.
Now what if Price has a has_and_belongs_to_many with something like Currency? The factories are getting out of hand.
I'd say by the time I get to PriceLevel then I want a single factory that will let me create the whole structure at once. And yet, I don't want to create whole structure every single time. Plus, these factories can be created from the bottom up or the top down.
Which way is better, bottom up or top down? Can I create DRY code that gives me options on a single factory or creating the whole shebang at once? Can I wrap after(:create) blocks within traits? Would I ever use build vs create in request specs?
Thanks very much for your attention!
First of all, build vs create in Request specs:
I commonly use build or build_stubbed in controller specs by stubbing Model.find to return the built instances. However, mocking in general should be avoided in Feature or Request specs, so go ahead andcreate things here.
Now as for the main problem:
This sounds like a perfect case for traits. In general, base factories should have only the attributes required to make a valid model. Traits can then be used to make handy "aliases" for common or verbose scenarios.
You can use before(:create) to build and assign related models. Rails will handle saving everything together when FG eventually calls save/create internally.
Example:
factory :user do
# ...
trait :with_products do
before(:create) do |user|
user.products = build_list(:product, 3)
end
end
trait :with_priced_products do
before(:create) do |user|
user.products = build_list(:product, 3, :with_prices)
end
end
end
factory :product do
# ...
trait :with_prices do
before(:create) do |product|
product.prices = build_list(:price, 3)
end
end
end
factory :price
If you wanted to customize how many products/prices are added at build-time, you can just add ignored attributes and use them via the 2-argument form of the before-hook.
ignore do
number_of_products 3
end
before(:create) do |user, evaluator|
user.products = build_list(product, evaluator.number_of_products)
end
If you wanted to be able to use these with_ traits in build or build_stubbed scenarios, you're going to have to duplicate the relevant hooks using the correct strategy in each case. There's currently no easy way to say "add some relations using the same strategy as the model", though that's a wishlist item for myself too.

Rails 3: Register logs in model or controller?

I have a Log model which registers some actions done to a Foo model. That means, every time I create or update a Foo instance, I have to create a new Log instance for its table to register the corresponding action. Where does this creation belongs to? To Foo's model or to Foo's controller?
I was thinking, in the model I can use the before_save method and that would keep my controller skinny, but I'm not sure if it's right to put that logic there. Thanks
Logs can be used anywhere, it depends on your need to put them into right place.
For this case, your logging seems related to model changes but has little to do with HTTP requests, I think the better option would be model related place.
Option 1: after_save and after_update callback, not before_save. (You only want to log it after change already made effect)
Option 2: Model Observer.
I myself prefer Observer in this case because Log is not something inside this model so better not to use model callback. Also Observers allow you to add more things later easier. The downside is Observers are easy to be forgot, not a big deal if you can overcome it.

Override destroy and delete in Rails 3 and removing the callbacks

I want to block destroy and delete in the User model only and I want to block the callback. In few words I want to block any possibility to delete a user record. I tried by overriding destroy and delete methods but they are not called and the user is alway deleted.
class User < ActiveRecord::Base
def destroy
end
def delete
end
end
I do not want to use any gem related to this, so do not cite any gem. I am not sure if overriding those methods is a good idea, probably I should just create a 'deactivate' method and leave the default delete and destroy method there, just in case I will need them in the Rails console
Refer to Rails API Methods.
It's not necessarily a bad thing to override these methods provided you know what you're doing and the consequences. Sometimes it's the cleanest way to solve a problem.
My recommendation is looking at the following Rails API method documentation entries and ensuring you're not missing any important actions here.
#destroy
#destroy!
#delete
By creating those methods in your model, it will override anything in the inherited Rails API so it should work just fine.
I'm not sure that I understand your question fully.
One way to do this is to make the destroy and delete methods private.
You say that the methods never get called, but that the objects are being deleted anyway. How does this happen? Are you deleting them from a collection? You may need to override a class method of User rather than those two instance methods.

How to access current_user from a Rails Notification?

I'm building an audit trail that needs to know which user is currently making the request. My audit trail is built using ActiveSupport::Notifications to receive an even that needs to be audited.
What I would like to do is use an ActiveSupport::Concern to encapsulate the logic for my audit needs, so that I can easily add auditing to any model in my system.
In general this is easy to do. I even blogged about it a while back. However, I'm having a hard time figuring out how to get the current user that making the request to the web server, so that I can log who is making what changes in my audit trail.
I know there are a ton of questions about "how do I get current_user in my model" but I'm not asking about doing it in a model, so I'm hoping there is a better set of answers. Since my audit code is infrastructure related, I am hoping that there is some way I can tap into the current request that is being processed, or something else that would definitively tell me who is currently logged in / making the request.
I've read a lot of "answers" that say to use thread storage and put the current_user in there. I don't like this answer for many of the reasons that others don't - there is no guarantee that thread storage is safe. it could bleed across multiple requests if the server uses the same thread to process multiple requests, etc.
so... given that I am not trying to access current_user from my model, but rather from either an ActiveSupport::Concern or ActiveSupport::Notifications event subscription, are there any good options for me to know who the current user is?
Update
I'm using devise for authentication, which uses Warden on the back end. devise retrieves the current_user by calling request.env['warden'].authenticate(:scope => :user) (assuming i use a "User" model for authentication).
Is there a way for me to access the current request object from within my concern or notification subscription? Back in my .NET days, I would have been able to call HttpContext.Current.Request and all would be good. What's the equivalent in Rails?
Rails' ActionController::Instrumentation has explicit support for this, using append_info_to_payload.
Add a method to your ApplicationController:
def append_info_to_payload(payload)
super
payload[:current_user_id] = current_user.try(&:id)
end
now, when your observer is called back, the information will be in the event.payload:
ActiveSupport::Notifications.subscribe /process_action.action_controller/ do |*args|
event = ActiveSupport::Notifications::Event.new(*args)
current_user_id = event.payload[:current_user_id]
# do something interesting with current_user_id here
end
You already have the answer, what you're doing is the same as when people are accessing the request in models. The current_user is just a method defined on your ApplicationController. When you're not in a controller or other class that inherits from it, you can't access that method.
HttpContext.Current.Request << I would bet a lot that this uses thread storage. Any other solution we find will also be thread storage at some level or another.
Either pull out what you need from the request in the controller and pass it down as parameters, or use thread storage -- but this is inherently dangerous anyway. What if you start using delayed job to do the notifications or something?

declarative authorization filter_access_to

I am attempting to secure a Rails3 controller using declarative_authorization.
The controller has the 7, RESTful actions, three custom member actions (activate, deactivate, copy), and one custom collection action (public). The 'public' action only returns one record, however.
Only the custom collection action (public) should be available to authenticated users; the remainder are only available to the current_user.
has_permission_on :foos, :to => :public
has_permission_on :foos, :to => [:full_control, :copy, :activate, :deactivate] do
if_attribute :user => is {user}
end
privilege :full_control, :includes => [:index, :show, :new, :create, :edit, :update, :destroy]
The 4 custom actions are defined in the routes.rb file:
resources :users do
resources :foos do
collection do
get :public
end
member do
post :activate, :copy, :deactivate
end
end
end
A User :has_many Foos; A Foo :belongs_to a User.
The 'standard' access control (filter_resource_access :nested_in => :user), as defined in the FoosController seems to control access to the 7, RESTful actions, but fails to control access to the other 4 (as expected).
When I change the FooController to:
filter_access_to :all, :nested_in => :users, :attribute_check => true
I get an error that reads "Couldn't find Foo without an ID".
Questions:
The documentation seems to suggest that a :before_filter will be called automatically to load the Foo model when filter_access_to is used. Am I mistaken? Do I need additional configuration of the filter_access_to? Do I need to manually configure a :before_filter?
Do I also need to add using_access_control to the model for my purposes? I'm a little unclear when one needs to add access control to the model when there is access control in the controller.
The documentation describes a privilege named 'create'--it is defined as: privilege :create, :includes => :new. In addition, to the :new action, does this privilege automatically include the :create action as a consequence of its name?
If the authentication_rules.rb file is changed, does the server need to be restarted for the new rules to be applied?
I think that automatic before filter, if it exists, is pretty limited. I've always had to write my own before filters as appropriate to the context. Eg--for the the index view unless permission is the same for all instances of the model, you'll need to have a model instance appropriate the the current context. Like a user viewing an index of posts, you would want to make a dummy new post for the user, or load their first but dummy is safer since first might not exist. Generally I have a dummy constructor for index and everything else can test whatever is actually to be seen (or touched).
Controller has been good enough for me so far, so model level is certainly not REQUIRED. That's not to say it wouldn't add some extra safety, but I'm not expert in when exactly that would become important. I'd hypothesize it would be when you have a model touched by many controllers (eg modeless controllers) and you want to be sure nothing sneaks by.
I haven't used privileges so I'm not sure, but I would guess that magic inheritance you describe doesn't happen. Creating permissions that aren't specifically requested seems like it would be a very sloppy approach.
No, no restart required--at least not in development mode.