Files
Dalan/app/models/user.rb
2026-08-13 19:56:46 +03:30

288 lines
8.0 KiB
Ruby

# frozen_string_literal: true
# User model
class User < ApplicationRecord
acts_as_eventable prefix: 'user', on: %i[create update]
include ModelCaching
CHANGE_PASS_ATTEMPTS = 3
has_secure_password
has_many :profiles, dependent: :destroy
has_many :phones, dependent: :destroy
has_many :data_storages, dependent: :destroy
has_many :comments, dependent: :destroy
has_many :documents, dependent: :destroy
has_many :labels, dependent: :destroy
has_many :activities, dependent: :destroy
has_many :service_accounts, dependent: :destroy, foreign_key: 'owner_id'
has_many :api_keys, dependent: :destroy, as: :key_holder_account, class_name: 'APIKey'
has_many :treasuries, dependent: :destroy
validates_length_of :data, maximum: 1024
validate :role_exists
validate :referral_exists
validates :data, data_is_json: true
validates :email, email: true, presence: true, uniqueness: true
validates :uid, presence: true, uniqueness: true
validates :password, presence: true, if: :should_validate?
validate :validate_pass!
scope :active, -> { where(state: 'active') }
scope :with_pending_or_replaced_docs, -> { self.joins(:labels).where(labels:
{ key: 'document', value: ['pending', 'replaced'], scope: 'private' }) }
before_validation :assign_uid
before_validation :generate_password, on: :create
after_update :disable_api_keys
after_update :disable_service_accounts
before_save :notfiy_new_level, if: :level_changed?
before_save :notify_changed_otp, if: :otp_changed?
def notfiy_new_level
::AMQP::Queue.enqueue_event("private" , self.uid, 'kyc', {msg: "new level is #{level}"})
end
def notify_changed_otp
::AMQP::Queue.enqueue_event("private" , self.uid, 'kyc', {msg: "otp state is #{otp}"})
end
def generate_password
self.password = SecureRandom.base64(30) unless password
end
def validate_pass!
return unless (new_record? && password.present?) || password.present?
validation_result = PasswordStrengthChecker.validate!(password)
errors.add(:password, validation_result) unless validation_result == 'strong'
end
def disable_service_accounts
if state != 'active'
service_accounts.each do |account|
account.update(state: state)
end
end
end
def disable_api_keys
if otp_previously_changed? && otp == false || state_previously_changed? && state != 'active'
service_accounts.each do |service_account|
service_account.api_keys.active.each do |key|
key.update(state: 'inactive')
end
end
api_keys.active.each do |key|
key.update(state: 'inactive')
end
end
end
def active?
self.state == 'active'
end
def superadmin?
self.role == 'superadmin'
end
def role_exists
return if Permission.pluck(:role).include?(role)
errors.add(:role, 'doesnt_exist')
end
# Check if refferal exist for assignment
def referral_exists
errors.add(:referral_id, 'doesnt_exist') if referral_id.present? && User.find_by(id: referral_id).blank?
end
def referral_uid
user = User.find_by(id: referral_id)
return if user.nil?
user.uid
end
def role
super.inquiry
end
def should_validate?
new_record? || password.present?
end
def kyc_info
user_points = 0
last_level = 0
tags = labels.with_private_scope.map { |l| [l.key, l.value].join ':' }
levels = Level.all.order(id: :asc)
levels.each do |lvl|
# lvl.value always == 'verified'
# so just verified label accepted
break unless tags.include?(lvl.key + ':' + lvl.value)
user_points += lvl.id
last_level = lvl.id
end
{ points: user_points.to_i, step: last_level.to_i, level: ::Level.what_level(user_points).to_i }
end
def update_level
update(level: Level.what_level(kyc_info.dig(:points))) unless level == kyc_info.dig(:level)
end
def update_state
@resulting_state = 'pending'
# check if user has all required labels for activation
@resulting_state = 'active' if labels_include?(BarongConfig.list['activation_requirements'])
# FIXME BarongConfig should be a feature of Barong::App
BarongConfig.list['state_triggers']&.each do |state, triggers|
triggers.each { |trigger|
# #TODO please fix it
# if my key level is bank : user become banned
labels.pluck(:key).each { |label| @resulting_state = state if label.start_with?(trigger) }
}
end
update(state: @resulting_state) if @resulting_state != self.state
end
# check if given key: values hash is a subset of private user labels
def labels_include?(labels_hash)
labels_hash <= private_labels_to_hash
end
# Select all key-value pairs from user labels with private scope, merge in one hash
def private_labels_to_hash
key_value_arr = self.labels.with_private_scope.map do
|l| { l.key => l.value }
end
key_value_hash = key_value_arr.inject(:merge)
key_value_hash || {}
end
def as_json_for_event_api
{
uid: uid,
email: email,
role: role,
level: level,
otp: otp,
state: state,
referral_uid: referral_uid,
created_at: format_iso8601_time(created_at),
updated_at: format_iso8601_time(updated_at)
}
end
def as_payload
referral_uid = self.class.find_by(referral_id: referral_id).uid if referral_id.present?
as_json(only: %i[uid email role level state otp]).merge(
'referral_uid' => referral_uid,
'last_change_pass' => change_pass_time,
'cards' => cards&.pluck('data'),
'ibans' => ibans&.pluck('data')
)
end
def language
if data.blank?
Barong::App.config.default_language.upcase
else
JSON.parse(data)['language'].upcase || Barong::App.config.default_language.upcase
end
end
def verified_profile
profiles&.find_by(state: 'verified')
end
def submitted_profile
profiles&.find_by(state: 'submitted')
end
def drafted_profile
profiles&.where(state: 'drafted').last
end
def front_icard
documents.where(doc_type: 'Identity card', doc_category: 'front_side')&.last
end
def selfie
documents.where(doc_type: 'Selfie', doc_category: 'front_side')&.last
end
def cards
treasuries.where(kind: :card, state: :verified)
end
def ibans
treasuries.where(kind: :iban, state: :verified)
end
def submitted_treasuries
treasuries&.where(state: 'submitted')
end
def last_password_activity
activities.where(action: ['password reset', 'password change'], result: 'succeed', topic: 'password')
.order('created_at DESC')&.last
end
def change_pass_time
last_password_activity&.created_at
end
def access_phone
phones.where(step: 'access_phone', category: 'mobile')&.last
end
def mobile(state = nil)
return phones.where(category: 'mobile').last unless state.present?
phones.where(category: 'mobile', state: state)&.last
end
def landline
return phones.where(category: 'landline').last unless step.present?
phones.where(category: 'landline', step: step).last
end
private
def assign_uid
return unless uid.blank?
self.uid = UIDGenerator.generate(Barong::App.config.uid_prefix)
end
end
# == Schema Information
# Schema version: 20210425062048
#
# Table name: users
#
# id :bigint not null, primary key
# uid :string(255) not null
# email :string(255) not null
# password_digest :string(255) not null
# role :string(255) default("member"), not null
# data :text(65535)
# level :integer default(0), not null
# otp :boolean default(FALSE)
# state :string(255) default("pending"), not null
# referral_id :bigint
# created_at :datetime not null
# updated_at :datetime not null
#
# Indexes
#
# index_users_on_email (email) UNIQUE
# index_users_on_uid (uid) UNIQUE
#