casino 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
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