In this chapter, you will provide admin methods. Only admins should be allowed to view admin profiles or the admin index. Only super admins should be allowed to delete regular admins. Super admins should not be able to delete each other or themselves. (Remember that admins can delete themselves through the registration edit process.)
NOTE: Brace yourself for a long chapter.
Enter the command "git checkout -b 10-02-admin_methods".
- Enter the command "rails generate controller Admins new".
- Enter the command "rm app/helpers/admins_helper.rb".
- Enter the command "rm app/views/admins/new.html.erb".
- Replace the contents of test/controllers/admins_controller_test.rb with the following:
require 'test_helper'
# rubocop:disable Metrics/ClassLength
class AdminsControllerTest < ActionController::TestCase
# PART 1: SHOW
test 'should redirect profile page when not logged in' do
get :show, params: { id: @a1 }
assert_redirected_to root_path
get :show, params: { id: @a2 }
assert_redirected_to root_path
get :show, params: { id: @a3 }
assert_redirected_to root_path
get :show, params: { id: @a4 }
assert_redirected_to root_path
get :show, params: { id: @a5 }
assert_redirected_to root_path
get :show, params: { id: @a6 }
assert_redirected_to root_path
end
test 'should redirect users from profile page' do
sign_in @u1, scope: :user
get :show, params: { id: @a1 }
assert_redirected_to root_path
get :show, params: { id: @a2 }
assert_redirected_to root_path
get :show, params: { id: @a3 }
assert_redirected_to root_path
get :show, params: { id: @a4 }
assert_redirected_to root_path
get :show, params: { id: @a5 }
assert_redirected_to root_path
get :show, params: { id: @a6 }
assert_redirected_to root_path
end
test 'should not redirect profile page when logged in as a super admin' do
sign_in @a1, scope: :admin
get :show, params: { id: @a1 }
assert :success
get :show, params: { id: @a2 }
assert :success
get :show, params: { id: @a3 }
assert :success
get :show, params: { id: @a4 }
assert :success
get :show, params: { id: @a5 }
assert :success
get :show, params: { id: @a6 }
assert :success
end
test 'should not redirect profile page when logged in as a regular admin' do
sign_in @a4, scope: :admin
get :show, params: { id: @a1 }
assert :success
get :show, params: { id: @a2 }
assert :success
get :show, params: { id: @a3 }
assert :success
get :show, params: { id: @a4 }
assert :success
get :show, params: { id: @a5 }
assert :success
get :show, params: { id: @a6 }
assert :success
end
# PART 2: INDEX
test 'should redirect index page when not logged in' do
get :index
assert_redirected_to root_path
end
test 'should redirect index page when logged in as a user' do
sign_in @u1, scope: :user
get :index
assert_redirected_to root_path
end
test 'should not redirect index page when logged in as a super admin' do
sign_in @a1, scope: :admin
get :index
assert :success
end
test 'should not redirect index page when logged in as a regular admin' do
sign_in @a4, scope: :admin
get :index
assert :success
end
# PART 3: DELETE
test 'should not allow visitor to delete admin' do
get :destroy, params: { id: @a5 }
assert_redirected_to root_path
get :destroy, params: { id: @a6 }
assert_redirected_to root_path
end
test 'should not allow user to delete admin' do
sign_in @u1, scope: :user
get :destroy, params: { id: @a5 }
assert_redirected_to root_path
get :destroy, params: { id: @a6 }
assert_redirected_to root_path
end
# NOTE: Admin can delete self through edit registration form.
test 'should not allow regular admin to delete self' do
sign_in @a6, scope: :admin
get :destroy, params: { id: @a6 }
assert_redirected_to root_path
end
# NOTE: Admin can delete self through edit registration form.
test 'should not allow super admin to delete self' do
sign_in @a5, scope: :admin
get :destroy, params: { id: @a5 }
assert_redirected_to root_path
end
test 'should not allow regular admin to delete another regular admin' do
sign_in @a4, scope: :admin
get :destroy, params: { id: @a6 }
assert_redirected_to root_path
end
test 'should not allow regular admin to delete super admin' do
sign_in @a6, scope: :admin
get :destroy, params: { id: @a5 }
assert_redirected_to root_path
end
test 'should not allow super admin to delete super admin' do
sign_in @a1, scope: :admin
get :destroy, params: { id: @a5 }
assert_redirected_to root_path
end
test 'should allow super admin to delete regular admin' do
sign_in @u5, scope: :admin
get :destroy, params: { id: @a6 }
assert :success
end
end
# rubocop:enable Metrics/ClassLength
- Enter the command "sh testc.sh". All 16 new controller tests will fail.
- Enter the command "rails generate integration_test admins_show".
- Replace the contents of the file test/integration/admins_show_test.rb with the following:
require 'test_helper'
class AdminsShowTest < ActionDispatch::IntegrationTest
def check_profile_disabled(a)
visit admin_path(a)
assert page.has_css?('title', text: full_title(''),
visible: false)
assert page.has_css?('h1', text: 'Home', visible: false)
end
# rubocop:disable Metrics/AbcSize
# rubocop:disable Metrics/MethodLength
def check_own_profile(a)
login_as(a, scope: :admin)
visit root_path
assert page.has_link?('Your Profile', href: admin_path(a))
click_on 'Logout'
end
def check_profile_enabled(a)
fn = a.first_name
ln = a.last_name
un = a.username
e = a.email
visit admin_path(a)
assert page.has_css?('title', text: full_title("Admin: #{fn} #{ln}"),
visible: false)
assert page.has_css?('h1', text: "Admin: #{fn} #{ln}",
visible: false)
assert page.has_text?("Username: #{un}")
assert page.has_text?("Email: #{e}")
end
# rubocop:enable Metrics/AbcSize
# rubocop:enable Metrics/MethodLength
test 'unregistered visitors may not view admin profile pages' do
check_profile_disabled(@a1)
check_profile_disabled(@a2)
check_profile_disabled(@a3)
check_profile_disabled(@a4)
check_profile_disabled(@a5)
check_profile_disabled(@a6)
end
test 'user may not view admin profile pages' do
login_as(@u1, scope: :user)
check_profile_disabled(@a1)
check_profile_disabled(@a2)
check_profile_disabled(@a3)
check_profile_disabled(@a4)
check_profile_disabled(@a5)
check_profile_disabled(@a6)
end
test 'regular admin can view all admin profiles' do
login_as(@a4, scope: :admin)
check_profile_enabled(@a1)
check_profile_enabled(@a2)
check_profile_enabled(@a3)
check_profile_enabled(@a4)
check_profile_enabled(@a5)
check_profile_enabled(@a6)
end
test 'super admin can view all admin profiles' do
login_as(@a1, scope: :admin)
check_profile_enabled(@a1)
check_profile_enabled(@a2)
check_profile_enabled(@a3)
check_profile_enabled(@a4)
check_profile_enabled(@a5)
check_profile_enabled(@a6)
end
test 'admins can access their own profiles from the menu bar' do
check_own_profile(@a1)
check_own_profile(@a2)
check_own_profile(@a3)
check_own_profile(@a4)
check_own_profile(@a5)
check_own_profile(@a6)
end
end
- Enter the command "alias test1='rails test test/integration/admins_show_test.rb'".
- Enter the command "test1". This runs only the tests in test/integration/admins_show_test.rb. All 4 integration tests will fail.
- Enter the command "rails generate integration_test admins_index".
- Replace the contents of the file test/integration/admins_index_test.rb with the following:
require 'test_helper'
class AdminsIndexTest < ActionDispatch::IntegrationTest
def check_index_disabled
visit admins_path
assert page.has_css?('title', text: full_title(''),
visible: false)
assert page.has_css?('h1', text: 'Home', visible: false)
end
def check_index_disabled_for_user(u)
login_as(u, scope: :user)
check_index_disabled
end
# rubocop:disable Metrics/AbcSize
# rubocop:disable Metrics/MethodLength
def check_index_enabled
visit admins_path
assert page.has_css?('title', text: full_title('Admin Index'),
visible: false)
assert page.has_css?('h1', text: 'Admin Index')
assert page.has_text?('Elle')
assert page.has_text?('Woods')
assert page.has_text?('Vivian')
assert page.has_text?('Kensington')
assert page.has_text?('Emmett')
assert page.has_text?('Richmond')
assert page.has_text?('Paulette')
assert page.has_text?('Bonafonte')
assert page.has_text?('Professor')
assert page.has_text?('Callahan')
assert page.has_text?('Warner')
assert page.has_text?('Huntington')
assert page.has_text?('elle_woods@example.com')
assert page.has_text?('vivian_kensingston@example.com')
assert page.has_text?('emmett_richmond@example.com')
assert page.has_text?('paulette_bonafonte@example.com')
assert page.has_text?('professor_callahan@example.com')
assert page.has_text?('warner_huntington@example.com')
# Verify that index page provides access to profile pages
assert page.has_link?('ewoods', href: admin_path(@a1))
assert page.has_link?('elle_woods@example.com', href: admin_path(@a1))
assert page.has_link?('vkensington', href: admin_path(@a2))
assert page.has_link?('vivian_kensingston@example.com', href: admin_path(@a2))
assert page.has_link?('erichmond', href: admin_path(@a3))
assert page.has_link?('emmett_richmond@example.com', href: admin_path(@a3))
assert page.has_link?('pbonafonte', href: admin_path(@a4))
assert page.has_link?('paulette_bonafonte@example.com', href: admin_path(@a4))
assert page.has_link?('pcallahan', href: admin_path(@a5))
assert page.has_link?('professor_callahan@example.com', href: admin_path(@a5))
assert page.has_link?('whuntington', href: admin_path(@a6))
assert page.has_link?('warner_huntington@example.com', href: admin_path(@a6))
# Verify that root page provides access to index page
click_on 'Home'
assert page.has_link?('Admin Index', href: admins_path)
end
# rubocop:enable Metrics/AbcSize
# rubocop:enable Metrics/MethodLength
test 'admins index page is not accessible to visitors' do
check_index_disabled
end
test 'admins index page is not accessible to users' do
check_index_disabled_for_user(@a1)
check_index_disabled_for_user(@a2)
check_index_disabled_for_user(@a3)
check_index_disabled_for_user(@a4)
check_index_disabled_for_user(@a5)
check_index_disabled_for_user(@a6)
end
test 'admins index page is accessible to regular admins' do
login_as(@a4, scope: :admin)
check_index_enabled
end
test 'admins index page is accessible to super admins' do
login_as(@a1, scope: :admin)
check_index_enabled
end
end
- Enter the command "alias test2='rails test test/integration/admins_index_test.rb'".
- Enter the command "test2". This tests only the tests in test/integration/admins_index_test.rb. All 4 integration tests will fail.
- Enter the command "rails generate integration_test admins_delete".
- Replace the contents of the file test/integration/admins_delete_test.rb with the following:
require 'test_helper'
class AdminsDeleteTest < ActionDispatch::IntegrationTest
test 'regular admin does not get button to delete self' do
login_as(@a6, scope: :admin)
visit admin_path(@a6)
assert page.has_no_link?('Delete', href: admin_path(@a6))
end
test 'super admin does not get button to delete self' do
login_as(@a5, scope: :admin)
visit admin_path(@a5)
assert page.has_no_link?('Delete', href: admin_path(@a5))
end
test 'regular admin does not get button to delete super admin' do
login_as(@a6, scope: :admin)
visit admin_path(@a5)
assert page.has_no_link?('Delete', href: admin_path(@a5))
end
test 'super admin can delete regular admin' do
login_as(@a5, scope: :admin)
visit root_path
click_on 'Admin Index'
assert_difference 'Admin.count', -1 do
click_on 'whuntington'
click_on 'Delete'
end
assert_text 'Admin deleted'
end
end
- Enter the command "alias test3='rails test test/integration/admins_delete_test.rb'".
- Enter the command "test3". All 4 tests will fail.
- Enter the command "sh testc.sh". All 16 tests will fail because the expected routes are not present.
- Update the routing. Edit the file config/routes.rb and add the following line to the end of the admin section:
resources :admins, only: [:show, :index, :destroy]
- In the config/routes.rb file, remove the line "get 'admins/new'", because this capability is not used in this app.
- Enter the command "sh testc.sh". All 16 tests still fail because the actions are not present in the controller.
- Replace the contents of the file app/controllers/admins_controller.rb with the following:
#
class AdminsController < ApplicationController
before_action :may_show_admin, only: [:index, :show]
before_action :may_destroy_admin, only: [:destroy]
def index
@admins = Admin.paginate(page: params[:page])
end
def show
@admin = Admin.find(params[:id])
end
def destroy
Admin.find(params[:id]).destroy
flash[:success] = 'Admin deleted'
redirect_to(admins_path)
end
private
def may_show_admin
return redirect_to(root_path) unless admin_signed_in? == true
end
helper_method :may_show_admin
def no_destroy
ta = Admin.find(params[:id]) # Target admin
ca = current_admin
# Do not delete if:
# 1. current_admin is nil OR
# 2. Attempting to delete self OR
# 3. Not a super admin OR
# 4. Target is super admin
ca.nil? || ca == ta || ca.super != true || ta.super == true
end
def may_destroy_admin
return redirect_to(root_path) if no_destroy == true
end
helper_method :may_destroy_admin
end
- Enter the command "sh testc.sh". 4 of the tests still fail due to missing templates.
- Create the file app/views/admins/show.html.erb with the following content:
<% require 'email_munger' %>
<% provide(:title, "Admin: #{@admin.first_name} #{@admin.last_name}") %>
<div class="row">
<section class="user_info">
<h1>
Admin: <%= @admin.first_name %> <%= @admin.last_name %>
</h1>
Username: <%= @admin.username %>
<br>
Email: <%= raw(EmailMunger.encode(@admin.email)) %>
<br>
<% if @admin.super != true && current_admin.super == true %>
<%= link_to "Delete", @admin, method: :delete,
data: { confirm: "Are you sure you wish to delete this admin?" },
class: "btn btn-lg btn-primary"
%>
<% end %>
</section>
</div>
- Create the file app/views/admins/index.html.erb with the following content:
<% provide(:title, 'Admin Index') %>
<h1>Admin Index</h1>
<table class="admins">
<tr>
<td><b>Last Name</b></td>
<td><b>First Name</b></td>
<td><b>Super?</b></td>
<td><b>Username</b></td>
<td><b>Email</b></td>
</tr>
<%= render @admins %>
</table>
<%= will_paginate %>
- Create the file app/views/admins/_admin.html.erb with the following content to show information on each admin in the index:
<% require 'email_munger' %>
<tr>
<td><%= admin.last_name %></td>
<td><%= admin.first_name %></td>
<td>
<% if admin.super == true %>
Y
<% else %>
N
<% end %>
</td>
<td><%= link_to admin.username, admin %></td>
<td><%= link_to raw(EmailMunger.encode(admin.email)), admin %></td>
</tr>
- Enter the command "sh testc.sh". All of the controller tests should now pass.
- Enter the command "test1". 1 of the tests will fail because of the lack of a link to the current admin's profile.
- In the admin section in app/views/layouts/_header.html.erb, add the following line just before the line containing "Edit Settings":
<li><%= link_to "Your Profile", admin_path(current_admin) %>
- Enter the command "test1". All tests should pass.
- Enter the command "test2". 2 of the tests will fail because of the lack of a link to the admin index page.
- In the admin section in app/views/layouts/_header.html.erb, add the following line just after the one containing "User Index":
<li><%= link_to "Admin Index", admins_path %></li>
- Enter the command "test2". All tests should pass.
- Enter the command "test3". All tests should pass.
- Enter the command "sh git_check.sh". All tests should pass, and there should be no offenses.
- Enter the following commands:
git add .
git commit -m "Added admin methods"
- Enter the command "git push origin 10-02-admin_methods".
- Go to the GitHub repository and click on the "Compare and pull request" button for this branch.
- The email display on the admin profile page will be flagged by Hakiri. Mark this as a false positive by clicking on Details -> 1 warning -> admin view -> 1 warning -> Mark as False Positive.
- Accept this pull request to merge it with the master branch, but do NOT delete this branch.
- Enter the following commands:
git checkout master
git pull
sh heroku.sh