| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426 | using MediaBrowser.Common.Events;using MediaBrowser.Common.Extensions;using MediaBrowser.Controller.Configuration;using MediaBrowser.Controller.Entities;using MediaBrowser.Controller.Library;using MediaBrowser.Controller.Persistence;using MediaBrowser.Model.Logging;using System;using System.Collections.Generic;using System.IO;using System.Linq;using System.Security.Cryptography;using System.Text;using System.Threading;using System.Threading.Tasks;namespace MediaBrowser.Server.Implementations.Library{    /// <summary>    /// Class UserManager    /// </summary>    public class UserManager : IUserManager    {        /// <summary>        /// The _users        /// </summary>        private IEnumerable<User> _users;        /// <summary>        /// The _user lock        /// </summary>        private object _usersSyncLock = new object();        /// <summary>        /// The _users initialized        /// </summary>        private bool _usersInitialized;        /// <summary>        /// Gets the users.        /// </summary>        /// <value>The users.</value>        public IEnumerable<User> Users        {            get            {                // Call ToList to exhaust the stream because we'll be iterating over this multiple times                LazyInitializer.EnsureInitialized(ref _users, ref _usersInitialized, ref _usersSyncLock, LoadUsers);                return _users;            }            internal set            {                _users = value;                if (value == null)                {                    _usersInitialized = false;                }            }        }        /// <summary>        /// The _logger        /// </summary>        private readonly ILogger _logger;        /// <summary>        /// Gets or sets the configuration manager.        /// </summary>        /// <value>The configuration manager.</value>        private IServerConfigurationManager ConfigurationManager { get; set; }        /// <summary>        /// Gets the active user repository        /// </summary>        /// <value>The user repository.</value>        private IUserRepository UserRepository { get; set; }        /// <summary>        /// Initializes a new instance of the <see cref="UserManager" /> class.        /// </summary>        /// <param name="logger">The logger.</param>        /// <param name="configurationManager">The configuration manager.</param>        public UserManager(ILogger logger, IServerConfigurationManager configurationManager, IUserRepository userRepository)        {            _logger = logger;            UserRepository = userRepository;            ConfigurationManager = configurationManager;        }        #region UserUpdated Event        /// <summary>        /// Occurs when [user updated].        /// </summary>        public event EventHandler<GenericEventArgs<User>> UserUpdated;        /// <summary>        /// Called when [user updated].        /// </summary>        /// <param name="user">The user.</param>        private void OnUserUpdated(User user)        {            EventHelper.QueueEventIfNotNull(UserUpdated, this, new GenericEventArgs<User> { Argument = user }, _logger);        }        #endregion        #region UserDeleted Event        /// <summary>        /// Occurs when [user deleted].        /// </summary>        public event EventHandler<GenericEventArgs<User>> UserDeleted;        /// <summary>        /// Called when [user deleted].        /// </summary>        /// <param name="user">The user.</param>        private void OnUserDeleted(User user)        {            EventHelper.QueueEventIfNotNull(UserDeleted, this, new GenericEventArgs<User> { Argument = user }, _logger);        }        #endregion        /// <summary>        /// Gets a User by Id        /// </summary>        /// <param name="id">The id.</param>        /// <returns>User.</returns>        /// <exception cref="System.ArgumentNullException"></exception>        public User GetUserById(Guid id)        {            if (id == Guid.Empty)            {                throw new ArgumentNullException("id");            }            return Users.FirstOrDefault(u => u.Id == id);        }        /// <summary>        /// Authenticates a User and returns a result indicating whether or not it succeeded        /// </summary>        /// <param name="user">The user.</param>        /// <param name="password">The password.</param>        /// <returns>Task{System.Boolean}.</returns>        /// <exception cref="System.ArgumentNullException">user</exception>        public async Task<bool> AuthenticateUser(User user, string password)        {            if (user == null)            {                throw new ArgumentNullException("user");            }            if (user.Configuration.IsDisabled)            {                throw new UnauthorizedAccessException(string.Format("The {0} account is currently disabled. Please consult with your administrator.", user.Name));            }            var existingPasswordString = string.IsNullOrEmpty(user.Password) ? GetSha1String(string.Empty) : user.Password;            var success = string.Equals(existingPasswordString, password.Replace("-", string.Empty), StringComparison.OrdinalIgnoreCase);            // Update LastActivityDate and LastLoginDate, then save            if (success)            {                user.LastActivityDate = user.LastLoginDate = DateTime.UtcNow;                await UpdateUser(user).ConfigureAwait(false);            }            _logger.Info("Authentication request for {0} {1}.", user.Name, (success ? "has succeeded" : "has been denied"));            return success;        }        /// <summary>        /// Gets the sha1 string.        /// </summary>        /// <param name="str">The STR.</param>        /// <returns>System.String.</returns>        private static string GetSha1String(string str)        {            using (var provider = SHA1.Create())            {                var hash = provider.ComputeHash(Encoding.UTF8.GetBytes(str));                return BitConverter.ToString(hash).Replace("-", string.Empty);            }        }        /// <summary>        /// Loads the users from the repository        /// </summary>        /// <returns>IEnumerable{User}.</returns>        private IEnumerable<User> LoadUsers()        {            var users = UserRepository.RetrieveAllUsers().ToList();            // There always has to be at least one user.            if (users.Count == 0)            {                var name = Environment.UserName;                var user = InstantiateNewUser(name);                var task = UserRepository.SaveUser(user, CancellationToken.None);                // Hate having to block threads                Task.WaitAll(task);                users.Add(user);            }            return users;        }        /// <summary>        /// Refreshes metadata for each user        /// </summary>        /// <param name="cancellationToken">The cancellation token.</param>        /// <param name="force">if set to <c>true</c> [force].</param>        /// <returns>Task.</returns>        public Task RefreshUsersMetadata(CancellationToken cancellationToken, bool force = false)        {            var tasks = Users.Select(user => user.RefreshMetadata(cancellationToken, forceRefresh: force)).ToList();            return Task.WhenAll(tasks);        }        /// <summary>        /// Renames the user.        /// </summary>        /// <param name="user">The user.</param>        /// <param name="newName">The new name.</param>        /// <returns>Task.</returns>        /// <exception cref="System.ArgumentNullException">user</exception>        /// <exception cref="System.ArgumentException"></exception>        public async Task RenameUser(User user, string newName)        {            if (user == null)            {                throw new ArgumentNullException("user");            }            if (string.IsNullOrEmpty(newName))            {                throw new ArgumentNullException("newName");            }            if (Users.Any(u => u.Id != user.Id && u.Name.Equals(newName, StringComparison.OrdinalIgnoreCase)))            {                throw new ArgumentException(string.Format("A user with the name '{0}' already exists.", newName));            }            if (user.Name.Equals(newName, StringComparison.Ordinal))            {                throw new ArgumentException("The new and old names must be different.");            }            await user.Rename(newName);            OnUserUpdated(user);        }        /// <summary>        /// Updates the user.        /// </summary>        /// <param name="user">The user.</param>        /// <exception cref="System.ArgumentNullException">user</exception>        /// <exception cref="System.ArgumentException"></exception>        public async Task UpdateUser(User user)        {            if (user == null)            {                throw new ArgumentNullException("user");            }            if (user.Id == Guid.Empty || !Users.Any(u => u.Id.Equals(user.Id)))            {                throw new ArgumentException(string.Format("User with name '{0}' and Id {1} does not exist.", user.Name, user.Id));            }            user.DateModified = DateTime.UtcNow;            await UserRepository.SaveUser(user, CancellationToken.None).ConfigureAwait(false);            OnUserUpdated(user);        }        public event EventHandler<GenericEventArgs<User>> UserCreated;                /// <summary>        /// Creates the user.        /// </summary>        /// <param name="name">The name.</param>        /// <returns>User.</returns>        /// <exception cref="System.ArgumentNullException">name</exception>        /// <exception cref="System.ArgumentException"></exception>        public async Task<User> CreateUser(string name)        {            if (string.IsNullOrEmpty(name))            {                throw new ArgumentNullException("name");            }            if (Users.Any(u => u.Name.Equals(name, StringComparison.OrdinalIgnoreCase)))            {                throw new ArgumentException(string.Format("A user with the name '{0}' already exists.", name));            }            var user = InstantiateNewUser(name);            var list = Users.ToList();            list.Add(user);            Users = list;            await UserRepository.SaveUser(user, CancellationToken.None).ConfigureAwait(false);            EventHelper.QueueEventIfNotNull(UserCreated, this, new GenericEventArgs<User> { Argument = user }, _logger);                        return user;        }        /// <summary>        /// Deletes the user.        /// </summary>        /// <param name="user">The user.</param>        /// <returns>Task.</returns>        /// <exception cref="System.ArgumentNullException">user</exception>        /// <exception cref="System.ArgumentException"></exception>        public async Task DeleteUser(User user)        {            if (user == null)            {                throw new ArgumentNullException("user");            }            var allUsers = Users.ToList();            if (allUsers.FirstOrDefault(u => u.Id == user.Id) == null)            {                throw new ArgumentException(string.Format("The user cannot be deleted because there is no user with the Name {0} and Id {1}.", user.Name, user.Id));            }            if (allUsers.Count == 1)            {                throw new ArgumentException(string.Format("The user '{0}' cannot be deleted because there must be at least one user in the system.", user.Name));            }            if (user.Configuration.IsAdministrator && allUsers.Count(i => i.Configuration.IsAdministrator) == 1)            {                throw new ArgumentException(string.Format("The user '{0}' cannot be deleted because there must be at least one admin user in the system.", user.Name));            }            await UserRepository.DeleteUser(user, CancellationToken.None).ConfigureAwait(false);            if (user.Configuration.UseCustomLibrary)            {                var path = user.RootFolderPath;                try                {                    Directory.Delete(path, true);                }                catch (IOException ex)                {                    _logger.ErrorException("Error deleting directory {0}", ex, path);                }                path = user.ConfigurationFilePath;                try                {                    File.Delete(path);                }                catch (IOException ex)                {                    _logger.ErrorException("Error deleting file {0}", ex, path);                }            }            OnUserDeleted(user);            // Force this to be lazy loaded again            Users = null;        }        /// <summary>        /// Resets the password by clearing it.        /// </summary>        /// <returns>Task.</returns>        public Task ResetPassword(User user)        {            return ChangePassword(user, string.Empty);        }        /// <summary>        /// Changes the password.        /// </summary>        /// <param name="user">The user.</param>        /// <param name="newPassword">The new password.</param>        /// <returns>Task.</returns>        public Task ChangePassword(User user, string newPassword)        {            if (user == null)            {                throw new ArgumentNullException("user");            }            user.Password = string.IsNullOrEmpty(newPassword) ? string.Empty : GetSha1String(newPassword);            return UpdateUser(user);        }        /// <summary>        /// Instantiates the new user.        /// </summary>        /// <param name="name">The name.</param>        /// <returns>User.</returns>        private User InstantiateNewUser(string name)        {            return new User            {                Name = name,                Id = ("MBUser" + name).GetMD5(),                DateCreated = DateTime.UtcNow,                DateModified = DateTime.UtcNow            };        }    }}
 |