Ruby on Rails private link sharing: Google Docs Style - ruby-on-rails

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 .

Related

How to implement per-page authorization in 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.

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.

Rails: How to restrict actions to only certain users?

I am currently doing a project for uploading pics. There are users, albums, and pics. I added a friendship model so that people can friend each other like a social network. However, I noticed that I put a lot of <% if current_user.friends.include?(#user) %> in the view to check if the user of the page I'm showing is a friend of the logged in client, and therefore allowing them to have certain privileges and forms and etc.. Is there a better way or place to do this than to pollute my views with if/else statements ? Also, I don't feel like my method is very secure since someone could always manually enter the url and mess with info that they're not supposed to.
You want an authorization framework such as CanCan.
In an ability file, you configure it that a user can, say, view something or edit some other thing, only if the user is a friend of the owner. Then in the view or the controller, you can just check that the user is authorized to do the appropriate action.
For specific details about setting up an ability based on details of the models (i.e. whether the owner is a friend of the current user), go to this documentation and look for "Hash of Conditions".
Consider using something like the mosaic-access gem, which allows you to white/blacklist controllers and specific actions for the currently logged in user.

Devise, requiring user to accept terms of service?

I want to mandate that all users accept terms of service, much like is described here:
Ruby on Rails / Devise - Bypassing custom validation option in model when resetting password
However there are 2 requirements that make the above approach not fit.
1) Users are not self registered, rather they are created by a system administrator. The administrator obviously can't accept terms and conditions on the users behalf. The user should be prompted to accept terms and conditions on their first login.
2) If the terms and conditions change after the fact. Users need to re-accept the new terms on subsequent logins.
These seem like pretty standard workflows. Does devise provide any way to accomplish this?
If not, I am thinking this check will need to be implemented in a before_filter on my application controller. (Feels dirty mixing authentication with the application logic...)
Will I have to resort to this or can anyone suggest a better way?
Thanks
this is not something devise would handle and devise should not handle this because a TOS does not pertain to authentication.
What you could do is something like implement a state machine but that may be more complex than what you need.
The simple way would be to add a accepted_tos boolean field to your user model and tie that into a before_filter then you would set the accepted_tos to false everytime the TOS is updated.
Couldn't you theoretically just disable moving further in your app using JQuery?
Disable your form "continue" or "log in" button until they have checked a box or hit a continue button, then enable your log in button once they have.
If you need to store that information just make a column in your database on the user which gets posted too when they check the box or hit the continue button, or you could even utilize the number of log in counts as a way of knowing.
If user.logs > 0 then the user has already accepted the terms.
If user.logs == 0 then the user needs to accept the terms before they log in.
I guess according to your question it seems like tying everything into Devise sounds overly complex.
Hope this helps.

User profile/account URLs

I'm required to provide functions for both users and administrators to edit account and profile details in a web application. An example of a URL for the public side of these profiles is:
http://example.com/user/joe
I'm still torn between two ways to design these URLs. I've thought of either this:
http://example.com/user/joe/edit
Or something non-specific and separate to the profiles:
http://example.com/account
The benefit of the first one is that it allows administrators to do their job through the same functions. This avoids building a whole different backend specifically for administrators. I suppose the negative here is that I'd have to be careful with authorization and make sure nobody can edit what they are not supposed to edit.
The second is a more standard way of doing things, it'd turn out to be simpler and easier to secure, though it means a separate interface for administrative users.
What is SO's opinions on this? Are there any more pros/cons for either way? Which method would you recommend to use?
I would have a different view for the administrator with such a security sensitive area. It makes things much more explicit having a separate view. It is likely even an administrator would only be able to edit certain user information and thus have a different view to the user editing themselves.
It makes the authorization much clearer even if the two views shared a common edit form
If you are using an MVC approach, then my suggestion would be:
http://example.com/user/edit/1234
or
http://example.com/user/edit/joe
Where user is the controller, edit the controller method and 1234 or joe the user id or username respectively.
But as Gumbo commented, administrators should not be allowed to edit user information. They should have some mecanism to disable the account in case of a profile has offensive content or false info. Forcing the user to update it to get the account active again.
The way we do it is the admin and the user share the same view. Items which are admin-only are protected from editing or viewing by the user.
The reason for the single view is:
It reduces the number of 'moving parts' - when a new field is added to the user screen, it only needs to be added once,
It is easier to move items to/from the user's purview. If all of a sudden, management decides to allow a user to manage their "FizzBar" then we only need make the change in one place, and
It is easier to segregate the roles and the functions at the controller level.
I think that you should go with the second approach. It's more secure and flexible, and shouldn't be harder to code than profile editing the profile inline.

Resources