From 95d2c36e2cd94b75c14aa7a4e6e1b4245f4c1377 Mon Sep 17 00:00:00 2001 From: Evann Regnault Date: Sun, 4 Aug 2024 03:00:06 +0200 Subject: [PATCH] Added AvatarFormatUrl to programatically set avatar on SSO Connect with OIDC --- SSO-Auth/Api/SSOController.cs | 70 ++++++++++++++++++++++++-- SSO-Auth/Config/PluginConfiguration.cs | 5 ++ SSO-Auth/Config/configPage.html | 18 +++++++ 3 files changed, 88 insertions(+), 5 deletions(-) diff --git a/SSO-Auth/Api/SSOController.cs b/SSO-Auth/Api/SSOController.cs index 5c055bc..f5c53b7 100644 --- a/SSO-Auth/Api/SSOController.cs +++ b/SSO-Auth/Api/SSOController.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; +using System.Net.Http; using System.Net.Mime; using System.Security.Cryptography; using System.Text.RegularExpressions; @@ -12,14 +14,15 @@ using Jellyfin.Plugin.SSO_Auth.Config; using Jellyfin.Plugin.SSO_Auth.Helpers; using MediaBrowser.Common.Api; using MediaBrowser.Controller.Authentication; +using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Net; +using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Session; using MediaBrowser.Model.Cryptography; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.Logging; using Newtonsoft.Json; using Newtonsoft.Json.Linq; @@ -38,6 +41,8 @@ public class SSOController : ControllerBase private readonly IAuthorizationContext _authContext; private readonly ILogger _logger; private readonly ICryptoProvider _cryptoProvider; + private readonly IProviderManager _providerManager; + private readonly IServerConfigurationManager _serverConfigurationManager; private static readonly IDictionary StateManager = new Dictionary(); /// @@ -48,13 +53,24 @@ public class SSOController : ControllerBase /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. - public SSOController(ILogger logger, ISessionManager sessionManager, IUserManager userManager, IAuthorizationContext authContext, ICryptoProvider cryptoProvider) + /// Instance of the interface. + /// Instance of the interface. + public SSOController( + ILogger logger, + ISessionManager sessionManager, + IUserManager userManager, + IAuthorizationContext authContext, + ICryptoProvider cryptoProvider, + IProviderManager providerManager, + IServerConfigurationManager serverConfigurationManager) { _sessionManager = sessionManager; _userManager = userManager; _authContext = authContext; _cryptoProvider = cryptoProvider; _logger = logger; + _providerManager = providerManager; + _serverConfigurationManager = serverConfigurationManager; _logger.LogInformation("SSO Controller initialized"); } @@ -117,6 +133,13 @@ public class SSOController : ControllerBase StateManager[state].EnableLiveTv = config.EnableLiveTv; StateManager[state].EnableLiveTvManagement = config.EnableLiveTvManagement; + if (config.AvatarUrlFormat is not null) + { + StateManager[state].AvatarURL = result.User.Claims.Aggregate( + config.AvatarUrlFormat, + (s, claim) => s.Contains($"@{{{claim.Type}}}") ? s.Replace($"@{{{claim.Type}}}", claim.Value) : s); + } + foreach (var claim in result.User.Claims) { if (claim.Type == (config.DefaultUsernameClaim?.Trim() ?? "preferred_username")) @@ -421,7 +444,7 @@ public class SSOController : ControllerBase { Guid userId = await CreateCanonicalLinkAndUserIfNotExist("oid", provider, kvp.Value.Username); - var authenticationResult = await Authenticate(userId, kvp.Value.Admin, config.EnableAuthorization, config.EnableAllFolders, kvp.Value.Folders.ToArray(), kvp.Value.EnableLiveTv, kvp.Value.EnableLiveTvManagement, response, config.DefaultProvider?.Trim()) + var authenticationResult = await Authenticate(userId, kvp.Value.Admin, config.EnableAuthorization, config.EnableAllFolders, kvp.Value.Folders.ToArray(), kvp.Value.EnableLiveTv, kvp.Value.EnableLiveTvManagement, response, config.DefaultProvider?.Trim(), kvp.Value.AvatarURL) .ConfigureAwait(false); return Ok(authenticationResult); } @@ -686,7 +709,7 @@ public class SSOController : ControllerBase Guid userId = await CreateCanonicalLinkAndUserIfNotExist("saml", provider, samlResponse.GetNameID()); - var authenticationResult = await Authenticate(userId, isAdmin, config.EnableAuthorization, config.EnableAllFolders, folders.ToArray(), liveTv, liveTvManagement, response, config.DefaultProvider?.Trim()) + var authenticationResult = await Authenticate(userId, isAdmin, config.EnableAuthorization, config.EnableAllFolders, folders.ToArray(), liveTv, liveTvManagement, response, config.DefaultProvider?.Trim(), null) .ConfigureAwait(false); return Ok(authenticationResult); } @@ -1020,7 +1043,8 @@ public class SSOController : ControllerBase /// Determines whether live TV can be managed by this user. /// The client information to authenticate the user with. /// The default provider of the user to be set after logging in. - private async Task Authenticate(Guid userId, bool isAdmin, bool enableAuthorization, bool enableAllFolders, string[] enabledFolders, bool enableLiveTv, bool enableLiveTvAdmin, AuthResponse authResponse, string defaultProvider) + /// The new avatar url for the user. + private async Task Authenticate(Guid userId, bool isAdmin, bool enableAuthorization, bool enableAllFolders, string[] enabledFolders, bool enableLiveTv, bool enableLiveTvAdmin, AuthResponse authResponse, string defaultProvider, string avatarUrl) { User user = _userManager.GetUserById(userId); if (enableAuthorization) @@ -1033,6 +1057,36 @@ public class SSOController : ControllerBase } } + if (avatarUrl is not null) + { + try + { + using var client = new HttpClient(); + var extension = avatarUrl.Split(".").Last(); + var stream = await client.GetStreamAsync(avatarUrl); + if (user != null) + { + var userDataPath = + Path.Combine( + _serverConfigurationManager.ApplicationPaths.UserConfigurationDirectoryPath, + user.Username); + if (user.ProfileImage is not null) + { + await _userManager.ClearProfileImageAsync(user).ConfigureAwait(false); + } + + user.ProfileImage = new ImageInfo(Path.Combine(userDataPath, "profile" + extension)); + + await _providerManager.SaveImage(stream, "image/" + extension, user.ProfileImage.Path) + .ConfigureAwait(false); + } + } + catch (Exception e) + { + _logger.LogError(e.Message); + } + } + user.SetPermission(PermissionKind.EnableLiveTvAccess, enableLiveTv); user.SetPermission(PermissionKind.EnableLiveTvManagement, enableLiveTvAdmin); @@ -1150,6 +1204,7 @@ public class TimedAuthorizeState IsLinking = false; EnableLiveTv = false; EnableLiveTvManagement = false; + AvatarURL = null; } /// @@ -1197,4 +1252,9 @@ public class TimedAuthorizeState /// Gets or sets a value indicating whether the user is allowed to manage live TV. /// public bool EnableLiveTvManagement { get; set; } + + /// + /// Gets or set the user avatar url. + /// + public string AvatarURL { get; set; } } diff --git a/SSO-Auth/Config/PluginConfiguration.cs b/SSO-Auth/Config/PluginConfiguration.cs index 767ed2e..601fcfa 100644 --- a/SSO-Auth/Config/PluginConfiguration.cs +++ b/SSO-Auth/Config/PluginConfiguration.cs @@ -293,6 +293,11 @@ public class OidConfig /// public string DefaultUsernameClaim { get; set; } + /// + /// Gets or sets the URL format of the new user avatar. + /// + public string AvatarUrlFormat { get; set; } + /// /// Gets or sets a value indicating whether HTTPS in the discovery endpoint is required. /// diff --git a/SSO-Auth/Config/configPage.html b/SSO-Auth/Config/configPage.html index 7aa1f56..965ef04 100644 --- a/SSO-Auth/Config/configPage.html +++ b/SSO-Auth/Config/configPage.html @@ -559,6 +559,24 @@ +
+ + +
+ The url of the avatar with sso variable format: + example : https://example.com/@{user_id}.png +
+
+