@ -1,6 +1,4 @@
using System.Security.Cryptography ;
using System.Text ;
using MediaBrowser.Common.Events ;
using MediaBrowser.Common.Events ;
using MediaBrowser.Common.Extensions ;
using MediaBrowser.Common.Net ;
using MediaBrowser.Controller ;
@ -13,12 +11,14 @@ 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.Library ;
using MediaBrowser.Model.Logging ;
using MediaBrowser.Model.Serialization ;
using MediaBrowser.Model.Session ;
using MediaBrowser.Model.Users ;
using System ;
using System.Collections.Concurrent ;
using System.Collections.Generic ;
@ -27,7 +27,6 @@ using System.IO;
using System.Linq ;
using System.Threading ;
using System.Threading.Tasks ;
using MediaBrowser.Model.Users ;
namespace MediaBrowser.Server.Implementations.Session
{
@ -62,6 +61,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>
@ -104,7 +105,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 ;
@ -119,6 +120,7 @@ namespace MediaBrowser.Server.Implementations.Session
_jsonSerializer = jsonSerializer ;
_appHost = appHost ;
_httpClient = httpClient ;
_authRepo = authRepo ;
}
/// <summary>
@ -204,7 +206,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 ) )
{
@ -1157,7 +1164,37 @@ namespace MediaBrowser.Server.Implementations.Session
public void ValidateSecurityToken ( string token )
{
if ( string . IsNullOrWhiteSpace ( token ) )
{
throw new UnauthorizedAccessException ( ) ;
}
var result = _authRepo . Get ( new AuthenticationInfoQuery
{
AccessToken = token
} ) ;
var info = result . Items . FirstOrDefault ( ) ;
if ( info = = null )
{
throw new UnauthorizedAccessException ( ) ;
}
if ( ! info . IsActive )
{
throw new UnauthorizedAccessException ( "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 UnauthorizedAccessException ( "User account has been disabled." ) ;
}
}
}
/// <summary>
@ -1175,7 +1212,7 @@ namespace MediaBrowser.Server.Implementations.Session
/// <exception cref="UnauthorizedAccessException"></exception>
public async Task < AuthenticationResult > AuthenticateNewSession ( string username , string password , string clientType , string appVersion , string deviceId , string deviceName , string remoteEndPoint )
{
var result = await _userManager . AuthenticateUser ( username , password ) . ConfigureAwait ( false ) ;
var result = IsLocalhost ( remoteEndPoint ) | | await _userManager . AuthenticateUser ( username , password ) . ConfigureAwait ( false ) ;
if ( ! result )
{
@ -1185,6 +1222,8 @@ namespace MediaBrowser.Server.Implementations.Session
var user = _userManager . Users
. First ( i = > string . Equals ( username , i . Name , StringComparison . OrdinalIgnoreCase ) ) ;
var token = await GetAuthorizationToken ( user . Id . ToString ( "N" ) , deviceId , clientType , deviceName ) . ConfigureAwait ( false ) ;
var session = await LogSessionActivity ( clientType ,
appVersion ,
deviceId ,
@ -1197,11 +1236,108 @@ namespace MediaBrowser.Server.Implementations.Session
{
User = _dtoService . GetUserDto ( user ) ,
SessionInfo = GetSessionInfoDto ( session ) ,
A uthenticationToken = Guid . NewGuid ( ) . ToString ( "N" )
A ccessToken = token
} ;
}
private bool IsLocal ( string remoteEndpoint )
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 ) ;
}
}
private bool IsLocalhost ( string remoteEndpoint )
{
if ( string . IsNullOrWhiteSpace ( remoteEndpoint ) )
{
throw new ArgumentNullException ( "remoteEndpoint" ) ;
}
return remoteEndpoint . IndexOf ( "localhost" , StringComparison . OrdinalIgnoreCase ) ! = - 1 | |
remoteEndpoint . StartsWith ( "127." , StringComparison . OrdinalIgnoreCase ) | |
remoteEndpoint . StartsWith ( "::" , StringComparison . OrdinalIgnoreCase ) ;
}
public bool IsLocal ( string remoteEndpoint )
{
if ( string . IsNullOrWhiteSpace ( remoteEndpoint ) )
{
@ -1211,12 +1347,11 @@ namespace MediaBrowser.Server.Implementations.Session
// Private address space:
// http://en.wikipedia.org/wiki/Private_network
return remoteEndpoint. IndexOf ( "localhost" , StringComparison . OrdinalIgnoreCase ) ! = - 1 | |
return IsLocalhost( remoteEndpoint ) | |
remoteEndpoint . StartsWith ( "10." , StringComparison . OrdinalIgnoreCase ) | |
remoteEndpoint . StartsWith ( "192." , StringComparison . OrdinalIgnoreCase ) | |
remoteEndpoint . StartsWith ( "172." , StringComparison . OrdinalIgnoreCase ) | |
remoteEndpoint . StartsWith ( "127." , StringComparison . OrdinalIgnoreCase ) | |
remoteEndpoint . StartsWith ( "::" , StringComparison . OrdinalIgnoreCase ) ;
remoteEndpoint . StartsWith ( "169." , StringComparison . OrdinalIgnoreCase ) ;
}
/// <summary>