aboutsummaryrefslogtreecommitdiff
path: root/Emby.Server.Implementations/Library
diff options
context:
space:
mode:
Diffstat (limited to 'Emby.Server.Implementations/Library')
-rw-r--r--Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs1
-rw-r--r--Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs134
-rw-r--r--Emby.Server.Implementations/Library/LibraryManager.cs16
-rw-r--r--Emby.Server.Implementations/Library/UserManager.cs109
-rw-r--r--Emby.Server.Implementations/Library/UserViewManager.cs4
5 files changed, 93 insertions, 171 deletions
diff --git a/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs b/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs
index f1ae2fc9c0..8bdb387843 100644
--- a/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs
+++ b/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs
@@ -57,7 +57,6 @@ namespace Emby.Server.Implementations.Library
}
var filename = fileInfo.Name;
- var path = fileInfo.FullName;
// Ignore hidden files on UNIX
if (Environment.OSVersion.Platform != PlatformID.Win32NT
diff --git a/Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs b/Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs
index 2282b8efb6..c95b00ede2 100644
--- a/Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs
+++ b/Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs
@@ -2,24 +2,30 @@ using System;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
+using MediaBrowser.Common.Cryptography;
using MediaBrowser.Controller.Authentication;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Cryptography;
+using static MediaBrowser.Common.HexHelper;
namespace Emby.Server.Implementations.Library
{
public class DefaultAuthenticationProvider : IAuthenticationProvider, IRequiresResolvedUser
{
private readonly ICryptoProvider _cryptographyProvider;
+
public DefaultAuthenticationProvider(ICryptoProvider cryptographyProvider)
{
_cryptographyProvider = cryptographyProvider;
}
+ /// <inheritdoc />
public string Name => "Default";
+ /// <inheritdoc />
public bool IsEnabled => true;
+ /// <inheritdoc />
// This is dumb and an artifact of the backwards way auth providers were designed.
// This version of authenticate was never meant to be called, but needs to be here for interface compat
// Only the providers that don't provide local user support use this
@@ -28,6 +34,7 @@ namespace Emby.Server.Implementations.Library
throw new NotImplementedException();
}
+ /// <inheritdoc />
// This is the version that we need to use for local users. Because reasons.
public Task<ProviderAuthenticationResult> Authenticate(string username, string password, User resolvedUser)
{
@@ -46,10 +53,9 @@ namespace Emby.Server.Implementations.Library
});
}
- ConvertPasswordFormat(resolvedUser);
byte[] passwordbytes = Encoding.UTF8.GetBytes(password);
- PasswordHash readyHash = new PasswordHash(resolvedUser.Password);
+ PasswordHash readyHash = PasswordHash.Parse(resolvedUser.Password);
if (_cryptographyProvider.GetSupportedHashMethods().Contains(readyHash.Id)
|| _cryptographyProvider.DefaultHashMethod == readyHash.Id)
{
@@ -76,72 +82,31 @@ namespace Emby.Server.Implementations.Library
});
}
- // This allows us to move passwords forward to the newformat without breaking. They are still insecure, unsalted, and dumb before a password change
- // but at least they are in the new format.
- private void ConvertPasswordFormat(User user)
- {
- if (string.IsNullOrEmpty(user.Password))
- {
- return;
- }
-
- if (user.Password.IndexOf('$') == -1)
- {
- string hash = user.Password;
- user.Password = string.Format("$SHA1${0}", hash);
- }
-
- if (user.EasyPassword != null
- && user.EasyPassword.IndexOf('$') == -1)
- {
- string hash = user.EasyPassword;
- user.EasyPassword = string.Format("$SHA1${0}", hash);
- }
- }
-
+ /// <inheritdoc />
public bool HasPassword(User user)
=> !string.IsNullOrEmpty(user.Password);
+ /// <inheritdoc />
public Task ChangePassword(User user, string newPassword)
{
- ConvertPasswordFormat(user);
-
- // This is needed to support changing a no password user to a password user
- if (string.IsNullOrEmpty(user.Password))
+ if (string.IsNullOrEmpty(newPassword))
{
- PasswordHash newPasswordHash = new PasswordHash(_cryptographyProvider);
- newPasswordHash.Salt = _cryptographyProvider.GenerateSalt();
- newPasswordHash.Id = _cryptographyProvider.DefaultHashMethod;
- newPasswordHash.Hash = GetHashedChangeAuth(newPassword, newPasswordHash);
- user.Password = newPasswordHash.ToString();
+ user.Password = null;
return Task.CompletedTask;
}
- PasswordHash passwordHash = new PasswordHash(user.Password);
- if (passwordHash.Id == "SHA1"
- && passwordHash.Salt.Length == 0)
- {
- passwordHash.Salt = _cryptographyProvider.GenerateSalt();
- passwordHash.Id = _cryptographyProvider.DefaultHashMethod;
- passwordHash.Hash = GetHashedChangeAuth(newPassword, passwordHash);
- }
- else if (newPassword != null)
- {
- passwordHash.Hash = GetHashed(user, newPassword);
- }
-
- user.Password = passwordHash.ToString();
+ PasswordHash newPasswordHash = _cryptographyProvider.CreatePasswordHash(newPassword);
+ user.Password = newPasswordHash.ToString();
return Task.CompletedTask;
}
+ /// <inheritdoc />
public void ChangeEasyPassword(User user, string newPassword, string newPasswordHash)
{
- ConvertPasswordFormat(user);
-
if (newPassword != null)
{
- newPasswordHash = string.Format("$SHA1${0}", GetHashedString(user, newPassword));
+ newPasswordHash = _cryptographyProvider.CreatePasswordHash(newPassword).ToString();
}
if (string.IsNullOrWhiteSpace(newPasswordHash))
@@ -152,21 +117,12 @@ namespace Emby.Server.Implementations.Library
user.EasyPassword = newPasswordHash;
}
+ /// <inheritdoc />
public string GetEasyPasswordHash(User user)
{
- // This should be removed in the future. This was added to let user login after
- // Jellyfin 10.3.3 failed to save a well formatted PIN.
- ConvertPasswordFormat(user);
-
return string.IsNullOrEmpty(user.EasyPassword)
? null
- : PasswordHash.ConvertToByteString(new PasswordHash(user.EasyPassword).Hash);
- }
-
- internal byte[] GetHashedChangeAuth(string newPassword, PasswordHash passwordHash)
- {
- passwordHash.Hash = Encoding.UTF8.GetBytes(newPassword);
- return _cryptographyProvider.ComputeHash(passwordHash);
+ : ToHexString(PasswordHash.Parse(user.EasyPassword).Hash);
}
/// <summary>
@@ -174,54 +130,36 @@ namespace Emby.Server.Implementations.Library
/// </summary>
public string GetHashedString(User user, string str)
{
- PasswordHash passwordHash;
if (string.IsNullOrEmpty(user.Password))
{
- passwordHash = new PasswordHash(_cryptographyProvider);
- }
- else
- {
- ConvertPasswordFormat(user);
- passwordHash = new PasswordHash(user.Password);
+ return _cryptographyProvider.CreatePasswordHash(str).ToString();
}
- if (passwordHash.Salt != null)
- {
- // the password is modern format with PBKDF and we should take advantage of that
- passwordHash.Hash = Encoding.UTF8.GetBytes(str);
- return PasswordHash.ConvertToByteString(_cryptographyProvider.ComputeHash(passwordHash));
- }
- else
- {
- // the password has no salt and should be called with the older method for safety
- return PasswordHash.ConvertToByteString(_cryptographyProvider.ComputeHash(passwordHash.Id, Encoding.UTF8.GetBytes(str)));
- }
+ // TODO: make use of iterations parameter?
+ PasswordHash passwordHash = PasswordHash.Parse(user.Password);
+ return new PasswordHash(
+ passwordHash.Id,
+ _cryptographyProvider.ComputeHash(
+ passwordHash.Id,
+ Encoding.UTF8.GetBytes(str),
+ passwordHash.Salt),
+ passwordHash.Salt,
+ passwordHash.Parameters.ToDictionary(x => x.Key, y => y.Value)).ToString();
}
public byte[] GetHashed(User user, string str)
{
- PasswordHash passwordHash;
if (string.IsNullOrEmpty(user.Password))
{
- passwordHash = new PasswordHash(_cryptographyProvider);
- }
- else
- {
- ConvertPasswordFormat(user);
- passwordHash = new PasswordHash(user.Password);
+ return _cryptographyProvider.CreatePasswordHash(str).Hash;
}
- if (passwordHash.Salt != null)
- {
- // the password is modern format with PBKDF and we should take advantage of that
- passwordHash.Hash = Encoding.UTF8.GetBytes(str);
- return _cryptographyProvider.ComputeHash(passwordHash);
- }
- else
- {
- // the password has no salt and should be called with the older method for safety
- return _cryptographyProvider.ComputeHash(passwordHash.Id, Encoding.UTF8.GetBytes(str));
- }
+ // TODO: make use of iterations parameter?
+ PasswordHash passwordHash = PasswordHash.Parse(user.Password);
+ return _cryptographyProvider.ComputeHash(
+ passwordHash.Id,
+ Encoding.UTF8.GetBytes(str),
+ passwordHash.Salt);
}
}
}
diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs
index 36934f65f0..87e951f25d 100644
--- a/Emby.Server.Implementations/Library/LibraryManager.cs
+++ b/Emby.Server.Implementations/Library/LibraryManager.cs
@@ -779,12 +779,23 @@ namespace Emby.Server.Implementations.Library
{
var userRootPath = ConfigurationManager.ApplicationPaths.DefaultUserViewsPath;
+ _logger.LogDebug("Creating userRootPath at {path}", userRootPath);
Directory.CreateDirectory(userRootPath);
- var tmpItem = GetItemById(GetNewItemId(userRootPath, typeof(UserRootFolder))) as UserRootFolder;
+ var newItemId = GetNewItemId(userRootPath, typeof(UserRootFolder));
+ UserRootFolder tmpItem = null;
+ try
+ {
+ tmpItem = GetItemById(newItemId) as UserRootFolder;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error creating UserRootFolder {path}", newItemId);
+ }
if (tmpItem == null)
{
+ _logger.LogDebug("Creating new userRootFolder with DeepCopy");
tmpItem = ((Folder)ResolvePath(_fileSystem.GetDirectoryInfo(userRootPath))).DeepCopy<Folder, UserRootFolder>();
}
@@ -796,6 +807,7 @@ namespace Emby.Server.Implementations.Library
}
_userRootFolder = tmpItem;
+ _logger.LogDebug("Setting userRootFolder: {folder}", _userRootFolder);
}
}
}
@@ -1146,8 +1158,10 @@ namespace Emby.Server.Implementations.Library
public List<VirtualFolderInfo> GetVirtualFolders(bool includeRefreshState)
{
+ _logger.LogDebug("Getting topLibraryFolders");
var topLibraryFolders = GetUserRootFolder().Children.ToList();
+ _logger.LogDebug("Getting refreshQueue");
var refreshQueue = includeRefreshState ? _providerManagerFactory().GetRefreshQueue() : null;
return _fileSystem.GetDirectoryPaths(ConfigurationManager.ApplicationPaths.DefaultUserViewsPath)
diff --git a/Emby.Server.Implementations/Library/UserManager.cs b/Emby.Server.Implementations/Library/UserManager.cs
index a7ea13ca61..52b2f56ffc 100644
--- a/Emby.Server.Implementations/Library/UserManager.cs
+++ b/Emby.Server.Implementations/Library/UserManager.cs
@@ -8,6 +8,7 @@ using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Common.Cryptography;
using MediaBrowser.Common.Events;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller;
@@ -23,7 +24,6 @@ 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;
@@ -31,6 +31,7 @@ using MediaBrowser.Model.IO;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Users;
using Microsoft.Extensions.Logging;
+using static MediaBrowser.Common.HexHelper;
namespace Emby.Server.Implementations.Library
{
@@ -272,14 +273,12 @@ namespace Emby.Server.Implementations.Library
var user = Users.FirstOrDefault(i => string.Equals(username, i.Name, StringComparison.OrdinalIgnoreCase));
var success = false;
- string updatedUsername = null;
IAuthenticationProvider authenticationProvider = null;
if (user != null)
{
var authResult = await AuthenticateLocalUser(username, password, hashedPassword, user, remoteEndPoint).ConfigureAwait(false);
authenticationProvider = authResult.authenticationProvider;
- updatedUsername = authResult.username;
success = authResult.success;
}
else
@@ -287,7 +286,7 @@ namespace Emby.Server.Implementations.Library
// user is null
var authResult = await AuthenticateLocalUser(username, password, hashedPassword, null, remoteEndPoint).ConfigureAwait(false);
authenticationProvider = authResult.authenticationProvider;
- updatedUsername = authResult.username;
+ string updatedUsername = authResult.username;
success = authResult.success;
if (success
@@ -353,11 +352,11 @@ namespace Emby.Server.Implementations.Library
UpdateUser(user);
}
- UpdateInvalidLoginAttemptCount(user, 0);
+ ResetInvalidLoginAttemptCount(user);
}
else
{
- UpdateInvalidLoginAttemptCount(user, user.Policy.InvalidLoginAttemptCount + 1);
+ IncrementInvalidLoginAttemptCount(user);
}
_logger.LogInformation("Authentication request for {0} {1}.", user.Name, success ? "has succeeded" : "has been denied");
@@ -450,53 +449,38 @@ namespace Emby.Server.Implementations.Library
}
}
- private async Task<(IAuthenticationProvider authenticationProvider, string username, bool success)> AuthenticateLocalUser(string username, string password, string hashedPassword, User user, string remoteEndPoint)
+ private async Task<(IAuthenticationProvider authenticationProvider, string username, bool success)> AuthenticateLocalUser(
+ string username,
+ string password,
+ string hashedPassword,
+ User user,
+ string remoteEndPoint)
{
bool success = false;
IAuthenticationProvider authenticationProvider = null;
- if (password != null && user != null)
+ foreach (var provider in GetAuthenticationProviders(user))
{
- // Doesn't look like this is even possible to be used, because of password == null checks below
- hashedPassword = _defaultAuthenticationProvider.GetHashedString(user, password);
- }
+ var providerAuthResult = await AuthenticateWithProvider(provider, username, password, user).ConfigureAwait(false);
+ var updatedUsername = providerAuthResult.username;
+ success = providerAuthResult.success;
- if (password == null)
- {
- // legacy
- success = string.Equals(user.Password, hashedPassword.Replace("-", string.Empty), StringComparison.OrdinalIgnoreCase);
- }
- else
- {
- foreach (var provider in GetAuthenticationProviders(user))
+ if (success)
{
- var providerAuthResult = await AuthenticateWithProvider(provider, username, password, user).ConfigureAwait(false);
- var updatedUsername = providerAuthResult.username;
- success = providerAuthResult.success;
-
- if (success)
- {
- authenticationProvider = provider;
- username = updatedUsername;
- break;
- }
+ authenticationProvider = provider;
+ username = updatedUsername;
+ break;
}
}
- if (user != null
- && !success
+ if (!success
&& _networkManager.IsInLocalNetwork(remoteEndPoint)
&& user.Configuration.EnableLocalPassword)
{
- if (password == null)
- {
- // legacy
- success = string.Equals(GetLocalPasswordHash(user), hashedPassword.Replace("-", string.Empty), StringComparison.OrdinalIgnoreCase);
- }
- else
- {
- success = string.Equals(GetLocalPasswordHash(user), _defaultAuthenticationProvider.GetHashedString(user, password), StringComparison.OrdinalIgnoreCase);
- }
+ success = string.Equals(
+ GetLocalPasswordHash(user),
+ _defaultAuthenticationProvider.GetHashedString(user, password),
+ StringComparison.OrdinalIgnoreCase);
}
return (authenticationProvider, username, success);
@@ -506,44 +490,31 @@ namespace Emby.Server.Implementations.Library
{
return string.IsNullOrEmpty(user.EasyPassword)
? null
- : PasswordHash.ConvertToByteString(new PasswordHash(user.EasyPassword).Hash);
+ : ToHexString(PasswordHash.Parse(user.EasyPassword).Hash);
}
- private void UpdateInvalidLoginAttemptCount(User user, int newValue)
+ private void ResetInvalidLoginAttemptCount(User user)
{
- if (user.Policy.InvalidLoginAttemptCount == newValue || newValue <= 0)
- {
- return;
- }
-
- user.Policy.InvalidLoginAttemptCount = newValue;
-
- // Check for users without a value here and then fill in the default value
- // also protect from an always lockout if misconfigured
- if (user.Policy.LoginAttemptsBeforeLockout == null || user.Policy.LoginAttemptsBeforeLockout == 0)
- {
- user.Policy.LoginAttemptsBeforeLockout = user.Policy.IsAdministrator ? 5 : 3;
- }
-
- var maxCount = user.Policy.LoginAttemptsBeforeLockout;
-
- var fireLockout = false;
+ user.Policy.InvalidLoginAttemptCount = 0;
+ UpdateUserPolicy(user, user.Policy, false);
+ }
- // -1 can be used to specify no lockout value
- if (maxCount != -1 && newValue >= maxCount)
+ private void IncrementInvalidLoginAttemptCount(User user)
+ {
+ int invalidLogins = ++user.Policy.InvalidLoginAttemptCount;
+ int maxInvalidLogins = user.Policy.LoginAttemptsBeforeLockout;
+ if (maxInvalidLogins > 0
+ && invalidLogins >= maxInvalidLogins)
{
- _logger.LogDebug("Disabling user {0} due to {1} unsuccessful login attempts.", user.Name, newValue);
user.Policy.IsDisabled = true;
-
- fireLockout = true;
+ UserLockedOut?.Invoke(this, new GenericEventArgs<User>(user));
+ _logger.LogWarning(
+ "Disabling user {UserName} due to {Attempts} unsuccessful login attempts.",
+ user.Name,
+ invalidLogins);
}
UpdateUserPolicy(user, user.Policy, false);
-
- if (fireLockout)
- {
- UserLockedOut?.Invoke(this, new GenericEventArgs<User>(user));
- }
}
/// <summary>
diff --git a/Emby.Server.Implementations/Library/UserViewManager.cs b/Emby.Server.Implementations/Library/UserViewManager.cs
index 4d79cae139..88e2a8fa69 100644
--- a/Emby.Server.Implementations/Library/UserViewManager.cs
+++ b/Emby.Server.Implementations/Library/UserViewManager.cs
@@ -236,7 +236,7 @@ namespace Emby.Server.Implementations.Library
if (!parentId.Equals(Guid.Empty))
{
var parentItem = _libraryManager.GetItemById(parentId);
- if (parentItem is Channel parentItemChannel)
+ if (parentItem is Channel)
{
return _channelManager.GetLatestChannelItemsInternal(
new InternalItemsQuery(user)
@@ -248,7 +248,7 @@ namespace Emby.Server.Implementations.Library
IncludeItemTypes = request.IncludeItemTypes,
EnableTotalRecordCount = false
},
- CancellationToken.None).Result.Items;
+ CancellationToken.None).GetAwaiter().GetResult().Items;
}
if (parentItem is Folder parent)