Skip to main content Link Search Menu Expand Document (external link)

Routes Constraint

In this tutorial, we’ll create two different domains for our application. One is for our clients, and the other is for the administrators. Additionally, we will also restrict access to our admin users lists to administrators only.

Table Of Contents

  1. What is Domain?
  2. Host Authorization
  3. Route Constraint
  4. Domain Config

What is Domain?

A domain name is the address of your website. This is what users put into their browser’s search box to get to your website. What we are using right now as our domain/hostname is localhost with port 3000.

Host Authorization

Rails have a middleware called Host Authorization that prevents against DNS rebinding attacks. By default this feature only allows requests from 0.0.0.0, ::, and localhost.

Now, in order to allow our hostname to be accessed, we have to whitelist the development hostname to our development config.

# config/environments/development.rb

require "active_support/core_ext/integer/time"

Rails.application.configure do
  # ...
+ config.hosts << "client.com"
+ config.hosts << "admin.com"
end

Read Carefully:

Clearing the entire whitelist with config.hosts.clear will also do the trick. This will let through requests for all hostnames.

Next step is configuring your hosts file. Hosts files are used to test the DNS system by redirecting a Web browser or other application to a specified IP address.

In your terminal go to /private/etc, open the hosts file using editor.

 $~> cd /private/etc
 $~/private/etc> sudo {editor} hosts

Then add the following:

`127.0.0.1 client.com`
`127.0.0.1 admin.com`

We can now access our application with client.com:3000 or admin.com:3000. All that’s left is restricting the access of the admin users lists to admins only.

# app/controllers/admin/users_controller.rb

class Admin::UsersController < ApplicationController
+ before_action :authenticate_user!
+ before_action :check_admin
+
  def index
    @users = User.page params[:page]
  end
+
+ def check_admin
+   raise ActionController::RoutingError.new('Not Found') unless current_user.admin?
+ end
end

We can know who is accessing the admin users page by their genre, to do that, we have to make sure that a user is currently signed-in before accessing the page. When signed-in user is not an admin, it will raise a custom RoutingError with message Not Found.

Route Constraint

Security check features are also included in the Rails Route, and this is the constraint. Routing constraints are checks that are built around routes to clean and validate information before it reaches a controller action.

Let’s try using a constraint.

# config/routes.rb

Rails.application.routes.draw do
  devise_for :users
  root 'welcome#index'
  
- resources :posts do
-   resources :comments, except: :show
- end
+ constraints(ip: /127\.0\.0\.1$/) do
+   resources :posts do
+     resources :comments, except: :show
+   end
+ end
  # ...
end

This will restrict all IP’s to access posts and posts/comments route except the ip 127.0.0.1. Try accessing the posts page, it will show a RoutingError no route match.

We can also make a constraint module to make our routes clean. Create AdminDomainConstraint and ClientDomainConstraint under app/constraint.

# app/constraint/admin_domain_constraint.rb

class AdminDomainConstraint
  def matches?(request)
    domains = ['admin.com']
    domains.include?(request.domain.downcase)
  end
end
# app/constraint/client_domain_constraint.rb

class ClientDomainConstraint
  def matches?(request)
    domains = ['client.com']
    domains.include?(request.domain.downcase)
  end
end

This will check if the request domain is included on the domain whitelist. Now let’s implement this constraints to our route.

# config/routes.rb

Rails.application.routes.draw do
  devise_for :users
  root 'welcome#index'

- constraints(ip: /127\.0\.0\.1$/) do
+ constraints(ClientDomainConstraint.new) do
    resources :posts do
      resources :comments, except: :show
    end
  end
  resources :categories

- namespace :admin do
-   resources :users
- end
+ constraints(AdminDomainConstraint.new) do
+   namespace :admin do
+     resources :users
+   end
+ end
  # ...
end

Domain Config

Making a hardcoded domain whitelist is not really safe, in order to address this issue, let’s a config yml that will contain our domain whitelist. One yml file that will contain the example of our whitelist, and one that we will actually use for our application.

# config/domain.example.yml

development:
  admin:
    - aaa.com
  client:
    - bbb.com
# config/domain.yml

development:
  admin:
    - admin.com
  client:
    - client.com

To exclude the domain.yml file in commit, add it to .gitignore.

# .gitignore

  # ...
  /node_modules
+
+ /config/domain.yml

Final step is to implement the config to client and domain constraint.

# app/constraint/admin_domain_constraint.rb

class AdminDomainConstraint
  def matches?(request)
-   domains = ['admin.com']
+   domains = Rails.application.config_for(:domain)[:admin]
    domains.include? request.domain.downcase
  end
end
# app/constraint/client_domain_constraint.rb

class ClientDomainConstraint
  def matches?(request)
-   domains = ['client.com']
+   domains = Rails.application.config_for(:domain)[:client]
    domains.include? request.domain.downcase
  end
end

That is all for Routes Constraint.


Back to top

Copyright © 2020-2022 Secure Smarter Service, Inc. This site is powered by KodaCamp.