× {{alert.msg}} Never ask again
Get notified about new tutorials RECEIVE NEW TUTORIALS

Use custom Routing Constraints to limit access to Rails Routes via Warden.

Steven Harman
Feb 08, 2015
<p>Building upon <a href="http://github.com/hassox/warden">Warden</a>, we can use Rails' custom <a href="http://guides.rubyonrails.org/routing.html#advanced-constraints">Routing Constraints</a> to restrict access to sets of routes based upon attributes of our users. For example, is the user authenticated? Or are they an admin? etc...</p> <p>We'll start by creating a custom Routing Constraint. Rails will invoke the constraint object's <code>#matches?</code> method, passing in the current <code>request</code> object. We will then grab the current <code>Warden</code> object out of the request and make sure the user is both authenticated and a member of our "staff."</p> <pre><code class="language-ruby"># /lib/constraint/staff.rb module Constraint class Staff def matches?(request) warden(request).authenticated? &amp;&amp; warden(request).user.staff? end private def warden(request) request.env['warden'] end end end</code></pre> <p>If our constraint passes (returns <code>true</code>), Rails will route the request to routes. If not (the constraint returns <code>false</code>), Rails will continue looking for a matching route. If none can be found, a <code>404</code> will be returned.</p> <p>Next let's use the constraint to restrict access to a set of routes to only our staff members.</p> <pre><code class="language-ruby"># config/routes.rb YourApp::Application.routes.draw do namespace :staff, constraints: Constraint::Staff.new do resource :style_guide, only: [:show] resources :maintenance_tasks, only: [:index, :create] require 'sidekiq/web' mount Sidekiq::Web =&gt; 'sidekiq', as: :background_jobs root to: 'dashboards#show' end root :to =&gt; 'home#index' end</code></pre> <p>For completeness, here are some RSpec specs showing how I drove the design of this custom constraint.</p> <pre><code class="language-ruby"># specs/lib/constraint/staff_spec.rb require 'constraint/staff' describe Constraint::Staff do subject(:constraint) { described_class.new } let(:a_request) { double('Request', env: { 'warden' =&gt; warden }) } let(:warden) { double('Warden') } it 'denies an unauthenticated user' do warden.stub(:authenticated?) { false } expect(constraint.matches?(a_request)).to be_false end context 'when authenticated' do let(:user) { double('User') } before do warden.stub(:authenticated?) { true } warden.stub(:user) { user } end it 'denies a non-staff user' do user.stub(:staff?) { false } expect(constraint.matches?(a_request)).to be_false end it 'allows an staff user' do user.stub(:staff?) { true } expect(constraint.matches?(a_request)).to be_true end end end</code></pre> <p> </p>
comments powered by Disqus