< Summary - Jellyfin

Information
Class: Jellyfin.Data.Entities.User
Assembly: Jellyfin.Data
File(s): /srv/git/jellyfin/Jellyfin.Data/Entities/User.cs
Line coverage
72%
Covered lines: 72
Uncovered lines: 28
Coverable lines: 100
Total lines: 534
Line coverage: 72%
Branch coverage
42%
Covered branches: 12
Total branches: 28
Branch coverage: 42.8%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
OnSavingChanges()100%210%
HasPermission(...)50%22100%
SetPermission(...)50%2.03280%
GetPreference(...)50%44100%
GetPreferenceValues(...)25%40.77820%
SetPreference(...)0%620%
SetPreference(...)0%620%
IsParentalScheduleAllowed()100%22100%
IsFolderGrouped(...)100%210%
AddDefaultPermissions()100%11100%
AddDefaultPreferences()100%22100%
IsParentalScheduleAllowed(...)50%44100%

File(s)

/srv/git/jellyfin/Jellyfin.Data/Entities/User.cs

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.ComponentModel;
 4using System.ComponentModel.DataAnnotations;
 5using System.ComponentModel.DataAnnotations.Schema;
 6using System.Linq;
 7using System.Text.Json.Serialization;
 8using Jellyfin.Data.Enums;
 9using Jellyfin.Data.Interfaces;
 10
 11namespace Jellyfin.Data.Entities
 12{
 13    /// <summary>
 14    /// An entity representing a user.
 15    /// </summary>
 16    public class User : IHasPermissions, IHasConcurrencyToken
 17    {
 18        /// <summary>
 19        /// The values being delimited here are Guids, so commas work as they do not appear in Guids.
 20        /// </summary>
 21        private const char Delimiter = ',';
 22
 23        /// <summary>
 24        /// Initializes a new instance of the <see cref="User"/> class.
 25        /// Public constructor with required data.
 26        /// </summary>
 27        /// <param name="username">The username for the new user.</param>
 28        /// <param name="authenticationProviderId">The Id of the user's authentication provider.</param>
 29        /// <param name="passwordResetProviderId">The Id of the user's password reset provider.</param>
 30        public User(string username, string authenticationProviderId, string passwordResetProviderId)
 31        {
 5332            ArgumentException.ThrowIfNullOrEmpty(username);
 5333            ArgumentException.ThrowIfNullOrEmpty(authenticationProviderId);
 5334            ArgumentException.ThrowIfNullOrEmpty(passwordResetProviderId);
 35
 5336            Username = username;
 5337            AuthenticationProviderId = authenticationProviderId;
 5338            PasswordResetProviderId = passwordResetProviderId;
 39
 5340            AccessSchedules = new HashSet<AccessSchedule>();
 5341            DisplayPreferences = new HashSet<DisplayPreferences>();
 5342            ItemDisplayPreferences = new HashSet<ItemDisplayPreferences>();
 43            // Groups = new HashSet<Group>();
 5344            Permissions = new HashSet<Permission>();
 5345            Preferences = new HashSet<Preference>();
 46            // ProviderMappings = new HashSet<ProviderMapping>();
 47
 48            // Set default values
 5349            Id = Guid.NewGuid();
 5350            InvalidLoginAttemptCount = 0;
 5351            EnableUserPreferenceAccess = true;
 5352            MustUpdatePassword = false;
 5353            DisplayMissingEpisodes = false;
 5354            DisplayCollectionsView = false;
 5355            HidePlayedInLatest = true;
 5356            RememberAudioSelections = true;
 5357            RememberSubtitleSelections = true;
 5358            EnableNextEpisodeAutoPlay = true;
 5359            EnableAutoLogin = false;
 5360            PlayDefaultAudioTrack = true;
 5361            SubtitleMode = SubtitlePlaybackMode.Default;
 5362            SyncPlayAccess = SyncPlayUserAccessType.CreateAndJoinGroups;
 5363        }
 64
 65        /// <summary>
 66        /// Gets or sets the Id of the user.
 67        /// </summary>
 68        /// <remarks>
 69        /// Identity, Indexed, Required.
 70        /// </remarks>
 71        [JsonIgnore]
 72        public Guid Id { get; set; }
 73
 74        /// <summary>
 75        /// Gets or sets the user's name.
 76        /// </summary>
 77        /// <remarks>
 78        /// Required, Max length = 255.
 79        /// </remarks>
 80        [MaxLength(255)]
 81        [StringLength(255)]
 82        public string Username { get; set; }
 83
 84        /// <summary>
 85        /// Gets or sets the user's password, or <c>null</c> if none is set.
 86        /// </summary>
 87        /// <remarks>
 88        /// Max length = 65535.
 89        /// </remarks>
 90        [MaxLength(65535)]
 91        [StringLength(65535)]
 92        public string? Password { get; set; }
 93
 94        /// <summary>
 95        /// Gets or sets a value indicating whether the user must update their password.
 96        /// </summary>
 97        /// <remarks>
 98        /// Required.
 99        /// </remarks>
 100        public bool MustUpdatePassword { get; set; }
 101
 102        /// <summary>
 103        /// Gets or sets the audio language preference.
 104        /// </summary>
 105        /// <remarks>
 106        /// Max length = 255.
 107        /// </remarks>
 108        [MaxLength(255)]
 109        [StringLength(255)]
 110        public string? AudioLanguagePreference { get; set; }
 111
 112        /// <summary>
 113        /// Gets or sets the authentication provider id.
 114        /// </summary>
 115        /// <remarks>
 116        /// Required, Max length = 255.
 117        /// </remarks>
 118        [MaxLength(255)]
 119        [StringLength(255)]
 120        public string AuthenticationProviderId { get; set; }
 121
 122        /// <summary>
 123        /// Gets or sets the password reset provider id.
 124        /// </summary>
 125        /// <remarks>
 126        /// Required, Max length = 255.
 127        /// </remarks>
 128        [MaxLength(255)]
 129        [StringLength(255)]
 130        public string PasswordResetProviderId { get; set; }
 131
 132        /// <summary>
 133        /// Gets or sets the invalid login attempt count.
 134        /// </summary>
 135        /// <remarks>
 136        /// Required.
 137        /// </remarks>
 138        public int InvalidLoginAttemptCount { get; set; }
 139
 140        /// <summary>
 141        /// Gets or sets the last activity date.
 142        /// </summary>
 143        public DateTime? LastActivityDate { get; set; }
 144
 145        /// <summary>
 146        /// Gets or sets the last login date.
 147        /// </summary>
 148        public DateTime? LastLoginDate { get; set; }
 149
 150        /// <summary>
 151        /// Gets or sets the number of login attempts the user can make before they are locked out.
 152        /// </summary>
 153        public int? LoginAttemptsBeforeLockout { get; set; }
 154
 155        /// <summary>
 156        /// Gets or sets the maximum number of active sessions the user can have at once.
 157        /// </summary>
 158        public int MaxActiveSessions { get; set; }
 159
 160        /// <summary>
 161        /// Gets or sets the subtitle mode.
 162        /// </summary>
 163        /// <remarks>
 164        /// Required.
 165        /// </remarks>
 166        public SubtitlePlaybackMode SubtitleMode { get; set; }
 167
 168        /// <summary>
 169        /// Gets or sets a value indicating whether the default audio track should be played.
 170        /// </summary>
 171        /// <remarks>
 172        /// Required.
 173        /// </remarks>
 174        public bool PlayDefaultAudioTrack { get; set; }
 175
 176        /// <summary>
 177        /// Gets or sets the subtitle language preference.
 178        /// </summary>
 179        /// <remarks>
 180        /// Max length = 255.
 181        /// </remarks>
 182        [MaxLength(255)]
 183        [StringLength(255)]
 184        public string? SubtitleLanguagePreference { get; set; }
 185
 186        /// <summary>
 187        /// Gets or sets a value indicating whether missing episodes should be displayed.
 188        /// </summary>
 189        /// <remarks>
 190        /// Required.
 191        /// </remarks>
 192        public bool DisplayMissingEpisodes { get; set; }
 193
 194        /// <summary>
 195        /// Gets or sets a value indicating whether to display the collections view.
 196        /// </summary>
 197        /// <remarks>
 198        /// Required.
 199        /// </remarks>
 200        public bool DisplayCollectionsView { get; set; }
 201
 202        /// <summary>
 203        /// Gets or sets a value indicating whether the user has a local password.
 204        /// </summary>
 205        /// <remarks>
 206        /// Required.
 207        /// </remarks>
 208        public bool EnableLocalPassword { get; set; }
 209
 210        /// <summary>
 211        /// Gets or sets a value indicating whether the server should hide played content in "Latest".
 212        /// </summary>
 213        /// <remarks>
 214        /// Required.
 215        /// </remarks>
 216        public bool HidePlayedInLatest { get; set; }
 217
 218        /// <summary>
 219        /// Gets or sets a value indicating whether to remember audio selections on played content.
 220        /// </summary>
 221        /// <remarks>
 222        /// Required.
 223        /// </remarks>
 224        public bool RememberAudioSelections { get; set; }
 225
 226        /// <summary>
 227        /// Gets or sets a value indicating whether to remember subtitle selections on played content.
 228        /// </summary>
 229        /// <remarks>
 230        /// Required.
 231        /// </remarks>
 232        public bool RememberSubtitleSelections { get; set; }
 233
 234        /// <summary>
 235        /// Gets or sets a value indicating whether to enable auto-play for the next episode.
 236        /// </summary>
 237        /// <remarks>
 238        /// Required.
 239        /// </remarks>
 240        public bool EnableNextEpisodeAutoPlay { get; set; }
 241
 242        /// <summary>
 243        /// Gets or sets a value indicating whether the user should auto-login.
 244        /// </summary>
 245        /// <remarks>
 246        /// Required.
 247        /// </remarks>
 248        public bool EnableAutoLogin { get; set; }
 249
 250        /// <summary>
 251        /// Gets or sets a value indicating whether the user can change their preferences.
 252        /// </summary>
 253        /// <remarks>
 254        /// Required.
 255        /// </remarks>
 256        public bool EnableUserPreferenceAccess { get; set; }
 257
 258        /// <summary>
 259        /// Gets or sets the maximum parental age rating.
 260        /// </summary>
 261        public int? MaxParentalAgeRating { get; set; }
 262
 263        /// <summary>
 264        /// Gets or sets the remote client bitrate limit.
 265        /// </summary>
 266        public int? RemoteClientBitrateLimit { get; set; }
 267
 268        /// <summary>
 269        /// Gets or sets the internal id.
 270        /// This is a temporary stopgap for until the library db is migrated.
 271        /// This corresponds to the value of the index of this user in the library db.
 272        /// </summary>
 273        public long InternalId { get; set; }
 274
 275        /// <summary>
 276        /// Gets or sets the user's profile image. Can be <c>null</c>.
 277        /// </summary>
 278        // [ForeignKey("UserId")]
 279        public virtual ImageInfo? ProfileImage { get; set; }
 280
 281        /// <summary>
 282        /// Gets the user's display preferences.
 283        /// </summary>
 284        public virtual ICollection<DisplayPreferences> DisplayPreferences { get; private set; }
 285
 286        /// <summary>
 287        /// Gets or sets the level of sync play permissions this user has.
 288        /// </summary>
 289        public SyncPlayUserAccessType SyncPlayAccess { get; set; }
 290
 291        /// <summary>
 292        /// Gets or sets the cast receiver id.
 293        /// </summary>
 294        [StringLength(32)]
 295        public string? CastReceiverId { get; set; }
 296
 297        /// <inheritdoc />
 298        [ConcurrencyCheck]
 299        public uint RowVersion { get; private set; }
 300
 301        /// <summary>
 302        /// Gets the list of access schedules this user has.
 303        /// </summary>
 304        public virtual ICollection<AccessSchedule> AccessSchedules { get; private set; }
 305
 306        /// <summary>
 307        /// Gets the list of item display preferences.
 308        /// </summary>
 309        public virtual ICollection<ItemDisplayPreferences> ItemDisplayPreferences { get; private set; }
 310
 311        /*
 312        /// <summary>
 313        /// Gets the list of groups this user is a member of.
 314        /// </summary>
 315        public virtual ICollection<Group> Groups { get; private set; }
 316        */
 317
 318        /// <summary>
 319        /// Gets the list of permissions this user has.
 320        /// </summary>
 321        [ForeignKey("Permission_Permissions_Guid")]
 322        public virtual ICollection<Permission> Permissions { get; private set; }
 323
 324        /*
 325        /// <summary>
 326        /// Gets the list of provider mappings this user has.
 327        /// </summary>
 328        public virtual ICollection<ProviderMapping> ProviderMappings { get; private set; }
 329        */
 330
 331        /// <summary>
 332        /// Gets the list of preferences this user has.
 333        /// </summary>
 334        [ForeignKey("Preference_Preferences_Guid")]
 335        public virtual ICollection<Preference> Preferences { get; private set; }
 336
 337        /// <inheritdoc/>
 338        public void OnSavingChanges()
 339        {
 0340            RowVersion++;
 0341        }
 342
 343        /// <summary>
 344        /// Checks whether the user has the specified permission.
 345        /// </summary>
 346        /// <param name="kind">The permission kind.</param>
 347        /// <returns><c>True</c> if the user has the specified permission.</returns>
 348        public bool HasPermission(PermissionKind kind)
 349        {
 1299350            return Permissions.FirstOrDefault(p => p.Kind == kind)?.Value ?? false;
 351        }
 352
 353        /// <summary>
 354        /// Sets the given permission kind to the provided value.
 355        /// </summary>
 356        /// <param name="kind">The permission kind.</param>
 357        /// <param name="value">The value to set.</param>
 358        public void SetPermission(PermissionKind kind, bool value)
 359        {
 72360            var currentPermission = Permissions.FirstOrDefault(p => p.Kind == kind);
 72361            if (currentPermission is null)
 362            {
 0363                Permissions.Add(new Permission(kind, value));
 364            }
 365            else
 366            {
 72367                currentPermission.Value = value;
 368            }
 72369        }
 370
 371        /// <summary>
 372        /// Gets the user's preferences for the given preference kind.
 373        /// </summary>
 374        /// <param name="preference">The preference kind.</param>
 375        /// <returns>A string array containing the user's preferences.</returns>
 376        public string[] GetPreference(PreferenceKind preference)
 377        {
 227378            var val = Preferences.FirstOrDefault(p => p.Kind == preference)?.Value;
 379
 227380            return string.IsNullOrEmpty(val) ? Array.Empty<string>() : val.Split(Delimiter);
 381        }
 382
 383        /// <summary>
 384        /// Gets the user's preferences for the given preference kind.
 385        /// </summary>
 386        /// <param name="preference">The preference kind.</param>
 387        /// <typeparam name="T">Type of preference.</typeparam>
 388        /// <returns>A {T} array containing the user's preference.</returns>
 389        public T[] GetPreferenceValues<T>(PreferenceKind preference)
 390        {
 389391            var val = Preferences.FirstOrDefault(p => p.Kind == preference)?.Value;
 389392            if (string.IsNullOrEmpty(val))
 393            {
 389394                return Array.Empty<T>();
 395            }
 396
 397            // Convert array of {string} to array of {T}
 0398            var converter = TypeDescriptor.GetConverter(typeof(T));
 0399            var stringValues = val.Split(Delimiter);
 0400            var convertedCount = 0;
 0401            var parsedValues = new T[stringValues.Length];
 0402            for (var i = 0; i < stringValues.Length; i++)
 403            {
 404                try
 405                {
 0406                    var parsedValue = converter.ConvertFromString(stringValues[i].Trim());
 0407                    if (parsedValue is not null)
 408                    {
 0409                        parsedValues[convertedCount++] = (T)parsedValue;
 410                    }
 0411                }
 0412                catch (FormatException)
 413                {
 414                    // Unable to convert value
 0415                }
 416            }
 417
 0418            return parsedValues[..convertedCount];
 419        }
 420
 421        /// <summary>
 422        /// Sets the specified preference to the given value.
 423        /// </summary>
 424        /// <param name="preference">The preference kind.</param>
 425        /// <param name="values">The values.</param>
 426        public void SetPreference(PreferenceKind preference, string[] values)
 427        {
 0428            var value = string.Join(Delimiter, values);
 0429            var currentPreference = Preferences.FirstOrDefault(p => p.Kind == preference);
 0430            if (currentPreference is null)
 431            {
 0432                Preferences.Add(new Preference(preference, value));
 433            }
 434            else
 435            {
 0436                currentPreference.Value = value;
 437            }
 0438        }
 439
 440        /// <summary>
 441        /// Sets the specified preference to the given value.
 442        /// </summary>
 443        /// <param name="preference">The preference kind.</param>
 444        /// <param name="values">The values.</param>
 445        /// <typeparam name="T">The type of value.</typeparam>
 446        public void SetPreference<T>(PreferenceKind preference, T[] values)
 447        {
 0448            var value = string.Join(Delimiter, values);
 0449            var currentPreference = Preferences.FirstOrDefault(p => p.Kind == preference);
 0450            if (currentPreference is null)
 451            {
 0452                Preferences.Add(new Preference(preference, value));
 453            }
 454            else
 455            {
 0456                currentPreference.Value = value;
 457            }
 0458        }
 459
 460        /// <summary>
 461        /// Checks whether this user is currently allowed to use the server.
 462        /// </summary>
 463        /// <returns><c>True</c> if the current time is within an access schedule, or there are no access schedules.</re
 464        public bool IsParentalScheduleAllowed()
 465        {
 19466            return AccessSchedules.Count == 0
 19467                   || AccessSchedules.Any(i => IsParentalScheduleAllowed(i, DateTime.UtcNow));
 468        }
 469
 470        /// <summary>
 471        /// Checks whether the provided folder is in this user's grouped folders.
 472        /// </summary>
 473        /// <param name="id">The Guid of the folder.</param>
 474        /// <returns><c>True</c> if the folder is in the user's grouped folders.</returns>
 475        public bool IsFolderGrouped(Guid id)
 476        {
 0477            return Array.IndexOf(GetPreferenceValues<Guid>(PreferenceKind.GroupedFolders), id) != -1;
 478        }
 479
 480        /// <summary>
 481        /// Initializes the default permissions for a user. Should only be called on user creation.
 482        /// </summary>
 483        // TODO: make these user configurable?
 484        public void AddDefaultPermissions()
 485        {
 39486            Permissions.Add(new Permission(PermissionKind.IsAdministrator, false));
 39487            Permissions.Add(new Permission(PermissionKind.IsDisabled, false));
 39488            Permissions.Add(new Permission(PermissionKind.IsHidden, true));
 39489            Permissions.Add(new Permission(PermissionKind.EnableAllChannels, true));
 39490            Permissions.Add(new Permission(PermissionKind.EnableAllDevices, true));
 39491            Permissions.Add(new Permission(PermissionKind.EnableAllFolders, true));
 39492            Permissions.Add(new Permission(PermissionKind.EnableContentDeletion, false));
 39493            Permissions.Add(new Permission(PermissionKind.EnableContentDownloading, true));
 39494            Permissions.Add(new Permission(PermissionKind.EnableMediaConversion, true));
 39495            Permissions.Add(new Permission(PermissionKind.EnableMediaPlayback, true));
 39496            Permissions.Add(new Permission(PermissionKind.EnablePlaybackRemuxing, true));
 39497            Permissions.Add(new Permission(PermissionKind.EnablePublicSharing, true));
 39498            Permissions.Add(new Permission(PermissionKind.EnableRemoteAccess, true));
 39499            Permissions.Add(new Permission(PermissionKind.EnableSyncTranscoding, true));
 39500            Permissions.Add(new Permission(PermissionKind.EnableAudioPlaybackTranscoding, true));
 39501            Permissions.Add(new Permission(PermissionKind.EnableLiveTvAccess, true));
 39502            Permissions.Add(new Permission(PermissionKind.EnableLiveTvManagement, true));
 39503            Permissions.Add(new Permission(PermissionKind.EnableSharedDeviceControl, true));
 39504            Permissions.Add(new Permission(PermissionKind.EnableVideoPlaybackTranscoding, true));
 39505            Permissions.Add(new Permission(PermissionKind.ForceRemoteSourceTranscoding, false));
 39506            Permissions.Add(new Permission(PermissionKind.EnableRemoteControlOfOtherUsers, false));
 39507            Permissions.Add(new Permission(PermissionKind.EnableCollectionManagement, false));
 39508            Permissions.Add(new Permission(PermissionKind.EnableSubtitleManagement, false));
 39509            Permissions.Add(new Permission(PermissionKind.EnableLyricManagement, false));
 39510        }
 511
 512        /// <summary>
 513        /// Initializes the default preferences. Should only be called on user creation.
 514        /// </summary>
 515        public void AddDefaultPreferences()
 516        {
 1092517            foreach (var val in Enum.GetValues(typeof(PreferenceKind)).Cast<PreferenceKind>())
 518            {
 507519                Preferences.Add(new Preference(val, string.Empty));
 520            }
 39521        }
 522
 523        private static bool IsParentalScheduleAllowed(AccessSchedule schedule, DateTime date)
 524        {
 1525            var localTime = date.ToLocalTime();
 1526            var hour = localTime.TimeOfDay.TotalHours;
 1527            var currentDayOfWeek = localTime.DayOfWeek;
 528
 1529            return schedule.DayOfWeek.Contains(currentDayOfWeek)
 1530                   && hour >= schedule.StartHour
 1531                   && hour <= schedule.EndHour;
 532        }
 533    }
 534}