diff --git a/app/controllers/account_controller.rb b/app/controllers/account_controller.rb index 0b67dbbfee6..50e882d9640 100644 --- a/app/controllers/account_controller.rb +++ b/app/controllers/account_controller.rb @@ -40,14 +40,27 @@ class AccountController < ApplicationController # Login request and validation def login - if User.current.logged? + user = User.current + + if user.logged? redirect_to home_url elsif Concerns::OmniauthLogin.direct_login? - ps = {}.tap do |p| - p[:origin] = params[:back_url] if params[:back_url] - end + if flash.empty? + ps = {}.tap do |p| + p[:origin] = params[:back_url] if params[:back_url] + end - redirect_to Concerns::OmniauthLogin.direct_login_provider_url(ps) + redirect_to Concerns::OmniauthLogin.direct_login_provider_url(ps) + else + instructions = + if user.active? + I18n.t :instructions_after_error, signin: translation_signin_link + else + I18n.t :instructions_after_registration, signin: translation_signin_link + end + + render :exit, locals: { instructions: instructions } + end elsif request.post? authenticate_user end @@ -56,7 +69,15 @@ class AccountController < ApplicationController # Log out current user and redirect to welcome page def logout logout_user - redirect_to home_url + if Concerns::OmniauthLogin.direct_login? + flash.now[:notice] = I18n.t :notice_logged_out + render :exit, + locals: { + instructions: I18n.t(:instructions_after_logout, signin: translation_signin_link) + } + else + redirect_to home_url + end end # Enable user to choose a new password @@ -443,4 +464,8 @@ class AccountController < ApplicationController redirect_back_or_default :controller => '/my', :action => 'page' end end + + def translation_signin_link + view_context.link_to(I18n.t('label_here'), view_context.signin_path) + end end diff --git a/app/views/account/exit.html.erb b/app/views/account/exit.html.erb new file mode 100644 index 00000000000..5517536f69a --- /dev/null +++ b/app/views/account/exit.html.erb @@ -0,0 +1,45 @@ +<%#-- copyright +OpenProject is a project management system. +Copyright (C) 2012-2014 the OpenProject Foundation (OPF) + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License version 3. + +OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +Copyright (C) 2006-2013 Jean-Philippe Lang +Copyright (C) 2010-2013 the ChiliProject Team + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +See doc/COPYRIGHT.rdoc for more details. + +++#%> + +<%# +LOCALS + + instructions: String +#%> + +<% disable_accessibility_css! %> + +<% breadcrumb_paths(l(:label_login)) %> +<%= call_hook :view_account_login_top %> +
+

<%= I18n.t(:label_login) %>

+
+

<%= instructions.html_safe %>

