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