Handling URLs that change - url

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.

Related

Use RESTful routes and POST parameters

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

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.

Referral program - cookies and more (Rails)

I'm building a referral program for my Ruby on Rails app, such that a user can share a link that contains their user ID (app.com/?r=ID). If a referrer ID is present when a visitor lands on app's homepage, the signup form on the homepage contains a hidden field that populates with the referrer's ID. The controller then detects the ID and creates a new referral in a referral table if the referred visitor signs up. It works, and here's that chunk of code:
#referrer = User.find(params[:r]) rescue nil
unless #referrer.nil?
#referral = Referral.new(:referrer_id=>#referrer.id)
end
Pretty simple stuff, but it's pretty easy to break (ex: if visitor navigates away from the homepage, referrer ID is lost). I feel like cookies could be a more robust method, where a cookie containing the referrer's ID is stored on the referred user's computer for x days. This is pretty commonplace, especially with affiliate programs like Groupon, but I have never worked with cookies and have no idea where to start.
Also, is there any good way to mask or change the URLs of the referral system? Instead of having app.com/?r=1842, I would prefer something like app.com/x39f3 <- a randomly generated sequence of numbers associated with a given user, without the ?r= portion.
Any help is greatly appreciated. Thanks!
To answer the cookie question, it's quite easy to set them:
cookies['app-referrer-id'] = params[:r]
And then it's the same format to read them back (but without the assignment). I would suggest putting this code in a before_filter in your application controller. This way, the cookie will be set irrespective of the page on which your visitor first lands on your site.
With regards to changing the structure of the urls to the suggested format, you would need to have the referral codes match a specific pattern, otherwise you are likely to run into routing problems. If, for example, they matched the format of 3 letters followed by three numbers, you could put the following your routes file:
match '/:referrer_id' => 'app#index', :constraints => {:referrer_id => /[a-zA-Z]{3}[0-9]{3}/}
The reference to app#index should be changed to the controller in which you handle referrals and you can access the referrer_id through params[:referrer_id].
Hope this is of some use.
Robin

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