views:

67

answers:

2

I find a common issue in my RESTful Rails apps controllers that respond to multiple formats (HTML, XML, etc). The issue is that, for any given method (INDEX, CREATE, NEW, EDIT, SHOW, UPDATE, or DESTROY) I want to restrict access to admin users for 1 format, but not others. Of course I already have a "before_filter :admin_required" for this, but it is useless unless all formats for a given method adhere to the same permissions (which, many times, is not the case). I end up just having to open up the entire method and then add a "head :bad_request unless current_user.is_admin" to any of the formats that need protecting. This works, but for some reason feels wrong to me. It seems like I should be able to add a format parameter on the before_filter somehow, so as to keep things tidy. How do you guys do it and why?

UPDATED QUESTION:

I think people are not fully understanding my situation, so let me try to re-explain. First of all, just know that this already works for me and is secure and I have no problems with it. So basically, I have decided that HTML pages will only be for admins to create/update/edit/delete objects. The normal users will ONLY interact with the app via XML thru a flash interface. What this means is that there are essentially 2 different paths of execution (each with their own distinct code/logic etc.) for each action. So when the request comes in, the format dictates which path is taken. There are checks in each to make sure that no malicious requests are allowed, and a head :bad_request is returned in these cases. There is no way to "craft an XML request outside of flash" and somehow make the app do something that it otherwise shouldn't. The app could care less if the XML request came from Flash or not. It does not matter one bit. The only thing that matters is whether or not the request is valid based on the credentials of the user and attributes posted - not where it came from. Anyways, this all works great, the only downside is that a lot of my actions that would normally just have a "before_filter :admin_required" can't use that anymore. They need to be opened up to everyone essentially, and then I have to manually do a "head :bad_request unless current_user.is_admin" on certain action/format combination's that require it. I was just hoping that I could have more fine-grained control over the filters in the controllers so that I could do something like "before_filter :admin_required, :format => html"

+1  A: 

I'm not sure if fully understand you, but you can access the format parameter in your before_filter, eg:

before_filter :admin_required

...

private
def admin_required
  return nil unless params[:format]

  case params[:format].to_sym
  when :xml
    head :bad_request unless current_user.is_admin
  end
end

As @jdl mentioned, it sounds like it might be a security hole to do this. Usually you would render the same object(s), just with different formats, which means an attacker would just look at a different format to see all the information.

I can see where it might make sense if you want everybody to see the html, but only admins can see xml, but then users could just screen scrape the page to get equivalent information.

In your case, an attacker could use network traces to watch the xml requests that the flash application sends, and then craft their own requests based off that. I think you would have a hard time determining if the request really came from flash, or if it had been spoofed. This may not be a problem for you, because of course your html pages are always protected, but I think you would have to assume that anybody can see the XML versions.

ngoozeff
Yes, I suspected this would work, but it just seemed like there might be a more elegant way to achieve this. i.e. tied into the filter.
Buddy
Can you explain why this is a security hole? As long as I get my logic correct, and make the proper checks, any malicious requests should just get a "head :bad_request" right? Is there something I am missing?
Buddy
@Buddy: Updated.
ngoozeff
A: 

I think I foound a better way to handle this. First, get Rails 3. Then, you can restrict by format on the routes. Couple this with namespaces, and you can achieve what I was trying to do in a cleaner way.

Buddy