Use RESTful routes and POST parameters - ruby-on-rails

I have a website for a college that tracks student information and serves it up to faculty advisors, most of which is confidential. Many features in this site involve passing a student's ID number to the controller. Because student ID numbers are confidential I am curious if I can avoid having the student's ID appear in the URL string as a parameter. Here is what I have investigated so far:
Rather than passing a student's ID via GET I could POST the ID number. This would work fine, but then I am confused on how I could make use of RESTful route helper methods, when the router expects a GET request and I am sending a POST request. Is it possible to customize around this?
A second idea (which I fear might be a bit unelegant) is to store a hash in session data where some arbitrary number served to the user is the key to that students ID number. That arbitrary number appears in the URL string rather than the id number.
The other alternative I can think of is to not use restful resources at all. This is completely doable, but I want to see if there are any other options.
Or is there anything I'm not thinking of (very possible).
Thanks,

I think you may be confusing two different ideas here. Your students may have a unique id number to identify them in the educational system or the school records much like a National identification number. But you do not / should not use that number to identify the records in your rails app.
Instead you would have a normal auto incrementing column in the database to identify students in your application. There is no real reason that the number should be confidential.

The idea behind REST is to use each one of the methods supported GET, POST, PUT, and DELETE in the right scenario.
If the only concern that you have is that the student Id showing in the URL, I suggest that you use the record id (the one that is autoincremented automatically by the database) not the real Id of the student in the requests and add the real ID as an extra field in the table.
Cheers

Related

Handling URLs that change

If an example website contains blog posts created by users this would be an easy way to structure the URLs for each post:
www.example.com/jane-smith/my-first-blog-post
If the user was to change their username (which is unique per user) or the title of their blog post the URL would have to change to be updated. For example if the above user changed her username the URL could be like so:
www.example.com/jane-wilson/my-first-blog-post
A problem now arises as if people try to use the old URL, it would not work. How could this situation be avoided or worked around for an existing website without removing the feature to change usernames or blog post names?
A common way is to include a unique ID which never changes (for users and posts, in your case), and then 301-redirect to the current/canonical variant.
Example
This is what, for example, Stack Overflow is doing.
If you change your question’s title, the ID (36463097) will stay and the slug will change:
https://stackoverflow.com/questions/36463097/handling-urls-that-change
https://stackoverflow.com/questions/36463097/handling-urls-that-might-change
If you change your user name, the ID (2961662) will stay and the slug will change:
https://stackoverflow.com/users/2961662/psidhu
https://stackoverflow.com/users/2961662/john-doe
This also allows to have users with the same user name (in case you want to allow that):
http://example.com/23/john-doe
http://example.com/42/john-doe
Drawbacks
It’s not the best URL design:
/users/2961662/psidhu is not as beautiful as /users/psidhu
remembering a URL with such a cryptic ID is hard(er)
such an ID is not meaningful, so ideally it wouldn’t be part of the URL
Alternative
Track all changes and block user names that were once registered. So If I start as john and then change my name to john-doe, no one else may register john.
This is good practice for email addresses, and in my opinion it should also be good practice for every service that allows direct communication with users. If users get messages intended for the previous owner of that user name, that’s a serious problem; if users don’t get their favorite user name because someone else registered it before, it’s, at most, annoying.
So for each user you could track previous user names, and for each post previous titles, and then 301-redirect to the current/canonical one.

Rails 4.2 routing to ignore part of a URL

My first SO question... Please be gentle :)
I have a specific routing requirement for a Rails 4.2 application but I'm struggling to accomplish it. My client offers it's members a website to sell the company's products and earn a commission on sales (kind of like Amway does)... Basically a glorified affiliate program. The pattern of their URL's is https://www.company.com/membername/products/something-you-can-buy. The requirement is that they keep this pattern in the new application I am building for them.
The membername part could be anything, and this application will not have access to the existing user database... We need rails to ignore the membername part of the url and process routing based on the rest of the URL. So if the URL is https://www.company.com/hephalump/products/hamburger we need routing to ignore the hephalump bit, but still keep it in the URL, and process the routing based on the products/hamburger portion. The client also needs the hephalump bit to stay in there throughout the entire application and we need to be able to capture it as a param at the point of purchase (that's easy enough).
I've been bashing my head against the wall with this for two days with no luck... Any help would be greatly appreciated.
There are many ways to do that. Here's one:
get '/:_member/products/:product_name' => 'products#show'
Substitute the correct controller name prefix for products in products#show and the correct action name for show. In that controller params will have both :_member and :product_name keys, and you can just ignore the :_member key.
You can also add constraints on what pattern can represent a member or a product name, e.g.:
get '/:_member/products/:product_name' => 'products#show',
contraint: { _member: /\w+/ }

How do you create a unique identifier for users for tracking purposes?

