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.
Files changed (40) hide show
  1. data.tar.gz.sig +0 -0
  2. data/Gemfile.lock +11 -5
  3. data/app/assets/images/logo.png +0 -0
  4. data/app/assets/images/logo@2x.png +0 -0
  5. data/app/assets/stylesheets/{casino/index.css.scss → casino.scss} +177 -90
  6. data/app/controllers/casino/sessions_controller.rb +5 -0
  7. data/app/controllers/casino/two_factor_authenticators_controller.rb +15 -0
  8. data/app/views/casino/{sessions → application}/_footer.html.erb +0 -0
  9. data/app/views/casino/application/_messages.html.erb +5 -0
  10. data/app/views/casino/sessions/index.html.erb +13 -2
  11. data/app/views/casino/sessions/logout.html.erb +3 -0
  12. data/app/views/casino/sessions/new.html.erb +1 -5
  13. data/app/views/casino/sessions/validate_otp.html.erb +17 -0
  14. data/app/views/casino/two_factor_authenticators/new.html.erb +32 -0
  15. data/app/views/layouts/application.html.erb +1 -0
  16. data/casino.gemspec +4 -1
  17. data/config/locales/en.yml +23 -0
  18. data/config/routes.rb +3 -0
  19. data/lib/casino/listener.rb +5 -0
  20. data/lib/casino/listener/login_credential_acceptor.rb +5 -0
  21. data/lib/casino/listener/second_factor_authentication_acceptor.rb +26 -0
  22. data/lib/casino/listener/two_factor_authenticator_activator.rb +23 -0
  23. data/lib/casino/listener/two_factor_authenticator_destroyer.rb +16 -0
  24. data/lib/casino/listener/two_factor_authenticator_overview.rb +11 -0
  25. data/lib/casino/listener/two_factor_authenticator_registrator.rb +11 -0
  26. data/lib/casino/version.rb +1 -1
  27. data/lib/generators/casino/install_generator.rb +1 -3
  28. data/lib/generators/casino/templates/casino_and_overrides.scss +12 -0
  29. data/spec/controllers/listener/login_credential_acceptor_spec.rb +18 -0
  30. data/spec/controllers/listener/second_factor_authentication_acceptor_spec.rb +74 -0
  31. data/spec/controllers/listener/two_factor_authenticator_activator_spec.rb +64 -0
  32. data/spec/controllers/listener/two_factor_authenticator_destroyer_spec.rb +40 -0
  33. data/spec/controllers/listener/two_factor_authenticator_overview_spec.rb +16 -0
  34. data/spec/controllers/listener/two_factor_authenticator_registrator_spec.rb +27 -0
  35. data/spec/controllers/sessions_controller_spec.rb +10 -0
  36. data/spec/controllers/two_factor_authenticators_controller_spec.rb +34 -0
  37. metadata +63 -8
  38. metadata.gz.sig +1 -0
  39. data/lib/generators/casino/templates/casino.css +0 -3
  40. 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
@@ -0,0 +1,5 @@
1
+ <% flash.each do |name, msg| %>
2
+ <% if msg.is_a?(String) %>
3
+ <%= content_tag :div, msg, :id => "flash_#{name}" %>
4
+ <% end %>
5
+ <% end %>
@@ -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>
@@ -11,6 +11,9 @@
11
11
  <% end %>
12
12
  </p>
13
13
  </div>
14
+ <div class="logo">
15
+ <%= image_tag "logo.png" %>
16
+ </div>
14
17
  </div>
15
18
  <%= render 'footer' %>
16
19
  </div>
@@ -1,10 +1,6 @@
1
1
  <div class="container">
2
2
  <div class="login box">
3
- <% flash.each do |name, msg| %>
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 %>
@@ -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.2.0'
31
+ s.add_runtime_dependency 'casino_core', '~> 1.3.0'
29
32
  end
@@ -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."
@@ -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'
@@ -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
@@ -1,3 +1,3 @@
1
1
  module CASino
2
- VERSION = '1.0.0'
2
+ VERSION = '1.1.0'
3
3
  end
@@ -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
@@ -0,0 +1,12 @@
1
+
2
+ // Default CASino settings
3
+
4
+ // $buttonColor: #0074ad;
5
+ // $buttonSecondaryColor: #c2c2c2;
6
+ // $logo: "logo.png";
7
+ // $logoRetina: "logo@2x.png";
8
+ // $logoWidth: 146px;
9
+ // $logoHeight: 34px;
10
+
11
+
12
+ @import 'casino';
@@ -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