Sometimes, the pubkey might not be imported in the local keychain (anymore), but at this point in the code it had been successfully imported at least once before. So we just (re-)import every time for it to never fail.
245 lines
6.6 KiB
Ruby
245 lines
6.6 KiB
Ruby
require 'nostr'
|
|
|
|
class User < ApplicationRecord
|
|
include EmailValidatable
|
|
|
|
attr_accessor :current_password
|
|
attr_accessor :avatar_new
|
|
attr_accessor :display_name
|
|
attr_accessor :pgp_pubkey
|
|
|
|
serialize :preferences, coder: UserPreferences
|
|
|
|
#
|
|
# Relations
|
|
#
|
|
|
|
has_many :invitations, dependent: :destroy
|
|
has_one :invitation, inverse_of: :invitee, foreign_key: 'invited_user_id'
|
|
has_one :inviter, through: :invitation, source: :user
|
|
has_many :invitees, through: :invitations
|
|
has_many :donations, dependent: :nullify
|
|
has_many :remote_storage_authorizations
|
|
has_many :zaps
|
|
|
|
has_one :lndhub_user, class_name: "LndhubUser", inverse_of: "user",
|
|
primary_key: "ln_account", foreign_key: "login"
|
|
|
|
has_many :accounts, through: :lndhub_user
|
|
|
|
#
|
|
# Validations
|
|
#
|
|
|
|
validates_uniqueness_of :cn, scope: :ou
|
|
validates_length_of :cn, minimum: 3
|
|
validates_format_of :cn, with: /\A([a-z0-9\-])*\z/,
|
|
if: Proc.new{ |u| u.cn.present? },
|
|
message: "is invalid. Please use only letters, numbers and -"
|
|
validates_format_of :cn, without: /\A-/,
|
|
if: Proc.new{ |u| u.cn.present? },
|
|
message: "is invalid. Usernames need to start with a letter."
|
|
# FIXME This needs a server restart to apply values
|
|
validates_format_of :cn, without: /\A(#{Setting.reserved_usernames.join('|')})\z/i,
|
|
message: "has already been taken",
|
|
unless: Proc.new{ |u| u.persisted? }
|
|
|
|
validates_uniqueness_of :email
|
|
validates :email, email: true
|
|
|
|
validates_length_of :display_name, minimum: 3, maximum: 35, allow_blank: true,
|
|
if: -> { defined?(@display_name) }
|
|
|
|
validate :acceptable_avatar
|
|
|
|
validate :acceptable_pgp_key_format, if: -> { defined?(@pgp_pubkey) && @pgp_pubkey.present? }
|
|
|
|
#
|
|
# Scopes
|
|
#
|
|
|
|
scope :confirmed, -> { where.not(confirmed_at: nil) }
|
|
scope :pending, -> { where(confirmed_at: nil) }
|
|
scope :all_except, -> (user) { where.not(id: user) }
|
|
|
|
#
|
|
# Encrypted database columns
|
|
#
|
|
|
|
has_encrypted :ln_login, :ln_password
|
|
|
|
# Include default devise modules. Others available are:
|
|
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
|
|
devise :ldap_authenticatable,
|
|
:confirmable,
|
|
:recoverable,
|
|
:validatable,
|
|
:timeoutable,
|
|
:rememberable
|
|
|
|
def ldap_before_save
|
|
self.email = Devise::LDAP::Adapter.get_ldap_param(self.cn, "mail").first
|
|
self.ou = dn.split(',')
|
|
.select{|e| e[0..1] == "ou"}.first
|
|
.delete_prefix("ou=")
|
|
|
|
if self.confirmed_at.blank? && self.confirmation_token.blank?
|
|
# User had an account with a trusted email address before akkounts was a thing
|
|
self.confirmed_at = DateTime.now
|
|
end
|
|
end
|
|
|
|
def devise_after_confirmation
|
|
if ldap_entry[:mail] != self.email
|
|
# E-Mail update confirmed
|
|
LdapManager::UpdateEmail.call(dn: self.dn, address: self.email)
|
|
else
|
|
# E-Mail from signup confirmed (i.e. account activation)
|
|
enable_default_services
|
|
|
|
# TODO enable in development when we have easy setup of ejabberd etc.
|
|
return if Rails.env.development? || !Setting.ejabberd_enabled?
|
|
|
|
XmppExchangeContactsJob.perform_later(inviter, self) if inviter.present?
|
|
XmppSetDefaultBookmarksJob.perform_later(self)
|
|
end
|
|
end
|
|
|
|
def send_devise_notification(notification, *args)
|
|
devise_mailer.send(notification, self, *args).deliver_later
|
|
end
|
|
|
|
def reset_password(new_password, new_password_confirmation)
|
|
self.password = new_password
|
|
self.password_confirmation = new_password_confirmation
|
|
return false unless valid?
|
|
|
|
Devise::LDAP::Adapter.update_password(login_with, new_password)
|
|
clear_reset_password_token
|
|
save
|
|
end
|
|
|
|
def is_admin?
|
|
admin ||= if admin = Devise::LDAP::Adapter.get_ldap_param(self.cn, :admin)
|
|
!!admin.first
|
|
else
|
|
false
|
|
end
|
|
end
|
|
|
|
def address
|
|
"#{self.cn}@#{self.ou}"
|
|
end
|
|
|
|
def mastodon_address
|
|
return nil unless Setting.mastodon_enabled?
|
|
"#{self.cn.gsub("-", "_")}@#{Setting.mastodon_address_domain}"
|
|
end
|
|
|
|
def valid_attribute?(attribute_name)
|
|
self.valid?
|
|
self.errors[attribute_name].blank?
|
|
end
|
|
|
|
def enable_default_services
|
|
enable_service Setting.default_services
|
|
end
|
|
|
|
def dn
|
|
return @dn if defined?(@dn)
|
|
@dn = Devise::LDAP::Adapter.get_dn(self.cn)
|
|
end
|
|
|
|
def ldap_entry(reload: false)
|
|
return @ldap_entry if defined?(@ldap_entry) && !reload
|
|
@ldap_entry = ldap.fetch_users(uid: self.cn, ou: self.ou).first
|
|
end
|
|
|
|
def display_name
|
|
@display_name ||= ldap_entry[:display_name]
|
|
end
|
|
|
|
def nostr_pubkey
|
|
@nostr_pubkey ||= ldap_entry[:nostr_key]
|
|
end
|
|
|
|
def nostr_pubkey_bech32
|
|
return nil unless nostr_pubkey.present?
|
|
Nostr::PublicKey.new(nostr_pubkey).to_bech32
|
|
end
|
|
|
|
def pgp_pubkey
|
|
@pgp_pubkey ||= ldap_entry[:pgp_key]
|
|
end
|
|
|
|
def gnupg_key
|
|
return nil unless pgp_pubkey.present?
|
|
GPGME::Key.import(pgp_pubkey)
|
|
GPGME::Key.get(pgp_fpr)
|
|
end
|
|
|
|
def pgp_pubkey_contains_user_address?
|
|
gnupg_key.uids.map(&:email).include?(address)
|
|
end
|
|
|
|
def wkd_hash
|
|
ZBase32.encode(Digest::SHA1.digest(cn))
|
|
end
|
|
|
|
def avatar
|
|
@avatar_base64 ||= LdapManager::FetchAvatar.call(cn: cn)
|
|
end
|
|
|
|
def services_enabled
|
|
ldap_entry[:services_enabled] || []
|
|
end
|
|
|
|
def service_enabled?(name)
|
|
services_enabled.map(&:to_sym).include?(name.to_sym)
|
|
end
|
|
|
|
def enable_service(service)
|
|
current_services = services_enabled
|
|
new_services = Array(service).map(&:to_s)
|
|
services = (current_services + new_services).uniq.sort
|
|
ldap.replace_attribute(dn, :serviceEnabled, services)
|
|
end
|
|
|
|
def disable_service(service)
|
|
current_services = services_enabled
|
|
disabled_services = Array(service).map(&:to_s)
|
|
services = (current_services - disabled_services).uniq.sort
|
|
ldap.replace_attribute(dn, :serviceEnabled, services)
|
|
end
|
|
|
|
def disable_all_services
|
|
ldap.delete_attribute(dn,:service)
|
|
end
|
|
|
|
private
|
|
|
|
def ldap
|
|
return @ldap_service if defined?(@ldap_service)
|
|
@ldap_service = LdapService.new
|
|
end
|
|
|
|
def acceptable_avatar
|
|
return unless avatar_new.present?
|
|
|
|
if avatar_new.size > 1.megabyte
|
|
errors.add(:avatar, "file size is too large")
|
|
end
|
|
|
|
acceptable_types = ["image/jpeg", "image/png"]
|
|
unless acceptable_types.include?(avatar_new.content_type)
|
|
errors.add(:avatar, "must be a JPEG or PNG file")
|
|
end
|
|
end
|
|
|
|
def acceptable_pgp_key_format
|
|
unless GPGME::Key.valid?(pgp_pubkey)
|
|
errors.add(:pgp_pubkey, 'is not a valid armored PGP public key block')
|
|
end
|
|
end
|
|
end
|