user2490003
user2490003

Reputation: 11900

Constraining a rails route to only be called internally

In using Javascript, JQuery, and AJAX with Rails we often have a few internal routes/urls that we can call as helpers.

For example if I have a search bar that uses Twitter Typeahead, I might use an internal route like /search/suggestions that my Javascript can call to pre-load some typeahead suggestions.

How do I prevent routes like this from getting called by an external/public user? It's important that only the "internal app" have access to this route.

I tried to constrain the route to localhost with little luck -

constraints(ip: /127.0.0.1/) do
    get "/search/suggestions", to: "search#load_suggestions"
end

Rails complains that it can't find that route, presumably because of the constraint.

ActionController::RoutingError (No route matches [GET] "/search/suggestions"):
  actionpack (4.2.2) lib/action_dispatch/middleware/debug_exceptions.rb:21:in `call'
  actionpack (4.2.2) lib/action_dispatch/middleware/show_exceptions.rb:30:in `call'
  railties (4.2.2) lib/rails/rack/logger.rb:38:in `call_app'
  railties (4.2.2) lib/rails/rack/logger.rb:20:in `block in call'

Thanks!

Upvotes: 3

Views: 1318

Answers (1)

Elvn
Elvn

Reputation: 3057

With browsers anyone can type anything into the url. So how do you stop someone from accessing a url/route/page they have no right to access?

You have to hook the request before the controller executes the action. If the request into the controller is from a valid requestor, then perform the action, otherwise display a message and redirect, or whatever you need.

I have an app where a user can only see their own feeds not anyone elses; only edit their own posts, etc. The user state is maintained by the sessions controller.

Your twitter typeahead requestor will need something to identify itself, a state that can be validated coming into the controller. This can be implemented an a number of ways. I won't try to guess how your app is setup.

In the controller use before_action to test for valid requestor, which in your case is your twitter typeahead:

class YourController < ApplicationController 
before_action :validate_access 

private 
  def validate_access 
      unless twitter_user? 
          flash[:danger] = "You do not have access to this feature." 
      redirect_to root_url (whatever redirect you need) 
      end 
  end

Catchall 301 redirect -- safety net

In the routes I always like to have a catchall redirect for the case someone does type an invalid URL into the browser. If you don't have this and someone hand edits a URL that doesn't exist in your routes, your app crashes with route not found, which is ugly:

routes.rb

  # This is a catchall 301 redirect to home (does not help with (e) type errors)
  get "*path", to: redirect('/')

There might be other solutions, probably are, but give this idea a try if it seems useful to you. Please let me know if you have any questions.

Upvotes: 1

Related Questions