Add authentication history (#16408)
parent
946200b471
commit
d174d12c83
@ -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