+
+<%= call_hook :view_account_login_bottom %> diff --git a/config/locales/de.yml b/config/locales/de.yml index 79b1bfe8de9..829df2c1290 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -579,6 +579,10 @@ de: gui_validation_error: "1 Fehler" gui_validation_error_plural: "%{count} Fehler" + instructions_after_registration: "Sie können sich %{signin} einloggen sobald Ihr Konto aktiviert wurde." + instructions_after_logout: "Sie können sich %{signin} wieder einloggen." + instructions_after_error: "Sie können noch mal versuchen sich einzuloggen, indem Sie %{signin} klicken. Wenn der Fehler weiterhin auftritt, wenden Sie sich bitte an den Ihren Admin." + label_account: "Konto" label_activity: "Aktivität" label_add_another_file: "Eine weitere Datei hinzufügen" @@ -712,6 +716,7 @@ de: label_group_new: "Neue Gruppe" label_group_plural: "Gruppen" label_help: "Hilfe" + label_here: hier label_hide: "Verbergen" label_history: "Historie" label_home: "Hauptseite" @@ -1090,6 +1095,7 @@ de: notice_unable_delete_time_entry: "Der Zeiterfassungseintrag konnte nicht gelöscht werden." notice_unable_delete_version: "Die Version konnte nicht gelöscht werden." notice_automatic_set_of_standard_type: "Der Standard-Typ wurde automatisch gesetzt." + notice_logged_out: "Sie wurden ausgeloggt." error_types_in_use_by_work_packages: "Die folgenden Typen werden von Arbeitspaketen referenziert: %{types}" # Default format for numbers diff --git a/config/locales/en.yml b/config/locales/en.yml index b1335d14a9e..33ef9adf4b5 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -576,6 +576,10 @@ en: gui_validation_error: "1 error" gui_validation_error_plural: "%{count} errors" + instructions_after_registration: "You can sign in as soon as your account has been activated by clicking %{signin}." + instructions_after_logout: "You can sign in again by clicking %{signin}." + instructions_after_error: "You can try to sign in again by clicking %{signin}. If the error persists, ask your admin for help." + label_account: "Account" label_activity: "Activity" label_add_another_file: "Add another file" @@ -709,6 +713,7 @@ en: label_group_new: "New group" label_group_plural: "Groups" label_help: "Help" + label_here: here label_hide: "Hide" label_history: "History" label_home: "Home" @@ -1088,6 +1093,7 @@ en: notice_unable_delete_time_entry: "Unable to delete time log entry." notice_unable_delete_version: "Unable to delete version." notice_automatic_set_of_standard_type: "Set standard type automatically." + notice_logged_out: "You have been logged out." error_types_in_use_by_work_packages: "The following types are still referenced by work packages: %{types}" # Default format for numbers diff --git a/config/routes.rb b/config/routes.rb index 13c7c576947..8791bad27d7 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -57,6 +57,7 @@ OpenProject::Application.routes.draw do match '/login', :action => 'login', :as => 'signin', :via => [:get, :post] get '/logout', :action => 'logout', :as => 'signout' + get '/exit', :action => 'exit' end namespace :api do diff --git a/spec/features/omniauth/omniauth_spec.rb b/spec/features/omniauth/omniauth_spec.rb index 23cfd4c2122..a6ebd5a5730 100644 --- a/spec/features/omniauth/omniauth_spec.rb +++ b/spec/features/omniauth/omniauth_spec.rb @@ -101,6 +101,29 @@ describe 'Omniauth authentication' do end end + describe 'sign out a user with direct login' do + let!(:user) do + FactoryGirl.create(:user, + force_password_change: false, + identity_url: 'developer:omnibob@example.com', + login: 'omnibob', + mail: 'omnibob@example.com', + firstname: 'omni', + lastname: 'bob' + ) + end + + before do + Concerns::OmniauthLogin.stub(:direct_login_provider).and_return('developer') + end + + it 'shows a notice that the user has been logged out' do + visit signout_path + + expect(page).to have_content(I18n.t(:notice_logged_out)) + end + end + shared_examples 'omniauth user registration' do it 'should register new user' do visit '/auth/developer' @@ -166,15 +189,69 @@ describe 'Omniauth authentication' do end end + context 'registration by email' do + before do + allow(Setting).to receive(:self_registration?).and_return(true) + allow(Setting).to receive(:self_registration).and_return('1') + end + + shared_examples 'registration with registration by email' do + it 'shows a note explaining that the account has to be activated' do + visit login_path + + # login form developer strategy + fill_in 'first_name', with: 'Ifor' + fill_in 'last_name', with: 'McAlistar' + fill_in 'email', with: 'i.mcalistar@example.com' + + click_link_or_button 'Sign In' + + expect(page).to have_content(I18n.t(:notice_account_register_done)) + end + end + + it_behaves_like 'registration with registration by email' do + let(:login_path) { '/auth/developer' } + end + + context 'with direct login enabled' do + before do + Concerns::OmniauthLogin.stub(:direct_login_provider).and_return('developer') + end + + it_behaves_like 'registration with registration by email' do + # i.e. it still shows a notice + # instead of redirecting straight back to the omniauth login provider + let(:login_path) { signin_path } + end + end + end + context 'error occurs' do - it 'should fail with generic error message' do - # set omniauth to test mode will redirect all calls to omniauth - # directly to the callback and by setting the mock_auth provider - # to a symbol will force omniauth to fail /auth/failure - OmniAuth.config.test_mode = true - OmniAuth.config.mock_auth[:developer] = :invalid_credentials - visit '/auth/developer' - expect(page).to have_content(I18n.t(:error_external_authentication_failed)) + shared_examples 'omniauth signin error' do + it 'should fail with generic error message' do + # set omniauth to test mode will redirect all calls to omniauth + # directly to the callback and by setting the mock_auth provider + # to a symbol will force omniauth to fail /auth/failure + OmniAuth.config.test_mode = true + OmniAuth.config.mock_auth[:developer] = :invalid_credentials + visit login_path + expect(page).to have_content(I18n.t(:error_external_authentication_failed)) + end + end + + it_behaves_like 'omniauth signin error' do + let(:login_path) { '/auth/developer' } + end + + context 'with direct login' do + before do + Concerns::OmniauthLogin.stub(:direct_login_provider).and_return('developer') + end + + it_behaves_like 'omniauth signin error' do + let(:login_path) { signin_path } + end end end end