Add authentication history (#16408)
parent
7da4d99400
commit
2067b0bf34
@ -0,0 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Settings::LoginActivitiesController < Settings::BaseController
|
||||
def index
|
||||
@login_activities = LoginActivity.where(user: current_user).order(id: :desc).page(params[:page])
|
||||
end
|
||||
end
|
@ -0,0 +1,35 @@
|
||||
# frozen_string_literal: true
|
||||
# == Schema Information
|
||||
#
|
||||
# Table name: login_activities
|
||||
#
|
||||
# id :bigint(8) not null, primary key
|
||||
# user_id :bigint(8) not null
|
||||
# authentication_method :string
|
||||
# provider :string
|
||||
# success :boolean
|
||||
# failure_reason :string
|
||||
# ip :inet
|
||||
# user_agent :string
|
||||
# created_at :datetime
|
||||
#
|
||||
|
||||
class LoginActivity < ApplicationRecord
|
||||
enum authentication_method: { password: 'password', otp: 'otp', webauthn: 'webauthn', sign_in_token: 'sign_in_token', omniauth: 'omniauth' }
|
||||
|
||||
belongs_to :user
|
||||
|
||||
validates :authentication_method, inclusion: { in: authentication_methods.keys }
|
||||
|
||||
def detection
|
||||
@detection ||= Browser.new(user_agent)
|
||||
end
|
||||
|
||||
def browser
|
||||
detection.id
|
||||
end
|
||||
|
||||
def platform
|
||||
detection.platform.id
|
||||
end
|
||||
end
|
@ -0,0 +1,17 @@
|
||||
- method_str = content_tag(:span, login_activity.omniauth? ? t(login_activity.provider, scope: 'auth.providers') : t(login_activity.authentication_method, scope: 'login_activities.authentication_methods'), class: 'target')
|
||||
- ip_str = content_tag(:span, login_activity.ip, class: 'target')
|
||||
- browser_str = content_tag(:span, t('sessions.description', browser: t("sessions.browsers.#{login_activity.browser}", default: "#{login_activity.browser}"), platform: t("sessions.platforms.#{login_activity.platform}", default: "#{login_activity.platform}")), class: 'target')
|
||||
|
||||
.log-entry
|
||||
.log-entry__header
|
||||
.log-entry__avatar
|
||||
.indicator-icon{ class: login_activity.success? ? 'success' : 'failure' }
|
||||
= fa_icon login_activity.success? ? 'check' : 'times'
|
||||
.log-entry__content
|
||||
.log-entry__title
|
||||
- if login_activity.success?
|
||||
= t('login_activities.successful_sign_in_html', method: method_str, ip: ip_str, browser: browser_str)
|
||||
- else
|
||||
= t('login_activities.failed_sign_in_html', method: method_str, ip: ip_str, browser: browser_str)
|
||||
.log-entry__timestamp
|
||||
%time.formatted{ datetime: login_activity.created_at.iso8601 }
|
@ -0,0 +1,15 @@
|
||||
- content_for :page_title do
|
||||
= t 'login_activities.title'
|
||||
|
||||
%p= t('login_activities.description_html')
|
||||
|
||||
%hr.spacer/
|
||||
|
||||
- if @login_activities.empty?
|
||||
%div.muted-hint.center-text
|
||||
= t 'login_activities.empty'
|
||||
- else
|
||||
.announcements-list
|
||||
= render partial: 'login_activity', collection: @login_activities
|
||||
|
||||
= paginate @login_activities
|
@ -0,0 +1,14 @@
|
||||
class CreateLoginActivities < ActiveRecord::Migration[6.1]
|
||||
def change
|
||||
create_table :login_activities do |t|
|
||||
t.belongs_to :user, null: false, foreign_key: { on_delete: :cascade }
|
||||
t.string :authentication_method
|
||||
t.string :provider
|
||||
t.boolean :success
|
||||
t.string :failure_reason
|
||||
t.inet :ip
|
||||
t.string :user_agent
|
||||
t.datetime :created_at
|
||||
end
|
||||
end
|
||||
end
|
@ -0,0 +1,8 @@
|
||||
Fabricator(:login_activity) do
|
||||
user
|
||||
strategy 'password'
|
||||
success true
|
||||
failure_reason nil
|
||||
ip { Faker::Internet.ip_v4_address }
|
||||
user_agent { Faker::Internet.user_agent }
|
||||
end
|
@ -0,0 +1,5 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe LoginActivity, type: :model do
|
||||
|
||||
end
|
Loading…
Reference in new issue