-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Initial pass * Tests for MFA and locale cleanup * Brakeman * Update two-factor authentication status styling * Update app/models/user.rb Co-authored-by: Zach Gollwitzer <zach@maybe.co> Signed-off-by: Josh Pigford <josh@joshpigford.com> * Refactor MFA verification and session handling in tests --------- Signed-off-by: Josh Pigford <josh@joshpigford.com> Co-authored-by: Zach Gollwitzer <zach@maybe.co>
- Loading branch information
Showing
29 changed files
with
598 additions
and
33 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
class MfaController < ApplicationController | ||
layout :determine_layout | ||
skip_authentication only: [ :verify, :verify_code ] | ||
|
||
def new | ||
redirect_to root_path if Current.user.otp_required? | ||
Current.user.setup_mfa! unless Current.user.otp_secret.present? | ||
end | ||
|
||
def create | ||
if Current.user.verify_otp?(params[:code]) | ||
Current.user.enable_mfa! | ||
@backup_codes = Current.user.otp_backup_codes | ||
render :backup_codes | ||
else | ||
Current.user.disable_mfa! | ||
redirect_to new_mfa_path, alert: t(".invalid_code") | ||
end | ||
end | ||
|
||
def verify | ||
@user = User.find_by(id: session[:mfa_user_id]) | ||
redirect_to new_session_path unless @user | ||
end | ||
|
||
def verify_code | ||
@user = User.find_by(id: session[:mfa_user_id]) | ||
|
||
if @user&.verify_otp?(params[:code]) | ||
session.delete(:mfa_user_id) | ||
@session = create_session_for(@user) | ||
redirect_to root_path | ||
else | ||
flash.now[:alert] = t(".invalid_code") | ||
render :verify, status: :unprocessable_entity | ||
end | ||
end | ||
|
||
def disable | ||
Current.user.disable_mfa! | ||
redirect_to settings_security_path, notice: t(".success") | ||
end | ||
|
||
private | ||
|
||
def determine_layout | ||
if action_name.in?(%w[verify verify_code]) | ||
"auth" | ||
else | ||
"with_sidebar" | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
class Settings::SecuritiesController < SettingsController | ||
def show | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
module MfaHelper | ||
def generate_mfa_qr_code(provisioning_uri) | ||
qr_code = RQRCode::QRCode.new(provisioning_uri).as_svg( | ||
color: "141414", | ||
module_size: 4, | ||
standalone: true, | ||
use_path: true, | ||
svg_attributes: { | ||
width: "228", | ||
height: "228", | ||
viewBox: "0 0 57 57" | ||
} | ||
) | ||
|
||
# Whitelist specific SVG attributes and elements that we know are safe | ||
sanitize qr_code, | ||
tags: %w[svg g path rect], | ||
attributes: %w[viewBox height width fill stroke stroke-width d x y class] | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
<% | ||
header_title t(".title") | ||
header_description t(".description") | ||
%> | ||
|
||
<% content_for :sidebar do %> | ||
<%= render "settings/nav" %> | ||
<% end %> | ||
|
||
<div class="space-y-4"> | ||
<h1 class="text-gray-900 text-xl font-medium mb-4"><%= t(".page_title") %></h1> | ||
<%= settings_section title: t(".backup_codes_title"), subtitle: t(".backup_codes_description") do %> | ||
<div class="space-y-6"> | ||
<div class="grid grid-cols-2 gap-4"> | ||
<% @backup_codes.each do |code| %> | ||
<div class="p-3 bg-gray-100 rounded-lg font-mono text-lg"> | ||
<%= code %> | ||
</div> | ||
<% end %> | ||
</div> | ||
|
||
<div class="mt-6"> | ||
<%= link_to t(".continue"), settings_security_path, class: "w-full btn btn--primary" %> | ||
</div> | ||
</div> | ||
<% end %> | ||
|
||
<%= settings_nav_footer %> | ||
</div> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
<% | ||
header_title t(".title") | ||
header_description t(".description") | ||
%> | ||
|
||
<% content_for :sidebar do %> | ||
<%= render "settings/nav" %> | ||
<% end %> | ||
|
||
<div class="space-y-4"> | ||
<h1 class="text-gray-900 text-xl font-medium mb-4"><%= t(".page_title") %></h1> | ||
<%= settings_section title: t(".scan_title"), subtitle: t(".scan_description") do %> | ||
<div class="space-y-6"> | ||
<div> | ||
<%= generate_mfa_qr_code(Current.user.provisioning_uri) %> | ||
</div> | ||
|
||
<div> | ||
<h3 class="text-lg font-medium leading-6 text-gray-900"><%= t(".verify_title") %></h3> | ||
<div class="mt-2 text-sm text-gray-500"> | ||
<p><%= t(".verify_description") %></p> | ||
</div> | ||
</div> | ||
|
||
<%= styled_form_with url: mfa_path, method: :post, class: "mt-5", data: { turbo: false } do |f| %> | ||
<div> | ||
<%= f.text_field :code, | ||
required: true, | ||
autofocus: true, | ||
autocomplete: "one-time-code", | ||
inputmode: "numeric", | ||
pattern: "[0-9]*", | ||
label: t(".code_label"), | ||
placeholder: t(".code_placeholder") %> | ||
|
||
<div class="flex justify-end mt-4"> | ||
<%= f.submit t(".verify_button"), class: "bg-gray-900 hover:bg-gray-700 cursor-pointer text-white rounded-lg px-3 py-2" %> | ||
</div> | ||
</div> | ||
<% end %> | ||
</div> | ||
<% end %> | ||
|
||
<%= settings_nav_footer %> | ||
</div> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
<% | ||
header_title t(".title") | ||
header_description t(".description") | ||
%> | ||
|
||
<%= styled_form_with url: verify_mfa_path, method: :post, class: "space-y-4" do |form| %> | ||
<%= form.text_field :code, | ||
required: true, | ||
autofocus: true, | ||
autocomplete: "one-time-code", | ||
label: t(".page_title") %> | ||
|
||
<%= form.submit t(".verify_button") %> | ||
<% end %> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
<% content_for :sidebar do %> | ||
<%= render "settings/nav" %> | ||
<% end %> | ||
|
||
<div class="space-y-4"> | ||
<h1 class="text-gray-900 text-xl font-medium mb-4"><%= t(".page_title") %></h1> | ||
<%= settings_section title: t(".mfa_title"), subtitle: t(".mfa_description") do %> | ||
<div class="space-y-4"> | ||
<div class="p-3 shadow-xs bg-white border border-alpha-black-200 rounded-lg flex justify-between items-center"> | ||
<div class="flex items-center gap-3"> | ||
<div class="w-9 h-9 rounded-full bg-gray-25 flex justify-center items-center"> | ||
<%= lucide_icon "shield-check", class: "w-5 h-5 text-gray-500" %> | ||
</div> | ||
|
||
<div class="text-sm space-y-1"> | ||
<% if Current.user.otp_required? %> | ||
<p class="text-gray-900">Two-factor authentication is <span class="font-medium text-green-600">enabled</span></p> | ||
<p class="text-gray-500">Your account is protected with an additional layer of security.</p> | ||
<% else %> | ||
<p class="text-gray-900">Two-factor authentication is <span class="font-medium text-red-600">disabled</span></p> | ||
<p class="text-gray-500">Enable 2FA to add an extra layer of security to your account.</p> | ||
<% end %> | ||
</div> | ||
</div> | ||
|
||
<% if Current.user.otp_required? %> | ||
<%= button_to t(".disable_mfa"), disable_mfa_path, | ||
method: :delete, | ||
class: "btn btn--secondary flex items-center gap-1", | ||
data: { turbo_confirm: { | ||
title: t(".disable_mfa_confirm"), | ||
body: t(".disable_mfa_confirm"), | ||
accept: t(".disable_mfa"), | ||
acceptClass: "w-full bg-red-500 text-white rounded-xl text-center p-[10px] border mb-2" | ||
} } %> | ||
<% else %> | ||
<%= link_to t(".enable_mfa"), new_mfa_path, | ||
class: "btn btn--primary flex items-center gap-1" %> | ||
<% end %> | ||
</div> | ||
</div> | ||
<% end %> | ||
|
||
<%= settings_nav_footer %> | ||
</div> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.