前回までにデザインのないテストは受入テストで実施できたが、実際のデザインはブラウザで確認する必要がある。しかしながら LDAP 接続できない場所では、ブラウザで確認することはできない。そこで、他のユーザになりすませるローカルログインする admin でアクセスする。ここでは、kyoumu ユーザ権限を付けるテストを行う。
feature の記述
以下のように spec/acceptance/features/admin_privilege.feature を記述する。
# language: ja 機能: admin ユーザの権限取得 シナリオ: admin のトップページから教務室の権限を得る。その後年度変更に飛び、権限を確認する。 前提 管理者になる かつ 教務室アカウントを作成する ならば 管理者トップページと表示されていること もし "[教務室]"リンクをクリックする かつ "[年度変更]"リンクをクリックする ならば 年度一覧と表示されていること
まず、「管理者になる」step と「教務室アカウントを作成する」 step を spec/acceptance/steps/login_steps.rb に追加する。
step %(管理者になる) do login_as(FactoryGirl.create(:admin), scope: :admin) visit root_path end step %(:jnameアカウントを作成する) do |jname| FactoryGirl.create(USERS.assoc(jname).last) end
この結果 Guard は以下のようになる。Welcome#index に留まっており、管理者ページに飛んでいない。
Failure/Error: ならば 管理者トップページと表示されていること expected to find text "管理者トップページ" in "Welcome#index Find me in app/views/welcome/index.html.haml ログアウト"
spec/controllers/welcome_controller_spec.rb にテストを追加する。
context "for :admin" do it do login_admin_as :admin get :index expect(response).to redirect_to(admins_path) end end
「login_admin_as」がないと言われるので、spec/rails_helper.rb にメソッドを追加する。
def login_admin_as(fg_key) admin = FactoryGirl.create(fg_key) sign_in :admin, admin admin end
今度は admins_path がないと言われるので、controller を作成する。
$ bin/rails g controller admins create app/controllers/admins_controller.rb invoke haml create app/views/admins invoke rspec create spec/controllers/admins_controller_spec.rb invoke assets invoke coffee invoke scss
また、config/routes.rb に admins の routing を追加する。ここではトップページ用の index と権限付与用の update のみを設定する。
resources :admins, only: [ :index, :update ]
この結果、以下のようなエラーとなる。
2) WelcomeController GET index for :admin should redirect to "/admins" Failure/Error: expect(response).to redirect_to(admins_path) Expected response to be a <redirect>, but was <200>
app/controllers/welcome_controller.rb を修正して、リダイレクトするように変更する。
def index if user_signed_in? current_user.set_privileges(user_session) redirect_to teacher_kyoumus_path if is_kyoumu? elsif admin_signed_in? redirect_to admins_path end end
controller のテストは通過したが、feature のテストが残っている。
Failure/Error: 前提 管理者になる AbstractController::ActionNotFound: The action 'index' could not be found for AdminsController
まず、spec/controllers/admins_controller_spec.rb を記述する。admin でログインしていない場合には、root_path にリダイレクトすることを確認する。
require 'rails_helper' RSpec.describe AdminsController, :type => :controller do describe "GET index" do it "returns http success when admin is logining" do login_admin_as :admin get :index expect(response).to have_http_status(:success) end it "redirect_to root_path when admin is not logining" do get :index expect(response).to redirect_to(root_path) end end end
このテストを通過させるために、app/controllers/admins_controller.rb に index を作成する。
before_action :admin_login_required def index end
admin_login_required がないと言われるので、app/controllers/application_controller.rb に記述する。ここでは、権限がないときにメッセージを出して root_path にリダイレクトする redirect_to_root_path_with_message を作成し、これを呼び出すことにする。
# @param [Boolean] flag 権限がある時に true # @return [Boolean] flag が true の場合に true を返す # @note flag が false の時は root_path にリダイレクト def redirect_to_root_path_with_message(flag) unless flag flash[:alert] = 'このページを表示する権限がありません。' redirect_to root_path end true end # @note admin でログインしていない場合には root_path にリダイレクト # @return [Boolean] flag が true の場合に true を返す def admin_login_required redirect_to_root_path_with_message(admin_signed_in?) end
さらに、app/views/admins/index.html.haml を作成する。教員用と共用のどちらのサーバも admin はログインできるので、サーバごとに選択できる権限を区別する。特殊アカウントと職員は教職員のみ、学生は共用のみ、教員はどちらでもログインできるようにする予定であるので、サーバごとに表示を切り替えている。
- @is_kyouin_server = Rails.application.config.IsKyouinServer %h1 管理者トップページ (#{ @is_kyouin_server ? "教職員" : “共用” }サーバ) %p 権限の選択 - if @is_kyouin_server %h2 特殊アカウント %table %tr %th{colspan: User::SpecialAccounts.count} %tr - User::SpecialAccounts.each do |account, str| %td= link_to lh(str), admin_path(current_admin, privilege: account), method: :put - else %h2 学生 -if @is_kyouin_server %h2 教員 -if @is_kyouin_server %h2 職員
教員用サーバかどうかは Rails.application.config.IsKyouinServer で判断する。これは、config/application.rb で設定し、サーバ起動時に確定する。旧システムと同様 RAILS_ROOT に .kyouin_server というファイルがあった場合には、教職員専用サーバであるとする。
config.IsKyouinServer = File.exists?('.kyouin_server')
.kyouin_server はリポジトリに入れないので、.gitignore に登録しておく。また、テストのために .kyouin_server を作っておく。
touch .kyouin_server
特殊アカウントは User モデルの定数として app/models/user.rb に用意しておく(後で増やす予定)。
# 特殊アカウント一覧 SpecialAccounts = [ %w( skyoumu 教務室 ), ]
リンク文字列に [] を付与する lh メソッドを app/helpers/applicatoin_helper.rb に追記する。
module ApplicationHelper # リンク文字列を作成 def lh(str) "[#{str}]" end end
Guard は以下のようになる。
Failure/Error: もし "[教務室]"リンクをクリックする AbstractController::ActionNotFound: The action 'update' could not be found for AdminsController
実装を記述する前に spec/controllers/admins_controller_spec.rb にテストを追加する。update では、権限になりたいユーザの name を admin_session[:mode] にセットする。
describe "PUT update" do context "for user skyoumu" do before do admin = login_admin_as :admin skyoumu = FactoryGirl.create(:user_skyoumu) put :update, id: admin.id, privilege: 'skyoumu' end it("controller.admin_session['user'] should eq skyoumu") { expect(controller.admin_session['user']).to eq('skyoumu') } it { expect(response).to redirect_to(root_path) } end end
app/controllers/admins_controller.rb の実装は以下のようになる。
def update admin_session['user'] = params[:privilege] redirect_to root_path end
admin_session[:user] に値がセットされているときは、admins_path ではなく、権限に従ったリダイレクトされるように変更する。実装をする前に spec/controllers/welcome_controller_spec.rb を修正する。’user’ に正しくない値(‘unknown’)が入っている場合のテストも忘れないようにしておく。
context "for :admin" do context "without admin_session['user']" do it do login_admin_as :admin get :index expect(response).to redirect_to(admins_path) end end context "admin_session['user'] is 'unknown'" do it do login_admin_as :admin controller.admin_session['user'] = 'unknown' get :index expect(response).to redirect_to(admins_path) end end context "admin_session['user'] is 'skyoumu'" do it do login_admin_as :admin controller.admin_session['user'] = 'skyoumu' user = FactoryGirl.create(:user_skyoumu) get :index expect(response).to redirect_to(teacher_kyoumus_path) end end end
app/controllers/welcome_controller.rb は以下のようになる。当初、admin_session.delete('user') を忘れ、正しくないユーザの場合に、無限ループになっていてハマった。
def index if user_signed_in? current_user.set_privileges(user_session) redirect_to teacher_kyoumus_path if is_kyoumu? elsif admin_signed_in? p admin_session if name = admin_session['user'] user = User.name_value_is(name).first if user # find user user.set_privileges(admin_session) if is_kyoumu? redirect_to teacher_kyoumus_path else admin_session.delete('user') redirect_to admins_path end else # Can't find user admin_session.delete('user') redirect_to admins_path end else redirect_to admins_path end end end
今日はここまで。
written by iHatenaSync