Skip to content

Instantly share code, notes, and snippets.

@adamcaron
Last active September 16, 2015 01:02
Show Gist options
  • Save adamcaron/ad7a8afd6948850b679d to your computer and use it in GitHub Desktop.
Save adamcaron/ad7a8afd6948850b679d to your computer and use it in GitHub Desktop.
Step by step -- Adding Authorization for our My Jams app

Authorization for My Jams

Let's make it so an admin can see a list of all the users.

Write the test

touch test/integration/admin_users_test.rb ... In class, Mike helped us setup admin_categories_test.rb. Since this is 'My Jams', we may not be concerned with 'categories'. Instead, we'll make it so admins see a list of all the users (so perhaps an admin can change the role of a user or delete a user).

require 'test_helper'

class AdminUsersTest < ActionDispatch::IntegrationTest
  test "logged in admin sees user index" do
    admin = User.create(username: "admin", password: "admin", role: 1)
    ApplicationController.any_instance.stubs(:current_user).returns(admin)
    
    visit admin_users_path
    
    assert page.has_content?("All Users")
  end
end

Migrate: AddRoleToUsers

rails g migration AddRoleToUsers Add role column to users table; with type :integer and default 0, which will refer to the first element of an array containing ["default_user_role", and "admin"] ... (see next step)

class AddRoleToUsers < ActiveRecord::Migration
  def change
    add_column :users, :role, :integer, default: 0
  end
end

remember to rake db:migrate

Enable Mocha

rake test and see ...

1) Error:
AdminUsersTest#test_logged_in_admin_sees_user_index:
NoMethodError: undefined method `any_instance' for ApplicationController:Class
    test/integration/admin_users_test.rb:6:in `block in <class:AdminUsersTest>'

Minitest doesn't allow us to use .stubs by default (RSpec does). Add to the gemfile: gem 'mocha' (you may need to bundle install) Add to the test_helper.rb: require 'mocha/mini_test' rake test and you should see a new error message.

Set the Routes

  1) Error:
AdminUsersTest#test_logged_in_admin_sees_user_index:
NameError: undefined local variable or method `admin_users_path' for #<AdminUsersTest:0x007fe2509c28d8>
    test/integration/admin_users_test.rb:7:in `block in <class:AdminUsersTest>'

Our test is trying to visit admin_users_path. Let's setup that route. Add to routes.rb...

  namespace :admin do
    resources :users
  end

In Mike's class, resources :users was different. In class, Mike helped us setup resources :categories so admins could see all categories. Here we decided that an admin can see all users.

rake test and the error should be different now.

Adding an Admin::BaseController

rake test should reveal this error...

1) Error:
AdminUsersTest#test_logged_in_admin_sees_user_index:
ActionController::RoutingError: uninitialized constant Admin
    test/integration/admin_users_test.rb:7:in `block in <class:AdminUsersTest>'

mkdir app/controllers/admin then touch app/controllers/admin/base_controller.rb In our new base_controller.rb...

class Admin::BaseController < ApplicationController
  before_action :require_admin

  def require_admin
    render file: "/public/404" unless current_admin?
  end
end

Here, before we render anything for the admin, we check that the current user is in fact an admin.

Adding an Admin::UsersController

rake test and the new error complains that we don't have an Admin::UsersController

  1) Error:
AdminUsersTest#test_logged_in_admin_sees_user_index:
ActionController::RoutingError: uninitialized constant Admin::UsersController
    test/integration/admin_users_test.rb:7:in `block in <class:AdminUsersTest>'

create one! touch app/controllers/admin/users_controller.rb

class Admin::UsersController < Admin::BaseController
  def index
  end
end

Checking if current_user is an admin

  1) Error:
AdminUsersTest#test_logged_in_admin_sees_user_index:
NoMethodError: undefined method `current_admin?' for #<Admin::UsersController:0x007f9edf75abb8>
    app/controllers/admin/base_controller.rb:5:in `require_admin'
    test/integration/admin_users_test.rb:7:in `block in <class:AdminUsersTest>'

Our Admin::BaseController is attempting to check if the current user is an admin with current_admin? but we haven't created that method yet.

Go to application_controller.rb and add the method ...

  def current_admin?
    current_user && current_user.admin?
  end

so now the application_controller.rb looks something like ...

class ApplicationController < ActionController::Base
  # Prevent CSRF attacks by raising an exception.
  # For APIs, you may want to use :null_session instead.
  protect_from_forgery with: :exception

  helper_method :current_user
  def current_user
    @current_user ||= User.find(session[:user_id]) if session[:user_id]
  end

  def current_admin?
    current_user && current_user.admin?
  end
end

run rake test and see a new error ...

Add Admin for the User model

  1) Error:
AdminUsersTest#test_logged_in_admin_sees_user_index:
NoMethodError: undefined method `admin?' for #<User:0x007fb9772cc030>
    app/controllers/application_controller.rb:12:in `current_admin?'
    app/controllers/admin/base_controller.rb:5:in `require_admin'
    test/integration/admin_users_test.rb:7:in `block in <class:AdminUsersTest>'

go to User.rb and define the role for default users and admins. While we're at it, lets add validations for the User. (if you haven't already).

class User < ActiveRecord::Base
  has_secure_password

  validates :username, presence: true, uniqueness: true

  enum role: %w(default admin)
end

rake test and now we have a new error.

Adding an Index view so the admin can see all users

Our new error is

  1) Error:
AdminUsersTest#test_logged_in_admin_sees_user_index:
ActionView::MissingTemplate: Missing template admin/users/index, admin/base/index, application/index with {:locale=>[:en], :formats=>[:html], :variants=>[], :handlers=>[:erb, :builder, :raw, :ruby, :coffee, :jbuilder]}. Searched in:
  * "/Users/adam/Desktop/2/my_jams/app/views"

    test/integration/admin_users_test.rb:7:in `block in <class:AdminUsersTest>'

Create the view directory mkdir app/views/admin mkdir app/views/admin/users (It may not let you create both directories at once, so create the first one for /admin then create /users

Create the view touch app/views/admin/users/index.html.erb

In the view, add some html with the text we're expecting in our test. <h1>All Users</h1>

rake test and ...

Hooray! We're passing! We just created Authorization for our application.

git commit that shit.

if you want to play around with the sad paths, start with ...

  test "default user does not see admin users index" do
    user = User.create(username: "default_user", password: "cheese", role: 0)
    ApplicationController.any_instance.stubs(:current_user).returns(user)
    
    visit admin_users_path
    
    refute page.has_content?("All Users")
    assert page.has_content?("The page you were looking for doesn't exist.")
  end
@imwithsam
Copy link

Nice, Adam!

@mdorrance
Copy link

Well done Adam!

@biglovisa
Copy link

Thank you Adam!

@mrjaimisra
Copy link

Adam this is amazing!

@bad6e
Copy link

bad6e commented Sep 16, 2015

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment