diff --git a/Gemfile.lock b/Gemfile.lock index bd2d66de..9c71db6f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -37,21 +37,27 @@ GEM activesupport (3.2.13) i18n (= 0.6.1) multi_json (~> 1.0) - addressable (2.3.3) + addressable (2.3.4) arel (3.0.2) builder (3.0.4) - casino_core (1.4.0) + capybara (2.1.0) + mime-types (>= 1.16) + nokogiri (>= 1.3.3) + rack (>= 1.0.0) + rack-test (>= 0.5.4) + xpath (~> 2.0) + casino_core (1.4.3) activerecord (~> 3.2.9) addressable (~> 2.3) faraday (~> 0.8) rotp (~> 1.4) terminal-table (~> 1.4) useragent (~> 0.4) - diff-lcs (1.2.1) + diff-lcs (1.2.4) erubis (2.7.0) faraday (0.8.7) multipart-post (~> 1.1) - hike (1.2.1) + hike (1.2.2) http_accept_language (2.0.0.pre) i18n (0.6.1) journey (1.0.4) @@ -63,9 +69,10 @@ GEM i18n (>= 0.4.0) mime-types (~> 1.16) treetop (~> 1.4.8) - mime-types (1.21) + mime-types (1.23) multi_json (1.7.2) multipart-post (1.2.0) + nokogiri (1.5.9) polyglot (0.3.3) rack (1.4.5) rack-cache (1.2) @@ -89,7 +96,7 @@ GEM rake (>= 0.8.7) rdoc (~> 3.4) thor (>= 0.14.6, < 2.0) - rake (10.0.3) + rake (10.0.4) rdoc (3.12.2) json (~> 1.4) rotp (1.4.1) @@ -100,7 +107,7 @@ GEM rspec-core (2.13.1) rspec-expectations (2.13.0) diff-lcs (>= 1.1.3, < 2.0) - rspec-mocks (2.13.0) + rspec-mocks (2.13.1) rspec-rails (2.13.0) actionpack (>= 3.0) activesupport (>= 3.0) @@ -119,18 +126,21 @@ GEM tilt (~> 1.1, != 1.3.0) sqlite3 (1.3.7) terminal-table (1.4.5) - thor (0.17.0) - tilt (1.3.6) + thor (0.18.1) + tilt (1.3.7) treetop (1.4.12) polyglot polyglot (>= 0.3.1) tzinfo (0.3.37) useragent (0.5.0) + xpath (2.0.0) + nokogiri (~> 1.3) PLATFORMS ruby DEPENDENCIES + capybara (~> 2.1) casino! rake (~> 10.0) rspec (~> 2.12) diff --git a/app/views/casino/two_factor_authenticators/new.html.erb b/app/views/casino/two_factor_authenticators/new.html.erb index 8f1a35a6..afc2c369 100644 --- a/app/views/casino/two_factor_authenticators/new.html.erb +++ b/app/views/casino/two_factor_authenticators/new.html.erb @@ -13,7 +13,7 @@
+
<%= t('two_factor_authenticators.secret') %>: <%= @two_factor_authenticator.secret %>
diff --git a/casino.gemspec b/casino.gemspec index 81e5d626..8ac080e0 100644 --- a/casino.gemspec +++ b/casino.gemspec @@ -23,6 +23,7 @@ Gem::Specification.new do |s| s.cert_chain = ['casino-public_cert.pem'] end + s.add_development_dependency 'capybara', '~> 2.1' s.add_development_dependency 'rake', '~> 10.0' s.add_development_dependency 'rspec', '~> 2.12' s.add_development_dependency 'rspec-rails', '~> 2.0' diff --git a/spec/controllers/listener/legacy_validator_spec.rb b/spec/controllers/listener/legacy_validator_spec.rb index f8acd966..37e76ab9 100644 --- a/spec/controllers/listener/legacy_validator_spec.rb +++ b/spec/controllers/listener/legacy_validator_spec.rb @@ -3,20 +3,20 @@ describe CASino::Listener::LegacyValidator do let(:controller) { Object.new } let(:listener) { described_class.new(controller) } - let(:text) { "foobar\nbla\n" } - let(:render_parameters) { { text: text, content_type: 'text/plain' } } + let(:response_text) { "foobar\nbla\n" } + let(:render_parameters) { { text: response_text, content_type: 'text/plain' } } describe '#validation_succeeded' do it 'tells the controller to render the response text' do controller.should_receive(:render).with(render_parameters) - listener.validation_succeeded(text) + listener.validation_succeeded(response_text) end end describe '#validation_failed' do it 'tells the controller to render the response text' do controller.should_receive(:render).with(render_parameters) - listener.validation_failed(text) + listener.validation_failed(response_text) end end end diff --git a/spec/features/login_spec.rb b/spec/features/login_spec.rb new file mode 100644 index 00000000..960d07ef --- /dev/null +++ b/spec/features/login_spec.rb @@ -0,0 +1,77 @@ +require 'spec_helper' + +describe 'Login' do + include CASino::Engine.routes.url_helpers + + subject { page } + + context 'with two-factor authentication enabled' do + before do + in_browser(:other) do + sign_in + @totp = enable_two_factor_authentication + end + end + + context 'with valid username and password' do + before { sign_in } + + it { should_not have_button('Login') } + it { should have_button('Continue') } + its(:current_path) { should == login_path } + + context 'when filling in the correct otp' do + before do + fill_in :otp, with: @totp.now + click_button 'Continue' + end + + it { should_not have_button('Login') } + it { should_not have_button('Continue') } + its(:current_path) { should == sessions_path } + end + + context 'when filling in an incorrect otp' do + before do + fill_in :otp, with: 'aaaaa' + click_button 'Continue' + end + + it { should have_text('The one-time password you entered is not correct') } + it { should have_button('Continue') } + end + end + end + + context 'with two-factor authentication disabled' do + context 'with valid username and password' do + before { sign_in } + + it { should_not have_button('Login') } + its(:current_path) { should == sessions_path } + end + end + + context 'with invalid username' do + before { sign_in username: 'lalala', password: 'foobar123' } + + it { should have_button('Login') } + it { should have_text('Incorrect username or password') } + end + + context 'with blank password' do + before { sign_in password: '' } + + it { should have_button('Login') } + it { should have_text('Incorrect username or password') } + end + + context 'with german locale' do + before do + page.driver.header 'Accept-Language', 'de' + visit login_path + end + + it { should have_text('Benutzername') } + end +end diff --git a/spec/features/logout_spec.rb b/spec/features/logout_spec.rb new file mode 100644 index 00000000..c25aa6a9 --- /dev/null +++ b/spec/features/logout_spec.rb @@ -0,0 +1,16 @@ +require 'spec_helper' + +describe 'Logout' do + include CASino::Engine.routes.url_helpers + + subject { page } + + context 'when logged in' do + before do + sign_in + click_link 'Logout' + end + + it { should have_content('logged out') } + end +end diff --git a/spec/features/session_overview_spec.rb b/spec/features/session_overview_spec.rb new file mode 100644 index 00000000..f3b0d8f9 --- /dev/null +++ b/spec/features/session_overview_spec.rb @@ -0,0 +1,56 @@ +require 'spec_helper' + +describe 'Session overview' do + include CASino::Engine.routes.url_helpers + + subject { page } + + context 'when logged in' do + before do + sign_in + visit sessions_path + end + + it { should have_link('Logout', href: logout_path) } + it { should have_text('Your Active Sessions') } + it { should have_text('Active Session') } + + context 'without other sessions' do + it { should_not have_link('End session') } + end + + context 'when other sessions exist' do + before do + in_browser(:other) do + sign_in + end + visit sessions_path + end + it { should have_link('End session') } + end + + context 'with two-factor authentication disabled' do + before do + in_browser(:other) do + sign_in + end + visit sessions_path + end + it { should have_link('Enable', href: new_two_factor_authenticator_path) } + it { should_not have_link('Disable') } + end + + context 'with two-factor authentication enabled' do + before { enable_two_factor_authentication } + it { should_not have_link('Enable', href: new_two_factor_authenticator_path) } + it { should have_link('Disable') } + end + end + + context 'when not logged in' do + before { visit sessions_path } + + it { should have_button('Login') } + its(:current_path) { should == login_path } + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index d40cd597..4900990c 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -8,6 +8,8 @@ require 'rspec/rails' require 'rspec/autorun' +require 'capybara/rails' + ENGINE_RAILS_ROOT = File.join(File.dirname(__FILE__), '../') # Requires supporting ruby files with custom matchers and macros, etc, @@ -15,13 +17,7 @@ Dir[File.join(ENGINE_RAILS_ROOT, 'spec/support/**/*.rb')].each {|f| require f } RSpec.configure do |config| - # ## Mock Framework - # - # If you prefer to use mocha, flexmock or RR, uncomment the appropriate line: - # - # config.mock_with :mocha - # config.mock_with :flexmock - # config.mock_with :rr + config.use_transactional_fixtures = true # If true, the base class of anonymous controllers will be inferred # automatically. This will be the default behavior in future versions of diff --git a/spec/support/features_helper.rb b/spec/support/features_helper.rb new file mode 100644 index 00000000..eca827b2 --- /dev/null +++ b/spec/support/features_helper.rb @@ -0,0 +1,28 @@ +module FeatureHelpers + def in_browser(name) + original_browser = Capybara.session_name + Capybara.session_name = name + yield + Capybara.session_name = original_browser + end + + def sign_in(options = {}) + visit login_path + fill_in 'username', with: options[:username] || 'testuser' + fill_in 'password', with: options[:password] || 'foobar123' + click_button 'Login' + end + + def enable_two_factor_authentication + visit new_two_factor_authenticator_path + secret = find('p#secret').text.gsub(/^Secret:\s*/, '') + ROTP::TOTP.new(secret).tap do |totp| + fill_in 'otp', with: "#{totp.now}" + click_button 'Verify and enable' + end + end +end + +RSpec.configure do |config| + config.include FeatureHelpers, type: :feature +end diff --git a/spec/support/sign_in.rb b/spec/support/sign_in.rb deleted file mode 100644 index bdc662aa..00000000 --- a/spec/support/sign_in.rb +++ /dev/null @@ -1,11 +0,0 @@ -def test_sign_in(options = {}) - request.env['HTTP_USER_AGENT'] = options[:user_agent] || 'TestBrowser 1.2' - ticket = TicketGrantingTicket.create!({ - ticket: controller.random_ticket_string('TGC'), - username: options[:username] || 'user1', - extra_attributes: options[:extra_attributes], - user_agent: request.env['HTTP_USER_AGENT'] - }) - request.cookies[:tgt] = ticket.ticket - return ticket -end