views:

73

answers:

5

What is the best practice for excluding certain fields/data in a RESTful response if the user requesting it shouldn't be able to see all of the data?

Example:

Person has a First Name, Last Name, and Date of Birth.

Both authenticated and non-authenticated users can make RESTful requests to /people.xml to get a full list of people. However, only authenticated users should be able to view all of the information. Non-authenticated users should only have the First and Last Name fields returned (excluding the Date Of Birth data).

Should the Person controller check for authentication before building the response? If user is authenticated they get everything, else they only get a subset? Does that break any rules of REST where /people.xml can send two separate results?

+3  A: 

By the book, REST says that one resource should return the same result every request.

I would create a namespace with "unauthenticated" or something and make

/people.xml for authenticated

and

/unauthenticated/people.xml

They could be the same controller building different xml results for each request.

robertokl
Yep, by definition the API shouldn't have multiple state based on details of the client invocation. Either provide a different API, or provide "private" "public" entry points. Your implementation code could be the same, just invoked from multiple endpoints with various parameters such that the API was consistent
Taylor
@robertokl Would you mind pointing me to an authoritative reference where it says a resource should return the same result every time? And could you tell me if you believe that GET /CurrentWeather could be implemented RESTfully?
Darrel Miller
@Darrel Miller http://www.infoq.com/articles/rest-introduction read the Communicate statelessly section. I think it explains well why it should return the same RESOURCE every time. And this answers your second question. The resource should be always the same, but that does not mean that the data of that resource can't change. I could change the name of a person and the data would be different. The same way I can change the current weather value. If 2 browsers in different places access the same URI in the same exactly millisecond, they should see exactly the same thing.
robertokl
@robertokl You are absolutely correct. However, I may have misread your original statement to mean something different. Just wanted to clarify what you meant.
Darrel Miller
I disagree. /people.xml can return different representations of the resource based on authentication information. Having two URLs pointing to the same resource in different representations violates REST (for the same reason, it should really be /people instead of /people.xml; content negotiation happens using headers, not using the URI).
dzuelke
@robertokl I have two problems with the answer: 1. What URI should I use if I want to refer to the "list of people"? REST benefits from having one URI for each resource, although it's not a constraint per se. 2. What if two sets of credentials should return two resources, e.g. only the list of people that are my friends? (**EDIT** I guess I'm saying that if you define a resource to mean "the list of people you are allowed to see" then the response can vary with who is asking.)
mogsie
A: 

You can use the :only option of the to_xml method to restrict the fields returned by the controller, i.e.:

def show
  @person = Person.find(params[:id])
  payload = current_user.nil? ? @person.to_xml(:only => 
                  [:first_name, :last_name, :dob]) : @person

  respond_to do |format|
    format.xml  { render :xml => payload }  
  end    
end

I use serialize_with_options plugin, as most of the view data access configuration can be done at the model level.

class Person
  serialize_with_options(:anonymous) do
    only   :first_name, :last_name, :dob
  end
end


class PersonController
  def show
    @person = Person.find(:params[:id])
    respond_to do |format|
      format.xml { render :xml => current_user.nil? ? @person.to_xml(:anonymous):
                             @person   }
    end    
  end
end

Reference

1 serialize_with_options

KandadaBoggu
This is a really sophisticated solution. If you don't mind breaking some Rest rules, I would go with this option.
robertokl
Data granularity is not specified in the REST rules. The implementer can decide the scope of the data exposed to the client. This is a common technique used in order to expose multiple views of the model data based on user privilege.
KandadaBoggu
+1  A: 

Consider that different representations of the same data (depending on the user) could have surprising consequences if you have a cache somewhere in the middle. If the fully authenticated user makes the request first, then followed by the 'lesser' user, they might see surprising result: The same view as the fully authenticated user before. That's because there is no difference in the two requests as far as the cache is concerned (unless you make it aware of these issues). So, tread carefully.

I would also suggest separate namespaces, as proposed by Robert.

jbrendel
Responses to authenticated requests are by default not cached, and if they are, I believe the cache would cause a heck of a lot of other problems :-).That said, caching the response of an authenticated request could be useful as long as the cache does a validation with the origin server in order to know if it can serve the cached response to the other client.
mogsie
A: 

The same URL can yield different representations, depending on the request-headers. For example, Accept is commonly used to control the format of the response (f.ex. XML or JSON). Likewise, authentication-headers could be used to control how much is returned for an entity.

troelskn
A: 

No, that's fine. It's the same resource, but with a different representations based on the authentication information. You could also serve different versions depending on what the Accept header contained (you should use that one instead of file extensions like .xml, by the way), or you could serve different language versions, or you could render the page different if the logged in user has specific personalization options defined. It's all legal. Consider a website that has a login box. If you're logged in, the page will be different. That's the same thing, except it doesn't specifically affect the enclosed information per se. Controlling caching and so forth in these cases is exactly what Cache-Control, Vary and friends are for. Also see http://www.subbu.org/blog/2007/12/vary-header-for-restful-applications

dzuelke