Commit 5aa537e
Changed files (10)
app
javascript
controllers
styles
models
views
my
audits
config
locales
spec
requests
my
app/javascript/controllers/mfa/test_controller.js
@@ -1,10 +1,13 @@
import ApplicationController from '../application_controller';
export default class extends ApplicationController {
- static targets = ["output"];
+ static targets = ['output'];
onSuccess(event) {
- let [data, status, xhr] = event.detail;
- this.outputTarget.innerHTML = xhr.response;
+ const [data, status, xhr] = event.detail;
+ super.log(data);
+ if (status === 'OK') {
+ this.outputTarget.innerHTML = xhr.response;
+ }
}
}
app/javascript/controllers/clipboard_controller.js
@@ -2,8 +2,9 @@ import ApplicationController from './application_controller';
export default class extends ApplicationController {
static targets = ['source'];
+
connect() {
- if (document.queryCommandSupported("copy")) {
+ if (document.queryCommandSupported('copy')) {
this.element.classList.add('clipboard--supported');
}
}
@@ -11,6 +12,6 @@ export default class extends ApplicationController {
copy(event) {
event.preventDefault();
this.sourceTarget.select();
- document.execCommand("copy");
+ document.execCommand('copy');
}
}
app/javascript/styles/application.scss
@@ -9,6 +9,7 @@
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
+
header {
min-height: 52px;
}
app/models/mfa.rb
@@ -21,6 +21,7 @@ class Mfa
def disable!(entered_code)
return false unless authenticate(entered_code)
+
user.update!(mfa_secret: nil)
end
app/models/user_session.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
class UserSession < ApplicationRecord
- IDLE_TIMEOUT=30.minutes
+ IDLE_TIMEOUT = 30.minutes
audited associated_with: :user, except: [:key, :accessed_at]
has_secure_token :key
belongs_to :user
app/views/my/audits/_audit.html.erb
@@ -9,7 +9,7 @@
<div class="content">
<p>
<strong><%= audit.auditable_type.constantize.model_name.human %></strong> <small><%= t(audit.action) %></small> <small><%= local_time_ago(audit.created_at) %></small>
- by <%= audit.user&.email %> from <%= audit.remote_address %> (version <%= audit.version %>)
+ <%= t('.by') %> <%= audit.user&.email %> <%= t('.from') %> <%= audit.remote_address %> <%= t('.version', version: audit.version) %>
</p>
<table class="table is-bordered is-narrow is-hoverable is-striped is-fullwidth">
<thead>
app/views/my/mfas/edit.html.erb
@@ -4,7 +4,7 @@
<div class="columns is-centered">
<div class="column is-half">
<h1 class="title"><%= t('.title') %></h1>
- <p class="content">MFA gives you a second line of defense against unauthorized attempts to access your account.</p>
+ <p class="content"><%= t('.blurb') %></p>
<%= form_for current_user, url: my_mfa_path, method: :delete do |form| %>
<div class="field">
<div class="control">
app/views/my/mfas/new.html.erb
@@ -3,15 +3,15 @@
<div class="columns is-centered">
<div class="column is-half">
<h1 class="title"><%= t('.title') %></h1>
- <p class="content">MFA gives you a second line of defense against unauthorized attempts to access your account.</p>
+ <p class="content"><%= t('.blurb') %></p>
<div class="box content">
- <h2>Step 1: Download Authenticator</h2>
- <p>To enable MFA, you must have a device that can run Google Authenticator or another <a href="https://tools.ietf.org/html/rfc6238">RFC-6238</a> compatible app.</p>
+ <h2><%= t('.step_1') %></h2>
+ <p><%= t('.step_1_blurb_html') %></p>
<a href="https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2"><span class="icon is-large"><i class="fab fa-android fa-3x" aria-hidden="true"></i></span></a>
<a href="https://itunes.apple.com/us/app/google-authenticator/id388497605?mt=8"><span class="icon is-large"><i class="fab fa-apple fa-3x" aria-hidden="true"></i></span></a>
</div>
<div class="box content">
- <h2>Step 2: Scan QR Code</h2>
+ <h2><%= t('.step_2') %></h2>
<div data-controller="mfa--setup">
<canvas id="canvas" data-target="mfa--setup.canvas"></canvas>
<%= form_with model: current_user, url: '#' do |form| %>
@@ -37,11 +37,11 @@
</div>
</div>
<div class="box content" data-controller="mfa--test">
- <h2>Step 3: Test Verfication Code</h2>
+ <h2><%= t('.step_3') %></h2>
<span data-target="mfa--test.output"></span>
<ol type="1">
- <li>Open your Authenticator app</li>
- <li>Enter the verification code displayed</li>
+ <li><%= t('.step_3_1') %></li>
+ <li><%= t('.step_3_2') %></li>
</ol>
<%= form_with model: current_user, url: test_my_mfa_path, method: :post, data: { action: 'ajax:success->mfa--test#onSuccess' } do |form| %>
<div class="field has-addons">
config/locales/en.yml
@@ -46,8 +46,11 @@ en:
audits:
audit:
attribute: Attribute
+ by: By
+ from: From
new: New
old: Old
+ version: "(Version %{version})"
index:
title: Audit Log
clients:
@@ -73,6 +76,7 @@ en:
error: MFA code is incorrect
success: MFA has been disabled
edit:
+ blurb: MFA gives you a second line of defense against unauthorized attempts to access your account.
cancel: Cancel
disable: Disable
provisioning_uri: Provisioning uri
@@ -80,10 +84,17 @@ en:
secret: Secret
title: Multi-Factor Authentication (MFA)
new:
+ blurb: MFA gives you a second line of defense against unauthorized attempts to access your account.
cancel: Cancel
copy: Copy
enable: Enable
saving: Saving
+ step_1: 'Step 1: Download Authenticator'
+ step_1_blurb_html: To enable MFA, you must have a device that can run Google Authenticator or another <a href="https://tools.ietf.org/html/rfc6238">RFC-6238</a> compatible app.
+ step_2: Step 2: Scan QR Code
+ step_3: Step 3: Test Verfication Code
+ step_3_1: Open your Authenticator app
+ step_3_2: Enter the verification code displayed
test: Test
title: Multi Factor Authentication - Setup
test:
spec/requests/my/mfas_spec.rb
@@ -59,6 +59,7 @@ RSpec.describe '/my/mfa' do
let(:current_code) { ROTP::TOTP.new(mfa_secret).now }
before { post '/my/mfa/test', params: { user: { code: current_code, mfa_secret: mfa_secret } }, xhr: true }
+
specify { expect(response).to have_http_status(:ok) }
end
@@ -67,6 +68,7 @@ RSpec.describe '/my/mfa' do
let(:current_code) { "12345" }
before { post '/my/mfa/test', params: { user: { code: current_code, mfa_secret: mfa_secret } }, xhr: true }
+
specify { expect(response).to have_http_status(:ok) }
specify { expect(response.body).to include(I18n.t('my.mfas.test.invalid')) }
end