diff options
Diffstat (limited to 'MediaBrowser.Server.Implementations/Session/SessionManager.cs')
| -rw-r--r-- | MediaBrowser.Server.Implementations/Session/SessionManager.cs | 239 |
1 files changed, 206 insertions, 33 deletions
diff --git a/MediaBrowser.Server.Implementations/Session/SessionManager.cs b/MediaBrowser.Server.Implementations/Session/SessionManager.cs index afde4af93..d55d26e44 100644 --- a/MediaBrowser.Server.Implementations/Session/SessionManager.cs +++ b/MediaBrowser.Server.Implementations/Session/SessionManager.cs @@ -11,12 +11,16 @@ using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.Persistence; +using MediaBrowser.Controller.Security; using MediaBrowser.Controller.Session; using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Events; using MediaBrowser.Model.Library; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Session; +using MediaBrowser.Model.Users; +using MediaBrowser.Server.Implementations.Security; using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -59,6 +63,8 @@ namespace MediaBrowser.Server.Implementations.Session private readonly IJsonSerializer _jsonSerializer; private readonly IServerApplicationHost _appHost; + private readonly IAuthenticationRepository _authRepo; + /// <summary> /// Gets or sets the configuration manager. /// </summary> @@ -71,6 +77,10 @@ namespace MediaBrowser.Server.Implementations.Session private readonly ConcurrentDictionary<string, SessionInfo> _activeConnections = new ConcurrentDictionary<string, SessionInfo>(StringComparer.OrdinalIgnoreCase); + public event EventHandler<GenericEventArgs<AuthenticationRequest>> AuthenticationFailed; + + public event EventHandler<GenericEventArgs<AuthenticationRequest>> AuthenticationSucceeded; + /// <summary> /// Occurs when [playback start]. /// </summary> @@ -101,7 +111,7 @@ namespace MediaBrowser.Server.Implementations.Session /// <param name="logger">The logger.</param> /// <param name="userRepository">The user repository.</param> /// <param name="libraryManager">The library manager.</param> - public SessionManager(IUserDataManager userDataRepository, IServerConfigurationManager configurationManager, ILogger logger, IUserRepository userRepository, ILibraryManager libraryManager, IUserManager userManager, IMusicManager musicManager, IDtoService dtoService, IImageProcessor imageProcessor, IItemRepository itemRepo, IJsonSerializer jsonSerializer, IServerApplicationHost appHost, IHttpClient httpClient) + public SessionManager(IUserDataManager userDataRepository, IServerConfigurationManager configurationManager, ILogger logger, IUserRepository userRepository, ILibraryManager libraryManager, IUserManager userManager, IMusicManager musicManager, IDtoService dtoService, IImageProcessor imageProcessor, IItemRepository itemRepo, IJsonSerializer jsonSerializer, IServerApplicationHost appHost, IHttpClient httpClient, IAuthenticationRepository authRepo) { _userDataRepository = userDataRepository; _configurationManager = configurationManager; @@ -116,6 +126,7 @@ namespace MediaBrowser.Server.Implementations.Session _jsonSerializer = jsonSerializer; _appHost = appHost; _httpClient = httpClient; + _authRepo = authRepo; } /// <summary> @@ -201,7 +212,12 @@ namespace MediaBrowser.Server.Implementations.Session /// <returns>Task.</returns> /// <exception cref="System.ArgumentNullException">user</exception> /// <exception cref="System.UnauthorizedAccessException"></exception> - public async Task<SessionInfo> LogSessionActivity(string clientType, string appVersion, string deviceId, string deviceName, string remoteEndPoint, User user) + public async Task<SessionInfo> LogSessionActivity(string clientType, + string appVersion, + string deviceId, + string deviceName, + string remoteEndPoint, + User user) { if (string.IsNullOrEmpty(clientType)) { @@ -222,7 +238,7 @@ namespace MediaBrowser.Server.Implementations.Session if (user != null && user.Configuration.IsDisabled) { - throw new UnauthorizedAccessException(string.Format("The {0} account is currently disabled. Please consult with your administrator.", user.Name)); + throw new AuthenticationException(string.Format("The {0} account is currently disabled. Please consult with your administrator.", user.Name)); } var activityDate = DateTime.UtcNow; @@ -356,7 +372,7 @@ namespace MediaBrowser.Server.Implementations.Session private string GetSessionKey(string clientType, string appVersion, string deviceId) { - return clientType + deviceId + appVersion; + return clientType + deviceId; } /// <summary> @@ -388,6 +404,11 @@ namespace MediaBrowser.Server.Implementations.Session Id = Guid.NewGuid().ToString("N") }; + sessionInfo.DeviceName = deviceName; + sessionInfo.UserId = userId; + sessionInfo.UserName = username; + sessionInfo.RemoteEndPoint = remoteEndPoint; + OnSessionStarted(sessionInfo); return sessionInfo; @@ -613,6 +634,20 @@ namespace MediaBrowser.Server.Implementations.Session info.MediaSourceId = info.ItemId; } + if (!string.IsNullOrWhiteSpace(info.ItemId) && libraryItem != null) + { + var current = session.NowPlayingItem; + + if (current == null || !string.Equals(current.Id, info.ItemId, StringComparison.OrdinalIgnoreCase)) + { + info.Item = GetItemInfo(libraryItem, libraryItem, info.MediaSourceId); + } + else + { + info.Item = current; + } + } + RemoveNowPlayingItem(session); var users = GetUsers(session); @@ -1087,12 +1122,7 @@ namespace MediaBrowser.Server.Implementations.Session { var session = GetSession(sessionId); - if (!session.UserId.HasValue) - { - throw new UnauthorizedAccessException("Cannot modify additional users without authenticating first."); - } - - if (session.UserId.Value == userId) + if (session.UserId.HasValue && session.UserId.Value == userId) { throw new ArgumentException("The requested user is already the primary user of the session."); } @@ -1120,12 +1150,7 @@ namespace MediaBrowser.Server.Implementations.Session { var session = GetSession(sessionId); - if (!session.UserId.HasValue) - { - throw new UnauthorizedAccessException("Cannot modify additional users without authenticating first."); - } - - if (session.UserId.Value == userId) + if (session.UserId.HasValue && session.UserId.Value == userId) { throw new ArgumentException("The requested user is already the primary user of the session."); } @@ -1138,28 +1163,175 @@ namespace MediaBrowser.Server.Implementations.Session } } + public void ValidateSecurityToken(string token) + { + if (string.IsNullOrWhiteSpace(token)) + { + throw new AuthenticationException(); + } + + var result = _authRepo.Get(new AuthenticationInfoQuery + { + AccessToken = token + }); + + var info = result.Items.FirstOrDefault(); + + if (info == null) + { + throw new AuthenticationException(); + } + + if (!info.IsActive) + { + throw new AuthenticationException("Access token has expired."); + } + + if (!string.IsNullOrWhiteSpace(info.UserId)) + { + var user = _userManager.GetUserById(new Guid(info.UserId)); + + if (user == null || user.Configuration.IsDisabled) + { + throw new AuthenticationException("User account has been disabled."); + } + } + } + /// <summary> /// Authenticates the new session. /// </summary> - /// <param name="user">The user.</param> - /// <param name="password">The password.</param> - /// <param name="clientType">Type of the client.</param> - /// <param name="appVersion">The application version.</param> - /// <param name="deviceId">The device identifier.</param> - /// <param name="deviceName">Name of the device.</param> - /// <param name="remoteEndPoint">The remote end point.</param> + /// <param name="request">The request.</param> + /// <param name="isLocal">if set to <c>true</c> [is local].</param> /// <returns>Task{SessionInfo}.</returns> - /// <exception cref="UnauthorizedAccessException"></exception> - public async Task<SessionInfo> AuthenticateNewSession(User user, string password, string clientType, string appVersion, string deviceId, string deviceName, string remoteEndPoint) + /// <exception cref="AuthenticationException">Invalid user or password entered.</exception> + /// <exception cref="System.UnauthorizedAccessException">Invalid user or password entered.</exception> + /// <exception cref="UnauthorizedAccessException">Invalid user or password entered.</exception> + public async Task<AuthenticationResult> AuthenticateNewSession(AuthenticationRequest request, + bool isLocal) { - var result = await _userManager.AuthenticateUser(user, password).ConfigureAwait(false); + var result = (isLocal && string.Equals(request.App, "Dashboard", StringComparison.OrdinalIgnoreCase)) || + await _userManager.AuthenticateUser(request.Username, request.Password, request.RemoteEndPoint).ConfigureAwait(false); if (!result) { - throw new UnauthorizedAccessException("Invalid user or password entered."); + EventHelper.FireEventIfNotNull(AuthenticationFailed, this, new GenericEventArgs<AuthenticationRequest>(request), _logger); + + throw new AuthenticationException("Invalid user or password entered."); } - return await LogSessionActivity(clientType, appVersion, deviceId, deviceName, remoteEndPoint, user).ConfigureAwait(false); + var user = _userManager.Users + .First(i => string.Equals(request.Username, i.Name, StringComparison.OrdinalIgnoreCase)); + + var token = await GetAuthorizationToken(user.Id.ToString("N"), request.DeviceId, request.App, request.DeviceName).ConfigureAwait(false); + + EventHelper.FireEventIfNotNull(AuthenticationSucceeded, this, new GenericEventArgs<AuthenticationRequest>(request), _logger); + + var session = await LogSessionActivity(request.App, + request.AppVersion, + request.DeviceId, + request.DeviceName, + request.RemoteEndPoint, + user) + .ConfigureAwait(false); + + return new AuthenticationResult + { + User = _userManager.GetUserDto(user, request.RemoteEndPoint), + SessionInfo = GetSessionInfoDto(session), + AccessToken = token, + ServerId = _appHost.ServerId + }; + } + + private async Task<string> GetAuthorizationToken(string userId, string deviceId, string app, string deviceName) + { + var existing = _authRepo.Get(new AuthenticationInfoQuery + { + DeviceId = deviceId, + IsActive = true, + UserId = userId, + Limit = 1 + }); + + if (existing.Items.Length > 0) + { + _logger.Debug("Reissuing access token"); + return existing.Items[0].AccessToken; + } + + var newToken = new AuthenticationInfo + { + AppName = app, + DateCreated = DateTime.UtcNow, + DeviceId = deviceId, + DeviceName = deviceName, + UserId = userId, + IsActive = true, + AccessToken = Guid.NewGuid().ToString("N") + }; + + _logger.Debug("Creating new access token for user {0}", userId); + await _authRepo.Create(newToken, CancellationToken.None).ConfigureAwait(false); + + return newToken.AccessToken; + } + + public async Task Logout(string accessToken) + { + if (string.IsNullOrWhiteSpace(accessToken)) + { + throw new ArgumentNullException("accessToken"); + } + + var existing = _authRepo.Get(new AuthenticationInfoQuery + { + Limit = 1, + AccessToken = accessToken + + }).Items.FirstOrDefault(); + + if (existing != null) + { + existing.IsActive = false; + + await _authRepo.Update(existing, CancellationToken.None).ConfigureAwait(false); + + var sessions = Sessions + .Where(i => string.Equals(i.DeviceId, existing.DeviceId, StringComparison.OrdinalIgnoreCase)) + .ToList(); + + foreach (var session in sessions) + { + try + { + ReportSessionEnded(session.Id); + } + catch (Exception ex) + { + _logger.ErrorException("Error reporting session ended", ex); + } + } + } + } + + public async Task RevokeUserTokens(string userId) + { + var existing = _authRepo.Get(new AuthenticationInfoQuery + { + IsActive = true, + UserId = userId + }); + + foreach (var info in existing.Items) + { + await Logout(info.AccessToken).ConfigureAwait(false); + } + } + + public Task RevokeToken(string token) + { + return Logout(token); } /// <summary> @@ -1260,15 +1432,10 @@ namespace MediaBrowser.Server.Implementations.Session DeviceName = session.DeviceName, Id = session.Id, LastActivityDate = session.LastActivityDate, - NowPlayingPositionTicks = session.PlayState.PositionTicks, - IsPaused = session.PlayState.IsPaused, - IsMuted = session.PlayState.IsMuted, NowViewingItem = session.NowViewingItem, ApplicationVersion = session.ApplicationVersion, - CanSeek = session.PlayState.CanSeek, QueueableMediaTypes = session.QueueableMediaTypes, PlayableMediaTypes = session.PlayableMediaTypes, - RemoteEndPoint = session.RemoteEndPoint, AdditionalUsers = session.AdditionalUsers, SupportedCommands = session.SupportedCommands, UserName = session.UserName, @@ -1516,5 +1683,11 @@ namespace MediaBrowser.Server.Implementations.Session { ReportTranscodingInfo(deviceId, null); } + + public SessionInfo GetSession(string deviceId, string client, string version) + { + return Sessions.FirstOrDefault(i => string.Equals(i.DeviceId, deviceId) && + string.Equals(i.Client, client)); + } } }
\ No newline at end of file |
