From 542e0f27d6a3665ae23683dc53a984db978a12e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karel=20Pi=C4=8Dman?= Date: Thu, 26 Sep 2024 13:10:58 +0200 Subject: [PATCH] #32 Autologin --- README.md | 3 +- app/controllers/redmine_oauth_controller.rb | 21 ++++++- .../_view_account_login_bottom.html.erb | 25 ++++++--- app/views/settings/_oauth_settings.html.erb | 9 +++ assets/stylesheets/redmine_oauth.css | 11 ++++ config/locales/cs.yml | 39 +++++++------ config/locales/de.yml | 45 ++++++++------- config/locales/en.yml | 35 ++++++------ config/locales/fr.yml | 43 +++++++------- init.rb | 2 + .../controllers/account_controller_hooks.rb | 34 +++++++++++ .../hooks/views/login_view_hooks.rb | 2 +- .../patches/account_controller_patch.rb | 26 ++++++++- .../patches/settings_controller_patch.rb | 3 + test/functional/account_controller_test.rb | 56 +++++++++++++++++++ ...st.rb => redmine_oauth_controller_test.rb} | 13 ++--- 16 files changed, 274 insertions(+), 93 deletions(-) rename app/views/hooks/{ => redmine_oauth}/_view_account_login_bottom.html.erb (65%) create mode 100644 lib/redmine_oauth/hooks/controllers/account_controller_hooks.rb create mode 100644 test/functional/account_controller_test.rb rename test/functional/{redmine_o_auth_controller_test.rb => redmine_oauth_controller_test.rb} (77%) diff --git a/README.md b/README.md index 41dbc44..c90fe9f 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,8 @@ The user is identified by the email registered with the OAuth provider. The emai in Redmine. If such an email is not found, the user will be offered to register in Redmine, depending on Redmine's setting **Self-registration**. OAuth logout is also supported, if it is set in the options. Access to Redmine can controlled by roles assigned in your OAuth provider. -See [#36](https://github.com/kontron/redmine_oauth/issues/36#issuecomment-2348842432) for details. +See [#36](https://github.com/kontron/redmine_oauth/issues/36#issuecomment-2348842432) for details; as well as OAuth +autologin. Inspired by Gucin's plugin https://github.com/Gucin/redmine_omniauth_azure. diff --git a/app/controllers/redmine_oauth_controller.rb b/app/controllers/redmine_oauth_controller.rb index f456279..9d313c7 100644 --- a/app/controllers/redmine_oauth_controller.rb +++ b/app/controllers/redmine_oauth_controller.rb @@ -28,6 +28,7 @@ class RedmineOauthController < AccountController def oauth session[:back_url] = params[:back_url] session[:autologin] = params[:autologin] + session[:oauth_autologin] = params[:oauth_autologin] oauth_csrf_token = generate_csrf_token session[:oauth_csrf_token] = oauth_csrf_token case Setting.plugin_redmine_oauth[:oauth_name] @@ -154,6 +155,7 @@ def oauth_callback end # Try to log in + set_params try_to_login email, user_info rescue StandardError => e Rails.logger.error e.message @@ -161,13 +163,30 @@ def oauth_callback redirect_to signin_path end + def set_oauth_autologin_cookie(value, request) + cookie_options = { + value: value, + expires: 1.year.from_now, + path: RedmineApp::Application.config.relative_url_root || '/', + same_site: :lax, + secure: request.ssl?, + httponly: true + } + cookies[:oauth_autologin] = cookie_options + end + private - def try_to_login(email, info) + def set_params params['back_url'] = session[:back_url] session.delete :back_url params['autologin'] = session[:autologin] session.delete :autologin + params['oauth_autologin'] = session[:oauth_autologin] + session.delete :oauth_autologin + end + + def try_to_login(email, info) user = User.joins(:email_addresses).where(email_addresses: { address: email }).first if user # Existing user if user.registered? # Registered diff --git a/app/views/hooks/_view_account_login_bottom.html.erb b/app/views/hooks/redmine_oauth/_view_account_login_bottom.html.erb similarity index 65% rename from app/views/hooks/_view_account_login_bottom.html.erb rename to app/views/hooks/redmine_oauth/_view_account_login_bottom.html.erb index a2238b5..89f7671 100644 --- a/app/views/hooks/_view_account_login_bottom.html.erb +++ b/app/views/hooks/redmine_oauth/_view_account_login_bottom.html.erb @@ -28,14 +28,25 @@ <%= l(:button_login) %> <% end %> - <%= form_tag(oauth_path(back_url: back_url), method: :get, id: 'oauth-login') do %> - <%= back_url_hidden_field_tag %> - <%= button_tag(name: 'login-oauth', tabindex: 6, id: 'login-oauth-submit', title: l(:oauth_login_with), - style: "background: #{Setting.plugin_redmine_oauth[:button_color]}") do %> - - <%= l(:oauth_login_via, oauth: Setting.plugin_redmine_oauth[:custom_name].blank? ? Setting.plugin_redmine_oauth[:oauth_name] : Setting.plugin_redmine_oauth[:custom_name]).html_safe %> +
+ <%= form_tag(oauth_path(back_url: back_url), method: :get, id: 'oauth-login') do %> + <%= back_url_hidden_field_tag %> + <%= button_tag(name: 'login-oauth', tabindex: 7, id: 'login-oauth-submit', title: l(:oauth_login_with), + style: "background: #{Setting.plugin_redmine_oauth[:button_color]}") do %> + + <%= l(:oauth_login_via, + oauth: Setting.plugin_redmine_oauth[:custom_name].blank? ? Setting.plugin_redmine_oauth[:oauth_name] : Setting.plugin_redmine_oauth[:custom_name]).html_safe %> + <% end %> + <% if Setting.plugin_redmine_oauth[:oauth_login] %> +
+ + <% end %> <% end %> - <% end %> +
<% end %> <%= javascript_tag do %> diff --git a/app/views/settings/_oauth_settings.html.erb b/app/views/settings/_oauth_settings.html.erb index 67ad920..d7c7e6c 100644 --- a/app/views/settings/_oauth_settings.html.erb +++ b/app/views/settings/_oauth_settings.html.erb @@ -146,6 +146,15 @@ <%= l(:label_default) %>: <%= l(:general_text_No) %>

