aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Jellyfin.Server.Implementations/Users/DefaultPasswordResetProvider.cs40
-rw-r--r--Jellyfin.Server.Implementations/Users/UserManager.cs24
-rw-r--r--MediaBrowser.Controller/Authentication/IPasswordResetProvider.cs5
-rw-r--r--MediaBrowser.Model/Users/ForgotPasswordAction.cs4
4 files changed, 41 insertions, 32 deletions
diff --git a/Jellyfin.Server.Implementations/Users/DefaultPasswordResetProvider.cs b/Jellyfin.Server.Implementations/Users/DefaultPasswordResetProvider.cs
index f20fb2d92..49a9fda94 100644
--- a/Jellyfin.Server.Implementations/Users/DefaultPasswordResetProvider.cs
+++ b/Jellyfin.Server.Implementations/Users/DefaultPasswordResetProvider.cs
@@ -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
};
}
diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs
index d0b41a7f6..b534ccd1b 100644
--- a/Jellyfin.Server.Implementations/Users/UserManager.cs
+++ b/Jellyfin.Server.Implementations/Users/UserManager.cs
@@ -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];
}
diff --git a/MediaBrowser.Controller/Authentication/IPasswordResetProvider.cs b/MediaBrowser.Controller/Authentication/IPasswordResetProvider.cs
index 592ce9955..36cd5c5d1 100644
--- a/MediaBrowser.Controller/Authentication/IPasswordResetProvider.cs
+++ b/MediaBrowser.Controller/Authentication/IPasswordResetProvider.cs
@@ -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; }
diff --git a/MediaBrowser.Model/Users/ForgotPasswordAction.cs b/MediaBrowser.Model/Users/ForgotPasswordAction.cs
index f198476e3..55907e6c8 100644
--- a/MediaBrowser.Model/Users/ForgotPasswordAction.cs
+++ b/MediaBrowser.Model/Users/ForgotPasswordAction.cs
@@ -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
}
}