Commit ade5ba7
Changed files (10)
app
controllers
views
dashboards
my
config
spec
app/controllers/my/mfas_controller.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+module My
+ class MfasController < ApplicationController
+ def new
+ return redirect_to edit_my_mfa_path if current_user.tfa.setup?
+ current_user.tfa.build_secret
+ end
+
+ def create
+ current_user.update!(params.require(:user).permit(:tfa_secret))
+ redirect_to dashboard_path, notice: "successfully updated!"
+ end
+
+ def edit; end
+
+ def destroy
+ current_user.tfa.disable!
+ redirect_to dashboard_path
+ end
+ end
+end
app/controllers/tfas_controller.rb
@@ -1,20 +0,0 @@
-# frozen_string_literal: true
-
-class TfasController < ApplicationController
- def new
- return redirect_to edit_tfa_path if current_user.tfa.setup?
- current_user.tfa.build_secret
- end
-
- def create
- current_user.update!(params.require(:user).permit(:tfa_secret))
- redirect_to dashboard_path
- end
-
- def edit; end
-
- def destroy
- current_user.tfa.disable!
- redirect_to dashboard_path
- end
-end
app/views/dashboards/show.html.erb
@@ -3,9 +3,9 @@
<div class="col">
<h1>Dashboard</h1>
<% if current_user.tfa.setup? %>
- <%= link_to "TFA", edit_tfa_path %>
+ <%= link_to "TFA", edit_my_mfa_path %>
<% else %>
- <%= link_to "Setup TFA", new_tfa_path %>
+ <%= link_to "Setup TFA", new_my_mfa_path %>
<% end %>
<%= button_to "Logout", session_path, method: :delete %>
<table class="table">
app/views/tfas/edit.html.erb → app/views/my/mfas/edit.html.erb
File renamed without changes
app/views/tfas/new.html.erb → app/views/my/mfas/new.html.erb
@@ -1,13 +1,13 @@
<div class="container">
<div class="row">
<div class="col">
- <h1>Two Factor Authentication (TFA) - Setup</h1>
+ <h1>Multi Factor Authentication - Setup</h1>
<div data-controller="tfa--setup">
<canvas id="canvas" data-target="tfa--setup.canvas"></canvas>
<p>Secret: <%= current_user.tfa.secret %></p>
<p>Provisioning URI: <%= current_user.tfa.provisioning_uri %></p>
- <%= form_for current_user, url: tfa_path, method: :post do |form| %>
+ <%= form_for current_user, url: my_mfa_path, method: :post do |form| %>
<%= form.hidden_field :tfa_secret, data: { target: 'tfa--setup.secret' } %>
<%= form.submit "Save", class: 'btn btn-primary', data: { disable_with: 'Saving…' } %>
<%= link_to "Cancel", dashboard_path, class: 'btn' %>
config/routes.rb
@@ -6,8 +6,9 @@ Rails.application.routes.draw do
resource :metadata, only: [:show]
resource :dashboard, only: [:show]
resources :registrations, only: [:new, :create]
- resource :tfa, only: [:new, :edit, :create, :destroy]
-
+ namespace :my do
+ resource :mfa, only: [:new, :edit, :create, :destroy]
+ end
namespace :scim do
namespace :v2, defaults: { format: :scim } do
post ".search", to: "search#index"
spec/requests/my/mfas_spec.rb
@@ -0,0 +1,46 @@
+require 'rails_helper'
+
+RSpec.describe '/my/mfa' do
+ context "when logged in" do
+ let(:current_user) { create(:user) }
+ before { http_login(current_user) }
+
+ describe "GET /my/mfa" do
+ context "when mfa has not been set up yet" do
+ before { get '/my/mfa/new' }
+
+ specify { expect(response).to have_http_status(:ok) }
+ specify { expect(response.body).to include('Provisioning URI') }
+ end
+
+ context "when mfa has been set up" do
+ let(:current_user) { create(:user, :mfa_configured) }
+ before { get '/my/mfa/new' }
+
+ specify { expect(response).to redirect_to(edit_my_mfa_path) }
+ end
+ end
+
+ describe "POST /my/mfa" do
+ context "when the secret is valid" do
+ let(:secret) { SecureRandom.hex(20) }
+ before { post '/my/mfa', params: { user: { tfa_secret: secret } } }
+
+ specify { expect(current_user.reload.tfa_secret).to eql(secret) }
+ specify { expect(response).to redirect_to(dashboard_path) }
+ specify { expect(flash[:notice]).to include("successfully updated!") }
+ end
+ end
+
+ describe "DELETE /my/mfa" do
+ context "when mfa is enabled" do
+ let(:current_user) { create(:user, :mfa_configured) }
+
+ before { delete '/my/mfa' }
+
+ specify { expect(current_user.reload.tfa_secret).to be_nil }
+ specify { expect(response).to redirect_to(dashboard_path) }
+ end
+ end
+ end
+end
spec/support/request.rb
@@ -0,0 +1,7 @@
+RSpec.configure do |config|
+ config.include(Module.new do
+ def http_login(user)
+ post '/session', params: { user: { email: user.email, password: user.password } }
+ end
+ end)
+end
spec/support/system.rb
@@ -1,3 +1,5 @@
+require 'capybara-screenshot/rspec'
+
RSpec.configure do |config|
config.before(:each, type: :system) do
driven_by :rack_test
@@ -7,5 +9,3 @@ RSpec.configure do |config|
driven_by :selenium_chrome_headless
end
end
-
-require 'capybara-screenshot/rspec'
spec/factories.rb
@@ -3,5 +3,9 @@ FactoryBot.define do
email { FFaker::Internet.email }
uuid { SecureRandom.uuid }
password { FFaker::Internet.password }
+
+ trait :mfa_configured do
+ tfa_secret { ::ROTP::Base32.random_base32 }
+ end
end
end