+

+ + <%= check_box_tag 'settings[oauth_login]', true, @settings[:oauth_login] %> + + <%= l(:oauth_login_info) %> +
+ <%= l(:label_default) %>: <%= l(:general_text_No) %> +
+

<% style = %w(Custom).exclude?(@settings[:oauth_name]) ? 'display: none' : 'display: block' %>

diff --git a/assets/stylesheets/redmine_oauth.css b/assets/stylesheets/redmine_oauth.css index e64df50..52f5af9 100644 --- a/assets/stylesheets/redmine_oauth.css +++ b/assets/stylesheets/redmine_oauth.css @@ -25,6 +25,17 @@ button#login-oauth-submit { display:block; } +label[for="oauth_autologin"] { + margin:auto; + display:block; + width: fit-content; +} + +div#oauth-form { + margin: auto; + width: 340px; +} + input#button_color { padding: 0; } diff --git a/config/locales/cs.yml b/config/locales/cs.yml index 97bb5bd..f9b1d90 100644 --- a/config/locales/cs.yml +++ b/config/locales/cs.yml @@ -25,41 +25,44 @@ cs: oauth_invalid_provider: Neplatný poskytovatel OAuth oauth_none: Žádný oauth_provider: Poskytovatel - oauth_provider_info: Poskytovatel OAuth + oauth_provider_info: Poskytovatel OAuth. oauth_site: Stránka - oauth_site_info: Ověřovací URL poskytovatele OAuth + oauth_site_info: Ověřovací URL poskytovatele OAuth. oauth_client_id: ID klienta - oauth_client_id_info: ID aplikace (klienta) + oauth_client_id_info: ID aplikace (klienta). oauth_client_secret: Tajný kód - oauth_client_secret_info: Heslo aplikace + oauth_client_secret_info: Heslo aplikace. oauth_tenant_id: ID tenanta - oauth_tenant_id_info: ID adresáře (tenanta) - oauth_button_info: Barva a ikonka (třída Awesome fontu) přihlašovacího tlačítka OAuth (prázdné pro žádné tlačítko) + oauth_tenant_id_info: ID adresáře (tenanta). + oauth_button_info: Barva a ikonka (třída Awesome fontu) přihlašovacího tlačítka OAuth (prázdné pro žádné tlačítko). oauth_login_button: Přihlašovací tlačítko oauth_custom_name: Název poskytovatele - oauth_custom_name_info: Titulek zobrazený na přihlašovacím tlačítku OAuth + oauth_custom_name_info: Titulek zobrazený na přihlašovacím tlačítku OAuth. oauth_custom_auth_endpoint: Autorizační endpoint - oauth_custom_auth_endpoint_info: Autorizační endpoint aplikace + oauth_custom_auth_endpoint_info: Autorizační endpoint aplikace. oauth_custom_token_endpoint: Endpoint tokenu - oauth_custom_token_endpoint_info: Endpoint tokenu aplikace + oauth_custom_token_endpoint_info: Endpoint tokenu aplikace. oauth_custom_profile_endpoint: Endpoint profilu - oauth_custom_profile_endpoint_info: Endpoint profilu aplikace + oauth_custom_profile_endpoint_info: Endpoint profilu aplikace. oauth_custom_scope: OAuth scope - oauth_custom_scope_info: "OAuth scope (výchozí: 'openid profile email')" + oauth_custom_scope_info: "OAuth scope (výchozí: 'openid profile email')." oauth_custom_uid_field: UID - oauth_custom_uid_field_info: "Pole UID (výchozí: preferred_username)" + oauth_custom_uid_field_info: "Pole UID (výchozí: preferred_username)." oauth_custom_email_field: E-mail - oauth_custom_email_field_info: "Pole e-mail (výchozí: email)" + oauth_custom_email_field_info: "Pole e-mail (výchozí: email)." oauth_hide_login_form: Skrýt přihlašovací formulář oauth_custom_firstname_field: Pole jména u poskytovatele oauth_custom_lastname_field: Pole příjmení u poskytovatele - oauth_update_login: Aktualizovat login - oauth_update_login_info: Aktualizovat login uživatele po úspěšném přihlášení. - oauth_logout: OAuth odhlášení - oauth_logout_info: Odhlásit se také od poskytovatele OAuth po dohlášení z Redminu + oauth_update_login: Aktualizovat přihlašovací jméno + oauth_update_login_info: Aktualizovat přihlašovací jméno uživatele po úspěšném přihlášení. + oauth_login: Přihlášení OAuth + oauth_login_info: Umožní uživatelům přihlásit se automaticky pomocí poskytovatele OAuth bez přihlašovacího formuláře. + oauth_logout: Odhlášení OAuth + oauth_logout_info: Odhlásit se také od poskytovatele OAuth po dohlášení z Redminu. oauth_custom_logout_endpoint: Endpoint odhlášení - oauth_custom_logout_endpoint_info: Endpoint pro odhlášení od poskytovatele + oauth_custom_logout_endpoint_info: Endpoint pro odhlášení od poskytovatele. oauth_validate_user_roles_info: "Klíč, kde jsou definované role v tokenu. Pokud je nastaveno, tak 'user' role povolí přihlášení, 'admin' přidělí práva administrátora. Jestliže role 'user' není uvedena, přístup je odmítnut. Jestliže chybí role 'admin', práva administrátora jsou odebrána. př.: Zadejte 'resource_access.redmine.roles', když máte v tokenu nakonfigurováno toto: 'resource_access': { 'redmine': { 'roles': ['user', 'admin'] } }." + oauth_autologin: "Automatické přihlašování s %{oauth}" diff --git a/config/locales/de.yml b/config/locales/de.yml index 0b64d76..d2499be 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -21,45 +21,50 @@ de: oauth_login_via: "Weiter mit %{oauth}" oauth_login_with: Anmelden mit with OAuth - oauth_no_verified_email: Keine verifizierte E-Mail zur Verfügung gestellt. Überprüfen Sie Ihre OAuth-Anbieter-Einstellung. + oauth_no_verified_email: "Keine verifizierte E-Mail zur Verfügung gestellt. Überprüfen Sie Ihre + OAuth-Anbieter-Einstellung." oauth_invalid_provider: Ungültiger OAuth-Anbieter oauth_none: Keiner oauth_provider: Anbieter - oauth_provider_info: OAuth-Anbieter + oauth_provider_info: OAuth-Anbieter. oauth_site: Seite - oauth_site_info: URI des OAuth-Anbieter + oauth_site_info: URI des OAuth-Anbieter. oauth_client_id: Client-ID - oauth_client_id_info: Anwendungs-ID (Client) + oauth_client_id_info: Anwendungs-ID (Client). oauth_client_secret: Geheime Zeichenfolge - oauth_client_secret_info: Anwendungskennwort + oauth_client_secret_info: Anwendungskennwort. oauth_tenant_id: Mandant-ID - oauth_tenant_id_info: Verzeichnis-ID (Mandant) - oauth_button_info: Farbe und Symbol (Awesome-Schriftklasse) des OAuth-Anmeldebuttons (Leer für keinen Button) + oauth_tenant_id_info: Verzeichnis-ID (Mandant). + oauth_button_info: Farbe und Symbol (Awesome-Schriftklasse) des OAuth-Anmeldebuttons (Leer für keinen Button). oauth_login_button: Anmeldebutton oauth_custom_name: Name des Anbieters - oauth_custom_name_info: Titel auf dem OAuth-Login-Button + oauth_custom_name_info: Titel auf dem OAuth-Login-Button. oauth_custom_auth_endpoint: Auth endpoint - oauth_custom_auth_endpoint_info: Application Auth endpoint + oauth_custom_auth_endpoint_info: Application Auth endpoint. oauth_custom_token_endpoint: Token endpoint - oauth_custom_token_endpoint_info: Application Token endpoint + oauth_custom_token_endpoint_info: Application Token endpoint. oauth_custom_profile_endpoint: Profile endpoint - oauth_custom_profile_endpoint_info: Application Profile endpoint + oauth_custom_profile_endpoint_info: Application Profile endpoint. oauth_custom_scope: OAuth scope - oauth_custom_scope_info: "OAuth scope (Standard: 'openid profile email')" + oauth_custom_scope_info: "OAuth scope (Standard: 'openid profile email')." oauth_custom_uid_field: UID-Feld - oauth_custom_uid_field_info: "UID-Feld (Stadard: preferred_username)" + oauth_custom_uid_field_info: "UID-Feld (Stadard: preferred_username)." oauth_custom_email_field: E-Mail-Feld - oauth_custom_email_field_info: "E-Mail-Feld (Standard: email)" + oauth_custom_email_field_info: "E-Mail-Feld (Standard: email)." oauth_hide_login_form: Login-Formular ausblenden oauth_custom_firstname_field: Vorname-Feld des Anbieters oauth_custom_lastname_field: Nachname-Feld des Anbieters oauth_update_login: Login aktualizieren oauth_update_login_info: Aktualizieren Login des Benutzers nach einer erfolgreichen Anmeldung. oauth_logout: OAuth-Abmeldung - oauth_logout_info: Abmelden auch vom OAuth-Anbieter nach der Redmine-Abmeldung + oauth_logout_info: Abmelden auch vom OAuth-Anbieter nach der Redmine-Abmeldung. + oauth_login: OAuth login + oauth_login_info: Ermöglicht der Benutzer automatisches Anmelden über den OAuth-Anbieter ohne des Anmeldeformulars. oauth_custom_logout_endpoint: Abmeldung-Endpoint - oauth_custom_logout_endpoint_info: Abmeldung-Endpoint für Abmeldung vom OAuth-Anbieter - oauth_validate_user_roles_info: "A key, where user roles are present in the token. If set, 'user' role grants access, - 'admin' grants admin rights. If 'user' role is not present, access is denied. If 'admin role is missing, admin - rights are revoked. E.g.: Enter 'resource_access.redmine.roles', if you have your token set as follows: - 'resource_access': { 'redmine': { 'roles': ['user', 'admin'] } }" + oauth_custom_logout_endpoint_info: Abmeldung-Endpoint für Abmeldung vom OAuth-Anbieter. + oauth_validate_user_roles_info: "Ein Schlüssel, bei dem Benutzerrollen im Token vorhanden sind. Wenn gesetzt, + die Rolle 'user' gewährt Zugriff, 'admin' gewährt Admin-Rechte. Wenn die Rolle 'user' nicht vorhanden ist, + wird der Zugriff verweigert. Wenn die Rolle 'admin' fehlt, werden die Admin-Rechte widerrufen. z.B.: Geben Sie + 'resource_access.redmine.roles' ein, wenn Sie den Token als + 'resource_access': { 'redmine': { 'roles': ['user', 'admin'] } } eingestellt." + oauth_autologin: "Automatische Anmeldung mit %{oauth}" diff --git a/config/locales/en.yml b/config/locales/en.yml index f788ac5..5ab9603 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -25,42 +25,45 @@ en: oauth_invalid_provider: Invalid OAuth provider oauth_none: None oauth_provider: Provider - oauth_provider_info: OAuth provider + oauth_provider_info: OAuth provider. oauth_site: Site - oauth_site_info: OAuth provider's authentication URL + oauth_site_info: OAuth provider's authentication URL. oauth_client_id: Client ID - oauth_client_id_info: Application (client) ID + oauth_client_id_info: Application (client) ID. oauth_client_secret: Client secret - oauth_client_secret_info: Application password + oauth_client_secret_info: Application password. oauth_tenant_id: Tenant ID / Realm - oauth_tenant_id_info: Directory (tenant) ID - oauth_button_info: Colour and icon (Awesome font class) of the OAuth login button (Empty for no button) + oauth_tenant_id_info: Directory (tenant) ID. + oauth_button_info: Colour and icon (Awesome font class) of the OAuth login button (Empty for no button). oauth_login_button: Login button oauth_custom_name: Provider name - oauth_custom_name_info: Title to be shown on the OAuth login button + oauth_custom_name_info: Title to be shown on the OAuth login button. oauth_custom_auth_endpoint: Auth endpoint - oauth_custom_auth_endpoint_info: Application Auth endpoint + oauth_custom_auth_endpoint_info: Application Auth endpoint. oauth_custom_token_endpoint: Token endpoint - oauth_custom_token_endpoint_info: Application Token endpoint + oauth_custom_token_endpoint_info: Application Token endpoint. oauth_custom_profile_endpoint: Profile endpoint - oauth_custom_profile_endpoint_info: Application Profile endpoint + oauth_custom_profile_endpoint_info: Application Profile endpoint. oauth_custom_scope: OAuth scope - oauth_custom_scope_info: "OAuth scope (default: 'openid profile email')" + oauth_custom_scope_info: "OAuth scope (default: 'openid profile email')." oauth_custom_uid_field: UID field - oauth_custom_uid_field_info: "UID field (default: preferred_username)" + oauth_custom_uid_field_info: "UID field (default: preferred_username)." oauth_custom_email_field: Email field - oauth_custom_email_field_info: "Email field (default: email)" + oauth_custom_email_field_info: "Email field (default: email)." oauth_hide_login_form: Hide login form oauth_custom_firstname_field: Provider's firstname field oauth_custom_lastname_field: Provider's lastname field oauth_update_login: Update login - oauth_update_login_info: Update the user's login after a successful login + oauth_update_login_info: Update the user's login after a successful login. oauth_logout: OAuth logout - oauth_logout_info: Log out from the OAuth provider too after Redmine log out + oauth_logout_info: Log out from the OAuth provider too after Redmine logout. + oauth_login: OAuth login + oauth_login_info: Allow users to log in automatically via OAuth provider skipping the login form. oauth_custom_logout_endpoint: Logout endpoint - oauth_custom_logout_endpoint_info: Application Logout endpoint + oauth_custom_logout_endpoint_info: Application Logout endpoint. oauth_validate_user_roles: Validate user roles oauth_validate_user_roles_info: "A key, where user roles are present in the token. If set, 'user' role grants access, 'admin' grants admin rights. If 'user' role is not present, access is denied. If 'admin role is missing, admin rights are revoked. E.g.: Enter 'resource_access.redmine.roles', if you have your token set as follows: 'resource_access': { 'redmine': { 'roles': ['user', 'admin'] } }" + oauth_autologin: "Autologin with %{oauth}" diff --git a/config/locales/fr.yml b/config/locales/fr.yml index bf8d245..b7057b2 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -21,45 +21,50 @@ fr: oauth_login_via: "Continuer avec %{oauth}" oauth_login_with: Se connecter avec OAuth - oauth_no_verified_email: Aucun email vérifié n'a été fourni, vérifiez vos réglages sur le site de votre fournisseur Oauth. + oauth_no_verified_email: "Aucun email vérifié n'a été fourni, vérifiez vos réglages sur le site de votre fournisseur + Oauth." oauth_invalid_provider: Fournisseur OAuth invalide oauth_none: Aucun oauth_provider: Fournisseur - oauth_provider_info: Fournisseur OAuth + oauth_provider_info: Fournisseur OAuth. oauth_site: Site - oauth_site_info: URL d'authentification du fournisseur OAuth + oauth_site_info: URL d'authentification du fournisseur OAuth. oauth_client_id: ID client - oauth_client_id_info: ID d'application + oauth_client_id_info: ID d'application. oauth_client_secret: Secret client - oauth_client_secret_info: Mot de passe d'application + oauth_client_secret_info: Mot de passe d'application. oauth_tenant_id: ID de Tenant / Realm - oauth_tenant_id_info: ID de répertoire (tenant) - oauth_button_info: Couleur et icône (classe Font Awesome) du bouton de connexion via OAuth (Laissez vite pour masquer le bouton) + oauth_tenant_id_info: ID de répertoire (tenant). + oauth_button_info: "Couleur et icône (classe Font Awesome) du bouton de connexion via OAuth (Laissez vite pour masquer + le bouton)." oauth_login_button: Bouton de connexion oauth_custom_name: Provider name - oauth_custom_name_info: Title to be shown on the OAuth login button + oauth_custom_name_info: Title to be shown on the OAuth login button. oauth_custom_auth_endpoint: Auth endpoint - oauth_custom_auth_endpoint_info: Application Auth endpoint + oauth_custom_auth_endpoint_info: Application Auth endpoint. oauth_custom_token_endpoint: Token endpoint - oauth_custom_token_endpoint_info: Application Token endpoint + oauth_custom_token_endpoint_info: Application Token endpoint. oauth_custom_profile_endpoint: Profile endpoint - oauth_custom_profile_endpoint_info: Application Profile endpoint + oauth_custom_profile_endpoint_info: Application Profile endpoint. oauth_custom_scope: OAuth scope - oauth_custom_scope_info: OAuth scope (default - 'openid profile email') + oauth_custom_scope_info: OAuth scope (default - 'openid profile email'). oauth_custom_uid_field: UID field - oauth_custom_uid_field_info: UID field (default - preferred_username) + oauth_custom_uid_field_info: UID field (default - preferred_username). oauth_custom_email_field: Email field - oauth_custom_email_field_info: Email field (default - email) + oauth_custom_email_field_info: Email field (default - email). oauth_hide_login_form: Hide login form oauth_custom_firstname_field: Provider's firstname field oauth_custom_lastname_field: Provider's lastname field oauth_update_login: Update login oauth_update_login_info: Update the user's login after a successful login. oauth_logout: OAuth logout - oauth_logout_info: Log out from the OAuth provider too after Redmine log out + oauth_logout_info: Log out from the OAuth provider too after Redmine logout. + oauth_login: OAuth login + oauth_login_info: Allow users to log in automatically via OAuth provider skipping the login form. oauth_custom_logout_endpoint: Logout endpoint - oauth_custom_logout_endpoint_info: Application Logout endpoint + oauth_custom_logout_endpoint_info: Application Logout endpoint. oauth_validate_user_roles_info: "A key, where user roles are present in the token. If set, 'user' role grants access, - 'admin' grants admin rights. If 'user' role is not present, access is denied. If 'admin role is missing, admin - rights are revoked. E.g.: Enter 'resource_access.redmine.roles', if you have your token set as follows: - 'resource_access': { 'redmine': { 'roles': ['user', 'admin'] } }" + 'admin' grants admin rights. If 'user' role is not present, access is denied. If 'admin role is missing, admin + rights are revoked. E.g.: Enter 'resource_access.redmine.roles', if you have your token set as follows: + 'resource_access': { 'redmine': { 'roles': ['user', 'admin'] } }" + oauth_autologin: "Autologin with %{oauth}" diff --git a/init.rb b/init.rb index e93f7d1..ee674e1 100644 --- a/init.rb +++ b/init.rb @@ -19,6 +19,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. require 'redmine' +require File.expand_path('lib/redmine_oauth/hooks/controllers/account_controller_hooks', __dir__) require File.expand_path('lib/redmine_oauth/hooks/views/base_view_hooks', __dir__) require File.expand_path('lib/redmine_oauth/hooks/views/login_view_hooks', __dir__) require File.expand_path('lib/redmine_oauth/patches/settings_controller_patch', __dir__) @@ -55,6 +56,7 @@ custom_lastname_field: 'family_name', update_login: nil, oauth_logout: nil, + oauth_login: nil, custom_logout_endpoint: '', validate_user_roles: '' }, partial: 'settings/oauth_settings' diff --git a/lib/redmine_oauth/hooks/controllers/account_controller_hooks.rb b/lib/redmine_oauth/hooks/controllers/account_controller_hooks.rb new file mode 100644 index 0000000..1aa993d --- /dev/null +++ b/lib/redmine_oauth/hooks/controllers/account_controller_hooks.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +# Redmine plugin OAuth +# +# Karel Pičman +# +# 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. + +module RedmineOauth + module Hooks + module Controllers + # AccountController hooks + class AccountControllerHooks < Redmine::Hook::Listener + def controller_account_success_authentication_after(context = {}) + return unless Setting.plugin_redmine_oauth[:oauth_login] && context[:controller].params[:oauth_autologin] + + context[:controller].set_oauth_autologin_cookie context[:user], context[:request] + end + end + end + end +end diff --git a/lib/redmine_oauth/hooks/views/login_view_hooks.rb b/lib/redmine_oauth/hooks/views/login_view_hooks.rb index d369369..c7cf39d 100644 --- a/lib/redmine_oauth/hooks/views/login_view_hooks.rb +++ b/lib/redmine_oauth/hooks/views/login_view_hooks.rb @@ -28,7 +28,7 @@ def view_account_login_bottom(context = {}) return unless oauth.present? && (oauth != 'none') context[:controller].send( - :render_to_string, { partial: 'hooks/view_account_login_bottom', locals: context } + :render_to_string, { partial: 'hooks/redmine_oauth/view_account_login_bottom', locals: context } ) end end diff --git a/lib/redmine_oauth/patches/account_controller_patch.rb b/lib/redmine_oauth/patches/account_controller_patch.rb index 9444e16..6597ba2 100644 --- a/lib/redmine_oauth/patches/account_controller_patch.rb +++ b/lib/redmine_oauth/patches/account_controller_patch.rb @@ -24,13 +24,20 @@ module Patches module AccountControllerPatch ################################################################################################################ # Overridden methods - # + + def login + return super if request.post? || oauth_autologin_cookie.blank? + + redirect_to oauth_path + end + def logout + delete_oauth_autologin_cookie return super if User.current.anonymous? || !request.post? || Setting.plugin_redmine_oauth[:oauth_logout].blank? - site = Setting.plugin_redmine_oauth[:client_id] + site = Setting.plugin_redmine_oauth[:site]&.chomp('/') id = Setting.plugin_redmine_oauth[:client_id] - url = signin_url + url = signout_url case Setting.plugin_redmine_oauth[:oauth_name] when 'Azure AD' logout_user @@ -55,6 +62,19 @@ def logout flash['error'] = e.message redirect_to signin_path end + + ################################################################################################################ + # New methods + + private + + def delete_oauth_autologin_cookie + cookies.delete :oauth_autologin + end + + def oauth_autologin_cookie + cookies[:oauth_autologin] + end end end end diff --git a/lib/redmine_oauth/patches/settings_controller_patch.rb b/lib/redmine_oauth/patches/settings_controller_patch.rb index 17f8300..1f9e0a9 100644 --- a/lib/redmine_oauth/patches/settings_controller_patch.rb +++ b/lib/redmine_oauth/patches/settings_controller_patch.rb @@ -28,6 +28,9 @@ def self.prepended(base) end end + ################################################################################################################ + # New methods + def cipher_settings return unless request.post? && (params[:id] == 'redmine_oauth') diff --git a/test/functional/account_controller_test.rb b/test/functional/account_controller_test.rb new file mode 100644 index 0000000..fc3a343 --- /dev/null +++ b/test/functional/account_controller_test.rb @@ -0,0 +1,56 @@ +# frozen_string_literal: true + +# Redmine plugin OAuth +# +# Karel Pičman +# +# 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. + +# Load the normal Rails helper +require File.expand_path('../../../../test/test_helper', __dir__) + +# Account controller patch +class AccountControllerTest < ActionDispatch::IntegrationTest + fixtures :users, :email_addresses + + def test_login_oauth + get '/login', headers: { 'HTTP_COOKIE' => 'oauth_autologin=1;' } + assert_redirected_to oauth_path + end + + def test_login + get '/login' + assert_response :success + end + + def test_logout_oauth + post '/login', params: { username: 'jsmith', password: 'jsmith' } + Setting.plugin_redmine_oauth[:oauth_logout] = '' + post '/logout' + assert_redirected_to home_path + end + + def test_logout + post '/login', params: { username: 'jsmith', password: 'jsmith' } + Setting.plugin_redmine_oauth[:oauth_logout] = '1' + site = 'https://login.microsoftonline.com' + Setting.plugin_redmine_oauth[:site] = site + client_id = 'acgd0c84-8784-4f1e-8052-31iabf4b7o00' + Setting.plugin_redmine_oauth[:client_id] = client_id + Setting.plugin_redmine_oauth[:oauth_name] = 'Azure AD' + post '/logout' + assert_redirected_to "#{site}/#{client_id}/oauth2/logout?post_logout_redirect_uri=#{signout_url}" + end +end diff --git a/test/functional/redmine_o_auth_controller_test.rb b/test/functional/redmine_oauth_controller_test.rb similarity index 77% rename from test/functional/redmine_o_auth_controller_test.rb rename to test/functional/redmine_oauth_controller_test.rb index 7739a67..212f4e7 100644 --- a/test/functional/redmine_o_auth_controller_test.rb +++ b/test/functional/redmine_oauth_controller_test.rb @@ -19,20 +19,19 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # Load the normal Rails helper -require Rails.root.join('test/test_helper') +require File.expand_path('../../../../test/test_helper', __dir__) # OAuth controller -class RedmineOAuthControllerTest < ActionDispatch::IntegrationTest +class RedmineOauthControllerTest < ActionDispatch::IntegrationTest include Redmine::I18n fixtures :users def test_oauth - with_settings plugin_redmine_oauth: { 'oauth_name' => '' } do - get '/oauth' - assert_redirected_to signin_path - assert_equal l(:oauth_invalid_provider), flash[:error] - end + Setting.plugin_redmine_oauth[:oauth_name] = '' + get '/oauth' + assert_redirected_to signin_path + assert_equal l(:oauth_invalid_provider), flash[:error] end def test_oauth_callback_csrf