@ -33,7 +33,7 @@ namespace Jellyfin.Server.Implementations.Users
/// </summary>
public class UserManager : IUserManager
{
private readonly JellyfinDbProvider _dbProvider ;
private readonly IDbContextFactory< JellyfinDb > _dbProvider ;
private readonly IEventManager _eventManager ;
private readonly ICryptoProvider _cryptoProvider ;
private readonly INetworkManager _networkManager ;
@ -59,7 +59,7 @@ namespace Jellyfin.Server.Implementations.Users
/// <param name="imageProcessor">The image processor.</param>
/// <param name="logger">The logger.</param>
public UserManager (
JellyfinDbProvider dbProvider ,
IDbContextFactory< JellyfinDb > dbProvider ,
IEventManager eventManager ,
ICryptoProvider cryptoProvider ,
INetworkManager networkManager ,
@ -83,7 +83,7 @@ namespace Jellyfin.Server.Implementations.Users
_defaultPasswordResetProvider = _passwordResetProviders . OfType < DefaultPasswordResetProvider > ( ) . First ( ) ;
_users = new ConcurrentDictionary < Guid , User > ( ) ;
using var dbContext = _dbProvider . Create Context( ) ;
using var dbContext = _dbProvider . Create Db Context( ) ;
foreach ( var user in dbContext . Users
. Include ( user = > user . Permissions )
. Include ( user = > user . Preferences )
@ -139,31 +139,35 @@ namespace Jellyfin.Server.Implementations.Users
throw new ArgumentException ( "The new and old names must be different." ) ;
}
await using var dbContext = _dbProvider . CreateContext ( ) ;
if ( await dbContext . Users
. AsQueryable ( )
. AnyAsync ( u = > u . Username = = newName & & ! u . Id . Equals ( user . Id ) )
. ConfigureAwait ( false ) )
var dbContext = await _dbProvider . CreateDbContextAsync ( ) . ConfigureAwait ( false ) ;
await using ( dbContext . ConfigureAwait ( false ) )
{
throw new ArgumentException ( string . Format (
CultureInfo . InvariantCulture ,
"A user with the name '{0}' already exists." ,
newName ) ) ;
if ( await dbContext . Users
. AsQueryable ( )
. AnyAsync ( u = > u . Username = = newName & & ! u . Id . Equals ( user . Id ) )
. ConfigureAwait ( false ) )
{
throw new ArgumentException ( string . Format (
CultureInfo . InvariantCulture ,
"A user with the name '{0}' already exists." ,
newName ) ) ;
}
user . Username = newName ;
await UpdateUserInternalAsync ( dbContext , user ) . ConfigureAwait ( false ) ;
}
user . Username = newName ;
await UpdateUserAsync ( user ) . ConfigureAwait ( false ) ;
OnUserUpdated ? . Invoke ( this , new GenericEventArgs < User > ( user ) ) ;
}
/// <inheritdoc/>
public async Task UpdateUserAsync ( User user )
{
await using var dbContext = _dbProvider . CreateContext ( ) ;
dbContext . Users . Update ( user ) ;
_users [ user . Id ] = user ;
await dbContext . SaveChangesAsync ( ) . ConfigureAwait ( false ) ;
var dbContext = await _dbProvider . CreateDbContextAsync ( ) . ConfigureAwait ( false ) ;
await using ( dbContext . ConfigureAwait ( false ) )
{
await UpdateUserInternalAsync ( dbContext , user ) . ConfigureAwait ( false ) ;
}
}
internal async Task < User > CreateUserInternalAsync ( string name , JellyfinDb dbContext )
@ -202,12 +206,15 @@ namespace Jellyfin.Server.Implementations.Users
name ) ) ;
}
await using var dbContext = _dbProvider . CreateContext ( ) ;
var newUser = await CreateUserInternalAsync ( name , dbContext ) . ConfigureAwait ( false ) ;
User newUser ;
var dbContext = await _dbProvider . CreateDbContextAsync ( ) . ConfigureAwait ( false ) ;
await using ( dbContext . ConfigureAwait ( false ) )
{
newUser = await CreateUserInternalAsync ( name , dbContext ) . ConfigureAwait ( false ) ;
dbContext . Users . Add ( newUser ) ;
await dbContext . SaveChangesAsync ( ) . ConfigureAwait ( false ) ;
dbContext . Users . Add ( newUser ) ;
await dbContext . SaveChangesAsync ( ) . ConfigureAwait ( false ) ;
}
await _eventManager . PublishAsync ( new UserCreatedEventArgs ( newUser ) ) . ConfigureAwait ( false ) ;
@ -241,9 +248,13 @@ namespace Jellyfin.Server.Implementations.Users
nameof ( userId ) ) ;
}
await using var dbContext = _dbProvider . CreateContext ( ) ;
dbContext . Users . Remove ( user ) ;
await dbContext . SaveChangesAsync ( ) . ConfigureAwait ( false ) ;
var dbContext = await _dbProvider . CreateDbContextAsync ( ) . ConfigureAwait ( false ) ;
await using ( dbContext . ConfigureAwait ( false ) )
{
dbContext . Users . Remove ( user ) ;
await dbContext . SaveChangesAsync ( ) . ConfigureAwait ( false ) ;
}
_users . Remove ( userId ) ;
await _eventManager . PublishAsync ( new UserDeletedEventArgs ( user ) ) . ConfigureAwait ( false ) ;
@ -288,7 +299,7 @@ namespace Jellyfin.Server.Implementations.Users
user . EasyPassword = newPasswordSha1 ;
await UpdateUserAsync ( user ) . ConfigureAwait ( false ) ;
_eventManager . Publish ( new UserPasswordChangedEventArgs ( user ) ) ;
await _eventManager . Publish Async ( new UserPasswordChangedEventArgs ( user ) ) . ConfigureAwait ( false ) ;
}
/// <inheritdoc/>
@ -541,14 +552,17 @@ namespace Jellyfin.Server.Implementations.Users
_logger . LogWarning ( "No users, creating one with username {UserName}" , defaultName ) ;
await using var dbContext = _dbProvider . CreateContext ( ) ;
var newUser = await CreateUserInternalAsync ( defaultName , dbContext ) . ConfigureAwait ( false ) ;
newUser . SetPermission ( PermissionKind . IsAdministrator , true ) ;
newUser . SetPermission ( PermissionKind . EnableContentDeletion , true ) ;
newUser . SetPermission ( PermissionKind . EnableRemoteControlOfOtherUsers , true ) ;
var dbContext = await _dbProvider . CreateDbContextAsync ( ) . ConfigureAwait ( false ) ;
await using ( dbContext . ConfigureAwait ( false ) )
{
var newUser = await CreateUserInternalAsync ( defaultName , dbContext ) . ConfigureAwait ( false ) ;
newUser . SetPermission ( PermissionKind . IsAdministrator , true ) ;
newUser . SetPermission ( PermissionKind . EnableContentDeletion , true ) ;
newUser . SetPermission ( PermissionKind . EnableRemoteControlOfOtherUsers , true ) ;
dbContext . Users . Add ( newUser ) ;
await dbContext . SaveChangesAsync ( ) . ConfigureAwait ( false ) ;
dbContext . Users . Add ( newUser ) ;
await dbContext . SaveChangesAsync ( ) . ConfigureAwait ( false ) ;
}
}
/// <inheritdoc/>
@ -584,105 +598,111 @@ namespace Jellyfin.Server.Implementations.Users
/// <inheritdoc/>
public async Task UpdateConfigurationAsync ( Guid userId , UserConfiguration config )
{
await using var dbContext = _dbProvider . CreateContext ( ) ;
var user = dbContext . Users
. Include ( u = > u . Permissions )
. Include ( u = > u . Preferences )
. Include ( u = > u . AccessSchedules )
. Include ( u = > u . ProfileImage )
. FirstOrDefault ( u = > u . Id . Equals ( userId ) )
? ? throw new ArgumentException ( "No user exists with given Id!" ) ;
user . SubtitleMode = config . SubtitleMode ;
user . HidePlayedInLatest = config . HidePlayedInLatest ;
user . EnableLocalPassword = config . EnableLocalPassword ;
user . PlayDefaultAudioTrack = config . PlayDefaultAudioTrack ;
user . DisplayCollectionsView = config . DisplayCollectionsView ;
user . DisplayMissingEpisodes = config . DisplayMissingEpisodes ;
user . AudioLanguagePreference = config . AudioLanguagePreference ;
user . RememberAudioSelections = config . RememberAudioSelections ;
user . EnableNextEpisodeAutoPlay = config . EnableNextEpisodeAutoPlay ;
user . RememberSubtitleSelections = config . RememberSubtitleSelections ;
user . SubtitleLanguagePreference = config . SubtitleLanguagePreference ;
user . SetPreference ( PreferenceKind . OrderedViews , config . OrderedViews ) ;
user . SetPreference ( PreferenceKind . GroupedFolders , config . GroupedFolders ) ;
user . SetPreference ( PreferenceKind . MyMediaExcludes , config . MyMediaExcludes ) ;
user . SetPreference ( PreferenceKind . LatestItemExcludes , config . LatestItemsExcludes ) ;
dbContext . Update ( user ) ;
_users [ user . Id ] = user ;
await dbContext . SaveChangesAsync ( ) . ConfigureAwait ( false ) ;
var dbContext = await _dbProvider . CreateDbContextAsync ( ) . ConfigureAwait ( false ) ;
await using ( dbContext . ConfigureAwait ( false ) )
{
var user = dbContext . Users
. Include ( u = > u . Permissions )
. Include ( u = > u . Preferences )
. Include ( u = > u . AccessSchedules )
. Include ( u = > u . ProfileImage )
. FirstOrDefault ( u = > u . Id . Equals ( userId ) )
? ? throw new ArgumentException ( "No user exists with given Id!" ) ;
user . SubtitleMode = config . SubtitleMode ;
user . HidePlayedInLatest = config . HidePlayedInLatest ;
user . EnableLocalPassword = config . EnableLocalPassword ;
user . PlayDefaultAudioTrack = config . PlayDefaultAudioTrack ;
user . DisplayCollectionsView = config . DisplayCollectionsView ;
user . DisplayMissingEpisodes = config . DisplayMissingEpisodes ;
user . AudioLanguagePreference = config . AudioLanguagePreference ;
user . RememberAudioSelections = config . RememberAudioSelections ;
user . EnableNextEpisodeAutoPlay = config . EnableNextEpisodeAutoPlay ;
user . RememberSubtitleSelections = config . RememberSubtitleSelections ;
user . SubtitleLanguagePreference = config . SubtitleLanguagePreference ;
user . SetPreference ( PreferenceKind . OrderedViews , config . OrderedViews ) ;
user . SetPreference ( PreferenceKind . GroupedFolders , config . GroupedFolders ) ;
user . SetPreference ( PreferenceKind . MyMediaExcludes , config . MyMediaExcludes ) ;
user . SetPreference ( PreferenceKind . LatestItemExcludes , config . LatestItemsExcludes ) ;
dbContext . Update ( user ) ;
_users [ user . Id ] = user ;
await dbContext . SaveChangesAsync ( ) . ConfigureAwait ( false ) ;
}
}
/// <inheritdoc/>
public async Task UpdatePolicyAsync ( Guid userId , UserPolicy policy )
{
await using var dbContext = _dbProvider . CreateContext ( ) ;
var user = dbContext . Users
. Include ( u = > u . Permissions )
. Include ( u = > u . Preferences )
. Include ( u = > u . AccessSchedules )
. Include ( u = > u . ProfileImage )
. FirstOrDefault ( u = > u . Id . Equals ( userId ) )
? ? throw new ArgumentException ( "No user exists with given Id!" ) ;
// The default number of login attempts is 3, but for some god forsaken reason it's sent to the server as "0"
int? maxLoginAttempts = policy . LoginAttemptsBeforeLockout switch
{
- 1 = > null ,
0 = > 3 ,
_ = > policy . LoginAttemptsBeforeLockout
} ;
var dbContext = await _dbProvider . CreateDbContextAsync ( ) . ConfigureAwait ( false ) ;
await using ( dbContext . ConfigureAwait ( false ) )
{
var user = dbContext . Users
. Include ( u = > u . Permissions )
. Include ( u = > u . Preferences )
. Include ( u = > u . AccessSchedules )
. Include ( u = > u . ProfileImage )
. FirstOrDefault ( u = > u . Id . Equals ( userId ) )
? ? throw new ArgumentException ( "No user exists with given Id!" ) ;
// The default number of login attempts is 3, but for some god forsaken reason it's sent to the server as "0"
int? maxLoginAttempts = policy . LoginAttemptsBeforeLockout switch
{
- 1 = > null ,
0 = > 3 ,
_ = > policy . LoginAttemptsBeforeLockout
} ;
user . MaxParentalAgeRating = policy . MaxParentalRating ;
user . EnableUserPreferenceAccess = policy . EnableUserPreferenceAccess ;
user . RemoteClientBitrateLimit = policy . RemoteClientBitrateLimit ;
user . AuthenticationProviderId = policy . AuthenticationProviderId ;
user . PasswordResetProviderId = policy . PasswordResetProviderId ;
user . InvalidLoginAttemptCount = policy . InvalidLoginAttemptCount ;
user . LoginAttemptsBeforeLockout = maxLoginAttempts ;
user . MaxActiveSessions = policy . MaxActiveSessions ;
user . SyncPlayAccess = policy . SyncPlayAccess ;
user . SetPermission ( PermissionKind . IsAdministrator , policy . IsAdministrator ) ;
user . SetPermission ( PermissionKind . IsHidden , policy . IsHidden ) ;
user . SetPermission ( PermissionKind . IsDisabled , policy . IsDisabled ) ;
user . SetPermission ( PermissionKind . EnableSharedDeviceControl , policy . EnableSharedDeviceControl ) ;
user . SetPermission ( PermissionKind . EnableRemoteAccess , policy . EnableRemoteAccess ) ;
user . SetPermission ( PermissionKind . EnableLiveTvManagement , policy . EnableLiveTvManagement ) ;
user . SetPermission ( PermissionKind . EnableLiveTvAccess , policy . EnableLiveTvAccess ) ;
user . SetPermission ( PermissionKind . EnableMediaPlayback , policy . EnableMediaPlayback ) ;
user . SetPermission ( PermissionKind . EnableAudioPlaybackTranscoding , policy . EnableAudioPlaybackTranscoding ) ;
user . SetPermission ( PermissionKind . EnableVideoPlaybackTranscoding , policy . EnableVideoPlaybackTranscoding ) ;
user . SetPermission ( PermissionKind . EnableContentDeletion , policy . EnableContentDeletion ) ;
user . SetPermission ( PermissionKind . EnableContentDownloading , policy . EnableContentDownloading ) ;
user . SetPermission ( PermissionKind . EnableSyncTranscoding , policy . EnableSyncTranscoding ) ;
user . SetPermission ( PermissionKind . EnableMediaConversion , policy . EnableMediaConversion ) ;
user . SetPermission ( PermissionKind . EnableAllChannels , policy . EnableAllChannels ) ;
user . SetPermission ( PermissionKind . EnableAllDevices , policy . EnableAllDevices ) ;
user . SetPermission ( PermissionKind . EnableAllFolders , policy . EnableAllFolders ) ;
user . SetPermission ( PermissionKind . EnableRemoteControlOfOtherUsers , policy . EnableRemoteControlOfOtherUsers ) ;
user . SetPermission ( PermissionKind . EnablePlaybackRemuxing , policy . EnablePlaybackRemuxing ) ;
user . SetPermission ( PermissionKind . ForceRemoteSourceTranscoding , policy . ForceRemoteSourceTranscoding ) ;
user . SetPermission ( PermissionKind . EnablePublicSharing , policy . EnablePublicSharing ) ;
user . AccessSchedules . Clear ( ) ;
foreach ( var policyAccessSchedule in policy . AccessSchedules )
{
user . AccessSchedules . Add ( policyAccessSchedule ) ;
}
// TODO: fix this at some point
user . SetPreference ( PreferenceKind . BlockUnratedItems , policy . BlockUnratedItems ? ? Array . Empty < UnratedItem > ( ) ) ;
user . SetPreference ( PreferenceKind . BlockedTags , policy . BlockedTags ) ;
user . SetPreference ( PreferenceKind . EnabledChannels , policy . EnabledChannels ) ;
user . SetPreference ( PreferenceKind . EnabledDevices , policy . EnabledDevices ) ;
user . SetPreference ( PreferenceKind . EnabledFolders , policy . EnabledFolders ) ;
user . SetPreference ( PreferenceKind . EnableContentDeletionFromFolders , policy . EnableContentDeletionFromFolders ) ;
dbContext . Update ( user ) ;
_users [ user . Id ] = user ;
await dbContext . SaveChangesAsync ( ) . ConfigureAwait ( false ) ;
user . MaxParentalAgeRating = policy . MaxParentalRating ;
user . EnableUserPreferenceAccess = policy . EnableUserPreferenceAccess ;
user . RemoteClientBitrateLimit = policy . RemoteClientBitrateLimit ;
user . AuthenticationProviderId = policy . AuthenticationProviderId ;
user . PasswordResetProviderId = policy . PasswordResetProviderId ;
user . InvalidLoginAttemptCount = policy . InvalidLoginAttemptCount ;
user . LoginAttemptsBeforeLockout = maxLoginAttempts ;
user . MaxActiveSessions = policy . MaxActiveSessions ;
user . SyncPlayAccess = policy . SyncPlayAccess ;
user . SetPermission ( PermissionKind . IsAdministrator , policy . IsAdministrator ) ;
user . SetPermission ( PermissionKind . IsHidden , policy . IsHidden ) ;
user . SetPermission ( PermissionKind . IsDisabled , policy . IsDisabled ) ;
user . SetPermission ( PermissionKind . EnableSharedDeviceControl , policy . EnableSharedDeviceControl ) ;
user . SetPermission ( PermissionKind . EnableRemoteAccess , policy . EnableRemoteAccess ) ;
user . SetPermission ( PermissionKind . EnableLiveTvManagement , policy . EnableLiveTvManagement ) ;
user . SetPermission ( PermissionKind . EnableLiveTvAccess , policy . EnableLiveTvAccess ) ;
user . SetPermission ( PermissionKind . EnableMediaPlayback , policy . EnableMediaPlayback ) ;
user . SetPermission ( PermissionKind . EnableAudioPlaybackTranscoding , policy . EnableAudioPlaybackTranscoding ) ;
user . SetPermission ( PermissionKind . EnableVideoPlaybackTranscoding , policy . EnableVideoPlaybackTranscoding ) ;
user . SetPermission ( PermissionKind . EnableContentDeletion , policy . EnableContentDeletion ) ;
user . SetPermission ( PermissionKind . EnableContentDownloading , policy . EnableContentDownloading ) ;
user . SetPermission ( PermissionKind . EnableSyncTranscoding , policy . EnableSyncTranscoding ) ;
user . SetPermission ( PermissionKind . EnableMediaConversion , policy . EnableMediaConversion ) ;
user . SetPermission ( PermissionKind . EnableAllChannels , policy . EnableAllChannels ) ;
user . SetPermission ( PermissionKind . EnableAllDevices , policy . EnableAllDevices ) ;
user . SetPermission ( PermissionKind . EnableAllFolders , policy . EnableAllFolders ) ;
user . SetPermission ( PermissionKind . EnableRemoteControlOfOtherUsers , policy . EnableRemoteControlOfOtherUsers ) ;
user . SetPermission ( PermissionKind . EnablePlaybackRemuxing , policy . EnablePlaybackRemuxing ) ;
user . SetPermission ( PermissionKind . ForceRemoteSourceTranscoding , policy . ForceRemoteSourceTranscoding ) ;
user . SetPermission ( PermissionKind . EnablePublicSharing , policy . EnablePublicSharing ) ;
user . AccessSchedules . Clear ( ) ;
foreach ( var policyAccessSchedule in policy . AccessSchedules )
{
user . AccessSchedules . Add ( policyAccessSchedule ) ;
}
// TODO: fix this at some point
user . SetPreference ( PreferenceKind . BlockUnratedItems , policy . BlockUnratedItems ? ? Array . Empty < UnratedItem > ( ) ) ;
user . SetPreference ( PreferenceKind . BlockedTags , policy . BlockedTags ) ;
user . SetPreference ( PreferenceKind . EnabledChannels , policy . EnabledChannels ) ;
user . SetPreference ( PreferenceKind . EnabledDevices , policy . EnabledDevices ) ;
user . SetPreference ( PreferenceKind . EnabledFolders , policy . EnabledFolders ) ;
user . SetPreference ( PreferenceKind . EnableContentDeletionFromFolders , policy . EnableContentDeletionFromFolders ) ;
dbContext . Update ( user ) ;
_users [ user . Id ] = user ;
await dbContext . SaveChangesAsync ( ) . ConfigureAwait ( false ) ;
}
}
/// <inheritdoc/>
@ -693,9 +713,13 @@ namespace Jellyfin.Server.Implementations.Users
return ;
}
await using var dbContext = _dbProvider . CreateContext ( ) ;
dbContext . Remove ( user . ProfileImage ) ;
await dbContext . SaveChangesAsync ( ) . ConfigureAwait ( false ) ;
var dbContext = await _dbProvider . CreateDbContextAsync ( ) . ConfigureAwait ( false ) ;
await using ( dbContext . ConfigureAwait ( false ) )
{
dbContext . Remove ( user . ProfileImage ) ;
await dbContext . SaveChangesAsync ( ) . ConfigureAwait ( false ) ;
}
user . ProfileImage = null ;
_users [ user . Id ] = user ;
}
@ -859,5 +883,12 @@ namespace Jellyfin.Server.Implementations.Users
await UpdateUserAsync ( user ) . ConfigureAwait ( false ) ;
}
private async Task UpdateUserInternalAsync ( JellyfinDb dbContext , User user )
{
dbContext . Users . Update ( user ) ;
_users [ user . Id ] = user ;
await dbContext . SaveChangesAsync ( ) . ConfigureAwait ( false ) ;
}
}
}