Commit c54b61e
Changed files (9)
spec
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