Referral program - cookies and more (Rails) - ruby-on-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

Related

Multiple Domain pointing to single rails app displaying different content with the same url path

I have searched around the web and there are answers that have helped me abit, however I am still stuck, so here goes.
I a Rails 4 app that allows users to create a biography/blog and then access it using their own domain.
Users can choose from several pre-made website templates (main page, about me page, my hobbies page, etc...), and then they load up their content using a CMS. The content will then be displayed using their chosen template when visitors visit their domain.
Eg:
User 1:
Domain: www.user1.com
Template: Template A
User 2:
Domain: www.user2.com
Template: Template B
Desired Results
When a visitor visits www.user1.com, they will see the main page. When they click on "About Me", they will be redirect to www.user1.com/about-me. If a visitor visits the "About Me" page for user 2, they will see www.user2.com/about-me.
My question here is, how do I set this up?
Based on this answer: Rails routing to handle multiple domains on single application
class Domain
def self.matches?(request)
request.domain.present? && request.domain != "mydomain.com"
end
end
------in routes.rb------
require 'subdomain'
constraints(Domain) do
match '/' => 'blogs#show'
end
I know I can route a different domain compared to mine to a separate controller, however, I need to route it to different template controllers which can change at any moment (users can change templates at will).
I know I can set up a general controller that can read incoming requests, then based on the hostname, I can extract the appropriate template and then redirect the request to that template's controller (eg: Template1Controller), however the url gets messed up, becoming something like "/template/template1/index" or "/template/template1/about-me" which is very bad and ugly. Furthermore, it will be extremely tricky to handle paths specific to only some templates (Template A might have a "My Resume" page while template B might have a "Family History" page instead).
Is there a way to do this?
I have thought about a method where I have a single controller that will handle everything (without redirects) and then just calls render template1/index, but I think it is a bad way of doing it (different template might need different data in each page).
Btw, this will be hosted on EC2.
EDIT
What I am looking to implement is quite similar to this question Mapping multiple domain names to different resources in a Rails app , but unfortunately no answers then. Im hoping 5 years later, someone might know how to get this done.
Thanks!
I do this pretty simple with Heroku. It's probably not hard anywhere.
Once you have DNS set up.. the Rails layer can look like...
Create a before_filter in ApplicationController. before_filter :domain_check
In my domain_check method I just have if request.host ~= /whatever/ do this elsif ... elsif ... end
"do this" can be a redirect or a render or whatever.

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+/ }

Generating secret URLs for a resource in rails

I've created a task management app that consists of lists and tasks. Users can only view their own lists and tasks. I would like to add the ability for a user to share a list if they like. Here are the steps I would like to accomplish:
User clicks a link from /list/show to share the list
User receives a secret URL to share: myapp.com/lists/1/23534512345234523 or whatever.
Secret URL redirects to a view other than /lists/show. Something like /lists/1/23534512345234523 which would be routed to /lists/secret_show or whatev.
Only users who have that url can see the information on that page.
Hope that is making sense. I imagine I would have to update the list record with a unique token to list.token. Then I would some how have to recieve the incoming URL and through a new action
lists#secret_share
def secret_share
...
end
Where I filtered for the list record by list.token and routed to secret_share. Then perhaps in the view I could simply restrict the view by the presence of the token in the URL.
Thoughts?
Whatever "secret URL" you hand out should not redirect to the real URL or you're going to create all kinds of opportunities for information leakage. It should be a strictly alternate URL.
Using routing for this seems like a good idea instead of using a separate controller. In your route you might want to pass an additional parameter to indicate this is a "secret" URL, like :secret => true where the value in question is something that cannot be submitted by the user to fake things out. User parameters are always strings, for instance, so using true should be a safe alternative.
This special parameter might disable access checking on your controller so that the page can be viewed by people that don't normally have access. You could also show a different layout using the layout method in your controller.

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