The LDAP attribute for the user password; only required if Allow Password Change is enabled.
+
@@ -230,6 +243,8 @@
${HeaderLibraryAccess}
txtLdapUidAttribute: document.querySelector("#txtLdapUidAttribute"),
txtLdapUsernameAttribute: document.querySelector("#txtLdapUsernameAttribute"),
txtLdapPasswordAttribute: document.querySelector("#txtLdapPasswordAttribute"),
+ chkEnableProfileImageSync: document.querySelector("#chkEnableProfileImageSync"),
+ txtLdapProfileImageAttribute: document.querySelector("#txtLdapProfileImageAttribute"),
chkEnableAllFolders: document.querySelector('#chkEnableAllFolders'),
folderAccessList: document.querySelector('.folderAccess'),
txtPasswordResetUrl: document.querySelector("#txtLdapPasswordResetUrl")
@@ -264,6 +279,8 @@
${HeaderLibraryAccess}
LdapConfigurationPage.txtLdapUidAttribute.value = config.LdapUidAttribute;
LdapConfigurationPage.txtLdapUsernameAttribute.value = config.LdapUsernameAttribute;
LdapConfigurationPage.txtLdapPasswordAttribute.value = config.LdapPasswordAttribute;
+ LdapConfigurationPage.chkEnableProfileImageSync.checked = config.EnableLdapProfileImageSync;
+ LdapConfigurationPage.txtLdapProfileImageAttribute.value = config.LdapProfileImageAttribute;
config.EnableAllFolders = config.EnableAllFolders || false;
LdapConfigurationPage.chkEnableAllFolders.checked = config.EnableAllFolders;
/* Default to empty array if Enabled Folders is not set */
@@ -351,6 +368,8 @@
${HeaderLibraryAccess}
config.LdapUidAttribute = LdapConfigurationPage.txtLdapUidAttribute.value;
config.LdapUsernameAttribute = LdapConfigurationPage.txtLdapUsernameAttribute.value;
config.LdapPasswordAttribute = LdapConfigurationPage.txtLdapPasswordAttribute.value;
+ config.EnableLdapProfileImageSync = LdapConfigurationPage.chkEnableProfileImageSync.checked;
+ config.LdapProfileImageAttribute = LdapConfigurationPage.txtLdapProfileImageAttribute.value;
/* Map the set of checked input items to an array of library Id's */
config.EnableAllFolders = LdapConfigurationPage.chkEnableAllFolders.checked || false;
let folders = document.querySelectorAll('#folderList input');
diff --git a/LDAP-Auth/Helpers/ProfileImageUpdater.cs b/LDAP-Auth/Helpers/ProfileImageUpdater.cs
new file mode 100644
index 0000000..36ad96f
--- /dev/null
+++ b/LDAP-Auth/Helpers/ProfileImageUpdater.cs
@@ -0,0 +1,38 @@
+using System.IO;
+using System.Net.Mime;
+using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Providers;
+
+namespace Jellyfin.Plugin.LDAP_Auth.Helpers
+{
+ ///
+ /// Provides utility methods to update the profile image of a user.
+ ///
+ public static class ProfileImageUpdater
+ {
+ ///
+ /// Sets the profile image of a user to the provided image.
+ ///
+ ///
The user to update.
+ ///
The data representing the profile image to set.
+ ///
Instance of the
interface, used to retrieve the path to save the profile picture.
+ ///
Instance of the
interface, used to save the profile picture.
+ ///
A representing the asynchronous operation.
+ public static async Task SetProfileImage(
+ User user,
+ byte[] ldapProfileImage,
+ IServerConfigurationManager serverConfigurationManager,
+ IProviderManager providerManager)
+ {
+ var userDataPath = Path.Combine(serverConfigurationManager.ApplicationPaths.UserConfigurationDirectoryPath, user.Username);
+ user.ProfileImage = new ImageInfo(Path.Combine(userDataPath, "profile.jpg"));
+
+ using var profileImageMemoryStream = new MemoryStream(ldapProfileImage);
+ await providerManager
+ .SaveImage(profileImageMemoryStream, MediaTypeNames.Image.Jpeg, user.ProfileImage.Path)
+ .ConfigureAwait(false);
+ }
+ }
+}
diff --git a/LDAP-Auth/LDAPAuthenticationProviderPlugin.cs b/LDAP-Auth/LDAPAuthenticationProviderPlugin.cs
index 319331c..2d87b1c 100644
--- a/LDAP-Auth/LDAPAuthenticationProviderPlugin.cs
+++ b/LDAP-Auth/LDAPAuthenticationProviderPlugin.cs
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Net.Security;
+using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;
@@ -12,7 +13,9 @@
using Jellyfin.Plugin.LDAP_Auth.Helpers;
using MediaBrowser.Common;
using MediaBrowser.Controller.Authentication;
+using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Users;
using Microsoft.Extensions.Logging;
using Novell.Directory.Ldap;
@@ -44,6 +47,10 @@ public LdapAuthenticationProviderPlugin(IApplicationHost applicationHost, ILogge
private string UsernameAttr => LdapPlugin.Instance.Configuration.LdapUsernameAttribute;
+ private bool EnableProfileImageSync => LdapPlugin.Instance.Configuration.EnableLdapProfileImageSync;
+
+ private string ProfileImageAttr => LdapPlugin.Instance.Configuration.LdapProfileImageAttribute;
+
private string SearchFilter => LdapPlugin.Instance.Configuration.LdapSearchFilter;
private string AdminFilter => LdapPlugin.Instance.Configuration.LdapAdminFilter;
@@ -105,7 +112,7 @@ public async Task
Authenticate(string username, st
else
{
// Add the user to our Ldap users
- LdapPlugin.Instance.Configuration.AddUser(user.Id, ldapUid);
+ LdapPlugin.Instance.Configuration.AddUser(user.Id, ldapUid, string.Empty);
LdapPlugin.Instance.SaveConfiguration();
}
}
@@ -192,10 +199,21 @@ public async Task Authenticate(string username, st
user.SetPreference(PreferenceKind.EnabledFolders, LdapPlugin.Instance.Configuration.EnabledFolders);
}
+ var providerManager = _applicationHost.Resolve();
+ var serverConfigurationManager = _applicationHost.Resolve();
+ var ldapProfileImage = GetAttribute(ldapUser, ProfileImageAttr)?.ByteValue;
+ var ldapProfileImageHash = string.Empty;
+ if (ldapProfileImage is not null && EnableProfileImageSync)
+ {
+ ldapProfileImageHash = Convert.ToBase64String(MD5.HashData(ldapProfileImage));
+
+ await ProfileImageUpdater.SetProfileImage(user, ldapProfileImage, serverConfigurationManager, providerManager).ConfigureAwait(false);
+ }
+
await userManager.UpdateUserAsync(user).ConfigureAwait(false);
// Add the user to our Ldap users
- LdapPlugin.Instance.Configuration.AddUser(user.Id, ldapUid);
+ LdapPlugin.Instance.Configuration.AddUser(user.Id, ldapUid, ldapProfileImageHash);
LdapPlugin.Instance.SaveConfiguration();
}
else
@@ -455,7 +473,7 @@ public LdapEntry LocateLdapUser(string username)
LdapPlugin.Instance.Configuration.LdapBaseDn,
LdapConnection.ScopeSub,
realSearchFilter,
- new[] { UsernameAttr, UidAttr },
+ new[] { UsernameAttr, UidAttr, ProfileImageAttr },
false);
}
catch (LdapException e)
@@ -512,7 +530,13 @@ public Task RedeemPasswordResetPin(string pin)
throw new NotImplementedException();
}
- private LdapAttribute GetAttribute(LdapEntry userEntry, string attr)
+ ///
+ /// Retrieves the requested attribute from a .
+ ///
+ /// The to retrieve the attribute from.
+ /// The attribute to retrieve from the .
+ /// The value of the or null if it does not exist.
+ public LdapAttribute GetAttribute(LdapEntry userEntry, string attr)
{
var attributeSet = userEntry.GetAttributeSet();
if (attributeSet.ContainsKey(attr))
diff --git a/LDAP-Auth/LdapProfileImageSyncTask.cs b/LDAP-Auth/LdapProfileImageSyncTask.cs
new file mode 100644
index 0000000..c055f6f
--- /dev/null
+++ b/LDAP-Auth/LdapProfileImageSyncTask.cs
@@ -0,0 +1,164 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Security.Cryptography;
+using System.Threading;
+using System.Threading.Tasks;
+using Jellyfin.Plugin.LDAP_Auth.Helpers;
+using MediaBrowser.Common;
+using MediaBrowser.Controller.Authentication;
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.Globalization;
+using MediaBrowser.Model.Tasks;
+using Microsoft.Extensions.Logging;
+using Novell.Directory.Ldap;
+
+namespace Jellyfin.Plugin.LDAP_Auth
+{
+ ///
+ /// Ldap Authentication Provider Plugin.
+ ///
+ public class LdapProfileImageSyncTask : IScheduledTask
+ {
+ private readonly ILocalizationManager _localization;
+ private readonly IApplicationHost _applicationHost;
+ private readonly ILogger _logger;
+ private readonly IUserManager _userManager;
+ private readonly IProviderManager _providerManager;
+ private readonly IServerConfigurationManager _serverConfigurationManager;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Instance of the interface.
+ /// Instance of the interface.
+ /// Instance of the interface.
+ /// Instance of the interface.
+ /// Instance of the interface.
+ /// Instance of the interface.
+ public LdapProfileImageSyncTask(
+ IApplicationHost applicationHost,
+ IUserManager userManager,
+ IProviderManager providerManager,
+ IServerConfigurationManager serverConfigurationManager,
+ ILogger logger,
+ ILocalizationManager localization)
+ {
+ _logger = logger;
+ _localization = localization;
+ _applicationHost = applicationHost;
+ _userManager = userManager;
+ _providerManager = providerManager;
+ _serverConfigurationManager = serverConfigurationManager;
+ }
+
+ private bool EnableProfileImageSync => LdapPlugin.Instance.Configuration.EnableLdapProfileImageSync;
+
+ private string ProfileImageAttr => LdapPlugin.Instance.Configuration.LdapProfileImageAttribute;
+
+ ///
+ public string Name => "LDAP - Synchronize profile images";
+
+ ///
+ public string Key => "LdapProfileImageSync";
+
+ ///
+ public string Description => "Synchronizes user profile images from LDAP.";
+
+ ///
+ public string Category => _localization.GetLocalizedString("TasksApplicationCategory");
+
+ ///
+ public async Task ExecuteAsync(IProgress progress, CancellationToken cancellationToken)
+ {
+ if (!EnableProfileImageSync)
+ {
+ _logger.LogDebug("Synchronizing profile images is deactivated");
+ return;
+ }
+
+ var ldapAuthProvider = _applicationHost.GetExports(false).First();
+ var updatePluginConfig = false;
+
+ foreach (var configUser in LdapPlugin.Instance.Configuration.GetAllLdapUsers())
+ {
+ var user = _userManager.GetUserById(configUser.LinkedJellyfinUserId);
+ LdapEntry ldapUser;
+ try
+ {
+ ldapUser = ldapAuthProvider.LocateLdapUser(configUser.LdapUid);
+ }
+ catch (AuthenticationException)
+ {
+ _logger.LogWarning("User '{configUser}' is not found in LDAP. Cannot synchronize profile image.", configUser.LdapUid);
+ continue;
+ }
+
+ var ldapProfileImage = ldapAuthProvider.GetAttribute(ldapUser, ProfileImageAttr)?.ByteValue;
+
+ if (ldapProfileImage is not null)
+ {
+ // Found a profile image in LDAP data. Check if image changed since last synchronization and update if so
+ var ldapProfileImageHash = Convert.ToBase64String(MD5.HashData(ldapProfileImage));
+
+ if (user.ProfileImage is null ||
+ !string.Equals(ldapProfileImageHash, configUser.ProfileImageHash, StringComparison.Ordinal))
+ {
+ _logger.LogDebug("Updating profile image for user {Username}", configUser.LdapUid);
+
+ if (user.ProfileImage is not null)
+ {
+ await _userManager.ClearProfileImageAsync(user).ConfigureAwait(false);
+ }
+
+ await ProfileImageUpdater.SetProfileImage(user, ldapProfileImage, _serverConfigurationManager, _providerManager).ConfigureAwait(false);
+ configUser.ProfileImageHash = ldapProfileImageHash;
+ updatePluginConfig = true;
+
+ await _userManager.UpdateUserAsync(user).ConfigureAwait(false);
+ }
+ }
+ else if (user.ProfileImage is not null)
+ {
+ // Did not find a profile image in LDAP data but user still has a profile image set. Reset it.
+ _logger.LogDebug("Removing profile image for user {Username}", configUser.LdapUid);
+
+ try
+ {
+ File.Delete(user.ProfileImage.Path);
+ }
+ catch (IOException e)
+ {
+ _logger.LogError(e, "Error deleting user profile image during LDAP user profile image update");
+ }
+
+ configUser.ProfileImageHash = string.Empty;
+ updatePluginConfig = true;
+
+ await _userManager.ClearProfileImageAsync(user).ConfigureAwait(false);
+ }
+ }
+
+ if (updatePluginConfig)
+ {
+ LdapPlugin.Instance.SaveConfiguration();
+ }
+ }
+
+ ///
+ public IEnumerable GetDefaultTriggers()
+ {
+ return new[]
+ {
+ new TaskTriggerInfo
+ {
+ Type = TaskTriggerInfo.TriggerInterval,
+ IntervalTicks = TimeSpan.FromHours(24).Ticks
+ }
+ };
+ }
+ }
+}
\ No newline at end of file