@ -11,7 +11,9 @@ using MediaBrowser.Controller.QuickConnect;
using MediaBrowser.Controller.Security ;
using MediaBrowser.Model.QuickConnect ;
using MediaBrowser.Model.Services ;
using MediaBrowser.Common ;
using Microsoft.Extensions.Logging ;
using MediaBrowser.Common.Extensions ;
namespace Emby.Server.Implementations.QuickConnect
{
@ -64,9 +66,7 @@ namespace Emby.Server.Implementations.QuickConnect
public QuickConnectState State { get ; private set ; } = QuickConnectState . Unavailable ;
/// <inheritdoc/>
public int RequestExpiry { get ; set ; } = 30 ;
private bool TemporaryActivation { get ; set ; } = false ;
public int Timeout { get ; set ; } = 5 ;
private DateTime DateActivated { get ; set ; }
@ -82,10 +82,9 @@ namespace Emby.Server.Implementations.QuickConnect
/// <inheritdoc/>
public QuickConnectResult Activate ( )
{
// This should not call SetEnabled since that would persist the "temporary" activation to the configuration file
State = QuickConnectState . Active ;
SetEnabled ( QuickConnectState . Active ) ;
DateActivated = DateTime . Now ;
TemporaryActivation = true ;
return new QuickConnectResult ( ) ;
}
@ -96,12 +95,10 @@ namespace Emby.Server.Implementations.QuickConnect
_logger . LogDebug ( "Changed quick connect state from {0} to {1}" , State , newState ) ;
ExpireRequests ( true ) ;
State = newState ;
_config . SaveConfiguration ( "quickconnect" , new QuickConnectConfiguration ( )
{
State = State
} ) ;
State = newState ;
_config . Configuration . QuickConnectAvailable = newState = = QuickConnectState . Available | | newState = = QuickConnectState . Active ;
_config . SaveConfiguration ( ) ;
_logger . LogDebug ( "Configuration saved" ) ;
}
@ -123,17 +120,16 @@ namespace Emby.Server.Implementations.QuickConnect
_logger . LogDebug ( "Got new quick connect request from {friendlyName}" , friendlyName ) ;
var lookup = GenerateSecureRandom ( ) ;
var code = GenerateCode ( ) ;
var result = new QuickConnectResult ( )
{
Lookup = lookup ,
Secret = GenerateSecureRandom ( ) ,
FriendlyName = friendlyName ,
DateAdded = DateTime . Now ,
Code = GenerateCode( )
Code = code
} ;
_currentRequests [ lookup ] = result ;
_currentRequests [ code ] = result ;
return result ;
}
@ -143,17 +139,16 @@ namespace Emby.Server.Implementations.QuickConnect
ExpireRequests ( ) ;
AssertActive ( ) ;
string lookup = _currentRequests . Where ( x = > x . Value . Secret = = secret ) . Select ( x = > x . Value . Lookup ) . DefaultIfEmpty ( string . Empty ) . First ( ) ;
string code = _currentRequests . Where ( x = > x . Value . Secret = = secret ) . Select ( x = > x . Value . Code ) . DefaultIfEmpty ( string . Empty ) . First ( ) ;
if ( ! _currentRequests . TryGetValue ( lookup , out QuickConnectResult result ) )
if ( ! _currentRequests . TryGetValue ( code , out QuickConnectResult result ) )
{
throw new KeyNotFoundException( "Unable to find request with provided identifier ") ;
throw new ResourceNotFoundException( "Unable to find request with provided secret ") ;
}
return result ;
}
/// <inheritdoc/>
public List < QuickConnectResultDto > GetCurrentRequests ( )
{
return GetCurrentRequestsInternal ( ) . Select ( x = > ( QuickConnectResultDto ) x ) . ToList ( ) ;
@ -186,16 +181,16 @@ namespace Emby.Server.Implementations.QuickConnect
}
/// <inheritdoc/>
public bool AuthorizeRequest ( IRequest request , string lookup )
public bool AuthorizeRequest ( IRequest request , string code )
{
ExpireRequests ( ) ;
AssertActive ( ) ;
var auth = _authContext . GetAuthorizationInfo ( request ) ;
if ( ! _currentRequests . TryGetValue ( lookup , out QuickConnectResult result ) )
if ( ! _currentRequests . TryGetValue ( code , out QuickConnectResult result ) )
{
throw new Key NotFoundException( "Unable to find request" ) ;
throw new Resource NotFoundException( "Unable to find request" ) ;
}
if ( result . Authenticated )
@ -205,9 +200,9 @@ namespace Emby.Server.Implementations.QuickConnect
result . Authentication = Guid . NewGuid ( ) . ToString ( "N" , CultureInfo . InvariantCulture ) ;
// Advance the time on the request so it expires sooner as the client will pick up the changes in a few seconds
var added = result . DateAdded ? ? DateTime . Now . Subtract ( new TimeSpan ( 0 , RequestExpiry , 0 ) ) ;
result . DateAdded = added . Subtract ( new TimeSpan ( 0 , RequestExpiry - 1 , 0 ) ) ;
// Change the time on the request so it expires one minute into the future. It can't expire immediately as otherwise some clients wouldn't ever see that they have been authenticated.
var added = result . DateAdded ? ? DateTime . Now . Subtract ( new TimeSpan ( 0 , Timeout , 0 ) ) ;
result . DateAdded = added . Subtract ( new TimeSpan ( 0 , Timeout - 1 , 0 ) ) ;
_authenticationRepository . Create ( new AuthenticationInfo
{
@ -271,7 +266,7 @@ namespace Emby.Server.Implementations.QuickConnect
var bytes = new byte [ length ] ;
_rng . GetBytes ( bytes ) ;
return string . Join ( string . Empty , bytes . Select ( x = > x . ToString ( "x2" , CultureInfo . InvariantCulture ) ) ) ;
return Hex . Encode ( bytes ) ;
}
/// <summary>
@ -281,12 +276,11 @@ namespace Emby.Server.Implementations.QuickConnect
private void ExpireRequests ( bool expireAll = false )
{
// Check if quick connect should be deactivated
if ( TemporaryActivation & & DateTime . Now > DateActivated . AddMinutes ( 10 ) & & State = = QuickConnectState . Active & & ! expireAll )
if ( State = = QuickConnectState . Active & & DateTime . Now > DateActivated . AddMinutes ( Timeout ) & & ! expireAll )
{
_logger . LogDebug ( "Quick connect time expired, deactivating" ) ;
SetEnabled ( QuickConnectState . Available ) ;
expireAll = true ;
TemporaryActivation = false ;
}
// Expire stale connection requests
@ -296,28 +290,28 @@ namespace Emby.Server.Implementations.QuickConnect
for ( int i = 0 ; i < values . Count ; i + + )
{
var added = values [ i ] . DateAdded ? ? DateTime . UnixEpoch ;
if ( DateTime . Now > added . AddMinutes ( RequestExpiry ) | | expireAll )
if ( DateTime . Now > added . AddMinutes ( Timeout ) | | expireAll )
{
delete . Add ( values [ i ] . Lookup ) ;
delete . Add ( values [ i ] . Code ) ;
}
}
foreach ( var lookup in delete )
foreach ( var code in delete )
{
_logger . LogDebug ( "Removing expired request { lookup}", lookup ) ;
_logger . LogDebug ( "Removing expired request { code}", code ) ;
if ( ! _currentRequests . TryRemove ( lookup , out _ ) )
if ( ! _currentRequests . TryRemove ( code , out _ ) )
{
_logger . LogWarning ( "Request { lookup} already expired", lookup ) ;
_logger . LogWarning ( "Request { code} already expired", code ) ;
}
}
}
private void ReloadConfiguration ( )
{
var config = _config . GetQuickConnectConfiguration ( ) ;
var available = _config . Configuration . QuickConnectAvailable ;
State = config. Stat e;
State = available ? QuickConnectState . Available : QuickConnectState . Unavailabl e;
}
}
}