Commit c54b61e

mokha <mokha@cisco.com>
2018-09-22 18:43:01
create authentications model
1 parent e202466
app/models/authentication.rb
@@ -0,0 +1,3 @@
+class Authentication < ApplicationRecord
+  belongs_to :user
+end
app/models/password_authentication.rb
@@ -0,0 +1,5 @@
+class PasswordAuthentication < Authentication
+  def authenticate(password)
+    user.authenticate(password)
+  end
+end
app/models/totp_authentication.rb
@@ -0,0 +1,5 @@
+class TotpAuthentication < Authentication
+  def authenticate(code)
+    user.mfa.authenticate(code) ? user : false
+  end
+end
app/models/user.rb
@@ -3,6 +3,7 @@
 class User < ApplicationRecord
   has_secure_password
   has_many :sessions, foreign_key: "user_id", class_name: UserSession.name
+  has_many :authentications
 
   validates :email, presence: true, email: true, uniqueness: {
     case_sensitive: false
db/migrate/20180922174543_create_authentications.rb
@@ -0,0 +1,10 @@
+class CreateAuthentications < ActiveRecord::Migration[5.2]
+  def change
+    create_table :authentications do |t|
+      t.references :user, foreign_key: true
+      t.string :type, null: false
+
+      t.timestamps
+    end
+  end
+end
db/schema.rb
@@ -10,7 +10,15 @@
 #
 # It's strongly recommended that you check this file into your version control system.
 
-ActiveRecord::Schema.define(version: 2018_09_22_153546) do
+ActiveRecord::Schema.define(version: 2018_09_22_174543) do
+
+  create_table "authentications", force: :cascade do |t|
+    t.integer "user_id"
+    t.string "type", null: false
+    t.datetime "created_at", null: false
+    t.datetime "updated_at", null: false
+    t.index ["user_id"], name: "index_authentications_on_user_id"
+  end
 
   create_table "authorizations", force: :cascade do |t|
     t.integer "user_id"
spec/factories/authentication.rb
@@ -3,5 +3,6 @@ FactoryBot.define do
     user
 
     factory :password_authentication, class: PasswordAuthentication
+    factory :totp_authentication, class: TotpAuthentication
   end
 end
spec/models/authentication_spec.rb
@@ -0,0 +1,29 @@
+require 'rails_helper'
+
+RSpec.describe Authentication do
+  describe PasswordAuthentication do
+    describe "#authenticate" do
+      subject { create(:password_authentication, user: user) }
+      let(:user) { create(:user, password: password) }
+      let(:password) { generate(:password) }
+      let(:invalid) { SecureRandom.hex(20) }
+
+      specify { expect(subject.authenticate(password)).to eql(user) }
+      specify { expect(subject.authenticate(invalid)).to be(false) }
+    end
+  end
+
+  describe TotpAuthentication do
+    describe "#authenticate" do
+      subject { create(:totp_authentication, user: user) }
+      let(:user) { create(:user, :mfa_configured) }
+      let(:current_totp) { ROTP::TOTP.new(user.mfa_secret).now }
+      let(:invalid_totp) { rand(99_999).to_s }
+
+      before { freeze_time }
+
+      specify { expect(subject.authenticate(current_totp)).to eql(user) }
+      specify { expect(subject.authenticate(invalid_totp)).to be(false) }
+    end
+  end
+end
spec/requests/oauth_spec.rb
@@ -35,7 +35,7 @@ RSpec.describe '/oauth' do
         before { get "/oauth/authorize", params: { client_id: client.to_param, response_type: 'code', state: state } }
 
         specify { expect(response).to have_http_status(:ok) }
-        specify { expect(response.body).to include(client.name) }
+        specify { expect(response.body).to include(CGI.escapeHTML(client.name)) }
         specify { expect(response.body).to include(state) }
       end
     end