UserManager.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403
  1. using MediaBrowser.Common.Events;
  2. using MediaBrowser.Common.Extensions;
  3. using MediaBrowser.Common.Kernel;
  4. using MediaBrowser.Controller.Entities;
  5. using MediaBrowser.Model.Connectivity;
  6. using System;
  7. using System.Collections.Concurrent;
  8. using System.Collections.Generic;
  9. using System.Linq;
  10. using System.Threading;
  11. using System.Threading.Tasks;
  12. using MediaBrowser.Model.Logging;
  13. namespace MediaBrowser.Controller.Library
  14. {
  15. /// <summary>
  16. /// Class UserManager
  17. /// </summary>
  18. public class UserManager : BaseManager<Kernel>
  19. {
  20. /// <summary>
  21. /// The _active connections
  22. /// </summary>
  23. private readonly ConcurrentBag<ClientConnectionInfo> _activeConnections =
  24. new ConcurrentBag<ClientConnectionInfo>();
  25. /// <summary>
  26. /// Gets all connections.
  27. /// </summary>
  28. /// <value>All connections.</value>
  29. public IEnumerable<ClientConnectionInfo> AllConnections
  30. {
  31. get { return _activeConnections.Where(c => Kernel.GetUserById(c.UserId) != null).OrderByDescending(c => c.LastActivityDate); }
  32. }
  33. /// <summary>
  34. /// Gets the active connections.
  35. /// </summary>
  36. /// <value>The active connections.</value>
  37. public IEnumerable<ClientConnectionInfo> ActiveConnections
  38. {
  39. get { return AllConnections.Where(c => (DateTime.UtcNow - c.LastActivityDate).TotalMinutes <= 10); }
  40. }
  41. /// <summary>
  42. /// The _logger
  43. /// </summary>
  44. private readonly ILogger _logger;
  45. /// <summary>
  46. /// Initializes a new instance of the <see cref="UserManager" /> class.
  47. /// </summary>
  48. /// <param name="kernel">The kernel.</param>
  49. /// <param name="logger">The logger.</param>
  50. public UserManager(Kernel kernel, ILogger logger)
  51. : base(kernel)
  52. {
  53. _logger = logger;
  54. }
  55. #region UserUpdated Event
  56. /// <summary>
  57. /// Occurs when [user updated].
  58. /// </summary>
  59. public event EventHandler<GenericEventArgs<User>> UserUpdated;
  60. /// <summary>
  61. /// Called when [user updated].
  62. /// </summary>
  63. /// <param name="user">The user.</param>
  64. internal void OnUserUpdated(User user)
  65. {
  66. EventHelper.QueueEventIfNotNull(UserUpdated, this, new GenericEventArgs<User> { Argument = user }, _logger);
  67. // Notify connected ui's
  68. Kernel.TcpManager.SendWebSocketMessage("UserUpdated", new DtoBuilder(_logger).GetDtoUser(user));
  69. }
  70. #endregion
  71. #region UserDeleted Event
  72. /// <summary>
  73. /// Occurs when [user deleted].
  74. /// </summary>
  75. public event EventHandler<GenericEventArgs<User>> UserDeleted;
  76. /// <summary>
  77. /// Called when [user deleted].
  78. /// </summary>
  79. /// <param name="user">The user.</param>
  80. internal void OnUserDeleted(User user)
  81. {
  82. EventHelper.QueueEventIfNotNull(UserDeleted, this, new GenericEventArgs<User> { Argument = user }, _logger);
  83. // Notify connected ui's
  84. Kernel.TcpManager.SendWebSocketMessage("UserDeleted", user.Id.ToString());
  85. }
  86. #endregion
  87. /// <summary>
  88. /// Authenticates a User and returns a result indicating whether or not it succeeded
  89. /// </summary>
  90. /// <param name="user">The user.</param>
  91. /// <param name="password">The password.</param>
  92. /// <returns>Task{System.Boolean}.</returns>
  93. /// <exception cref="System.ArgumentNullException">user</exception>
  94. public async Task<bool> AuthenticateUser(User user, string password)
  95. {
  96. if (user == null)
  97. {
  98. throw new ArgumentNullException("user");
  99. }
  100. password = password ?? string.Empty;
  101. var existingPassword = string.IsNullOrEmpty(user.Password) ? string.Empty.GetMD5().ToString() : user.Password;
  102. var success = password.GetMD5().ToString().Equals(existingPassword);
  103. // Update LastActivityDate and LastLoginDate, then save
  104. if (success)
  105. {
  106. user.LastActivityDate = user.LastLoginDate = DateTime.UtcNow;
  107. await UpdateUser(user).ConfigureAwait(false);
  108. }
  109. _logger.Info("Authentication request for {0} {1}.", user.Name, (success ? "has succeeded" : "has been denied"));
  110. return success;
  111. }
  112. /// <summary>
  113. /// Logs the user activity.
  114. /// </summary>
  115. /// <param name="user">The user.</param>
  116. /// <param name="clientType">Type of the client.</param>
  117. /// <param name="deviceName">Name of the device.</param>
  118. /// <returns>Task.</returns>
  119. /// <exception cref="System.ArgumentNullException">user</exception>
  120. public Task LogUserActivity(User user, ClientType clientType, string deviceName)
  121. {
  122. if (user == null)
  123. {
  124. throw new ArgumentNullException("user");
  125. }
  126. var activityDate = DateTime.UtcNow;
  127. user.LastActivityDate = activityDate;
  128. LogConnection(user.Id, clientType, deviceName, activityDate);
  129. // Save this directly. No need to fire off all the events for this.
  130. return Kernel.UserRepository.SaveUser(user, CancellationToken.None);
  131. }
  132. /// <summary>
  133. /// Updates the now playing item id.
  134. /// </summary>
  135. /// <param name="user">The user.</param>
  136. /// <param name="clientType">Type of the client.</param>
  137. /// <param name="deviceName">Name of the device.</param>
  138. /// <param name="item">The item.</param>
  139. /// <param name="currentPositionTicks">The current position ticks.</param>
  140. public void UpdateNowPlayingItemId(User user, ClientType clientType, string deviceName, BaseItem item, long? currentPositionTicks = null)
  141. {
  142. var conn = GetConnection(user.Id, clientType, deviceName);
  143. conn.NowPlayingPositionTicks = currentPositionTicks;
  144. conn.NowPlayingItem = DtoBuilder.GetBaseItemInfo(item);
  145. }
  146. /// <summary>
  147. /// Removes the now playing item id.
  148. /// </summary>
  149. /// <param name="user">The user.</param>
  150. /// <param name="clientType">Type of the client.</param>
  151. /// <param name="deviceName">Name of the device.</param>
  152. /// <param name="item">The item.</param>
  153. public void RemoveNowPlayingItemId(User user, ClientType clientType, string deviceName, BaseItem item)
  154. {
  155. var conn = GetConnection(user.Id, clientType, deviceName);
  156. if (conn.NowPlayingItem != null && conn.NowPlayingItem.Id.Equals(item.Id.ToString()))
  157. {
  158. conn.NowPlayingItem = null;
  159. conn.NowPlayingPositionTicks = null;
  160. }
  161. }
  162. /// <summary>
  163. /// Logs the connection.
  164. /// </summary>
  165. /// <param name="userId">The user id.</param>
  166. /// <param name="clientType">Type of the client.</param>
  167. /// <param name="deviceName">Name of the device.</param>
  168. /// <param name="lastActivityDate">The last activity date.</param>
  169. private void LogConnection(Guid userId, ClientType clientType, string deviceName, DateTime lastActivityDate)
  170. {
  171. GetConnection(userId, clientType, deviceName).LastActivityDate = lastActivityDate;
  172. }
  173. /// <summary>
  174. /// Gets the connection.
  175. /// </summary>
  176. /// <param name="userId">The user id.</param>
  177. /// <param name="clientType">Type of the client.</param>
  178. /// <param name="deviceName">Name of the device.</param>
  179. /// <returns>ClientConnectionInfo.</returns>
  180. private ClientConnectionInfo GetConnection(Guid userId, ClientType clientType, string deviceName)
  181. {
  182. var conn = _activeConnections.FirstOrDefault(c => c.UserId == userId && c.ClientType == clientType && string.Equals(deviceName, c.DeviceName, StringComparison.OrdinalIgnoreCase));
  183. if (conn == null)
  184. {
  185. conn = new ClientConnectionInfo
  186. {
  187. UserId = userId,
  188. ClientType = clientType,
  189. DeviceName = deviceName
  190. };
  191. _activeConnections.Add(conn);
  192. }
  193. return conn;
  194. }
  195. /// <summary>
  196. /// Loads the users from the repository
  197. /// </summary>
  198. /// <returns>IEnumerable{User}.</returns>
  199. internal IEnumerable<User> LoadUsers()
  200. {
  201. var users = Kernel.UserRepository.RetrieveAllUsers().ToList();
  202. // There always has to be at least one user.
  203. if (users.Count == 0)
  204. {
  205. var name = Environment.UserName;
  206. var user = InstantiateNewUser(name);
  207. var task = Kernel.UserRepository.SaveUser(user, CancellationToken.None);
  208. // Hate having to block threads
  209. Task.WaitAll(task);
  210. users.Add(user);
  211. }
  212. return users;
  213. }
  214. /// <summary>
  215. /// Refreshes metadata for each user
  216. /// </summary>
  217. /// <param name="cancellationToken">The cancellation token.</param>
  218. /// <param name="force">if set to <c>true</c> [force].</param>
  219. /// <returns>Task.</returns>
  220. public Task RefreshUsersMetadata(CancellationToken cancellationToken, bool force = false)
  221. {
  222. var tasks = Kernel.Users.Select(user => user.RefreshMetadata(cancellationToken, forceRefresh: force)).ToList();
  223. return Task.WhenAll(tasks);
  224. }
  225. /// <summary>
  226. /// Renames the user.
  227. /// </summary>
  228. /// <param name="user">The user.</param>
  229. /// <param name="newName">The new name.</param>
  230. /// <returns>Task.</returns>
  231. /// <exception cref="System.ArgumentNullException">user</exception>
  232. /// <exception cref="System.ArgumentException"></exception>
  233. public async Task RenameUser(User user, string newName)
  234. {
  235. if (user == null)
  236. {
  237. throw new ArgumentNullException("user");
  238. }
  239. if (string.IsNullOrEmpty(newName))
  240. {
  241. throw new ArgumentNullException("newName");
  242. }
  243. if (Kernel.Users.Any(u => u.Id != user.Id && u.Name.Equals(newName, StringComparison.OrdinalIgnoreCase)))
  244. {
  245. throw new ArgumentException(string.Format("A user with the name '{0}' already exists.", newName));
  246. }
  247. if (user.Name.Equals(newName, StringComparison.Ordinal))
  248. {
  249. throw new ArgumentException("The new and old names must be different.");
  250. }
  251. await user.Rename(newName);
  252. OnUserUpdated(user);
  253. }
  254. /// <summary>
  255. /// Updates the user.
  256. /// </summary>
  257. /// <param name="user">The user.</param>
  258. /// <exception cref="System.ArgumentNullException">user</exception>
  259. /// <exception cref="System.ArgumentException"></exception>
  260. public async Task UpdateUser(User user)
  261. {
  262. if (user == null)
  263. {
  264. throw new ArgumentNullException("user");
  265. }
  266. if (user.Id == Guid.Empty || !Kernel.Users.Any(u => u.Id.Equals(user.Id)))
  267. {
  268. throw new ArgumentException(string.Format("User with name '{0}' and Id {1} does not exist.", user.Name, user.Id));
  269. }
  270. user.DateModified = DateTime.UtcNow;
  271. await Kernel.UserRepository.SaveUser(user, CancellationToken.None).ConfigureAwait(false);
  272. OnUserUpdated(user);
  273. }
  274. /// <summary>
  275. /// Creates the user.
  276. /// </summary>
  277. /// <param name="name">The name.</param>
  278. /// <returns>User.</returns>
  279. /// <exception cref="System.ArgumentNullException">name</exception>
  280. /// <exception cref="System.ArgumentException"></exception>
  281. public async Task<User> CreateUser(string name)
  282. {
  283. if (string.IsNullOrEmpty(name))
  284. {
  285. throw new ArgumentNullException("name");
  286. }
  287. if (Kernel.Users.Any(u => u.Name.Equals(name, StringComparison.OrdinalIgnoreCase)))
  288. {
  289. throw new ArgumentException(string.Format("A user with the name '{0}' already exists.", name));
  290. }
  291. var user = InstantiateNewUser(name);
  292. var list = Kernel.Users.ToList();
  293. list.Add(user);
  294. Kernel.Users = list;
  295. await Kernel.UserRepository.SaveUser(user, CancellationToken.None).ConfigureAwait(false);
  296. return user;
  297. }
  298. /// <summary>
  299. /// Deletes the user.
  300. /// </summary>
  301. /// <param name="user">The user.</param>
  302. /// <returns>Task.</returns>
  303. /// <exception cref="System.ArgumentNullException">user</exception>
  304. /// <exception cref="System.ArgumentException"></exception>
  305. public async Task DeleteUser(User user)
  306. {
  307. if (user == null)
  308. {
  309. throw new ArgumentNullException("user");
  310. }
  311. if (Kernel.Users.FirstOrDefault(u => u.Id == user.Id) == null)
  312. {
  313. 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));
  314. }
  315. if (Kernel.Users.Count() == 1)
  316. {
  317. throw new ArgumentException(string.Format("The user '{0}' be deleted because there must be at least one user in the system.", user.Name));
  318. }
  319. await Kernel.UserRepository.DeleteUser(user, CancellationToken.None).ConfigureAwait(false);
  320. OnUserDeleted(user);
  321. // Force this to be lazy loaded again
  322. Kernel.Users = null;
  323. }
  324. /// <summary>
  325. /// Instantiates the new user.
  326. /// </summary>
  327. /// <param name="name">The name.</param>
  328. /// <returns>User.</returns>
  329. private User InstantiateNewUser(string name)
  330. {
  331. return new User
  332. {
  333. Name = name,
  334. Id = ("MBUser" + name).GetMD5(),
  335. DateCreated = DateTime.UtcNow,
  336. DateModified = DateTime.UtcNow
  337. };
  338. }
  339. }
  340. }