From d5ab53294799c2e61e8c66be5c435e110d6646c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A2u=20Cao?= Date: Wed, 6 Sep 2023 20:42:26 +0200 Subject: [PATCH] Store and retrieve avatars in/from LDAP exclusively 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. --- app/controllers/admin/users_controller.rb | 2 ++ app/controllers/settings_controller.rb | 8 +++----- app/models/user.rb | 22 ++++++---------------- app/services/ldap_manager/fetch_avatar.rb | 18 ++++++++++++++++++ app/services/ldap_manager/update_avatar.rb | 19 +++++++++++++++++-- app/services/ldap_manager_service.rb | 3 +++ app/views/admin/users/show.html.erb | 4 ++++ app/views/settings/_profile.html.erb | 4 ++-- 8 files changed, 55 insertions(+), 25 deletions(-) create mode 100644 app/services/ldap_manager/fetch_avatar.rb diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb index 71cac46..3cb5735 100644 --- a/app/controllers/admin/users_controller.rb +++ b/app/controllers/admin/users_controller.rb @@ -20,6 +20,8 @@ class Admin::UsersController < Admin::BaseController end @services_enabled = @user.services_enabled + + @avatar = LdapManager::FetchAvatar.call(cn: @user.cn, ou: @user.ou) end private diff --git a/app/controllers/settings_controller.rb b/app/controllers/settings_controller.rb index dc969df..d4efea5 100644 --- a/app/controllers/settings_controller.rb +++ b/app/controllers/settings_controller.rb @@ -19,17 +19,15 @@ class SettingsController < ApplicationController def update @user.preferences.merge!(user_params[:preferences] || {}) @user.display_name = user_params[:display_name] - current_avatar_checksum = @user.avatar.attached? ? @user.avatar.blob.checksum : nil - @user.avatar = user_params[:avatar] if user_params[:avatar].present? + @user.avatar_new = user_params[:avatar] if @user.save if @user.display_name && (@user.display_name != @user.ldap_entry[:display_name]) LdapManager::UpdateDisplayName.call(@user.dn, @user.display_name) end - if @user.avatar.attached? && - (@user.avatar.blob.checksum != current_avatar_checksum) - LdapManager::UpdateAvatar.call(@user.dn, @user.avatar_base64) + if @user.avatar_new.present? + LdapManager::UpdateAvatar.call(@user.dn, @user.avatar_new) end redirect_to setting_path(@settings_section), flash: { diff --git a/app/models/user.rb b/app/models/user.rb index 326226f..acda860 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -2,19 +2,10 @@ class User < ApplicationRecord include EmailValidatable attr_accessor :display_name + attr_accessor :avatar_new 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 # @@ -167,9 +158,8 @@ class User < ApplicationRecord @display_name ||= ldap_entry[:display_name] end - - def avatar_base64(variant: :large) - Base64.strict_encode64 avatar.variant(variant).processed.download + def avatar + @avatar_base64 ||= LdapManager::FetchAvatar.call(cn: cn, ou: ou) end def services_enabled @@ -202,14 +192,14 @@ class User < ApplicationRecord end 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") end 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") end end diff --git a/app/services/ldap_manager/fetch_avatar.rb b/app/services/ldap_manager/fetch_avatar.rb new file mode 100644 index 0000000..03e3fd8 --- /dev/null +++ b/app/services/ldap_manager/fetch_avatar.rb @@ -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 diff --git a/app/services/ldap_manager/update_avatar.rb b/app/services/ldap_manager/update_avatar.rb index aa3f437..f26b92c 100644 --- a/app/services/ldap_manager/update_avatar.rb +++ b/app/services/ldap_manager/update_avatar.rb @@ -1,12 +1,27 @@ +require "image_processing/vips" + module LdapManager class UpdateAvatar < LdapManagerService - def initialize(dn, img_data) + def initialize(dn, file) @dn = dn - @img_data = img_data + @img_data = process(file) end def call replace_attribute @dn, :jpegPhoto, @img_data 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 diff --git a/app/services/ldap_manager_service.rb b/app/services/ldap_manager_service.rb index 0f43e32..c7a2599 100644 --- a/app/services/ldap_manager_service.rb +++ b/app/services/ldap_manager_service.rb @@ -1,2 +1,5 @@ class LdapManagerService < LdapService + def suffix + @suffix ||= ENV["LDAP_SUFFIX"] || "dc=kosmos,dc=org" + end end diff --git a/app/views/admin/users/show.html.erb b/app/views/admin/users/show.html.erb index 9a95e23..b4c3a45 100644 --- a/app/views/admin/users/show.html.erb +++ b/app/views/admin/users/show.html.erb @@ -63,6 +63,10 @@
+

LDAP

+

+ +

diff --git a/app/views/settings/_profile.html.erb b/app/views/settings/_profile.html.erb index 910ad1e..04d3546 100644 --- a/app/views/settings/_profile.html.erb +++ b/app/views/settings/_profile.html.erb @@ -39,9 +39,9 @@ Default profile picture

- <% if current_user.avatar.attached? %> + <% if current_user.avatar.present? %>

- <%= 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" %>

<% end %>