I would like to be able to add a user referal params on all invite links sent out from my site... example:
http://site.com/invited_by?=ajraux
How can I generate a code that is short like "ajraux" for all users on my site? Does it need to be a field in the database? Can it be created on the spot? How are web companies doing this?
Thanks
You could create random numbers and encode them in base-36, something simple like this:
rand(1e12).to_s(36)
Generate one for each user on first use and store it with the user. Add a unique constraint on your random token (in both your model and the database) and generate a new one if you get a uniqueness violation. You might want to log a warning somewhere that you'll see it if you need to try more than, say, five times to get a unique value; if you start getting a lot of warnings then bump that 1e12 up to 1e15 (or higher).
That would give you a random looking token attached to each user, the tokens would be URL-safe, they're quick and easy to generate, you shouldn't get that many collisions, and it will be easy to backtrack from a token to the user.
One way of doing this is to use the unique identifier of the user, say "id", that's in the database, but this is also dangerous because you are revealing too much about your database.
So you can add a twist to the previous situation and encrypt and decrypt the id so that when it's in the url it is encrypted and then when you receive it you can decrypt it and use it.
As cjapes said, using ID is not a good solution and mu is too short's answer is good for most cases. However if you have a site which offers vanity URLs, like about.me does then each user will have a permalink kind of column storing their vanity URL. You can just use the value from that column when building the URL.
On the receiving end you can do:
#referring_friend = User.find_by_permalink(params[:permalink])

Prevent modification ("hacking") of hidden fields in form in rails3?

So lets say I have a form for submitting a new post.
The form has a hidden field which specify's the category_id. We are also on the show view for that very category.
What I'm worried about, is that someone using something like firebug, might just edit the category id in the code, and then submit the form - creating a post for a different category.
Obviously my form is more complicated and a different scenario - but the idea is the same. I also cannot define the category in the post's create controller, as the category will be different on each show view...
Any solutions?
EDIT:
Here is a better question - is it possible to grab the Category id in the create controller for the post, if its not in a hidden field?
Does your site have the concept of permissions / access control lists on the categories themselves? If the user would have access to the other category, then I'd say there's no worry here since there's nothing stopping them from going to that other category and doing the same.
If your categories are restricted in some manner, then I'd suggest nesting your Post under a category (nested resource routes) and do a before_filter to ensure you're granted access to the appropriate category.
config/routes.rb
resources :categories do
resources :posts
end
app/controllers/posts_controller
before_filter :ensure_category_access
def create
#post = #category.posts.new(params[:post])
...
end
private
def ensure_category_access
#category = Category.find(params[:category_id])
# do whatever you need to do. if you don't have to validate access, then I'm not sure I'd worry about this.
# If the user wants to change their category in their post instead of
# going to the other category and posting there, I don't think I see a concern?
end
URL would look like
GET
/categories/1/posts/new
POST
/categories/1/posts
pst is right- never trust the user. Double-check the value sent via the view in your controller and, if it does't match something valid, kick the user out (auto-logout) and send the admin an email. You may also want to lock the user's account if it keeps happening.
Never, ever trust the user, of course ;-)
Now, that being said, it is possible to with a very high degree of confidence rely on hidden fields for temporal storage/staging (although this can generally also be handled entirely on the server with the session as well): ASP.NET follows this model and it has proven to be very secure against tampering if used correctly -- so what's the secret?
Hash validation aka MAC (Message Authentication Code). The ASP.NET MAC and usage is discussed briefly this article. In short the MAC is a hash of the form data (built using a server -- and perhaps session -- secret key) which is embedded in the form as a hidden field. When the form submission occurs this MAC is re-calculated from the data and then compared with the original MAC. Because the secrets are known only to the server it is not (realistically) possible for a client to generate a valid MAC from the data itself.
However, I do not use RoR or know what modules, if any, may implement security like this. I do hope that someone can provide more insight (in their own answer ;-) if such solutions exist, because it is a very powerful construct and easily allows safe per-form data association and validation.
Happy coding.

Thoughts regarding model ids in rails routes and validation

I am new to RoR and started working on a typical 'has_many' association (ie. a user has many friends). I have everything working correctly, but I don't like having the ids exposed in the url. I find that I need to add extra validation in my controller to make sure the ids represent valid associations in case the user manually entered different ids.
Personally I would like to see the ids out of the url and passed via some other means but that is not always possible. Shallow nesting of resources will help reduce the number of ids I need to validate at least.
What is the RoR philosophy on this? I have not seen anything specific to this issue.
Thanks
the URL has parameters if it is a GET url.
Try using POST parameters, which means your url will no longer be cluttered. Note that a malicious user can still send a made-up POST request using curl.
My approach to this is implementing proper authorization. If the user requests information for an object he is not permitted to read, this should be handled by an authorization framework.
With CanCan or Declarative Authorization you can define rules that replace your "manual" (and error-prone) checks in controllers.
I like the IDs being in the URL. That is what REST is about. Getting information for specific Resources, which have to be identified with an ID.
You can use Friendly ID in order to replace the integer ID by a slug (e.g. users/tollbooth instead of users/42).
basically ror routes by default takes id as key to generate urls. If you are not fan of id based urls then you can always override urls by using to_param inside model.
def to_param
# make sure this field is always present & unique
username
end
then by default you will start seeing username instead of id inside urls
How to find object inside controller actions
User.find_by_username(params[:id])
If you dont want to do this manually make use of slug gems like friendly id

Resources