diff --git a/app/assets/stylesheets/modules/home.css b/app/assets/stylesheets/modules/home.css index c98b5f295b8..0201cd1b404 100644 --- a/app/assets/stylesheets/modules/home.css +++ b/app/assets/stylesheets/modules/home.css @@ -152,3 +152,24 @@ font-size: 16px; } .home__link:focus { outline: none; } + +/* Banner */ + +.banner { + background-color: #141c22; + text-align: center; + padding: 15px; + color: white; +} + +@media (max-width: 899px) { + .banner { + font-size: 12px; + } +} + +.banner a { + color: #e9573f; + text-decoration: underline; + font-weight: bold; +} diff --git a/app/assets/stylesheets/modules/org.css b/app/assets/stylesheets/modules/org.css index a65d3b1d2f0..17f9a0be4c8 100644 --- a/app/assets/stylesheets/modules/org.css +++ b/app/assets/stylesheets/modules/org.css @@ -1,18 +1,19 @@ /* Flash Intercoms */ .flash { - border-bottom: 1px solid #e9573f; - background-color: white; } + border-bottom: 1px solid #dcd3b1; + background-color: white; +} .flash-wrap { - padding-top: 5px; - padding-bottom: 5px; - background-image: linear-gradient(to right, rgba(233, 87, 63, 0.3), #e9573f); } + padding: 10px 0; + background-color: #fff6d2; +} .flash-wrap span { font-size: 13px; - font-style: italic; - color: #141c22; } + color: #141c22; +} .flash a { color: #141c22; @@ -265,3 +266,20 @@ float: right; } .about__assets__download:focus:before, .about__assets__download:hover:before { animation: arrow .75s infinite; } + +/* Banner */ + +#banner { + background-color: #141c22; + text-align: center; + padding: 12px 0; + font-weight: 500; + font-size: 16px; + color: white; +} + +#banner a { + color: #e9573f; + text-decoration: underline; + font-weight: bold; +} diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index d9a128e0e4b..975c58e83b2 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -80,6 +80,7 @@ def do_login sign_in(@user) do |status| if status.success? StatsD.increment "login.success" + set_login_flash redirect_back_or(url_after_create) else login_failure(status.failure_message) @@ -107,12 +108,20 @@ def who session_params[:who].is_a?(String) && session_params.fetch(:who) end - def url_after_create + def set_login_flash if current_user.mfa_recommended_not_yet_enabled? flash[:notice] = t("multifactor_auths.setup_recommended") - new_multifactor_auth_path elsif current_user.mfa_recommended_weak_level_enabled? flash[:notice] = t("multifactor_auths.strong_mfa_level_recommended") + elsif !current_user.webauthn_enabled? + flash[:notice_html] = t("multifactor_auths.setup_webauthn_html") + end + end + + def url_after_create + if current_user.mfa_recommended_not_yet_enabled? + new_multifactor_auth_path + elsif current_user.mfa_recommended_weak_level_enabled? edit_settings_path else dashboard_path diff --git a/app/views/home/index.html.erb b/app/views/home/index.html.erb index 22134450d73..abf96f11cd2 100644 --- a/app/views/home/index.html.erb +++ b/app/views/home/index.html.erb @@ -1,3 +1,7 @@ +<% content_for :banner do %> + <%= t('multifactor_auths.setup_webauthn_html') %> +<% end %> +
diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 1712e904c64..8ed6db18212 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -23,6 +23,11 @@ + <% if content_for?(:banner) %> + + <% end %>
<%= link_to(root_path, title: 'RubyGems', class: 'header__logo-wrap') do %> @@ -90,7 +95,7 @@ <% flash.each do |name, msg| %>
-
<%= flash_message(name, msg) %>
+
<%= flash_message(name, msg) %>
<% end %> diff --git a/app/views/settings/edit.html.erb b/app/views/settings/edit.html.erb index fd19f8c0f69..99b99ef3fb8 100644 --- a/app/views/settings/edit.html.erb +++ b/app/views/settings/edit.html.erb @@ -15,6 +15,20 @@ <% end %> <% end %> +

<%= t(".webauthn_credentials") %>

+ +

<%= t(".webauthn_credential_note")%>

+ + <% if @user.webauthn_credentials.none? %> +

<%= t(".no_webauthn_credentials") %>

+ <% else %> +
+ <%= render @user.webauthn_credentials %> +
+ <% end %> + + <%= render "webauthn_credentials/form", webauthn_credential: @webauthn_credential %> + <% if @user.totp_enabled? %>

<%= t(".mfa.otp") %>

@@ -38,26 +52,6 @@ <%= button_to t(".mfa.go_settings"), new_multifactor_auth_path, method: "get", class: "form__submit" %>

<% end %> - -
-

<%= t(".webauthn_credentials") %>

- <% if @user.webauthn_enabled? %> - <%= t(".mfa.enabled") %> - <% else %> - <%= t(".mfa.disabled") %> - <% end %> -
-

<%= t(".webauthn_credential_note")%>

- - <% if @user.webauthn_credentials.none? %> -

<%= t(".no_webauthn_credentials") %>

- <% else %> -
- <%= render @user.webauthn_credentials %> -
- <% end %> - - <%= render "webauthn_credentials/form", webauthn_credential: @webauthn_credential %>
diff --git a/config/locales/de.yml b/config/locales/de.yml index 83788117a8c..d57f626133f 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -176,6 +176,7 @@ de: sign_in: Anmelden sign_out: Abmelden sign_up: Registrieren + mfa_banner_html: mailer: confirm_your_email: confirmation_subject: @@ -354,6 +355,7 @@ de: setup_recommended: strong_mfa_level_required_html: strong_mfa_level_recommended: + setup_webauthn_html: new: title: scan_prompt: diff --git a/config/locales/en.yml b/config/locales/en.yml index e0d3a28982b..b31368c4eca 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -183,6 +183,7 @@ en: sign_in: Sign in sign_out: Sign out sign_up: Sign up + mfa_banner_html: 🎉 We now support security devices! Improve your account security by setting up a new device. [Learn more](link to blog post)! mailer: confirm_your_email: Please confirm your email address with the link sent to your email. confirmation_subject: Please confirm your email address with RubyGems.org @@ -348,8 +349,9 @@ en: require_webauthn_enabled: You don't have any security devices enabled. You have to associate a device to your account first. setup_required_html: For protection of your account and your gems, you are required to set up multi-factor authentication. Please read our blog post for more details. setup_recommended: For protection of your account and your gems, we encourage you to set up multi-factor authentication. Your account will be required to have MFA enabled in the future. - strong_mfa_level_required_html: For protection of your account and your gems, you are required to change your MFA level to "UI and gem signin" or "UI and API". Please read our blog post for more details. + strong_mfa_level_required_html: For protection of your account and your gems, you are required to change your MFA level to "UI and gem signin" or "UI and API". Please read our blog post for more details. strong_mfa_level_recommended: For protection of your account and your gems, we encourage you to change your MFA level to "UI and gem signin" or "UI and API". Your account will be required to have MFA enabled on one of these levels in the future. + setup_webauthn_html: 🎉 We now support security devices! Improve your account security by setting up a new device. Learn more! new: title: Enabling multi-factor auth scan_prompt: Please scan the qr-code with your authenticator app. If you cannot scan the code, add information below into your app manually. diff --git a/config/locales/es.yml b/config/locales/es.yml index da5a87f6dcc..13434776486 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -189,6 +189,7 @@ es: sign_in: Ingresar sign_out: Salir sign_up: Registrarte + mfa_banner_html: mailer: confirm_your_email: Por favor confirma tu dirección de correo con el enlace enviado. confirmation_subject: Por favor confirma tu dirección de correo con RubyGems.org @@ -371,6 +372,7 @@ es: setup_recommended: strong_mfa_level_required_html: strong_mfa_level_recommended: + setup_webauthn_html: new: title: Activando autenticación de múltiples factores scan_prompt: Por favor escanea el código QR con tu aplicación de Autenticación. diff --git a/config/locales/fr.yml b/config/locales/fr.yml index fb2d03808d7..fda87d3c582 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -189,6 +189,7 @@ fr: sign_in: Connexion sign_out: Déconnexion sign_up: Inscription + mfa_banner_html: mailer: confirm_your_email: Veuillez confirmer votre adresse email avec le lien qui vous a été envoyé par email. @@ -373,6 +374,7 @@ fr: setup_recommended: strong_mfa_level_required_html: strong_mfa_level_recommended: + setup_webauthn_html: new: title: Activer l'authentification multifacteur scan_prompt: Veuillez scanner le QR code avec votre app d'authentification. diff --git a/config/locales/ja.yml b/config/locales/ja.yml index ffa3db44af8..b36cf8112d6 100644 --- a/config/locales/ja.yml +++ b/config/locales/ja.yml @@ -178,6 +178,7 @@ ja: sign_in: ログイン sign_out: ログアウト sign_up: 新規登録 + mfa_banner_html: mailer: confirm_your_email: あなたのメールアドレスに送信されたURLを確認してください。 confirmation_subject: RubyGems.orgに登録されたあなたのメールアドレスを確認してください @@ -341,6 +342,7 @@ ja: setup_recommended: strong_mfa_level_required_html: strong_mfa_level_recommended: + setup_webauthn_html: new: title: scan_prompt: diff --git a/config/locales/nl.yml b/config/locales/nl.yml index 1fd733d33e4..c1086dc9694 100644 --- a/config/locales/nl.yml +++ b/config/locales/nl.yml @@ -181,6 +181,7 @@ nl: sign_in: Inloggen sign_out: Uitloggen sign_up: Registreren + mfa_banner_html: mailer: confirm_your_email: confirmation_subject: @@ -358,6 +359,7 @@ nl: setup_recommended: strong_mfa_level_required_html: strong_mfa_level_recommended: + setup_webauthn_html: new: title: scan_prompt: diff --git a/config/locales/pt-BR.yml b/config/locales/pt-BR.yml index e2104af2697..0d966f84193 100644 --- a/config/locales/pt-BR.yml +++ b/config/locales/pt-BR.yml @@ -186,6 +186,7 @@ pt-BR: sign_in: Fazer Login sign_out: Sair sign_up: Cadastrar + mfa_banner_html: mailer: confirm_your_email: Por favor, confirme seu endereço de email através do link que enviamos para o seu endereço de email. @@ -369,6 +370,7 @@ pt-BR: setup_recommended: strong_mfa_level_required_html: strong_mfa_level_recommended: + setup_webauthn_html: new: title: scan_prompt: diff --git a/config/locales/zh-CN.yml b/config/locales/zh-CN.yml index 5aaf2139610..0bea775f114 100644 --- a/config/locales/zh-CN.yml +++ b/config/locales/zh-CN.yml @@ -184,6 +184,7 @@ zh-CN: sign_in: 登录 sign_out: 退出 sign_up: 注册 + mfa_banner_html: mailer: confirm_your_email: 请在发送到您的邮件中点击链接,确认您的邮箱地址。 confirmation_subject: 请确认您的邮箱地址 @@ -351,6 +352,7 @@ zh-CN: setup_recommended: 为了保护您的帐户和您的 Gem,我们鼓励您设置多因素验证。在将来,您的帐户将被要求启用多因素验证。 strong_mfa_level_required_html: 为了保护您的帐户和您的 Gem,您需要将您的多因素验证级别更改为 "UI and gem signin" 或 "UI and API"。请阅读我们的博客文章了解详情。 strong_mfa_level_recommended: 为了保护您的帐户和您的 Gem,我们建议您将您的多因素验证级别更改为 "UI and gem signin" 或 "UI and API"。在将来,您的帐户将被要求在其中某一个级别上启用多因素验证。 + setup_webauthn_html: new: title: 启用多因素验证 scan_prompt: 请用您的身份验证程序扫描二维码。如果您没办法扫描,请在您的程序中手动输入下面的内容。 diff --git a/config/locales/zh-TW.yml b/config/locales/zh-TW.yml index b310a198cae..f8bfcd8cff6 100644 --- a/config/locales/zh-TW.yml +++ b/config/locales/zh-TW.yml @@ -175,6 +175,7 @@ zh-TW: sign_in: 登入 sign_out: 登出 sign_up: 註冊 + mfa_banner_html: mailer: confirm_your_email: confirmation_subject: @@ -342,6 +343,7 @@ zh-TW: setup_recommended: strong_mfa_level_required_html: strong_mfa_level_recommended: + setup_webauthn_html: new: title: 啟用多重要素驗證 scan_prompt: 請用你的驗證裝置掃描 QR-code。如果你沒辦法掃描,手動輸入下面的資料。 diff --git a/test/functional/home_controller_test.rb b/test/functional/home_controller_test.rb index 7f6d675b59b..27bd6c6a8bf 100644 --- a/test/functional/home_controller_test.rb +++ b/test/functional/home_controller_test.rb @@ -12,6 +12,10 @@ class HomeControllerTest < ActionController::TestCase should "display counts" do assert page.has_content?("11,000,000") end + + should "display mfa banner" do + assert page.has_content? "We now support security devices!" + end end should "on GET to index with non html accept header" do diff --git a/test/functional/sessions_controller_test.rb b/test/functional/sessions_controller_test.rb index b9c48cb5bb7..4089b0163f7 100644 --- a/test/functional/sessions_controller_test.rb +++ b/test/functional/sessions_controller_test.rb @@ -181,22 +181,35 @@ class SessionsControllerTest < ActionController::TestCase context "on POST to create" do context "when login and password are correct" do setup do - user = User.new(email_confirmed: true) - User.expects(:authenticate).with("login", "pass").returns user - post :create, params: { session: { who: "login", password: "pass" } } + @user = User.new(email_confirmed: true) @controller.session[:mfa_expires_at] = 15.minutes.from_now.to_s end - should respond_with :redirect - should redirect_to("the dashboard") { dashboard_path } + context "when mfa is not recommended" do + setup do + User.expects(:authenticate).with("login", "pass").returns @user + post :create, params: { session: { who: "login", password: "pass" } } + end - should "sign in the user" do - assert_predicate @controller.request.env[:clearance], :signed_in? + should respond_with :redirect + should redirect_to("the dashboard") { dashboard_path } + + should "sign in the user" do + assert_predicate @controller.request.env[:clearance], :signed_in? + end + + should "set security device notice" do + expected_notice = "🎉 We now support security devices! Improve your account security by " \ + "setting up a new device. " \ + "Learn more!" + + assert_equal expected_notice, flash[:notice_html] + assert_nil flash[:notice] + end end context "when mfa is recommended" do setup do - @user = User.new(email_confirmed: true, handle: "test") @user.stubs(:mfa_recommended?).returns true end @@ -214,6 +227,7 @@ class SessionsControllerTest < ActionController::TestCase "Your account will be required to have MFA enabled in the future." assert_equal expected_notice, flash[:notice] + assert_nil flash[:notice_html] end end @@ -239,6 +253,7 @@ class SessionsControllerTest < ActionController::TestCase "on one of these levels in the future." assert_equal expected_notice, flash[:notice] + assert_nil flash[:notice_html] end end @@ -361,6 +376,10 @@ class SessionsControllerTest < ActionController::TestCase assert_not page.has_field?("OTP code") assert page.has_button?("Authenticate with security device") end + + should "not set security device notice" do + assert_nil flash[:notice_html] + end end context "when user has webauthn credentials but no recovery code" do diff --git a/test/integration/sign_in_test.rb b/test/integration/sign_in_test.rb index 900462f91cc..0fc3e2e115f 100644 --- a/test/integration/sign_in_test.rb +++ b/test/integration/sign_in_test.rb @@ -15,6 +15,7 @@ class SignInTest < SystemTest click_button "Sign in" assert page.has_content? "Sign out" + assert page.has_content? "We now support security devices!" end test "signing in with uppercase email" do @@ -79,6 +80,7 @@ class SignInTest < SystemTest end assert page.has_content? "Sign out" + assert page.has_content? "We now support security devices!" end test "signing in with current valid otp when mfa enabled but 30 minutes has passed" do diff --git a/test/system/sign_in_webauthn_test.rb b/test/system/sign_in_webauthn_test.rb index 3bc14a76c63..3b6236a502e 100644 --- a/test/system/sign_in_webauthn_test.rb +++ b/test/system/sign_in_webauthn_test.rb @@ -30,6 +30,7 @@ class SignInWebauthnTest < ApplicationSystemTestCase click_on "Authenticate with security device" assert page.has_content? "Dashboard" + refute page.has_content? "We now support security devices!" end test "sign in with webauthn but it expired" do