Add persistent setting configuration and temporary activation

This commit is contained in:
ConfusedPolarBear 2020-04-19 01:33:09 -05:00
parent 36f3e933a2
commit 387a07c6dd
5 changed files with 150 additions and 15 deletions

View File

@ -0,0 +1,30 @@
#pragma warning disable CS1591
using System.Collections.Generic;
using MediaBrowser.Common.Configuration;
namespace Emby.Server.Implementations.QuickConnect
{
public static class ConfigurationExtension
{
public static QuickConnectConfiguration GetQuickConnectConfiguration(this IConfigurationManager manager)
{
return manager.GetConfiguration<QuickConnectConfiguration>("quickconnect");
}
}
public class QuickConnectConfigurationFactory : IConfigurationFactory
{
public IEnumerable<ConfigurationStore> GetConfigurations()
{
return new ConfigurationStore[]
{
new ConfigurationStore
{
Key = "quickconnect",
ConfigurationType = typeof(QuickConnectConfiguration)
}
};
}
}
}

View File

@ -0,0 +1,13 @@
using MediaBrowser.Model.QuickConnect;
namespace Emby.Server.Implementations.QuickConnect
{
public class QuickConnectConfiguration
{
public QuickConnectConfiguration()
{
}
public QuickConnectState State { get; set; }
}
}

View File

