|
@@ -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 KeyNotFoundException("Unable to find request");
|
|
|
+ throw new ResourceNotFoundException("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.State;
|
|
|
+ State = available ? QuickConnectState.Available : QuickConnectState.Unavailable;
|
|
|
}
|
|
|
}
|
|
|
}
|