fix(devices): read devices and device options through to the database
The device cache was filled once at construction, so a token minted by one replica was unknown to every other replica already running and a token revoked on one replica stayed valid on the others until they restarted. - drop the eager device and device options dictionaries - read devices and device options from the database on every query - push the device query filters and ordering into SQL - open the request's database context only for the api key fallback - cover both directions against real PostgreSQL with two manager instances
This commit is contained in:
@@ -126,95 +126,95 @@ namespace Jellyfin.Server.Implementations.Security
|
||||
return authInfo;
|
||||
}
|
||||
|
||||
var device = (await _deviceManager.GetDevices(
|
||||
new DeviceQuery { AccessToken = token }).ConfigureAwait(false)).Items.FirstOrDefault();
|
||||
|
||||
if (device is not null)
|
||||
{
|
||||
authInfo.IsAuthenticated = true;
|
||||
var updateToken = false;
|
||||
|
||||
// TODO: Remove these checks for IsNullOrWhiteSpace
|
||||
if (string.IsNullOrWhiteSpace(authInfo.Client))
|
||||
{
|
||||
authInfo.Client = device.AppName;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(authInfo.DeviceId))
|
||||
{
|
||||
authInfo.DeviceId = device.DeviceId;
|
||||
}
|
||||
|
||||
// Temporary. TODO - allow clients to specify that the token has been shared with a casting device
|
||||
var allowTokenInfoUpdate = !authInfo.Client.Contains("chromecast", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(authInfo.Device))
|
||||
{
|
||||
authInfo.Device = device.DeviceName;
|
||||
}
|
||||
else if (!string.Equals(authInfo.Device, device.DeviceName, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (allowTokenInfoUpdate)
|
||||
{
|
||||
updateToken = true;
|
||||
device.DeviceName = authInfo.Device;
|
||||
}
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(authInfo.Version))
|
||||
{
|
||||
authInfo.Version = device.AppVersion;
|
||||
}
|
||||
else if (!string.Equals(authInfo.Version, device.AppVersion, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (allowTokenInfoUpdate)
|
||||
{
|
||||
updateToken = true;
|
||||
device.AppVersion = authInfo.Version;
|
||||
}
|
||||
}
|
||||
|
||||
if ((DateTime.UtcNow - device.DateLastActivity).TotalMinutes > 3)
|
||||
{
|
||||
device.DateLastActivity = DateTime.UtcNow;
|
||||
updateToken = true;
|
||||
}
|
||||
|
||||
authInfo.User = _userManager.GetUserById(device.UserId);
|
||||
|
||||
if (updateToken)
|
||||
{
|
||||
await _deviceManager.UpdateDevice(device).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return authInfo;
|
||||
}
|
||||
|
||||
var dbContext = await _jellyfinDbProvider.CreateDbContextAsync().ConfigureAwait(false);
|
||||
await using (dbContext.ConfigureAwait(false))
|
||||
{
|
||||
var device = _deviceManager.GetDevices(
|
||||
new DeviceQuery { AccessToken = token }).Items.FirstOrDefault();
|
||||
|
||||
if (device is not null)
|
||||
var key = await dbContext.ApiKeys.FirstOrDefaultAsync(apiKey => apiKey.AccessToken == token).ConfigureAwait(false);
|
||||
if (key is not null)
|
||||
{
|
||||
authInfo.IsAuthenticated = true;
|
||||
var updateToken = false;
|
||||
|
||||
// TODO: Remove these checks for IsNullOrWhiteSpace
|
||||
if (string.IsNullOrWhiteSpace(authInfo.Client))
|
||||
{
|
||||
authInfo.Client = device.AppName;
|
||||
}
|
||||
|
||||
authInfo.Client = key.Name;
|
||||
authInfo.Token = key.AccessToken;
|
||||
if (string.IsNullOrWhiteSpace(authInfo.DeviceId))
|
||||
{
|
||||
authInfo.DeviceId = device.DeviceId;
|
||||
authInfo.DeviceId = _serverApplicationHost.SystemId;
|
||||
}
|
||||
|
||||
// Temporary. TODO - allow clients to specify that the token has been shared with a casting device
|
||||
var allowTokenInfoUpdate = !authInfo.Client.Contains("chromecast", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(authInfo.Device))
|
||||
{
|
||||
authInfo.Device = device.DeviceName;
|
||||
}
|
||||
else if (!string.Equals(authInfo.Device, device.DeviceName, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (allowTokenInfoUpdate)
|
||||
{
|
||||
updateToken = true;
|
||||
device.DeviceName = authInfo.Device;
|
||||
}
|
||||
authInfo.Device = _serverApplicationHost.Name;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(authInfo.Version))
|
||||
{
|
||||
authInfo.Version = device.AppVersion;
|
||||
}
|
||||
else if (!string.Equals(authInfo.Version, device.AppVersion, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (allowTokenInfoUpdate)
|
||||
{
|
||||
updateToken = true;
|
||||
device.AppVersion = authInfo.Version;
|
||||
}
|
||||
authInfo.Version = _serverApplicationHost.ApplicationVersionString;
|
||||
}
|
||||
|
||||
if ((DateTime.UtcNow - device.DateLastActivity).TotalMinutes > 3)
|
||||
{
|
||||
device.DateLastActivity = DateTime.UtcNow;
|
||||
updateToken = true;
|
||||
}
|
||||
|
||||
authInfo.User = _userManager.GetUserById(device.UserId);
|
||||
|
||||
if (updateToken)
|
||||
{
|
||||
await _deviceManager.UpdateDevice(device).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var key = await dbContext.ApiKeys.FirstOrDefaultAsync(apiKey => apiKey.AccessToken == token).ConfigureAwait(false);
|
||||
if (key is not null)
|
||||
{
|
||||
authInfo.IsAuthenticated = true;
|
||||
authInfo.Client = key.Name;
|
||||
authInfo.Token = key.AccessToken;
|
||||
if (string.IsNullOrWhiteSpace(authInfo.DeviceId))
|
||||
{
|
||||
authInfo.DeviceId = _serverApplicationHost.SystemId;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(authInfo.Device))
|
||||
{
|
||||
authInfo.Device = _serverApplicationHost.Name;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(authInfo.Version))
|
||||
{
|
||||
authInfo.Version = _serverApplicationHost.ApplicationVersionString;
|
||||
}
|
||||
|
||||
authInfo.IsApiKey = true;
|
||||
}
|
||||
authInfo.IsApiKey = true;
|
||||
}
|
||||
|
||||
return authInfo;
|
||||
|
||||
Reference in New Issue
Block a user