casino 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data.tar.gz.sig +0 -0
- data/Gemfile.lock +11 -5
- data/app/assets/images/logo.png +0 -0
- data/app/assets/images/logo@2x.png +0 -0
- data/app/assets/stylesheets/{casino/index.css.scss → casino.scss} +177 -90
- data/app/controllers/casino/sessions_controller.rb +5 -0
- data/app/controllers/casino/two_factor_authenticators_controller.rb +15 -0
- data/app/views/casino/{sessions → application}/_footer.html.erb +0 -0
- data/app/views/casino/application/_messages.html.erb +5 -0
- data/app/views/casino/sessions/index.html.erb +13 -2
- data/app/views/casino/sessions/logout.html.erb +3 -0
- data/app/views/casino/sessions/new.html.erb +1 -5
- data/app/views/casino/sessions/validate_otp.html.erb +17 -0
- data/app/views/casino/two_factor_authenticators/new.html.erb +32 -0
- data/app/views/layouts/application.html.erb +1 -0
- data/casino.gemspec +4 -1
- data/config/locales/en.yml +23 -0
- data/config/routes.rb +3 -0
- data/lib/casino/listener.rb +5 -0
- data/lib/casino/listener/login_credential_acceptor.rb +5 -0
- data/lib/casino/listener/second_factor_authentication_acceptor.rb +26 -0
- data/lib/casino/listener/two_factor_authenticator_activator.rb +23 -0
- data/lib/casino/listener/two_factor_authenticator_destroyer.rb +16 -0
- data/lib/casino/listener/two_factor_authenticator_overview.rb +11 -0
- data/lib/casino/listener/two_factor_authenticator_registrator.rb +11 -0
- data/lib/casino/version.rb +1 -1
- data/lib/generators/casino/install_generator.rb +1 -3
- data/lib/generators/casino/templates/casino_and_overrides.scss +12 -0
- data/spec/controllers/listener/login_credential_acceptor_spec.rb +18 -0
- data/spec/controllers/listener/second_factor_authentication_acceptor_spec.rb +74 -0
- data/spec/controllers/listener/two_factor_authenticator_activator_spec.rb +64 -0
- data/spec/controllers/listener/two_factor_authenticator_destroyer_spec.rb +40 -0
- data/spec/controllers/listener/two_factor_authenticator_overview_spec.rb +16 -0
- data/spec/controllers/listener/two_factor_authenticator_registrator_spec.rb +27 -0
- data/spec/controllers/sessions_controller_spec.rb +10 -0
- data/spec/controllers/two_factor_authenticators_controller_spec.rb +34 -0
- metadata +63 -8
- metadata.gz.sig +1 -0
- data/lib/generators/casino/templates/casino.css +0 -3
- data/lib/generators/casino/templates/casino.js +0 -1
@@ -0,0 +1,15 @@
|
|
1
|
+
class CASino::TwoFactorAuthenticatorsController < CASino::ApplicationController
|
2
|
+
include CASino::SessionsHelper
|
3
|
+
|
4
|
+
def new
|
5
|
+
processor(:TwoFactorAuthenticatorRegistrator).process(cookies, request.user_agent)
|
6
|
+
end
|
7
|
+
|
8
|
+
def create
|
9
|
+
processor(:TwoFactorAuthenticatorActivator).process(params, cookies, request.user_agent)
|
10
|
+
end
|
11
|
+
|
12
|
+
def destroy
|
13
|
+
processor(:TwoFactorAuthenticatorDestroyer).process(params, cookies, request.user_agent)
|
14
|
+
end
|
15
|
+
end
|
File without changes
|
@@ -1,6 +1,6 @@
|
|
1
1
|
<div class="container">
|
2
2
|
<div class="sessions box">
|
3
|
-
|
3
|
+
<%= render 'messages' %>
|
4
4
|
<div class="info">
|
5
5
|
<h1><%= t('sessions.title') %></h1>
|
6
6
|
<p>
|
@@ -8,6 +8,18 @@
|
|
8
8
|
</p>
|
9
9
|
|
10
10
|
<%= link_to t('sessions.label_logout_button'), logout_path, :class => 'button' %>
|
11
|
+
</div>
|
12
|
+
|
13
|
+
<div class="logo">
|
14
|
+
<%= image_tag "logo.png" %>
|
15
|
+
</div>
|
16
|
+
|
17
|
+
<h3><%= t('two_factor_authenticators.title') %></h3>
|
18
|
+
<% if @two_factor_authenticators.blank? %>
|
19
|
+
<%= t('two_factor_authenticators.disabled') %> - <%= link_to t('two_factor_authenticators.enable'), new_two_factor_authenticator_path %>
|
20
|
+
<% else %>
|
21
|
+
<%= t('two_factor_authenticators.enabled') %> - <%= link_to t('two_factor_authenticators.disable'), two_factor_authenticator_path(@two_factor_authenticators[0].id), method: :delete %>
|
22
|
+
<% end %>
|
11
23
|
|
12
24
|
<h3><%= t('sessions.your_active_sessions') %></h3>
|
13
25
|
<table width="100%" class="tickets">
|
@@ -44,7 +56,6 @@
|
|
44
56
|
<% end %>
|
45
57
|
</tbody>
|
46
58
|
</table>
|
47
|
-
</div>
|
48
59
|
</div>
|
49
60
|
<%= render 'footer' %>
|
50
61
|
</div>
|
@@ -1,10 +1,6 @@
|
|
1
1
|
<div class="container">
|
2
2
|
<div class="login box">
|
3
|
-
|
4
|
-
<% if msg.is_a?(String) %>
|
5
|
-
<%= content_tag :div, msg, :id => "flash_#{name}" %>
|
6
|
-
<% end %>
|
7
|
-
<% end %>
|
3
|
+
<%= render 'messages' %>
|
8
4
|
<div class="logo">
|
9
5
|
<%= image_tag "logo.png" %>
|
10
6
|
</div>
|
@@ -0,0 +1,17 @@
|
|
1
|
+
<div class="container">
|
2
|
+
<div class="second_factor box">
|
3
|
+
<%= render 'messages' %>
|
4
|
+
<h1><%= t('validate_otp.title') %></h1>
|
5
|
+
|
6
|
+
<div class="form">
|
7
|
+
<%= form_tag(validate_otp_path, method: :post, id: 'validate_otp-form') do %>
|
8
|
+
<%= hidden_field_tag :tgt, @ticket_granting_ticket || params[:tgt] %>
|
9
|
+
<%= hidden_field_tag :service, params[:service] %>
|
10
|
+
<%= label_tag :code, t('validate_otp.code') %>
|
11
|
+
<%= text_field_tag :otp, nil, maxlength: 6 %>
|
12
|
+
<%= submit_tag t('validate_otp.submit') %>
|
13
|
+
<% end %>
|
14
|
+
</div>
|
15
|
+
</div>
|
16
|
+
<%= render 'footer' %>
|
17
|
+
</div>
|
@@ -0,0 +1,32 @@
|
|
1
|
+
<div class="container">
|
2
|
+
<div class="twofactor box">
|
3
|
+
<%= render 'messages' %>
|
4
|
+
<div class="info">
|
5
|
+
<h1><%= t('two_factor_authenticators.setup') %></h1>
|
6
|
+
<p>
|
7
|
+
<%= t('two_factor_authenticators.description').html_safe %>
|
8
|
+
</p>
|
9
|
+
|
10
|
+
<p>
|
11
|
+
<%= t('two_factor_authenticators.instructions') %>
|
12
|
+
</p>
|
13
|
+
<div id="qr-code">
|
14
|
+
<img src="http://chart.apis.google.com/chart?cht=qr&chs=250x250&chl=<%= u "otpauth://totp/CASino?secret=#{@two_factor_authenticator.secret}" %>" height="250" width="250"><br />
|
15
|
+
</div>
|
16
|
+
<p>
|
17
|
+
<%= t('two_factor_authenticators.secret') %>: <%= @two_factor_authenticator.secret %>
|
18
|
+
</p>
|
19
|
+
</div>
|
20
|
+
|
21
|
+
<div class="form">
|
22
|
+
<%= form_tag(two_factor_authenticators_path, method: :post, id: 'two_factor_authenticators-form') do %>
|
23
|
+
<%= hidden_field_tag :id, @two_factor_authenticator.id %>
|
24
|
+
<%= label_tag :code, t('two_factor_authenticators.code') %>
|
25
|
+
<%= text_field_tag :otp, nil, maxlength: 6 %>
|
26
|
+
<%= link_to t('two_factor_authenticators.cancel'), sessions_path, :class => 'secondary button' %>
|
27
|
+
<%= submit_tag t('two_factor_authenticators.submit') %>
|
28
|
+
<% end %>
|
29
|
+
</div>
|
30
|
+
</div>
|
31
|
+
<%= render 'footer' %>
|
32
|
+
</div>
|
@@ -2,6 +2,7 @@
|
|
2
2
|
<html>
|
3
3
|
<head>
|
4
4
|
<title>CASino</title>
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
5
6
|
<%= stylesheet_link_tag "application", :media => "all" %>
|
6
7
|
<%= javascript_include_tag "application" %>
|
7
8
|
<%= csrf_meta_tags %>
|
data/casino.gemspec
CHANGED
@@ -17,6 +17,9 @@ Gem::Specification.new do |s|
|
|
17
17
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
18
18
|
s.require_paths = ['lib']
|
19
19
|
|
20
|
+
s.signing_key = File.expand_path '~/.gem/casino-private_key.pem'
|
21
|
+
s.cert_chain = ['casino-public_cert.pem']
|
22
|
+
|
20
23
|
s.add_development_dependency 'rake', '~> 10.0'
|
21
24
|
s.add_development_dependency 'rspec', '~> 2.12'
|
22
25
|
s.add_development_dependency 'rspec-rails', '~> 2.0'
|
@@ -25,5 +28,5 @@ Gem::Specification.new do |s|
|
|
25
28
|
|
26
29
|
s.add_runtime_dependency 'rails', '~> 3.2.9'
|
27
30
|
s.add_runtime_dependency 'jquery-rails', '~> 2.1'
|
28
|
-
s.add_runtime_dependency 'casino_core', '~> 1.
|
31
|
+
s.add_runtime_dependency 'casino_core', '~> 1.3.0'
|
29
32
|
end
|
data/config/locales/en.yml
CHANGED
@@ -9,6 +9,12 @@ en:
|
|
9
9
|
label_username: "Username"
|
10
10
|
label_password: "Password"
|
11
11
|
label_button: "Login"
|
12
|
+
validate_otp:
|
13
|
+
title: "Two-factor authentication"
|
14
|
+
description: "Please enter a valid one-time password."
|
15
|
+
code: "Code"
|
16
|
+
submit: "Continue"
|
17
|
+
invalid_otp: "The one-time password you entered is not correct."
|
12
18
|
logout:
|
13
19
|
title: "Bye."
|
14
20
|
logged_out_without_url: "You have successfully logged out."
|
@@ -24,3 +30,20 @@ en:
|
|
24
30
|
column_activity: "Most recent activity"
|
25
31
|
current_session: "Current session"
|
26
32
|
end_session: "End session"
|
33
|
+
two_factor_authenticators:
|
34
|
+
title: "Two-factor authentication"
|
35
|
+
setup: "Set up two-factor authentication"
|
36
|
+
description: "Tow-factor authentication requires you to enter an additional one-time password (OTP) each time you try to login to your account. An OTP can be created with an application such as the <a href='http://support.google.com/accounts/bin/answer.py?hl=en&answer=1066447'>Google Authenticator</a> with your mobile phone."
|
37
|
+
instructions: "If you are using Google Authenticator, scan the QR code below with the application. Enter the verification code in the text field below."
|
38
|
+
disabled: "Currently disabled"
|
39
|
+
enable: "Enable"
|
40
|
+
enabled: "Currently enabled"
|
41
|
+
disable: "Disable"
|
42
|
+
cancel: "Cancel"
|
43
|
+
secret: "Secret"
|
44
|
+
code: "Confirmation code"
|
45
|
+
submit: "Verify and enable"
|
46
|
+
invalid_one_time_password: "The one-time password was not correct."
|
47
|
+
invalid_two_factor_authenticator: "The two-factor authenticator expired. Please follow the instructions below."
|
48
|
+
successfully_activated: "The two-factor authenticator is now linked to this account."
|
49
|
+
successfully_deleted: "The two-factor authenticator was successfully deleted."
|
data/config/routes.rb
CHANGED
@@ -1,8 +1,11 @@
|
|
1
1
|
CASino::Engine.routes.draw do
|
2
2
|
resources :sessions, only: [:index, :destroy]
|
3
|
+
resources :two_factor_authenticators, only: [:new, :create, :destroy]
|
4
|
+
|
3
5
|
get 'login' => 'sessions#new'
|
4
6
|
post 'login' => 'sessions#create'
|
5
7
|
get 'logout' => 'sessions#logout'
|
8
|
+
post 'validate_otp' => 'sessions#validate_otp'
|
6
9
|
|
7
10
|
get 'validate' => 'service_tickets#validate'
|
8
11
|
get 'serviceValidate' => 'service_tickets#service_validate'
|
data/lib/casino/listener.rb
CHANGED
@@ -9,9 +9,14 @@ module CASino
|
|
9
9
|
autoload :LoginCredentialRequestor, 'casino/listener/login_credential_requestor.rb'
|
10
10
|
autoload :Logout, 'casino/listener/logout.rb'
|
11
11
|
autoload :ProxyTicketProvider, 'casino/listener/proxy_ticket_provider.rb'
|
12
|
+
autoload :SecondFactorAuthenticationAcceptor, 'casino/listener/second_factor_authentication_acceptor.rb'
|
12
13
|
autoload :SessionDestroyer, 'casino/listener/session_destroyer.rb'
|
13
14
|
autoload :SessionOverview, 'casino/listener/session_overview.rb'
|
14
15
|
autoload :TicketValidator, 'casino/listener/ticket_validator.rb'
|
16
|
+
autoload :TwoFactorAuthenticatorActivator, 'casino/listener/two_factor_authenticator_activator.rb'
|
17
|
+
autoload :TwoFactorAuthenticatorDestroyer, 'casino/listener/two_factor_authenticator_destroyer.rb'
|
18
|
+
autoload :TwoFactorAuthenticatorOverview, 'casino/listener/two_factor_authenticator_overview.rb'
|
19
|
+
autoload :TwoFactorAuthenticatorRegistrator, 'casino/listener/two_factor_authenticator_registrator.rb'
|
15
20
|
|
16
21
|
def initialize(controller)
|
17
22
|
@controller = controller
|
@@ -10,6 +10,11 @@ class CASino::Listener::LoginCredentialAcceptor < CASino::Listener
|
|
10
10
|
end
|
11
11
|
end
|
12
12
|
|
13
|
+
def two_factor_authentication_pending(ticket_granting_ticket)
|
14
|
+
assign(:ticket_granting_ticket, ticket_granting_ticket)
|
15
|
+
@controller.render 'validate_otp'
|
16
|
+
end
|
17
|
+
|
13
18
|
def invalid_login_credentials(login_ticket)
|
14
19
|
@controller.flash.now[:error] = I18n.t('login_credential_acceptor.invalid_login_credentials')
|
15
20
|
rerender_login_page(login_ticket)
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'casino/listener'
|
2
|
+
|
3
|
+
class CASino::Listener::SecondFactorAuthenticationAcceptor < CASino::Listener
|
4
|
+
|
5
|
+
def user_not_logged_in
|
6
|
+
@controller.redirect_to login_path
|
7
|
+
end
|
8
|
+
|
9
|
+
def user_logged_in(url, ticket_granting_ticket)
|
10
|
+
@controller.cookies[:tgt] = ticket_granting_ticket
|
11
|
+
if url.nil?
|
12
|
+
@controller.redirect_to sessions_path, status: :see_other
|
13
|
+
else
|
14
|
+
@controller.redirect_to url, status: :see_other
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def invalid_one_time_password
|
19
|
+
@controller.flash.now[:error] = I18n.t('validate_otp.invalid_otp')
|
20
|
+
end
|
21
|
+
|
22
|
+
def service_not_allowed(service)
|
23
|
+
assign(:service, service)
|
24
|
+
@controller.render 'service_not_allowed', status: 403
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'casino/listener'
|
2
|
+
|
3
|
+
class CASino::Listener::TwoFactorAuthenticatorActivator < CASino::Listener
|
4
|
+
def user_not_logged_in
|
5
|
+
@controller.redirect_to login_path
|
6
|
+
end
|
7
|
+
|
8
|
+
def two_factor_authenticator_activated
|
9
|
+
@controller.flash[:notice] = I18n.t('two_factor_authenticators.successfully_activated')
|
10
|
+
@controller.redirect_to sessions_path
|
11
|
+
end
|
12
|
+
|
13
|
+
def invalid_one_time_password(two_factor_authenticator)
|
14
|
+
@controller.flash.now[:error] = I18n.t('two_factor_authenticators.invalid_one_time_password')
|
15
|
+
assign(:two_factor_authenticator, two_factor_authenticator)
|
16
|
+
@controller.render 'new'
|
17
|
+
end
|
18
|
+
|
19
|
+
def invalid_two_factor_authenticator
|
20
|
+
@controller.flash[:error] = I18n.t('two_factor_authenticators.invalid_two_factor_authenticator')
|
21
|
+
@controller.redirect_to new_two_factor_authenticator_path
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'casino/listener'
|
2
|
+
|
3
|
+
class CASino::Listener::TwoFactorAuthenticatorDestroyer < CASino::Listener
|
4
|
+
def user_not_logged_in
|
5
|
+
@controller.redirect_to login_path
|
6
|
+
end
|
7
|
+
|
8
|
+
def two_factor_authenticator_destroyed
|
9
|
+
@controller.flash[:notice] = I18n.t('two_factor_authenticators.successfully_deleted')
|
10
|
+
@controller.redirect_to sessions_path
|
11
|
+
end
|
12
|
+
|
13
|
+
def invalid_two_factor_authenticator
|
14
|
+
@controller.redirect_to sessions_path
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'casino/listener'
|
2
|
+
|
3
|
+
class CASino::Listener::TwoFactorAuthenticatorOverview < CASino::Listener
|
4
|
+
def user_not_logged_in
|
5
|
+
# nothing to do here
|
6
|
+
end
|
7
|
+
|
8
|
+
def two_factor_authenticators_found(two_factor_authenticators)
|
9
|
+
assign(:two_factor_authenticators, two_factor_authenticators)
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'casino/listener'
|
2
|
+
|
3
|
+
class CASino::Listener::TwoFactorAuthenticatorRegistrator < CASino::Listener
|
4
|
+
def user_not_logged_in
|
5
|
+
@controller.redirect_to login_path
|
6
|
+
end
|
7
|
+
|
8
|
+
def two_factor_authenticator_registered(two_factor_authenticator)
|
9
|
+
assign(:two_factor_authenticator, two_factor_authenticator)
|
10
|
+
end
|
11
|
+
end
|
data/lib/casino/version.rb
CHANGED
@@ -11,15 +11,13 @@ module Casino # CASino would lead to c_a_sino...
|
|
11
11
|
def copy_config_files
|
12
12
|
copy_file 'cas.yml', 'config/cas.yml'
|
13
13
|
copy_file 'database.yml', 'config/database.yml'
|
14
|
+
copy_file 'casino_and_overrides.scss', 'app/assets/stylesheets/casino_and_overrides.scss'
|
14
15
|
end
|
15
16
|
|
16
17
|
def insert_assets_loader
|
17
18
|
insert_into_file 'app/assets/javascripts/application.js', :after => %r{//= require +['"]?jquery_ujs['"]?} do
|
18
19
|
"\n//= require casino"
|
19
20
|
end
|
20
|
-
insert_into_file "app/assets/stylesheets/application.css", :after => %r{\*= require_self} do
|
21
|
-
"\n *= require casino"
|
22
|
-
end
|
23
21
|
end
|
24
22
|
|
25
23
|
def insert_engine_routes
|
@@ -77,4 +77,22 @@ describe CASino::Listener::LoginCredentialAcceptor do
|
|
77
77
|
controller.instance_variable_get(:@service).should == service
|
78
78
|
end
|
79
79
|
end
|
80
|
+
|
81
|
+
context '#two_factor_authentication_pending' do
|
82
|
+
let(:ticket_granting_ticket) { 'TGT-123' }
|
83
|
+
|
84
|
+
before(:each) do
|
85
|
+
controller.stub(:render)
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'tells the controller to render the service_not_allowed template' do
|
89
|
+
controller.should_receive(:render).with('validate_otp')
|
90
|
+
listener.send(:two_factor_authentication_pending, ticket_granting_ticket)
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'assigns the not allowed service' do
|
94
|
+
listener.send(:two_factor_authentication_pending, ticket_granting_ticket)
|
95
|
+
controller.instance_variable_get(:@ticket_granting_ticket).should == ticket_granting_ticket
|
96
|
+
end
|
97
|
+
end
|
80
98
|
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe CASino::Listener::SecondFactorAuthenticationAcceptor do
|
4
|
+
include CASino::Engine.routes.url_helpers
|
5
|
+
let(:controller) { Struct.new(:cookies).new(cookies: {}) }
|
6
|
+
let(:listener) { described_class.new(controller) }
|
7
|
+
|
8
|
+
before(:each) do
|
9
|
+
controller.stub(:redirect_to)
|
10
|
+
end
|
11
|
+
|
12
|
+
describe '#user_not_logged_in' do
|
13
|
+
it 'redirects to the login page' do
|
14
|
+
controller.should_receive(:redirect_to).with(login_path)
|
15
|
+
listener.user_not_logged_in
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe '#user_logged_in' do
|
20
|
+
let(:ticket_granting_ticket) { 'TGT-123' }
|
21
|
+
context 'with a service url' do
|
22
|
+
let(:url) { 'http://www.example.com/?ticket=ST-123' }
|
23
|
+
it 'tells the controller to redirect the client' do
|
24
|
+
controller.should_receive(:redirect_to).with(url, status: :see_other)
|
25
|
+
listener.user_logged_in(url, ticket_granting_ticket)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context 'without a service url' do
|
30
|
+
let(:url) { nil }
|
31
|
+
it 'tells the controller to redirect to the session overview' do
|
32
|
+
controller.should_receive(:redirect_to).with(sessions_path, status: :see_other)
|
33
|
+
listener.user_logged_in(url, ticket_granting_ticket)
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'creates the tgt cookie' do
|
37
|
+
listener.user_logged_in(url, ticket_granting_ticket)
|
38
|
+
controller.cookies[:tgt].should == ticket_granting_ticket
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context "#invalid_one_time_password" do
|
44
|
+
let(:flash) { ActionDispatch::Flash::FlashHash.new }
|
45
|
+
|
46
|
+
before(:each) do
|
47
|
+
controller.stub(:render)
|
48
|
+
controller.stub(:flash).and_return(flash)
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'should add an error message' do
|
52
|
+
listener.invalid_one_time_password
|
53
|
+
flash[:error].should == I18n.t('validate_otp.invalid_otp')
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context '#service_not_allowed' do
|
58
|
+
let(:service) { 'http://www.example.com/foo' }
|
59
|
+
|
60
|
+
before(:each) do
|
61
|
+
controller.stub(:render)
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'tells the controller to render the service_not_allowed template' do
|
65
|
+
controller.should_receive(:render).with('service_not_allowed', status: 403)
|
66
|
+
listener.send(:service_not_allowed, service)
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'assigns the not allowed service' do
|
70
|
+
listener.send(:service_not_allowed, service)
|
71
|
+
controller.instance_variable_get(:@service).should == service
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|