Commit 10ab9e4

mo <mo.khan@gmail.com>
2018-04-09 21:21:21
extract a tfa class.
1 parent 419a95d
Changed files (7)
app/controllers/tfas_controller.rb
@@ -2,8 +2,8 @@
 
 class TfasController < ApplicationController
   def new
-    return redirect_to edit_tfa_path if current_user.tfa_setup?
-    current_user.tfa_secret = ::ROTP::Base32.random_base32
+    return redirect_to edit_tfa_path if current_user.tfa.setup?
+    current_user.tfa.build_secret
   end
 
   def create
@@ -14,7 +14,7 @@ class TfasController < ApplicationController
   def edit; end
 
   def destroy
-    current_user.disable_tfa!
+    current_user.tfa.disable!
     redirect_to dashboard_path
   end
 end
app/models/tfa.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+class Tfa
+  attr_reader :user
+
+  def initialize(user)
+    @user = user
+  end
+
+  def setup?
+    secret.present?
+  end
+
+  def provisioning_uri
+    totp = ::ROTP::TOTP.new(secret, issuer: 'saml-kit')
+    totp.provisioning_uri(user.email)
+  end
+
+  def build_secret
+    user.tfa_secret = ::ROTP::Base32.random_base32
+  end
+
+  def disable!
+    user.update!(tfa_secret: nil)
+  end
+
+  def secret
+    user.tfa_secret
+  end
+
+  def current_totp
+    ROTP::TOTP.new(secret).now
+  end
+end
app/models/user.rb
@@ -16,17 +16,8 @@ class User < ApplicationRecord
     request.trusted? ? trusted_attributes_for(request) : {}
   end
 
-  def tfa_provisioning_uri
-    totp = ::ROTP::TOTP.new(tfa_secret, issuer: 'saml-kit')
-    totp.provisioning_uri(email)
-  end
-
-  def tfa_setup?
-    tfa_secret.present?
-  end
-
-  def disable_tfa!
-    update!(tfa_secret: nil)
+  def tfa
+    Tfa.new(self)
   end
 
   def self.login(email, password)
app/views/dashboards/show.html.erb
@@ -2,7 +2,7 @@
   <div class="row">
     <div class="col">
       <h1>Dashboard</h1>
-      <% if current_user.tfa_setup? %>
+      <% if current_user.tfa.setup? %>
         <%= link_to "TFA", edit_tfa_path %>
       <% else %>
         <%= link_to "Setup TFA", new_tfa_path %>
app/views/tfas/edit.html.erb
@@ -4,8 +4,8 @@
       <h1>Two Factor Authentication (TFA)</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>
+        <p>Secret: <%= current_user.tfa.secret %></p>
+        <p>Provisioning URI: <%= current_user.tfa.provisioning_uri %></p>
 
         <%= form_for current_user, url: tfa_path, method: :delete do |form| %>
           <%= form.hidden_field :tfa_secret, data: { target: 'tfa--setup.secret' } %>
app/views/tfas/new.html.erb
@@ -4,8 +4,9 @@
       <h1>Two Factor Authentication (TFA) - 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>
+        <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.hidden_field :tfa_secret, data: { target: 'tfa--setup.secret' } %>
           <%= form.submit "Save", class: 'btn btn-primary', data: { disable_with: 'Saving…' } %>
spec/system/direct_login_spec.rb
@@ -1,14 +1,32 @@
 require 'rails_helper'
 
 describe "when logging in directly in to the application", js: true do
-  let(:user) { create(:user) }
+  describe "when tfa is disabled", js: true do
+    let(:user) { create(:user) }
 
-  it 'redirects the user to the dashboard' do
-    visit root_path
-    fill_in "user_email", with: user.email
-    fill_in "user_password", with: user.password
-    click_button I18n.t('sessions.new.login')
+    it 'redirects the user to the dashboard' do
+      visit root_path
+      fill_in "user_email", with: user.email
+      fill_in "user_password", with: user.password
+      click_button I18n.t('sessions.new.login')
 
-    expect(page).to have_content('Dashboard')
+      expect(page).to have_content('Dashboard')
+    end
+  end
+
+  describe "when TFA is enabled", js: true do
+    let(:user) { create(:user, tfa_secret: ::ROTP::Base32.random_base32) }
+
+    it 'prompts for a TOTP code then redirect to the dashboard' do
+      pending
+      visit root_path
+      fill_in "user_email", with: user.email
+      fill_in "user_password", with: user.password
+      click_button I18n.t('sessions.new.login')
+
+      fill_in "totp", with: user.tfa.current_totp
+
+      expect(page).to have_content('Dashboard')
+    end
   end
 end