Variants are currently broken. So we process the original file with the most common avatar dimensions and stripping metadata, then hash and upload only that version.
266 lines
7.2 KiB
Ruby
266 lines
7.2 KiB
Ruby
require 'nostr'
|
|
|
|
class User < ApplicationRecord
|
|
include EmailValidatable
|
|
|
|
attr_accessor :current_password
|
|
attr_accessor :display_name
|
|
attr_accessor :avatar_new
|
|
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: "lndhub_username", foreign_key: "login"
|
|
|
|
has_many :accounts, through: :lndhub_user
|
|
|
|
#
|
|
# Attachments
|
|
#
|
|
|
|
has_one_attached :avatar
|
|
|
|
#
|
|
# 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
|
|
#
|
|
|
|
encrypts :lndhub_password
|
|
|
|
# Include default devise modules. Others available are:
|
|
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
|
|
devise :ldap_authenticatable,
|
|
:confirmable,
|
|
:recoverable,
|
|
:validatable,
|
|
:timeoutable,
|
|
:rememberable
|
|
|
|
#
|
|
# Methods
|
|
#
|
|
|
|
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
|
|
|
|
# TODO Variant keys are currently broken for some reason
|
|
# (They use the same key as the main blob, when it should be
|
|
# "/variants/#{key)"
|
|
# def avatar_variant(size: :medium)
|
|
# dimensions = case size
|
|
# when :large then [400, 400]
|
|
# when :medium then [256, 256]
|
|
# when :small then [64, 64]
|
|
# else [256, 256]
|
|
# end
|
|
# format = avatar.content_type == "image/png" ? :png : :jpeg
|
|
# avatar.variant(resize_to_fill: dimensions, format: format)
|
|
# 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 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, "must be less than 1MB file size")
|
|
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
|