mirror of
https://github.com/9p4/jellyfin-plugin-sso.git
synced 2026-09-19 13:12:19 +00:00
Added AvatarFormatUrl to programatically set avatar on SSO Connect with OIDC
This commit is contained in:
@@ -1,6 +1,8 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Net.Http;
|
||||||
using System.Net.Mime;
|
using System.Net.Mime;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
@@ -12,14 +14,15 @@ using Jellyfin.Plugin.SSO_Auth.Config;
|
|||||||
using Jellyfin.Plugin.SSO_Auth.Helpers;
|
using Jellyfin.Plugin.SSO_Auth.Helpers;
|
||||||
using MediaBrowser.Common.Api;
|
using MediaBrowser.Common.Api;
|
||||||
using MediaBrowser.Controller.Authentication;
|
using MediaBrowser.Controller.Authentication;
|
||||||
|
using MediaBrowser.Controller.Configuration;
|
||||||
using MediaBrowser.Controller.Library;
|
using MediaBrowser.Controller.Library;
|
||||||
using MediaBrowser.Controller.Net;
|
using MediaBrowser.Controller.Net;
|
||||||
|
using MediaBrowser.Controller.Providers;
|
||||||
using MediaBrowser.Controller.Session;
|
using MediaBrowser.Controller.Session;
|
||||||
using MediaBrowser.Model.Cryptography;
|
using MediaBrowser.Model.Cryptography;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.AspNetCore.Routing;
|
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
@@ -38,6 +41,8 @@ public class SSOController : ControllerBase
|
|||||||
private readonly IAuthorizationContext _authContext;
|
private readonly IAuthorizationContext _authContext;
|
||||||
private readonly ILogger<SSOController> _logger;
|
private readonly ILogger<SSOController> _logger;
|
||||||
private readonly ICryptoProvider _cryptoProvider;
|
private readonly ICryptoProvider _cryptoProvider;
|
||||||
|
private readonly IProviderManager _providerManager;
|
||||||
|
private readonly IServerConfigurationManager _serverConfigurationManager;
|
||||||
private static readonly IDictionary<string, TimedAuthorizeState> StateManager = new Dictionary<string, TimedAuthorizeState>();
|
private static readonly IDictionary<string, TimedAuthorizeState> StateManager = new Dictionary<string, TimedAuthorizeState>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -48,13 +53,24 @@ public class SSOController : ControllerBase
|
|||||||
/// <param name="authContext">Instance of the <see cref="IAuthorizationContext"/> interface.</param>
|
/// <param name="authContext">Instance of the <see cref="IAuthorizationContext"/> interface.</param>
|
||||||
/// <param name="userManager">Instance of the <see cref="IUserManager"/> interface.</param>
|
/// <param name="userManager">Instance of the <see cref="IUserManager"/> interface.</param>
|
||||||
/// <param name="cryptoProvider">Instance of the <see cref="ICryptoProvider"/> interface.</param>
|
/// <param name="cryptoProvider">Instance of the <see cref="ICryptoProvider"/> interface.</param>
|
||||||
public SSOController(ILogger<SSOController> logger, ISessionManager sessionManager, IUserManager userManager, IAuthorizationContext authContext, ICryptoProvider cryptoProvider)
|
/// <param name="providerManager">Instance of the <see cref="IProviderManager"/> interface.</param>
|
||||||
|
/// <param name="serverConfigurationManager">Instance of the <see cref="IServerConfigurationManager"/> interface.</param>
|
||||||
|
public SSOController(
|
||||||
|
ILogger<SSOController> logger,
|
||||||
|
ISessionManager sessionManager,
|
||||||
|
IUserManager userManager,
|
||||||
|
IAuthorizationContext authContext,
|
||||||
|
ICryptoProvider cryptoProvider,
|
||||||
|
IProviderManager providerManager,
|
||||||
|
IServerConfigurationManager serverConfigurationManager)
|
||||||
{
|
{
|
||||||
_sessionManager = sessionManager;
|
_sessionManager = sessionManager;
|
||||||
_userManager = userManager;
|
_userManager = userManager;
|
||||||
_authContext = authContext;
|
_authContext = authContext;
|
||||||
_cryptoProvider = cryptoProvider;
|
_cryptoProvider = cryptoProvider;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
|
_providerManager = providerManager;
|
||||||
|
_serverConfigurationManager = serverConfigurationManager;
|
||||||
_logger.LogInformation("SSO Controller initialized");
|
_logger.LogInformation("SSO Controller initialized");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,6 +133,13 @@ public class SSOController : ControllerBase
|
|||||||
StateManager[state].EnableLiveTv = config.EnableLiveTv;
|
StateManager[state].EnableLiveTv = config.EnableLiveTv;
|
||||||
StateManager[state].EnableLiveTvManagement = config.EnableLiveTvManagement;
|
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)
|
foreach (var claim in result.User.Claims)
|
||||||
{
|
{
|
||||||
if (claim.Type == (config.DefaultUsernameClaim?.Trim() ?? "preferred_username"))
|
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);
|
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);
|
.ConfigureAwait(false);
|
||||||
return Ok(authenticationResult);
|
return Ok(authenticationResult);
|
||||||
}
|
}
|
||||||
@@ -686,7 +709,7 @@ public class SSOController : ControllerBase
|
|||||||
|
|
||||||
Guid userId = await CreateCanonicalLinkAndUserIfNotExist("saml", provider, samlResponse.GetNameID());
|
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);
|
.ConfigureAwait(false);
|
||||||
return Ok(authenticationResult);
|
return Ok(authenticationResult);
|
||||||
}
|
}
|
||||||
@@ -1020,7 +1043,8 @@ public class SSOController : ControllerBase
|
|||||||
/// <param name="enableLiveTvAdmin">Determines whether live TV can be managed by this user.</param>
|
/// <param name="enableLiveTvAdmin">Determines whether live TV can be managed by this user.</param>
|
||||||
/// <param name="authResponse">The client information to authenticate the user with.</param>
|
/// <param name="authResponse">The client information to authenticate the user with.</param>
|
||||||
/// <param name="defaultProvider">The default provider of the user to be set after logging in.</param>
|
/// <param name="defaultProvider">The default provider of the user to be set after logging in.</param>
|
||||||
private async Task<AuthenticationResult> Authenticate(Guid userId, bool isAdmin, bool enableAuthorization, bool enableAllFolders, string[] enabledFolders, bool enableLiveTv, bool enableLiveTvAdmin, AuthResponse authResponse, string defaultProvider)
|
/// <param name="avatarUrl">The new avatar url for the user.</param>
|
||||||
|
private async Task<AuthenticationResult> 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);
|
User user = _userManager.GetUserById(userId);
|
||||||
if (enableAuthorization)
|
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.EnableLiveTvAccess, enableLiveTv);
|
||||||
user.SetPermission(PermissionKind.EnableLiveTvManagement, enableLiveTvAdmin);
|
user.SetPermission(PermissionKind.EnableLiveTvManagement, enableLiveTvAdmin);
|
||||||
|
|
||||||
@@ -1150,6 +1204,7 @@ public class TimedAuthorizeState
|
|||||||
IsLinking = false;
|
IsLinking = false;
|
||||||
EnableLiveTv = false;
|
EnableLiveTv = false;
|
||||||
EnableLiveTvManagement = false;
|
EnableLiveTvManagement = false;
|
||||||
|
AvatarURL = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -1197,4 +1252,9 @@ public class TimedAuthorizeState
|
|||||||
/// Gets or sets a value indicating whether the user is allowed to manage live TV.
|
/// Gets or sets a value indicating whether the user is allowed to manage live TV.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool EnableLiveTvManagement { get; set; }
|
public bool EnableLiveTvManagement { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or set the user avatar url.
|
||||||
|
/// </summary>
|
||||||
|
public string AvatarURL { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -293,6 +293,11 @@ public class OidConfig
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public string DefaultUsernameClaim { get; set; }
|
public string DefaultUsernameClaim { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the URL format of the new user avatar.
|
||||||
|
/// </summary>
|
||||||
|
public string AvatarUrlFormat { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a value indicating whether HTTPS in the discovery endpoint is required.
|
/// Gets or sets a value indicating whether HTTPS in the discovery endpoint is required.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -559,6 +559,24 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="inputContainer">
|
||||||
|
<label
|
||||||
|
class="inputLabel inputLabelUnfocused"
|
||||||
|
for="AvatarUrlFormat"
|
||||||
|
>Set avatar url format</label
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
is="emby-input"
|
||||||
|
id="AvatarUrlFormat"
|
||||||
|
type="text"
|
||||||
|
class="sso-text"
|
||||||
|
/>
|
||||||
|
<div class="fieldDescription">
|
||||||
|
The url of the avatar with sso variable format:
|
||||||
|
example : <code>https://example.com/@{user_id}.png</code>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="checkboxContainer">
|
<div class="checkboxContainer">
|
||||||
<label>
|
<label>
|
||||||
<input
|
<input
|
||||||
|
|||||||
Reference in New Issue
Block a user