diff options
Diffstat (limited to 'MediaBrowser.Server.Implementations/Library')
3 files changed, 288 insertions, 30 deletions
diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs index 13ea7fb447..5fbdf9815c 100644 --- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs +++ b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs @@ -530,7 +530,9 @@ namespace MediaBrowser.Server.Implementations.Library return item; } - public BaseItem ResolvePath(FileSystemInfo fileInfo, Folder parent = null, string collectionType = null) + public BaseItem ResolvePath(FileSystemInfo fileInfo, + Folder parent = null, + string collectionType = null) { return ResolvePath(fileInfo, new DirectoryService(_logger), parent, collectionType); } @@ -1188,7 +1190,7 @@ namespace MediaBrowser.Server.Implementations.Library return item; } - + /// <summary> /// Gets the intros. /// </summary> @@ -1506,27 +1508,22 @@ namespace MediaBrowser.Server.Implementations.Library return collectionTypes.Count == 1 ? collectionTypes[0] : null; } - public Task<UserView> GetNamedView(string name, string type, string sortName, CancellationToken cancellationToken) - { - return GetNamedView(name, null, type, sortName, cancellationToken); - } - - public async Task<UserView> GetNamedView(string name, string category, string type, string sortName, CancellationToken cancellationToken) + public async Task<UserView> GetNamedView(string name, + string type, + string sortName, + CancellationToken cancellationToken) { var path = Path.Combine(ConfigurationManager.ApplicationPaths.ItemsByNamePath, "views"); - if (!string.IsNullOrWhiteSpace(category)) - { - path = Path.Combine(path, _fileSystem.GetValidFilename(category)); - } - path = Path.Combine(path, _fileSystem.GetValidFilename(type)); var id = (path + "_namedview_" + name).GetMBId(typeof(UserView)); var item = GetItemById(id) as UserView; + var refresh = false; + if (item == null || !string.Equals(item.Path, path, StringComparison.OrdinalIgnoreCase)) { @@ -1544,7 +1541,89 @@ namespace MediaBrowser.Server.Implementations.Library await CreateItem(item, cancellationToken).ConfigureAwait(false); - await item.RefreshMetadata(cancellationToken).ConfigureAwait(false); + refresh = true; + } + + if (!refresh && item != null) + { + refresh = (DateTime.UtcNow - item.DateLastSaved).TotalHours >= 24; + } + + if (refresh) + { + await item.RefreshMetadata(new MetadataRefreshOptions + { + ForceSave = true + + }, cancellationToken).ConfigureAwait(false); + } + + return item; + } + + public async Task<UserView> GetSpecialFolder(User user, + string name, + string parentId, + string viewType, + string sortName, + CancellationToken cancellationToken) + { + if (string.IsNullOrWhiteSpace(name)) + { + throw new ArgumentNullException("name"); + } + + if (string.IsNullOrWhiteSpace(parentId)) + { + throw new ArgumentNullException("parentId"); + } + + if (string.IsNullOrWhiteSpace(viewType)) + { + throw new ArgumentNullException("viewType"); + } + + var id = ("7_namedview_" + name + user.Id.ToString("N") + parentId).GetMBId(typeof(UserView)); + + var path = BaseItem.GetInternalMetadataPathForId(id); + + var item = GetItemById(id) as UserView; + + var refresh = false; + + if (item == null) + { + Directory.CreateDirectory(path); + + item = new UserView + { + Path = path, + Id = id, + DateCreated = DateTime.UtcNow, + Name = name, + ViewType = viewType, + ForcedSortName = sortName, + UserId = user.Id, + ParentId = new Guid(parentId) + }; + + await CreateItem(item, cancellationToken).ConfigureAwait(false); + + refresh = true; + } + + if (!refresh && item != null) + { + refresh = (DateTime.UtcNow - item.DateLastSaved).TotalHours >= 24; + } + + if (refresh) + { + await item.RefreshMetadata(new MetadataRefreshOptions + { + ForceSave = true + + }, cancellationToken).ConfigureAwait(false); } return item; diff --git a/MediaBrowser.Server.Implementations/Library/UserManager.cs b/MediaBrowser.Server.Implementations/Library/UserManager.cs index f96d9882d0..9e3fd3f176 100644 --- a/MediaBrowser.Server.Implementations/Library/UserManager.cs +++ b/MediaBrowser.Server.Implementations/Library/UserManager.cs @@ -2,6 +2,7 @@ using MediaBrowser.Common.Events; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; +using MediaBrowser.Controller; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Connect; using MediaBrowser.Controller.Drawing; @@ -17,9 +18,11 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.Events; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Serialization; +using MediaBrowser.Model.Users; using MediaBrowser.Server.Implementations.Security; using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Security.Cryptography; @@ -65,7 +68,7 @@ namespace MediaBrowser.Server.Implementations.Library private readonly Func<IImageProcessor> _imageProcessorFactory; private readonly Func<IDtoService> _dtoServiceFactory; private readonly Func<IConnectManager> _connectFactory; - private readonly IApplicationHost _appHost; + private readonly IServerApplicationHost _appHost; /// <summary> /// Initializes a new instance of the <see cref="UserManager" /> class. @@ -73,7 +76,7 @@ namespace MediaBrowser.Server.Implementations.Library /// <param name="logger">The logger.</param> /// <param name="configurationManager">The configuration manager.</param> /// <param name="userRepository">The user repository.</param> - public UserManager(ILogger logger, IServerConfigurationManager configurationManager, IUserRepository userRepository, IXmlSerializer xmlSerializer, INetworkManager networkManager, Func<IImageProcessor> imageProcessorFactory, Func<IDtoService> dtoServiceFactory, Func<IConnectManager> connectFactory, IApplicationHost appHost) + public UserManager(ILogger logger, IServerConfigurationManager configurationManager, IUserRepository userRepository, IXmlSerializer xmlSerializer, INetworkManager networkManager, Func<IImageProcessor> imageProcessorFactory, Func<IDtoService> dtoServiceFactory, Func<IConnectManager> connectFactory, IServerApplicationHost appHost) { _logger = logger; UserRepository = userRepository; @@ -85,6 +88,8 @@ namespace MediaBrowser.Server.Implementations.Library _appHost = appHost; ConfigurationManager = configurationManager; Users = new List<User>(); + + DeletePinFile(); } #region UserUpdated Event @@ -145,6 +150,16 @@ namespace MediaBrowser.Server.Implementations.Library return GetUserById(new Guid(id)); } + public User GetUserByName(string name) + { + if (string.IsNullOrWhiteSpace(name)) + { + throw new ArgumentNullException("name"); + } + + return Users.FirstOrDefault(u => string.Equals(u.Name, name, StringComparison.OrdinalIgnoreCase)); + } + public async Task Initialize() { Users = await LoadUsers().ConfigureAwait(false); @@ -528,28 +543,38 @@ namespace MediaBrowser.Server.Implementations.Library /// <returns>Task.</returns> public Task ResetPassword(User user) { - return ChangePassword(user, string.Empty); + return ChangePassword(user, GetSha1String(string.Empty)); } /// <summary> /// Changes the password. /// </summary> /// <param name="user">The user.</param> - /// <param name="newPassword">The new password.</param> + /// <param name="newPasswordSha1">The new password sha1.</param> /// <returns>Task.</returns> - public async Task ChangePassword(User user, string newPassword) + /// <exception cref="System.ArgumentNullException"> + /// user + /// or + /// newPassword + /// </exception> + /// <exception cref="System.ArgumentException">Passwords for guests cannot be changed.</exception> + public async Task ChangePassword(User user, string newPasswordSha1) { if (user == null) { throw new ArgumentNullException("user"); } + if (string.IsNullOrWhiteSpace(newPasswordSha1)) + { + throw new ArgumentNullException("newPasswordSha1"); + } if (user.ConnectLinkType.HasValue && user.ConnectLinkType.Value == UserLinkType.Guest) { throw new ArgumentException("Passwords for guests cannot be changed."); } - user.Password = string.IsNullOrEmpty(newPassword) ? GetSha1String(string.Empty) : GetSha1String(newPassword); + user.Password = newPasswordSha1; await UpdateUser(user).ConfigureAwait(false); @@ -589,5 +614,157 @@ namespace MediaBrowser.Server.Implementations.Library EventHelper.FireEventIfNotNull(UserConfigurationUpdated, this, new GenericEventArgs<User> { Argument = user }, _logger); } + + private string PasswordResetFile + { + get { return Path.Combine(ConfigurationManager.ApplicationPaths.ProgramDataPath, "passwordreset.txt"); } + } + + private string _lastPin; + private PasswordPinCreationResult _lastPasswordPinCreationResult; + private int _pinAttempts; + + private PasswordPinCreationResult CreatePasswordResetPin() + { + var num = new Random().Next(1, 9999); + + var path = PasswordResetFile; + + var pin = num.ToString("0000", CultureInfo.InvariantCulture); + _lastPin = pin; + + var time = TimeSpan.FromMinutes(5); + var expiration = DateTime.UtcNow.Add(time); + + var text = new StringBuilder(); + + var info = _appHost.GetSystemInfo(); + var localAddress = info.LocalAddress ?? string.Empty; + + text.AppendLine("Use your web browser to visit:"); + text.AppendLine(string.Empty); + text.AppendLine(localAddress + "/mediabrowser/web/forgotpasswordpin.html"); + text.AppendLine(string.Empty); + text.AppendLine("Enter the following pin code:"); + text.AppendLine(string.Empty); + text.AppendLine(pin); + text.AppendLine(string.Empty); + text.AppendLine("The pin code will expire at " + expiration.ToLocalTime().ToShortDateString() + " " + expiration.ToLocalTime().ToShortTimeString()); + + File.WriteAllText(path, text.ToString(), Encoding.UTF8); + + var result = new PasswordPinCreationResult + { + PinFile = path, + ExpirationDate = expiration + }; + + _lastPasswordPinCreationResult = result; + _pinAttempts = 0; + + return result; + } + + public ForgotPasswordResult StartForgotPasswordProcess(string enteredUsername, bool isInNetwork) + { + DeletePinFile(); + + var user = string.IsNullOrWhiteSpace(enteredUsername) ? + null : + GetUserByName(enteredUsername); + + if (user != null && user.ConnectLinkType.HasValue && user.ConnectLinkType.Value == UserLinkType.Guest) + { + throw new ArgumentException("Unable to process forgot password request for guests."); + } + + var action = ForgotPasswordAction.InNetworkRequired; + string pinFile = null; + DateTime? expirationDate = null; + + if (user != null && !user.Configuration.IsAdministrator) + { + action = ForgotPasswordAction.ContactAdmin; + } + else + { + if (isInNetwork) + { + action = ForgotPasswordAction.PinCode; + } + + var result = CreatePasswordResetPin(); + pinFile = result.PinFile; + expirationDate = result.ExpirationDate; + } + + return new ForgotPasswordResult + { + Action = action, + PinFile = pinFile, + PinExpirationDate = expirationDate + }; + } + + public async Task<PinRedeemResult> RedeemPasswordResetPin(string pin) + { + DeletePinFile(); + + var usersReset = new List<string>(); + + var valid = !string.IsNullOrWhiteSpace(_lastPin) && + string.Equals(_lastPin, pin, StringComparison.OrdinalIgnoreCase) && + _lastPasswordPinCreationResult != null && + _lastPasswordPinCreationResult.ExpirationDate > DateTime.UtcNow; + + if (valid) + { + _lastPin = null; + _lastPasswordPinCreationResult = null; + + var users = Users.Where(i => !i.ConnectLinkType.HasValue || i.ConnectLinkType.Value != UserLinkType.Guest) + .ToList(); + + foreach (var user in users) + { + await ResetPassword(user).ConfigureAwait(false); + usersReset.Add(user.Name); + } + } + else + { + _pinAttempts++; + if (_pinAttempts >= 3) + { + _lastPin = null; + _lastPasswordPinCreationResult = null; + } + } + + return new PinRedeemResult + { + Success = valid, + UsersReset = usersReset.ToArray() + }; + } + + private void DeletePinFile() + { + try + { + File.Delete(PasswordResetFile); + } + catch + { + + } + } + + class PasswordPinCreationResult + { + public string PinFile { get; set; } + public DateTime ExpirationDate { get; set; } + } + } } diff --git a/MediaBrowser.Server.Implementations/Library/UserViewManager.cs b/MediaBrowser.Server.Implementations/Library/UserViewManager.cs index 768c29ce08..d2d435414c 100644 --- a/MediaBrowser.Server.Implementations/Library/UserViewManager.cs +++ b/MediaBrowser.Server.Implementations/Library/UserViewManager.cs @@ -68,24 +68,24 @@ namespace MediaBrowser.Server.Implementations.Library if (foldersWithViewTypes.Any(i => string.Equals(i.CollectionType, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase)) || foldersWithViewTypes.Any(i => string.IsNullOrWhiteSpace(i.CollectionType))) { - list.Add(await GetUserView(CollectionType.TvShows, user, string.Empty, cancellationToken).ConfigureAwait(false)); + list.Add(await GetUserView(CollectionType.TvShows, string.Empty, cancellationToken).ConfigureAwait(false)); } if (foldersWithViewTypes.Any(i => string.Equals(i.CollectionType, CollectionType.Music, StringComparison.OrdinalIgnoreCase)) || foldersWithViewTypes.Any(i => string.Equals(i.CollectionType, CollectionType.MusicVideos, StringComparison.OrdinalIgnoreCase))) { - list.Add(await GetUserView(CollectionType.Music, user, string.Empty, cancellationToken).ConfigureAwait(false)); + list.Add(await GetUserView(CollectionType.Music, string.Empty, cancellationToken).ConfigureAwait(false)); } if (foldersWithViewTypes.Any(i => string.Equals(i.CollectionType, CollectionType.Movies, StringComparison.OrdinalIgnoreCase)) || foldersWithViewTypes.Any(i => string.IsNullOrWhiteSpace(i.CollectionType))) { - list.Add(await GetUserView(CollectionType.Movies, user, string.Empty, cancellationToken).ConfigureAwait(false)); + list.Add(await GetUserView(CollectionType.Movies, string.Empty, cancellationToken).ConfigureAwait(false)); } if (foldersWithViewTypes.Any(i => string.Equals(i.CollectionType, CollectionType.Games, StringComparison.OrdinalIgnoreCase))) { - list.Add(await GetUserView(CollectionType.Games, user, string.Empty, cancellationToken).ConfigureAwait(false)); + list.Add(await GetUserView(CollectionType.Games, string.Empty, cancellationToken).ConfigureAwait(false)); } if (user.Configuration.DisplayCollectionsView && @@ -93,7 +93,7 @@ namespace MediaBrowser.Server.Implementations.Library .Except(standaloneFolders) .SelectMany(i => i.GetRecursiveChildren(user, false)).OfType<BoxSet>().Any()) { - list.Add(await GetUserView(CollectionType.BoxSets, user, string.Empty, cancellationToken).ConfigureAwait(false)); + list.Add(await GetUserView(CollectionType.BoxSets, string.Empty, cancellationToken).ConfigureAwait(false)); } if (foldersWithViewTypes.Any(i => string.Equals(i.CollectionType, CollectionType.Playlists, StringComparison.OrdinalIgnoreCase))) @@ -103,7 +103,7 @@ namespace MediaBrowser.Server.Implementations.Library if (user.Configuration.DisplayFoldersView) { - list.Add(await GetUserView(CollectionType.Folders, user, "zz_" + CollectionType.Folders, cancellationToken).ConfigureAwait(false)); + list.Add(await GetUserView(CollectionType.Folders, "zz_" + CollectionType.Folders, cancellationToken).ConfigureAwait(false)); } if (query.IncludeExternalContent) @@ -148,16 +148,18 @@ namespace MediaBrowser.Server.Implementations.Library .ThenBy(i => i.SortName); } - public Task<UserView> GetUserView(string category, string type, User user, string sortName, CancellationToken cancellationToken) + public Task<UserView> GetUserView(string parentId, string type, User user, string sortName, CancellationToken cancellationToken) { var name = _localizationManager.GetLocalizedString("ViewType" + type); - return _libraryManager.GetNamedView(name, category, type, sortName, cancellationToken); + return _libraryManager.GetSpecialFolder(user, name, parentId, type, sortName, cancellationToken); } - public Task<UserView> GetUserView(string type, User user, string sortName, CancellationToken cancellationToken) + public Task<UserView> GetUserView(string type, string sortName, CancellationToken cancellationToken) { - return GetUserView(null, type, user, sortName, cancellationToken); + var name = _localizationManager.GetLocalizedString("ViewType" + type); + + return _libraryManager.GetNamedView(name, type, sortName, cancellationToken); } } } |
