diff options
| author | BaronGreenback <jimcartlidge@yahoo.co.uk> | 2020-10-17 09:26:14 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-10-17 09:26:14 +0100 |
| commit | d42bb515ce3692abd9295008872c7f9d62b47652 (patch) | |
| tree | 53743242f3b34aeeb24334572e50d67dc9f92727 /Jellyfin.Server.Implementations/Users/DefaultPasswordResetProvider.cs | |
| parent | 75efb8f52d4234bfdefa2a0ac48f7261aa9ef58b (diff) | |
| parent | 86090ab1f65c66958c897cacd04221c537053eb3 (diff) | |
Merge branch 'master' into video-resolver
Diffstat (limited to 'Jellyfin.Server.Implementations/Users/DefaultPasswordResetProvider.cs')
| -rw-r--r-- | Jellyfin.Server.Implementations/Users/DefaultPasswordResetProvider.cs | 137 |
1 files changed, 137 insertions, 0 deletions
diff --git a/Jellyfin.Server.Implementations/Users/DefaultPasswordResetProvider.cs b/Jellyfin.Server.Implementations/Users/DefaultPasswordResetProvider.cs new file mode 100644 index 000000000..6cb13cd23 --- /dev/null +++ b/Jellyfin.Server.Implementations/Users/DefaultPasswordResetProvider.cs @@ -0,0 +1,137 @@ +#nullable enable + +using System; +using System.Collections.Generic; +using System.IO; +using System.Security.Cryptography; +using System.Text.Json; +using System.Threading.Tasks; +using Jellyfin.Data.Entities; +using MediaBrowser.Common; +using MediaBrowser.Common.Extensions; +using MediaBrowser.Controller.Authentication; +using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Library; +using MediaBrowser.Model.Users; + +namespace Jellyfin.Server.Implementations.Users +{ + /// <summary> + /// The default password reset provider. + /// </summary> + public class DefaultPasswordResetProvider : IPasswordResetProvider + { + private const string BaseResetFileName = "passwordreset"; + + private readonly IApplicationHost _appHost; + + private readonly string _passwordResetFileBase; + private readonly string _passwordResetFileBaseDir; + + /// <summary> + /// Initializes a new instance of the <see cref="DefaultPasswordResetProvider"/> class. + /// </summary> + /// <param name="configurationManager">The configuration manager.</param> + /// <param name="appHost">The application host.</param> + public DefaultPasswordResetProvider(IServerConfigurationManager configurationManager, IApplicationHost appHost) + { + _passwordResetFileBaseDir = configurationManager.ApplicationPaths.ProgramDataPath; + _passwordResetFileBase = Path.Combine(_passwordResetFileBaseDir, BaseResetFileName); + _appHost = appHost; + // TODO: Remove the circular dependency on UserManager + } + + /// <inheritdoc /> + public string Name => "Default Password Reset Provider"; + + /// <inheritdoc /> + public bool IsEnabled => true; + + /// <inheritdoc /> + public async Task<PinRedeemResult> RedeemPasswordResetPin(string pin) + { + var userManager = _appHost.Resolve<IUserManager>(); + var usersReset = new List<string>(); + foreach (var resetFile in Directory.EnumerateFiles(_passwordResetFileBaseDir, $"{BaseResetFileName}*")) + { + SerializablePasswordReset spr; + await using (var str = File.OpenRead(resetFile)) + { + spr = await JsonSerializer.DeserializeAsync<SerializablePasswordReset>(str).ConfigureAwait(false); + } + + if (spr.ExpirationDate < DateTime.UtcNow) + { + File.Delete(resetFile); + } + else if (string.Equals( + spr.Pin.Replace("-", string.Empty, StringComparison.Ordinal), + pin.Replace("-", string.Empty, StringComparison.Ordinal), + StringComparison.InvariantCultureIgnoreCase)) + { + var resetUser = userManager.GetUserByName(spr.UserName) + ?? throw new ResourceNotFoundException($"User with a username of {spr.UserName} not found"); + + await userManager.ChangePassword(resetUser, pin).ConfigureAwait(false); + usersReset.Add(resetUser.Username); + File.Delete(resetFile); + } + } + + if (usersReset.Count < 1) + { + throw new ResourceNotFoundException($"No Users found with a password reset request matching pin {pin}"); + } + + return new PinRedeemResult + { + Success = true, + UsersReset = usersReset.ToArray() + }; + } + + /// <inheritdoc /> + public async Task<ForgotPasswordResult> StartForgotPasswordProcess(User user, bool isInNetwork) + { + string pin; + using (var cryptoRandom = RandomNumberGenerator.Create()) + { + byte[] bytes = new byte[4]; + cryptoRandom.GetBytes(bytes); + 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 + }; + + await using (FileStream fileStream = File.OpenWrite(filePath)) + { + await JsonSerializer.SerializeAsync(fileStream, spr).ConfigureAwait(false); + await fileStream.FlushAsync().ConfigureAwait(false); + } + + user.EasyPassword = pin; + + return new ForgotPasswordResult + { + Action = ForgotPasswordAction.PinCode, + PinExpirationDate = expireTime, + }; + } + +#nullable disable + private class SerializablePasswordReset : PasswordPinCreationResult + { + public string Pin { get; set; } + + public string UserName { get; set; } + } + } +} |
