aboutsummaryrefslogtreecommitdiff
path: root/Jellyfin.Server.Implementations/Users/UserManager.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs')
-rw-r--r--Jellyfin.Server.Implementations/Users/UserManager.cs110
1 files changed, 49 insertions, 61 deletions
diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs
index d1de5408c..3d0a51ff6 100644
--- a/Jellyfin.Server.Implementations/Users/UserManager.cs
+++ b/Jellyfin.Server.Implementations/Users/UserManager.cs
@@ -5,7 +5,6 @@ using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
-using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Jellyfin.Data.Entities;
@@ -13,7 +12,6 @@ using Jellyfin.Data.Enums;
using Jellyfin.Data.Events;
using Jellyfin.Data.Events.Users;
using MediaBrowser.Common;
-using MediaBrowser.Common.Cryptography;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Authentication;
@@ -137,17 +135,20 @@ namespace Jellyfin.Server.Implementations.Users
throw new ArgumentNullException(nameof(user));
}
- if (string.IsNullOrWhiteSpace(newName))
- {
- throw new ArgumentException("Invalid username", nameof(newName));
- }
+ ThrowIfInvalidUsername(newName);
if (user.Username.Equals(newName, StringComparison.Ordinal))
{
throw new ArgumentException("The new and old names must be different.");
}
- if (Users.Any(u => u.Id != user.Id && u.Username.Equals(newName, StringComparison.Ordinal)))
+ await using var dbContext = _dbProvider.CreateContext();
+
+ if (await dbContext.Users
+ .AsQueryable()
+ .Where(u => u.Username == newName && u.Id != user.Id)
+ .AnyAsync()
+ .ConfigureAwait(false))
{
throw new ArgumentException(string.Format(
CultureInfo.InvariantCulture,
@@ -161,15 +162,6 @@ namespace Jellyfin.Server.Implementations.Users
}
/// <inheritdoc/>
- public void UpdateUser(User user)
- {
- using var dbContext = _dbProvider.CreateContext();
- dbContext.Users.Update(user);
- _users[user.Id] = user;
- dbContext.SaveChanges();
- }
-
- /// <inheritdoc/>
public async Task UpdateUserAsync(User user)
{
await using var dbContext = _dbProvider.CreateContext();
@@ -187,12 +179,15 @@ namespace Jellyfin.Server.Implementations.Users
var user = new User(
name,
- _defaultAuthenticationProvider.GetType().FullName,
- _defaultPasswordResetProvider.GetType().FullName)
+ _defaultAuthenticationProvider.GetType().FullName!,
+ _defaultPasswordResetProvider.GetType().FullName!)
{
InternalId = max + 1
};
+ user.AddDefaultPermissions();
+ user.AddDefaultPreferences();
+
_users.Add(user.Id, user);
return user;
@@ -201,9 +196,14 @@ namespace Jellyfin.Server.Implementations.Users
/// <inheritdoc/>
public async Task<User> CreateUserAsync(string name)
{
- if (!IsValidUsername(name))
+ ThrowIfInvalidUsername(name);
+
+ if (Users.Any(u => u.Username.Equals(name, StringComparison.OrdinalIgnoreCase)))
{
- throw new ArgumentException("Usernames can contain unicode symbols, numbers (0-9), dashes (-), underscores (_), apostrophes ('), and periods (.)");
+ throw new ArgumentException(string.Format(
+ CultureInfo.InvariantCulture,
+ "A user with the name '{0}' already exists.",
+ name));
}
await using var dbContext = _dbProvider.CreateContext();
@@ -246,16 +246,6 @@ namespace Jellyfin.Server.Implementations.Users
}
await using var dbContext = _dbProvider.CreateContext();
-
- // Clear all entities related to the user from the database.
- if (user.ProfileImage != null)
- {
- dbContext.Remove(user.ProfileImage);
- }
-
- dbContext.RemoveRange(user.Permissions);
- dbContext.RemoveRange(user.Preferences);
- dbContext.RemoveRange(user.AccessSchedules);
dbContext.Users.Remove(user);
await dbContext.SaveChangesAsync().ConfigureAwait(false);
_users.Remove(userId);
@@ -270,9 +260,9 @@ namespace Jellyfin.Server.Implementations.Users
}
/// <inheritdoc/>
- public void ResetEasyPassword(User user)
+ public Task ResetEasyPassword(User user)
{
- ChangeEasyPassword(user, string.Empty, null);
+ return ChangeEasyPassword(user, string.Empty, null);
}
/// <inheritdoc/>
@@ -290,7 +280,7 @@ namespace Jellyfin.Server.Implementations.Users
}
/// <inheritdoc/>
- public void ChangeEasyPassword(User user, string newPassword, string? newPasswordSha1)
+ public async Task ChangeEasyPassword(User user, string newPassword, string? newPasswordSha1)
{
if (newPassword != null)
{
@@ -303,7 +293,7 @@ namespace Jellyfin.Server.Implementations.Users
}
user.EasyPassword = newPasswordSha1;
- UpdateUser(user);
+ await UpdateUserAsync(user).ConfigureAwait(false);
_eventManager.Publish(new UserPasswordChangedEventArgs(user));
}
@@ -402,27 +392,18 @@ namespace Jellyfin.Server.Implementations.Users
}
var user = Users.FirstOrDefault(i => string.Equals(username, i.Username, StringComparison.OrdinalIgnoreCase));
- bool success;
- IAuthenticationProvider? authenticationProvider;
+ var authResult = await AuthenticateLocalUser(username, password, user, remoteEndPoint)
+ .ConfigureAwait(false);
+ var authenticationProvider = authResult.authenticationProvider;
+ var success = authResult.success;
- if (user != null)
- {
- var authResult = await AuthenticateLocalUser(username, password, user, remoteEndPoint)
- .ConfigureAwait(false);
- authenticationProvider = authResult.authenticationProvider;
- success = authResult.success;
- }
- else
+ if (user == null)
{
- var authResult = await AuthenticateLocalUser(username, password, null, remoteEndPoint)
- .ConfigureAwait(false);
- authenticationProvider = authResult.authenticationProvider;
string updatedUsername = authResult.username;
- success = authResult.success;
if (success
&& authenticationProvider != null
- && !(authenticationProvider is DefaultAuthenticationProvider))
+ && authenticationProvider is not DefaultAuthenticationProvider)
{
// Trust the username returned by the authentication provider
username = updatedUsername;
@@ -442,7 +423,7 @@ namespace Jellyfin.Server.Implementations.Users
{
var providerId = authenticationProvider.GetType().FullName;
- if (!string.Equals(providerId, user.AuthenticationProviderId, StringComparison.OrdinalIgnoreCase))
+ if (providerId != null && !string.Equals(providerId, user.AuthenticationProviderId, StringComparison.OrdinalIgnoreCase))
{
user.AuthenticationProviderId = providerId;
await UpdateUserAsync(user).ConfigureAwait(false);
@@ -547,11 +528,7 @@ namespace Jellyfin.Server.Implementations.Users
}
}
- return new PinRedeemResult
- {
- Success = false,
- UsersReset = Array.Empty<string>()
- };
+ return new PinRedeemResult();
}
/// <inheritdoc />
@@ -718,6 +695,11 @@ namespace Jellyfin.Server.Implementations.Users
/// <inheritdoc/>
public async Task ClearProfileImageAsync(User user)
{
+ if (user.ProfileImage == null)
+ {
+ return;
+ }
+
await using var dbContext = _dbProvider.CreateContext();
dbContext.Remove(user.ProfileImage);
await dbContext.SaveChangesAsync().ConfigureAwait(false);
@@ -725,12 +707,22 @@ namespace Jellyfin.Server.Implementations.Users
_users[user.Id] = user;
}
+ internal static void ThrowIfInvalidUsername(string name)
+ {
+ if (!string.IsNullOrWhiteSpace(name) && IsValidUsername(name))
+ {
+ return;
+ }
+
+ throw new ArgumentException("Usernames can contain unicode symbols, numbers (0-9), dashes (-), underscores (_), apostrophes ('), and periods (.)", nameof(name));
+ }
+
private static bool IsValidUsername(string name)
{
// This is some regex that matches only on unicode "word" characters, as well as -, _ and @
// In theory this will cut out most if not all 'control' characters which should help minimize any weirdness
// Usernames can contain letters (a-z + whatever else unicode is cool with), numbers (0-9), at-signs (@), dashes (-), underscores (_), apostrophes ('), periods (.) and spaces ( )
- return Regex.IsMatch(name, @"^[\w\ \-'._@]*$");
+ return Regex.IsMatch(name, @"^[\w\ \-'._@]+$");
}
private IAuthenticationProvider GetAuthenticationProvider(User user)
@@ -824,11 +816,7 @@ namespace Jellyfin.Server.Implementations.Users
{
// Check easy password
var passwordHash = PasswordHash.Parse(user.EasyPassword);
- var hash = _cryptoProvider.ComputeHash(
- passwordHash.Id,
- Encoding.UTF8.GetBytes(password),
- passwordHash.Salt.ToArray());
- success = passwordHash.Hash.SequenceEqual(hash);
+ success = _cryptoProvider.Verify(passwordHash, password);
}
return (authenticationProvider, username, success);