diff options
Diffstat (limited to 'Emby.Server.Implementations/Library/UserManager.cs')
| -rw-r--r-- | Emby.Server.Implementations/Library/UserManager.cs | 164 |
1 files changed, 68 insertions, 96 deletions
diff --git a/Emby.Server.Implementations/Library/UserManager.cs b/Emby.Server.Implementations/Library/UserManager.cs index 60d16c8a0..1b9c317d8 100644 --- a/Emby.Server.Implementations/Library/UserManager.cs +++ b/Emby.Server.Implementations/Library/UserManager.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -8,7 +10,6 @@ using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Common; using MediaBrowser.Common.Cryptography; using MediaBrowser.Common.Events; using MediaBrowser.Common.Net; @@ -25,6 +26,7 @@ using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Security; using MediaBrowser.Controller.Session; using MediaBrowser.Model.Configuration; +using MediaBrowser.Model.Cryptography; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Events; @@ -36,19 +38,19 @@ using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.Library { /// <summary> - /// Class UserManager + /// Class UserManager. /// </summary> public class UserManager : IUserManager { + private readonly object _policySyncLock = new object(); + private readonly object _configSyncLock = new object(); /// <summary> - /// The _logger + /// The logger. /// </summary> private readonly ILogger _logger; - private readonly object _policySyncLock = new object(); - /// <summary> - /// Gets the active user repository + /// Gets the active user repository. /// </summary> /// <value>The user repository.</value> private readonly IUserRepository _userRepository; @@ -60,6 +62,7 @@ namespace Emby.Server.Implementations.Library private readonly Func<IDtoService> _dtoServiceFactory; private readonly IServerApplicationHost _appHost; private readonly IFileSystem _fileSystem; + private readonly ICryptoProvider _cryptoProvider; private ConcurrentDictionary<Guid, User> _users; @@ -80,7 +83,8 @@ namespace Emby.Server.Implementations.Library Func<IDtoService> dtoServiceFactory, IServerApplicationHost appHost, IJsonSerializer jsonSerializer, - IFileSystem fileSystem) + IFileSystem fileSystem, + ICryptoProvider cryptoProvider) { _logger = logger; _userRepository = userRepository; @@ -91,6 +95,7 @@ namespace Emby.Server.Implementations.Library _appHost = appHost; _jsonSerializer = jsonSerializer; _fileSystem = fileSystem; + _cryptoProvider = cryptoProvider; _users = null; } @@ -179,12 +184,7 @@ namespace Emby.Server.Implementations.Library _defaultPasswordResetProvider = passwordResetProviders.OfType<DefaultPasswordResetProvider>().First(); } - /// <summary> - /// Gets a User by Id. - /// </summary> - /// <param name="id">The id.</param> - /// <returns>User.</returns> - /// <exception cref="ArgumentException"></exception> + /// <inheritdoc /> public User GetUserById(Guid id) { if (id == Guid.Empty) @@ -196,14 +196,6 @@ namespace Emby.Server.Implementations.Library return user; } - /// <summary> - /// Gets the user by identifier. - /// </summary> - /// <param name="id">The identifier.</param> - /// <returns>User.</returns> - public User GetUserById(string id) - => GetUserById(new Guid(id)); - public User GetUserByName(string name) { if (string.IsNullOrWhiteSpace(name)) @@ -263,7 +255,12 @@ namespace Emby.Server.Implementations.Library return builder.ToString(); } - public async Task<User> AuthenticateUser(string username, string password, string hashedPassword, string remoteEndPoint, bool isUserSession) + public async Task<User> AuthenticateUser( + string username, + string password, + string hashedPassword, + string remoteEndPoint, + bool isUserSession) { if (string.IsNullOrWhiteSpace(username)) { @@ -364,6 +361,8 @@ namespace Emby.Server.Implementations.Library return success ? user : null; } +#nullable enable + private static string GetAuthenticationProviderId(IAuthenticationProvider provider) { return provider.GetType().FullName; @@ -384,7 +383,7 @@ namespace Emby.Server.Implementations.Library return GetPasswordResetProviders(user)[0]; } - private IAuthenticationProvider[] GetAuthenticationProviders(User user) + private IAuthenticationProvider[] GetAuthenticationProviders(User? user) { var authenticationProviderId = user?.Policy.AuthenticationProviderId; @@ -398,14 +397,14 @@ namespace Emby.Server.Implementations.Library if (providers.Length == 0) { // Assign the user to the InvalidAuthProvider since no configured auth provider was valid/found - _logger.LogWarning("User {UserName} was found with invalid/missing Authentication Provider {AuthenticationProviderId}. Assigning user to InvalidAuthProvider until this is corrected", user.Name, user.Policy.AuthenticationProviderId); + _logger.LogWarning("User {UserName} was found with invalid/missing Authentication Provider {AuthenticationProviderId}. Assigning user to InvalidAuthProvider until this is corrected", user?.Name, user?.Policy.AuthenticationProviderId); providers = new IAuthenticationProvider[] { _invalidAuthProvider }; } return providers; } - private IPasswordResetProvider[] GetPasswordResetProviders(User user) + private IPasswordResetProvider[] GetPasswordResetProviders(User? user) { var passwordResetProviderId = user?.Policy.PasswordResetProviderId; @@ -424,11 +423,14 @@ namespace Emby.Server.Implementations.Library return providers; } - private async Task<(string username, bool success)> AuthenticateWithProvider(IAuthenticationProvider provider, string username, string password, User resolvedUser) + private async Task<(string username, bool success)> AuthenticateWithProvider( + IAuthenticationProvider provider, + string username, + string password, + User? resolvedUser) { try { - var authenticationResult = provider is IRequiresResolvedUser requiresResolvedUser ? await requiresResolvedUser.Authenticate(username, password, resolvedUser).ConfigureAwait(false) : await provider.Authenticate(username, password).ConfigureAwait(false); @@ -449,15 +451,15 @@ namespace Emby.Server.Implementations.Library } } - private async Task<(IAuthenticationProvider authenticationProvider, string username, bool success)> AuthenticateLocalUser( + private async Task<(IAuthenticationProvider? authenticationProvider, string username, bool success)> AuthenticateLocalUser( string username, string password, string hashedPassword, - User user, + User? user, string remoteEndPoint) { bool success = false; - IAuthenticationProvider authenticationProvider = null; + IAuthenticationProvider? authenticationProvider = null; foreach (var provider in GetAuthenticationProviders(user)) { @@ -475,24 +477,21 @@ namespace Emby.Server.Implementations.Library if (!success && _networkManager.IsInLocalNetwork(remoteEndPoint) - && user.Configuration.EnableLocalPassword) + && user?.Configuration.EnableLocalPassword == true + && !string.IsNullOrEmpty(user.EasyPassword)) { - success = string.Equals( - GetLocalPasswordHash(user), - _defaultAuthenticationProvider.GetHashedString(user, password), - StringComparison.OrdinalIgnoreCase); + // Check easy password + var passwordHash = PasswordHash.Parse(user.EasyPassword); + var hash = _cryptoProvider.ComputeHash( + passwordHash.Id, + Encoding.UTF8.GetBytes(password), + passwordHash.Salt); + success = passwordHash.Hash.SequenceEqual(hash); } return (authenticationProvider, username, success); } - private string GetLocalPasswordHash(User user) - { - return string.IsNullOrEmpty(user.EasyPassword) - ? null - : Hex.Encode(PasswordHash.Parse(user.EasyPassword).Hash); - } - private void ResetInvalidLoginAttemptCount(User user) { user.Policy.InvalidLoginAttemptCount = 0; @@ -538,6 +537,8 @@ namespace Emby.Server.Implementations.Library defaultName = "MyJellyfinUser"; } + _logger.LogWarning("No users, creating one with username {UserName}", defaultName); + var name = MakeValidUsername(defaultName); var user = InstantiateNewUser(name); @@ -555,6 +556,8 @@ namespace Emby.Server.Implementations.Library _users[user.Id] = user; } +#nullable restore + public UserDto GetUserDto(User user, string remoteEndPoint = null) { if (user == null) @@ -601,7 +604,7 @@ namespace Emby.Server.Implementations.Library catch (Exception ex) { // Have to use a catch-all unfortunately because some .net image methods throw plain Exceptions - _logger.LogError(ex, "Error generating PrimaryImageAspectRatio for {user}", user.Name); + _logger.LogError(ex, "Error generating PrimaryImageAspectRatio for {User}", user.Name); } } @@ -625,7 +628,7 @@ namespace Emby.Server.Implementations.Library } catch (Exception ex) { - _logger.LogError(ex, "Error getting {imageType} image info for {imagePath}", image.Type, image.Path); + _logger.LogError(ex, "Error getting {ImageType} image info for {ImagePath}", image.Type, image.Path); return null; } } @@ -756,13 +759,10 @@ namespace Emby.Server.Implementations.Library return user; } - /// <summary> - /// Deletes the user. - /// </summary> - /// <param name="user">The user.</param> - /// <returns>Task.</returns> - /// <exception cref="ArgumentNullException">user</exception> - /// <exception cref="ArgumentException"></exception> + /// <inheritdoc /> + /// <exception cref="ArgumentNullException">The <c>user</c> is <c>null</c>.</exception> + /// <exception cref="ArgumentException">The <c>user</c> doesn't exist, or is the last administrator.</exception> + /// <exception cref="InvalidOperationException">The <c>user</c> can't be deleted; there are no other users.</exception> public void DeleteUser(User user) { if (user == null) @@ -781,7 +781,7 @@ namespace Emby.Server.Implementations.Library if (_users.Count == 1) { - throw new ArgumentException(string.Format( + throw new InvalidOperationException(string.Format( CultureInfo.InvariantCulture, "The user '{0}' cannot be deleted because there must be at least one user in the system.", user.Name)); @@ -802,17 +802,20 @@ namespace Emby.Server.Implementations.Library _userRepository.DeleteUser(user); - try - { - _fileSystem.DeleteFile(configPath); - } - catch (IOException ex) + // Delete user config dir + lock (_configSyncLock) + lock (_policySyncLock) { - _logger.LogError(ex, "Error deleting file {path}", configPath); + try + { + Directory.Delete(user.ConfigurationDirectoryPath, true); + } + catch (IOException ex) + { + _logger.LogError(ex, "Error deleting user config dir: {Path}", user.ConfigurationDirectoryPath); + } } - DeleteUserPolicy(user); - _users.TryRemove(user.Id, out _); OnUserDeleted(user); @@ -920,10 +923,9 @@ namespace Emby.Server.Implementations.Library public UserPolicy GetUserPolicy(User user) { var path = GetPolicyFilePath(user); - if (!File.Exists(path)) { - return GetDefaultPolicy(user); + return GetDefaultPolicy(); } try @@ -933,19 +935,15 @@ namespace Emby.Server.Implementations.Library return (UserPolicy)_xmlSerializer.DeserializeFromFile(typeof(UserPolicy), path); } } - catch (IOException) - { - return GetDefaultPolicy(user); - } catch (Exception ex) { - _logger.LogError(ex, "Error reading policy file: {path}", path); + _logger.LogError(ex, "Error reading policy file: {Path}", path); - return GetDefaultPolicy(user); + return GetDefaultPolicy(); } } - private static UserPolicy GetDefaultPolicy(User user) + private static UserPolicy GetDefaultPolicy() { return new UserPolicy { @@ -985,27 +983,6 @@ namespace Emby.Server.Implementations.Library } } - private void DeleteUserPolicy(User user) - { - var path = GetPolicyFilePath(user); - - try - { - lock (_policySyncLock) - { - _fileSystem.DeleteFile(path); - } - } - catch (IOException) - { - - } - catch (Exception ex) - { - _logger.LogError(ex, "Error deleting policy file"); - } - } - private static string GetPolicyFilePath(User user) { return Path.Combine(user.ConfigurationDirectoryPath, "policy.xml"); @@ -1032,19 +1009,14 @@ namespace Emby.Server.Implementations.Library return (UserConfiguration)_xmlSerializer.DeserializeFromFile(typeof(UserConfiguration), path); } } - catch (IOException) - { - return new UserConfiguration(); - } catch (Exception ex) { - _logger.LogError(ex, "Error reading policy file: {path}", path); + _logger.LogError(ex, "Error reading policy file: {Path}", path); return new UserConfiguration(); } } - private readonly object _configSyncLock = new object(); public void UpdateConfiguration(Guid userId, UserConfiguration config) { var user = GetUserById(userId); |
