From bddd9ba36d9f4e86e2a4bbea77f967c143afa2cc Mon Sep 17 00:00:00 2001 From: Claire Date: Sun, 23 Jan 2022 15:52:58 +0100 Subject: [PATCH] Add OMNIAUTH_ONLY environment variable to enforce externa log-in (#17288) * Remove support for OAUTH_REDIRECT_AT_SIGN_IN Fixes #15959 Introduced in #6540, OAUTH_REDIRECT_AT_SIGN_IN allowed skipping the log-in form to instead redirect to the external OmniAuth login provider. However, it did not prevent the log-in form on /about introduced by #10232 from appearing, and completely broke with the introduction of #15228. As I restoring that previous log-in flow without introducing a security vulnerability may require extensive care and knowledge of how OmniAuth works, this commit removes support for OAUTH_REDIRECT_AT_SIGN_IN instead for the time being. * Add OMNIAUTH_ONLY environment variable to enforce external log-in only * Disable user registration when OMNIAUTH_ONLY is set to true * Replace log-in links When OMNIAUTH_ONLY is set with exactly one OmniAuth provider --- app/controllers/api/v1/accounts_controller.rb | 6 +++- .../auth/registrations_controller.rb | 6 +++- app/helpers/application_helper.rb | 26 ++++++++++++++++- app/views/about/_login.html.haml | 29 ++++++++++++------- app/views/auth/sessions/new.html.haml | 25 ++++++++-------- app/views/auth/shared/_links.html.haml | 2 +- app/views/layouts/public.html.haml | 2 +- app/views/statuses/_status.html.haml | 2 +- config/locales/en.yml | 1 + 9 files changed, 71 insertions(+), 28 deletions(-) diff --git a/app/controllers/api/v1/accounts_controller.rb b/app/controllers/api/v1/accounts_controller.rb index cbccd64f3..5c47158e0 100644 --- a/app/controllers/api/v1/accounts_controller.rb +++ b/app/controllers/api/v1/accounts_controller.rb @@ -83,10 +83,14 @@ class Api::V1::AccountsController < Api::BaseController end def check_enabled_registrations - forbidden if single_user_mode? || !allowed_registrations? + forbidden if single_user_mode? || omniauth_only? || !allowed_registrations? end def allowed_registrations? Setting.registrations_mode != 'none' end + + def omniauth_only? + ENV['OMNIAUTH_ONLY'] == 'true' + end end diff --git a/app/controllers/auth/registrations_controller.rb b/app/controllers/auth/registrations_controller.rb index 3c1730f25..f37e906fd 100644 --- a/app/controllers/auth/registrations_controller.rb +++ b/app/controllers/auth/registrations_controller.rb @@ -81,13 +81,17 @@ class Auth::RegistrationsController < Devise::RegistrationsController end def check_enabled_registrations - redirect_to root_path if single_user_mode? || !allowed_registrations? + redirect_to root_path if single_user_mode? || omniauth_only? || !allowed_registrations? end def allowed_registrations? Setting.registrations_mode != 'none' || @invite&.valid_for_use? end + def omniauth_only? + ENV['OMNIAUTH_ONLY'] == 'true' + end + def invite_code if params[:user] params[:user][:invite_code] diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 34fc46615..9e16de5b5 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -50,13 +50,37 @@ module ApplicationHelper end def available_sign_up_path - if closed_registrations? + if closed_registrations? || omniauth_only? 'https://joinmastodon.org/#getting-started' else new_user_registration_path end end + def omniauth_only? + ENV['OMNIAUTH_ONLY'] == 'true' + end + + def link_to_login(name = nil, html_options = nil, &block) + target = new_user_session_path + + if omniauth_only? && Devise.mappings[:user].omniauthable? && User.omniauth_providers.size == 1 + target = omniauth_authorize_path(:user, User.omniauth_providers[0]) + html_options ||= {} + html_options[:method] = :post + end + + if block_given? + link_to(target, html_options, &block) + else + link_to(name, target, html_options) + end + end + + def provider_sign_in_link(provider) + link_to I18n.t("auth.providers.#{provider}", default: provider.to_s.chomp('_oauth2').capitalize), omniauth_authorize_path(:user, provider), class: "button button-#{provider}", method: :post + end + def open_deletion? Setting.open_deletion end diff --git a/app/views/about/_login.html.haml b/app/views/about/_login.html.haml index fa58f04d7..0f19e8164 100644 --- a/app/views/about/_login.html.haml +++ b/app/views/about/_login.html.haml @@ -1,13 +1,22 @@ -= simple_form_for(new_user, url: user_session_path, namespace: 'login') do |f| - .fields-group - - if use_seamless_external_login? - = f.input :email, placeholder: t('simple_form.labels.defaults.username_or_email'), input_html: { 'aria-label' => t('simple_form.labels.defaults.username_or_email') }, hint: false - - else - = f.input :email, placeholder: t('simple_form.labels.defaults.email'), input_html: { 'aria-label' => t('simple_form.labels.defaults.email') }, hint: false +- unless omniauth_only? + = simple_form_for(new_user, url: user_session_path, namespace: 'login') do |f| + .fields-group + - if use_seamless_external_login? + = f.input :email, placeholder: t('simple_form.labels.defaults.username_or_email'), input_html: { 'aria-label' => t('simple_form.labels.defaults.username_or_email') }, hint: false + - else + = f.input :email, placeholder: t('simple_form.labels.defaults.email'), input_html: { 'aria-label' => t('simple_form.labels.defaults.email') }, hint: false - = f.input :password, placeholder: t('simple_form.labels.defaults.password'), input_html: { 'aria-label' => t('simple_form.labels.defaults.password') }, hint: false + = f.input :password, placeholder: t('simple_form.labels.defaults.password'), input_html: { 'aria-label' => t('simple_form.labels.defaults.password') }, hint: false - .actions - = f.button :button, t('auth.login'), type: :submit, class: 'button button-primary' + .actions + = f.button :button, t('auth.login'), type: :submit, class: 'button button-primary' - %p.hint.subtle-hint= link_to t('auth.trouble_logging_in'), new_user_password_path + %p.hint.subtle-hint= link_to t('auth.trouble_logging_in'), new_user_password_path + +- if Devise.mappings[:user].omniauthable? and User.omniauth_providers.any? + .simple_form.alternative-login + %h4= omniauth_only? ? t('auth.log_in_with') : t('auth.or_log_in_with') + + .actions + - User.omniauth_providers.each do |provider| + = provider_sign_in_link(provider) diff --git a/app/views/auth/sessions/new.html.haml b/app/views/auth/sessions/new.html.haml index 9713bdaeb..a4323d1d9 100644 --- a/app/views/auth/sessions/new.html.haml +++ b/app/views/auth/sessions/new.html.haml @@ -4,24 +4,25 @@ - content_for :header_tags do = render partial: 'shared/og' -= simple_form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| - .fields-group - - if use_seamless_external_login? - = f.input :email, autofocus: true, wrapper: :with_label, label: t('simple_form.labels.defaults.username_or_email'), input_html: { 'aria-label' => t('simple_form.labels.defaults.username_or_email') }, hint: false - - else - = f.input :email, autofocus: true, wrapper: :with_label, label: t('simple_form.labels.defaults.email'), input_html: { 'aria-label' => t('simple_form.labels.defaults.email') }, hint: false - .fields-group - = f.input :password, wrapper: :with_label, label: t('simple_form.labels.defaults.password'), input_html: { 'aria-label' => t('simple_form.labels.defaults.password'), :autocomplete => 'off' }, hint: false +- unless omniauth_only? + = simple_form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| + .fields-group + - if use_seamless_external_login? + = f.input :email, autofocus: true, wrapper: :with_label, label: t('simple_form.labels.defaults.username_or_email'), input_html: { 'aria-label' => t('simple_form.labels.defaults.username_or_email') }, hint: false + - else + = f.input :email, autofocus: true, wrapper: :with_label, label: t('simple_form.labels.defaults.email'), input_html: { 'aria-label' => t('simple_form.labels.defaults.email') }, hint: false + .fields-group + = f.input :password, wrapper: :with_label, label: t('simple_form.labels.defaults.password'), input_html: { 'aria-label' => t('simple_form.labels.defaults.password'), :autocomplete => 'off' }, hint: false - .actions - = f.button :button, t('auth.login'), type: :submit + .actions + = f.button :button, t('auth.login'), type: :submit - if devise_mapping.omniauthable? and resource_class.omniauth_providers.any? .simple_form.alternative-login - %h4= t('auth.or_log_in_with') + %h4= omniauth_only? ? t('auth.log_in_with') : t('auth.or_log_in_with') .actions - resource_class.omniauth_providers.each do |provider| - = link_to t("auth.providers.#{provider}", default: provider.to_s.chomp("_oauth2").capitalize), omniauth_authorize_path(resource_name, provider), class: "button button-#{provider}", method: :post + = provider_sign_in_link(provider) .form-footer= render 'auth/shared/links' diff --git a/app/views/auth/shared/_links.html.haml b/app/views/auth/shared/_links.html.haml index 66ed5b93f..f078e2f7e 100644 --- a/app/views/auth/shared/_links.html.haml +++ b/app/views/auth/shared/_links.html.haml @@ -3,7 +3,7 @@ %li= link_to t('settings.account_settings'), edit_user_registration_path - else - if controller_name != 'sessions' - %li= link_to t('auth.login'), new_user_session_path + %li= link_to_login t('auth.login') - if controller_name != 'registrations' %li= link_to t('auth.register'), available_sign_up_path diff --git a/app/views/layouts/public.html.haml b/app/views/layouts/public.html.haml index bdb8a3a8e..069931cfd 100644 --- a/app/views/layouts/public.html.haml +++ b/app/views/layouts/public.html.haml @@ -22,7 +22,7 @@ - if user_signed_in? = link_to t('settings.back'), root_url, class: 'nav-link nav-button webapp-btn' - else - = link_to t('auth.login'), new_user_session_path, class: 'webapp-btn nav-link nav-button' + = link_to_login t('auth.login'), class: 'webapp-btn nav-link nav-button' = link_to t('auth.register'), available_sign_up_path, class: 'webapp-btn nav-link nav-button' .container= yield diff --git a/app/views/statuses/_status.html.haml b/app/views/statuses/_status.html.haml index 9f3197d0d..3b7152753 100644 --- a/app/views/statuses/_status.html.haml +++ b/app/views/statuses/_status.html.haml @@ -56,6 +56,6 @@ - if include_threads && !embedded_view? && !user_signed_in? .entry{ class: entry_classes } - = link_to new_user_session_path, class: 'load-more load-gap' do + = link_to_login class: 'load-more load-gap' do = fa_icon 'comments' = t('statuses.sign_in_to_participate') diff --git a/config/locales/en.yml b/config/locales/en.yml index 13a252a47..85aa87c7a 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -844,6 +844,7 @@ en: invalid_reset_password_token: Password reset token is invalid or expired. Please request a new one. link_to_otp: Enter a two-factor code from your phone or a recovery code link_to_webauth: Use your security key device + log_in_with: Log in with login: Log in logout: Logout migrate_account: Move to a different account