Store and retrieve avatars in/from LDAP exclusively
Some checks failed
continuous-integration/drone/push Build is failing

No need to keep them in two places at the same time. We can fetch them
from LDAP whenever we want to do something with them.
This commit is contained in:
Râu Cao 2023-09-06 20:42:26 +02:00
parent 50c63d5c38
commit d5ab532947
Signed by: raucao
GPG Key ID: 15E65F399D084BA9
8 changed files with 55 additions and 25 deletions

View File

@ -20,6 +20,8 @@ class Admin::UsersController < Admin::BaseController
end end
@services_enabled = @user.services_enabled @services_enabled = @user.services_enabled
@avatar = LdapManager::FetchAvatar.call(cn: @user.cn, ou: @user.ou)
end end
private private

View File

@ -19,17 +19,15 @@ class SettingsController < ApplicationController
def update def update
@user.preferences.merge!(user_params[:preferences] || {}) @user.preferences.merge!(user_params[:preferences] || {})
@user.display_name = user_params[:display_name] @user.display_name = user_params[:display_name]
current_avatar_checksum = @user.avatar.attached? ? @user.avatar.blob.checksum : nil @user.avatar_new = user_params[:avatar]
@user.avatar = user_params[:avatar] if user_params[:avatar].present?
if @user.save if @user.save
if @user.display_name && (@user.display_name != @user.ldap_entry[:display_name]) if @user.display_name && (@user.display_name != @user.ldap_entry[:display_name])
LdapManager::UpdateDisplayName.call(@user.dn, @user.display_name) LdapManager::UpdateDisplayName.call(@user.dn, @user.display_name)
end end
if @user.avatar.attached? && if @user.avatar_new.present?
(@user.avatar.blob.checksum != current_avatar_checksum) LdapManager::UpdateAvatar.call(@user.dn, @user.avatar_new)
LdapManager::UpdateAvatar.call(@user.dn, @user.avatar_base64)
end end
redirect_to setting_path(@settings_section), flash: { redirect_to setting_path(@settings_section), flash: {

View File

@ -2,19 +2,10 @@ class User < ApplicationRecord
include EmailValidatable include EmailValidatable
attr_accessor :display_name attr_accessor :display_name
attr_accessor :avatar_new
serialize :preferences, UserPreferences serialize :preferences, UserPreferences
#
# File attachments
#
has_one_attached :avatar do |attachable|
attachable.variant :small, resize_to_fill: [64, 64], convert: :jpeg
attachable.variant :medium, resize_to_fill: [256, 256], convert: :jpeg
attachable.variant :large, resize_to_fill: [512, 512], convert: :jpeg
end
# #
# Relations # Relations
# #
@ -167,9 +158,8 @@ class User < ApplicationRecord
@display_name ||= ldap_entry[:display_name] @display_name ||= ldap_entry[:display_name]
end end
def avatar
def avatar_base64(variant: :large) @avatar_base64 ||= LdapManager::FetchAvatar.call(cn: cn, ou: ou)
Base64.strict_encode64 avatar.variant(variant).processed.download
end end
def services_enabled def services_enabled
@ -202,14 +192,14 @@ class User < ApplicationRecord
end end
def acceptable_avatar def acceptable_avatar
return unless avatar.attached? return unless avatar_new.present?
if avatar.blob.byte_size > 1.megabyte if avatar_new.size > 1.megabyte
errors.add(:avatar, "file size is too large") errors.add(:avatar, "file size is too large")
end end
acceptable_types = ["image/jpeg", "image/png"] acceptable_types = ["image/jpeg", "image/png"]
unless acceptable_types.include?(avatar.content_type) unless acceptable_types.include?(avatar_new.content_type)
errors.add(:avatar, "must be a JPEG or PNG file") errors.add(:avatar, "must be a JPEG or PNG file")
end end
end end

View File

@ -0,0 +1,18 @@
module LdapManager
class FetchAvatar < LdapManagerService
def initialize(cn:, ou: nil)
@cn = cn
@ou = ou
end
def call
treebase = @ou ? "ou=#{@ou},cn=users,#{suffix}" : ldap_config["base"]
puts treebase.inspect
attributes = %w{ jpegPhoto }
filter = Net::LDAP::Filter.eq("cn", @cn)
entry = ldap_client.search(base: treebase, filter: filter, attributes: attributes).first
entry.try(:jpegPhoto) ? entry.jpegPhoto.first : nil
end
end
end

View File

@ -1,12 +1,27 @@
require "image_processing/vips"
module LdapManager module LdapManager
class UpdateAvatar < LdapManagerService class UpdateAvatar < LdapManagerService
def initialize(dn, img_data) def initialize(dn, file)
@dn = dn @dn = dn
@img_data = img_data @img_data = process(file)
end end
def call def call
replace_attribute @dn, :jpegPhoto, @img_data replace_attribute @dn, :jpegPhoto, @img_data
end end
private
def process(file)
processed = ImageProcessing::Vips
.resize_to_fill(512, 512)
.source(file)
.convert("jpeg")
.saver(strip: true)
.call
Base64.strict_encode64 processed.read
end
end end
end end

View File

@ -1,2 +1,5 @@
class LdapManagerService < LdapService class LdapManagerService < LdapService
def suffix
@suffix ||= ENV["LDAP_SUFFIX"] || "dc=kosmos,dc=org"
end
end end

View File

@ -63,6 +63,10 @@
</section> </section>
<section class="sm:flex-1 sm:pt-0"> <section class="sm:flex-1 sm:pt-0">
<h3>LDAP<h3>
<p>
<img src="data:image/png;base64,<%= @avatar %>" class="h-48 w-48" />
</p>
<!-- <h3>Actions</h3> --> <!-- <h3>Actions</h3> -->
</section> </section>
</div> </div>

View File

@ -39,9 +39,9 @@
Default profile picture Default profile picture
</p> </p>
<div class="flex items-center gap-6"> <div class="flex items-center gap-6">
<% if current_user.avatar.attached? %> <% if current_user.avatar.present? %>
<p class="flex-none"> <p class="flex-none">
<%= image_tag current_user.reload.avatar.variant(:medium), class: "h-24 w-24 rounded-lg" %> <%= image_tag "data:image/jpeg;base64,#{current_user.avatar}", class: "h-24 w-24 rounded-lg" %>
</p> </p>
<% end %> <% end %>
<div class="grow"> <div class="grow">