commit
37fd9670a5
@ -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