@ -3,7 +3,9 @@ using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Security.Cryptography;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.QuickConnect;
@ -12,6 +14,7 @@ using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.QuickConnect;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Services;
using MediaBrowser.Model.Tasks;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.QuickConnect
@ -24,6 +27,7 @@ namespace Emby.Server.Implementations.QuickConnect
private readonly RNGCryptoServiceProvider _rng = new RNGCryptoServiceProvider();
private Dictionary<string, QuickConnectResult> _currentRequests = new Dictionary<string, QuickConnectResult>();
private IServerConfigurationManager _config;
private ILogger _logger;
private IUserManager _userManager;
private ILocalizationManager _localizationManager;
@ -31,11 +35,13 @@ namespace Emby.Server.Implementations.QuickConnect
private IAuthenticationRepository _authenticationRepository;
private IAuthorizationContext _authContext;
private IServerApplicationHost _appHost;
private ITaskManager _taskManager;
/// <summary>
/// Initializes a new instance of the <see cref="QuickConnectManager"/> class.
/// Should only be called at server startup when a singleton is created.
/// </summary>
/// <param name="config">Configuration.</param>
/// <param name="loggerFactory">Logger.</param>
/// <param name="userManager">User manager.</param>
/// <param name="localization">Localization.</param>
@ -43,15 +49,19 @@ namespace Emby.Server.Implementations.QuickConnect
/// <param name="appHost">Application host.</param>
/// <param name="authContext">Authentication context.</param>
/// <param name="authenticationRepository">Authentication repository.</param>
/// <param name="taskManager">Task scheduler.</param>
public QuickConnectManager(
IServerConfigurationManager config,
ILoggerFactory loggerFactory,
IUserManager userManager,
ILocalizationManager localization,
IJsonSerializer jsonSerializer,
IServerApplicationHost appHost,
IAuthorizationContext authContext,
IAuthenticationRepository authenticationRepository)
IAuthenticationRepository authenticationRepository,
ITaskManager taskManager)
{
_config = config;
_logger = loggerFactory.CreateLogger(nameof(QuickConnectManager));
_userManager = userManager;
_localizationManager = localization;
@ -59,6 +69,16 @@ namespace Emby.Server.Implementations.QuickConnect
_appHost = appHost;
_authContext = authContext;
_authenticationRepository = authenticationRepository;
_taskManager = taskManager;
ReloadConfiguration();
}
private void ReloadConfiguration()
{
var config = _config.GetQuickConnectConfiguration();
State = config.State;
}
/// <inheritdoc/>
@ -73,6 +93,10 @@ namespace Emby.Server.Implementations.QuickConnect
/// <inheritdoc/>
public int RequestExpiry { get; set; } = 30;
private bool TemporaryActivation { get; set; } = false;
private DateTime DateActivated { get; set; }
/// <inheritdoc/>
public void AssertActive()
{
@ -82,17 +106,37 @@ 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;
DateActivated = DateTime.Now;
TemporaryActivation = true;
return new QuickConnectResult();
}
/// <inheritdoc/>
public void SetEnabled(QuickConnectState newState)
{
_logger.LogDebug("Changed quick connect state from {0} to {1}", State, newState);
State = newState;
_config.SaveConfiguration("quickconnect", new QuickConnectConfiguration()
{
State = State
});
_logger.LogDebug("Configuration saved");
}
/// <inheritdoc/>
public QuickConnectResult TryConnect(string friendlyName)
{
ExpireRequests(true);
if (State != QuickConnectState.Active)
{
_logger.LogDebug("Refusing quick connect initiation request, current state is {0}", State);
@ -122,13 +166,11 @@ namespace Emby.Server.Implementations.QuickConnect
/// <inheritdoc/>
public QuickConnectResult CheckRequestStatus(string secret)
{
AssertActive();
ExpireRequests();
AssertActive();
string lookup = _currentRequests.Where(x => x.Value.Secret == secret).Select(x => x.Value.Lookup).DefaultIfEmpty(string.Empty).First();
_logger.LogDebug("Transformed private identifier {0} into public lookup {1}", secret, lookup);
if (!_currentRequests.ContainsKey(lookup))
{
throw new KeyNotFoundException("Unable to find request with provided identifier");
@ -146,8 +188,8 @@ namespace Emby.Server.Implementations.QuickConnect
/// <inheritdoc/>
public List<QuickConnectResult> GetCurrentRequestsInternal()
{
AssertActive();
ExpireRequests();
AssertActive();
return _currentRequests.Values.ToList();
}
@ -174,12 +216,11 @@ namespace Emby.Server.Implementations.QuickConnect
/// <inheritdoc/>
public bool AuthorizeRequest(IRequest request, string lookup)
{
ExpireRequests();
AssertActive();
var auth = _authContext.GetAuthorizationInfo(request);
ExpireRequests();
if (!_currentRequests.ContainsKey(lookup))
{
throw new KeyNotFoundException("Unable to find request");
@ -208,6 +249,8 @@ namespace Emby.Server.Implementations.QuickConnect
UserId = auth.UserId
});
_logger.LogInformation("Allowing device {0} to login as user {1} with quick connect code {2}", result.FriendlyName, auth.User.Name, result.Code);
return true;
}
@ -239,8 +282,21 @@ namespace Emby.Server.Implementations.QuickConnect
return string.Join(string.Empty, bytes.Select(x => x.ToString("x2", CultureInfo.InvariantCulture)));
}
private void ExpireRequests()
private void ExpireRequests(bool onlyCheckTime = false)
{
// check if quick connect should be deactivated
if (TemporaryActivation && DateTime.Now > DateActivated.AddMinutes(10) && State == QuickConnectState.Active)
{
_logger.LogDebug("Quick connect time expired, deactivating");
SetEnabled(QuickConnectState.Available);
}
if (onlyCheckTime)
{
return;
}
// expire stale connection requests
var delete = new List<string>();
var values = _currentRequests.Values.ToList();

View File

@ -98,6 +98,11 @@ namespace MediaBrowser.Api.QuickConnect
public object Get(QuickConnectList request)
{
if(_quickConnect.State != QuickConnectState.Active)
{
return Array.Empty<QuickConnectResultDto>();
}
return _quickConnect.GetCurrentRequests();
}
@ -124,15 +129,40 @@ namespace MediaBrowser.Api.QuickConnect
public object Post(Activate request)
{
if (_quickConnect.State == QuickConnectState.Available)
{
_quickConnect.SetEnabled(QuickConnectState.Active);
string name = _authContext.GetAuthorizationInfo(Request).User.Name;
string name = _authContext.GetAuthorizationInfo(Request).User.Name;
Logger.LogInformation("{name} enabled quick connect", name);
if(_quickConnect.State == QuickConnectState.Unavailable)
{
return new QuickConnectResult()
{
Error = "Quick connect is not enabled on this server"
};
}
return _quickConnect.State;
else if(_quickConnect.State == QuickConnectState.Available)
{
var result = _quickConnect.Activate();
if (string.IsNullOrEmpty(result.Error))
{
Logger.LogInformation("{name} temporarily activated quick connect", name);
}
return result;
}
else if(_quickConnect.State == QuickConnectState.Active)
{
return new QuickConnectResult()
{
Error = ""
};
}
return new QuickConnectResult()
{
Error = "Unknown current state"
};
}
public object Post(Available request)

View File

@ -35,10 +35,16 @@ namespace MediaBrowser.Controller.QuickConnect
/// </summary>
void AssertActive();
/// <summary>
/// Temporarily activates quick connect for a short amount of time.
/// </summary>
/// <returns>A quick connect result object indicating success.</returns>
QuickConnectResult Activate();
/// <summary>
/// Changes the status of quick connect.
/// </summary>
/// <param name="newState">New state to change to</param>
/// <param name="newState">New state to change to.</param>
void SetEnabled(QuickConnectState newState);
/// <summary>