How to implement per-page authorization in Rails - ruby-on-rails

Rails has some very good methods for role based authorization, e.g., cancan
But how can you grant authorization to specific pages, rather than controllers/actions.
For example, take the case of an app that contains a receipt or invoice model. A User can log in and generate an invoice. The User then needs to send his customer a URL to that invoice. The customer should not be able to access invoices of other customers, particularly important as Rails generates sequential and easily guessable path names (e.g., /invoices/1, /invoices/2, etc.).
Ideally the customer should not need to signup to view these pages. One solution might be to generate a random password on Invoice creation, and send this to the customer to unlock that page specific page.
This sounds like an issue that should be reasonably common, but after Googling I have not found much information of use (though I may be using incorrect search terms).
So I would like to know:
Is this something I should be attempting with Rails?
Are there any gems or example apps that I could study?
What are the potential considerations/ pitfalls of this approach?

Hi if the user you wish to have access was registered in the system it would be no issue at all as you could generate a permission record.
How ever your concern with the predictable urls can be easily solved by :
FriendlyID(https://github.com/norman/friendly_id)
or my personal favourite
Vanity permalinks (http://blog.teamtreehouse.com/creating-vanity-urls-in-rails)

Ideally the customer should not need to signup to view these pages. One solution might be to generate a random password on Invoice creation, and send this to the customer to unlock that page specific page.
So anyone with the information should be able to access it. One solution is to obfuscate the url or use a UUID, so don't give out /invoices/1, instead give /invoices/8a20ae59-30d5-41b6-86d3-ac66e3b43e9d. The url is unguessable. Still the url is the only piece of information one needs to access the contents.
If you want two-factor authorization, then the easiest way is to use http basic auth, generate a password from the url and your secret_base, and send it to the user separately. As it's generated, you don't need to store it, you can always check it by generating it again.

Related

How do I hide my API calls / routes from users of my Rails app?

I'm writing an app that make some calls to my API that have restrictions. If users were to figure out what these url routes were and the proper parameters and how to specify them, then they could exploit it right?
For example if casting a vote on something and I only want users to be able to cast one vote, a user knowing the route:
get '/castvote/' => 'votemanager#castvote'
could be problematic, could it not? Is it easy to figure out these API routes?
Does anyone know any ways to remove the possibility of this happening?
There is no way to hide AJAX calls - if nothing else, one just needs to open Developer Tools - Network panel, and simply see what was sent. Everything on clientside is an open book, if you just know how to read it.
Instead, do validation on serverside: in your example, record the votes and users that cast them; if a vote was already recorded by that user, don't let them do it again.
Your API should have authorization built into it. Only authorized users having specific access scopes should be allowed to consume your API. Checkout Doorkeeper and cancancan gems provided by the rails community.
As others have said, adding access_tokens/username/password authorisation is a good place to start. Also, if your application should only allow one vote per user, then this should be validated by your application logic on the server
This is a broader problem. There's no way to stop users from figuring out how voting works and trying to game it but there are different techniques used to make it harder. I list some solutions from least to most effective here:
Using a nonce or proof of work, in case of Rails this is implemented through authenticity token for non-GET requests. This will require user to at least load the page before voting, therefore limiting scripted replay attacks
Recording IP address or other identifiable information (i.e. browser fingerprinting). This will limit number of votes from a single device
Requiring signup. This is what other answers suggest
Requiring third-party login (i.e. Facebook, Twitter)
Require payment to cast a vote (like in tv talent shows)
None of those methods is perfect and you can quickly come up with ways to trick any of them.
The real question is what your threat model and how hard you want it to make for users to cast fake votes. From my practical experience requiring third-party login will ensure most votes are valid in typical use cases.

Ruby on Rails authentication without user name?

In all of my Rails applications I have a User model with name, email and password attributes (among others).
This seems to be the standard approach when building Rails apps.
The more Rails apps I build, the more I begin to wonder why the User.name is even necessary.
Wouldn't it be easier to just omit the user name everywhere right from the start?
From a user perspective, the sign up process will become easier. Instead of filling in four fields (username, email, password, and password confirmation), the user will have to fill in only three.
According to some usability experts this might increase the number of sign ups.
In addition to that, users will also have to remember less data, i.e. only their email address (which most people have memorized anyway).
So what might be negative implications of this approach?
I couldn't think of any so far.
You might need to make emails from your app personalized, maybe with greetings such as `Dear <%= username %>.
This doesn't mean you have to put name as one of the sign-up fields. You can put in the update form only, when the user edits their profile. Then you can make the edit_user_registration_path the after_sign_up_path_for devise.
I don't think using username is "standart" approach with rails apps. In fact, devise's vanilla approach is using only email on models.
However, being able to accept username or email has many other advantages. You may have other scenarios where users do not register at all. I mean, perhaps you are also creating accounts for users without any registration and you don't know their emails, if so using email will not be an option.
In some applications, we use more then 3 authentication strategies. Some users do not have a username or email at all..
In short, i think it really depends on your scenarios. But i am sure that using both email and username is not a rails convention.
If the main goal is a frictionless signup process then an OAUTH strategy would be the best way to go (4 fields of info down to two clicks), however you may want to collect the user info at a later time for a more personalized feel depending on what info you can capture from the callback.

Ruby on Rails private link sharing: Google Docs Style

What would be the best way to go about giving users the ability to share a private link that enables anyone who clicks it to view a certain page/document/item that have privacy restrictions in place?
In my case:
A User creates events which are limited to certain groups of relationships in the database (namely: friends, friends of friends, etc.) I have a :before_filter in the event controller that checks the eligibility of the current logged in user to make sure that that user has permission to see the event. If they don't they get booted to the root page with an error message.
However, I want a special scenario to exist where a user can create an event with those same privacy settings and IN ADDITION, be able to share a special link with his or her friends via e-mail, facebook, etc. Those users do NOT need an account (but will need to make one in order to sign up for the event). This is important because there is also a :before_filter in the application_controller which makes sure a user is logged in.
I'm thinking there is something I could do with routing here... Right now I just have the simple /events/72 setup. Should each event have two different links: a normal one, and a "special code" version which enables them to bypass those two :before_filter?
What are people's thoughts?
I agree with David Lyod's answer (separating this concern in a different controller).
But for creating the hash I strongly recommend you salting the hash with some secret phrase.
require "digest"
Digest::SHA512.hexdigest("#{created_at}#{user_id}.mysupersonicsecretSALT")
Doing this it is not possible, without the knowlegde of the secret phrase, to calculate the hashes and test them against your system until it hits an existing one.
If you're handling sensitive data you should not be lazy.
Cheers,
Lukas
I would have a separate controller that uses a hash value to reference the event.
Something simple like the created_at + user_id hashed to create a unique reference.
You could also simply skip the check on a certain action but I would much prefer the first solution .

Asp.NET MVC Customer Application

I'm designing (and developing) web software that will allow the general public to sign up for a service, become a customer, and exchange fairly sensitive data.
I'm working through the documentation and the tutorials, and of course the RESTful pattern adopted by the default routing in ASP.NET MVC is to do URL's like this: /customer/edit/3487.
I guess I am a little squeamish about displaying such technical details as customer ID in the URL bar.
What do the smart kids do these days? Does RESTful have to mean "put your record ID's on display"?
Edit: In an ASP.NET WebForm I would have stored this in the session, I think. But I'm finding that this is discouraged in ASP.NET MVC.
Edit:
I do not intend to rely on security through obscurity.
That still doesn't mean its a good idea to give the users any ideas, or any information about the underlying data. Let's say I have an app that's publishing information about the different business in a Chamber of Commerce, to be arbitrary. Once you are logged in, you have an administrative right to click on every business in the directory and see them all - but the application is supposed to spoon feed them to you as search results or the like. Just because the user technically is allowed to access all records, this doesn't mean it should be trivial for you to write a screen scraper that downloads all of my content in a few minutes. As well, the user can just look at customer ID's and make a guess about how many customers I might have. There's lots of good reasons not to display this.
As long is there is proper authentication and authorization being done on server side then displaying ids is not an issue.
Otherwise just try to encrypt the particular id or username in the URL, this way it will be difficult for the attacks.
You don't have to put the Id in the Url, you just need to use a unique value or unique combination of values to find the data you want to display.
I'd think that the actual bussinesses name would be good and also look good in the Url. So you would have something like this:
/Business/View/theouteredge/
Or if the business name is not unique you could use a combination of business name and zip/postal code.
/Business/View/theouteredge/78665/
You would have to write a new route to handle this.
routes.MapRoute(
"Bussiness",
"Business/{Action}/{name}/{zip}/",
new { controller = "Business", action = "Index", Name = "", PostalCode = "" }
);
All this action would need to be secured with the [authorize] attribute, or the controller its self.
If you also decorate your actions with [authorise] then if another user does use the id from another user, they will automatically be challenged for a login.
It's 6 of one and 1/2 dozen of the other as to whether you use an ID or a Name. Eventually they both resolve to a record.
The important thing is to only allow authorised persons to view the data by allowing them to log in.
I've got a site which has sensitive data but only if you are the holder of that info can you see it and I do that by decorating my actions and checking rights etc.
I think that putting an ID in a url is fine -- as long as it is a Surrogate Key. The key has no value, except to identify a record. Just make sure that the requester is authorized before you send sensitive data back to the client.
Update:
I can see how having a number as part of your URL is undesirable. After all, a URL for a web app is part of the user interface, and exposing such internal details can take away from the UI's elegance. However, you are faced with limited options.
Somehow, you have to identify the resource that you want to get. The crux of REST (IMO) is that a request to a server for a particular resource must be described entirely by the request. The key for the item you want has to be encoded into the HTTP GET somehow. Your options are: put it into the URL somehow, or add it to a cookie. However, adding a key to a cookie is frowned upon.
If you look at this site you will see the question id in the url. If you view your profile you will see your username. So you would probably want to use usernames intead of an id.
If you're really concerned about it you can use a Guid, which isn't very user friendly but would be very hard to guess. :)
If you use some other way than customer id simply because you're concerned about security, then that means you're using security through obscurity, which is a bad idea. Proper authorization would require something like you either 1) have to be logged in with that customer id, or 2) be logged in as an admin, to have that request succeed.

Associating source and search keywords with account creation

As a part of the signup process for my online application, I'm thinking of tracking the source and/or search keywords used to get to my site. This would allow me to see what advertising is working and from where with a somewhat finer grain than Google Analytics would.
I assume I could set some kind of cookie with this information when people get to my site, but I'm not sure how I would go about getting it. Is it even possible?
I'm using Rails, but a language-independent solution (or even just pointers to where to find this information) would be appreciated!
Your best bet IMO would be to use javascript to look for a cookie named "origReferrer" or something like that and if that cookie doesn't exist you should create one (with an expiry of ~24hours) and fill it with the current referrer.
That way you'll have preserved the original referrer all the way from your users first visit and when your users have completed whatever steps you want them to have completed (ie, account creation) you can read back that cookie on the server and do whatever parsing/analyzing you want.
Andy Brice explains the technique in his blog post Cookie tracking for profit and pleasure.

Resources