Backport pull request #15254 from jellyfin/release-10.11.z

Update password reset to always return the same response structure

Original-merge: 4ad3141875

Merged-by: crobibero <cody@robibe.ro>

Backported-by: Joshua M. Boniface <joshua@boniface.me>
This commit is contained in:
thornbill 2025-11-02 21:58:42 -05:00 committed by Joshua M. Boniface
parent 4258df4485
commit 1ccd10863e
4 changed files with 41 additions and 32 deletions

View File

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Security.Cryptography;
using System.Text.Json;
@ -92,33 +93,38 @@ namespace Jellyfin.Server.Implementations.Users
}
/// <inheritdoc />
public async Task<ForgotPasswordResult> StartForgotPasswordProcess(User user, bool isInNetwork)
public async Task<ForgotPasswordResult> StartForgotPasswordProcess(User? user, string enteredUsername, bool isInNetwork)
{
byte[] bytes = new byte[4];
RandomNumberGenerator.Fill(bytes);
string pin = BitConverter.ToString(bytes);
DateTime expireTime = DateTime.UtcNow.AddMinutes(30);
string filePath = _passwordResetFileBase + user.Id + ".json";
SerializablePasswordReset spr = new SerializablePasswordReset
{
ExpirationDate = expireTime,
Pin = pin,
PinFile = filePath,
UserName = user.Username
};
var usernameHash = enteredUsername.ToUpperInvariant().GetMD5().ToString("N", CultureInfo.InvariantCulture);
var pinFile = _passwordResetFileBase + usernameHash + ".json";
FileStream fileStream = AsyncFile.Create(filePath);
await using (fileStream.ConfigureAwait(false))
if (user is not null && isInNetwork)
{
await JsonSerializer.SerializeAsync(fileStream, spr).ConfigureAwait(false);
byte[] bytes = new byte[4];
RandomNumberGenerator.Fill(bytes);
string pin = BitConverter.ToString(bytes);
SerializablePasswordReset spr = new SerializablePasswordReset
{
ExpirationDate = expireTime,
Pin = pin,
PinFile = pinFile,
UserName = user.Username
};
FileStream fileStream = AsyncFile.Create(pinFile);
await using (fileStream.ConfigureAwait(false))
{
await JsonSerializer.SerializeAsync(fileStream, spr).ConfigureAwait(false);
}
}
return new ForgotPasswordResult
{
Action = ForgotPasswordAction.PinCode,
PinExpirationDate = expireTime,
PinFile = filePath
PinFile = pinFile
};
}

View File

@ -508,23 +508,18 @@ namespace Jellyfin.Server.Implementations.Users
public async Task<ForgotPasswordResult> StartForgotPasswordProcess(string enteredUsername, bool isInNetwork)
{
var user = string.IsNullOrWhiteSpace(enteredUsername) ? null : GetUserByName(enteredUsername);
var passwordResetProvider = GetPasswordResetProvider(user);
var result = await passwordResetProvider
.StartForgotPasswordProcess(user, enteredUsername, isInNetwork)
.ConfigureAwait(false);
if (user is not null && isInNetwork)
{
var passwordResetProvider = GetPasswordResetProvider(user);
var result = await passwordResetProvider
.StartForgotPasswordProcess(user, isInNetwork)
.ConfigureAwait(false);
await UpdateUserAsync(user).ConfigureAwait(false);
return result;
}
return new ForgotPasswordResult
{
Action = ForgotPasswordAction.InNetworkRequired,
PinFile = string.Empty
};
return result;
}
/// <inheritdoc/>
@ -760,8 +755,13 @@ namespace Jellyfin.Server.Implementations.Users
return GetAuthenticationProviders(user)[0];
}
private IPasswordResetProvider GetPasswordResetProvider(User user)
private IPasswordResetProvider GetPasswordResetProvider(User? user)
{
if (user is null)
{
return _defaultPasswordResetProvider;
}
return GetPasswordResetProviders(user)[0];
}

View File

@ -1,5 +1,3 @@
#nullable disable
#pragma warning disable CS1591
using System;
@ -15,11 +13,12 @@ namespace MediaBrowser.Controller.Authentication
bool IsEnabled { get; }
Task<ForgotPasswordResult> StartForgotPasswordProcess(User user, bool isInNetwork);
Task<ForgotPasswordResult> StartForgotPasswordProcess(User? user, string enteredUsername, bool isInNetwork);
Task<PinRedeemResult> RedeemPasswordResetPin(string pin);
}
#nullable disable
public class PasswordPinCreationResult
{
public string PinFile { get; set; }

View File

@ -1,11 +1,15 @@
#pragma warning disable CS1591
using System;
namespace MediaBrowser.Model.Users
{
public enum ForgotPasswordAction
{
[Obsolete("Returning different actions represents a security concern.")]
ContactAdmin = 0,
PinCode = 1,
[Obsolete("Returning different actions represents a security concern.")]
InNetworkRequired = 2
}
}