diff options
3 files changed, 126 insertions, 34 deletions
diff --git a/Jellyfin.Api/Controllers/ApiKeyController.cs b/Jellyfin.Api/Controllers/ApiKeyController.cs index 8c43d786a..96efde5fb 100644 --- a/Jellyfin.Api/Controllers/ApiKeyController.cs +++ b/Jellyfin.Api/Controllers/ApiKeyController.cs @@ -1,10 +1,8 @@ using System; using System.ComponentModel.DataAnnotations; -using System.Globalization; +using System.Threading.Tasks; using Jellyfin.Api.Constants; -using MediaBrowser.Controller; using MediaBrowser.Controller.Security; -using MediaBrowser.Controller.Session; using MediaBrowser.Model.Querying; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; @@ -18,24 +16,15 @@ namespace Jellyfin.Api.Controllers [Route("Auth")] public class ApiKeyController : BaseJellyfinApiController { - private readonly ISessionManager _sessionManager; - private readonly IServerApplicationHost _appHost; - private readonly IAuthenticationRepository _authRepo; + private readonly IAuthenticationManager _authenticationManager; /// <summary> /// Initializes a new instance of the <see cref="ApiKeyController"/> class. /// </summary> - /// <param name="sessionManager">Instance of <see cref="ISessionManager"/> interface.</param> - /// <param name="appHost">Instance of <see cref="IServerApplicationHost"/> interface.</param> - /// <param name="authRepo">Instance of <see cref="IAuthenticationRepository"/> interface.</param> - public ApiKeyController( - ISessionManager sessionManager, - IServerApplicationHost appHost, - IAuthenticationRepository authRepo) + /// <param name="authenticationManager">Instance of <see cref="IAuthenticationManager"/> interface.</param> + public ApiKeyController(IAuthenticationManager authenticationManager) { - _sessionManager = sessionManager; - _appHost = appHost; - _authRepo = authRepo; + _authenticationManager = authenticationManager; } /// <summary> @@ -46,14 +35,15 @@ namespace Jellyfin.Api.Controllers [HttpGet("Keys")] [Authorize(Policy = Policies.RequiresElevation)] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult<QueryResult<AuthenticationInfo>> GetKeys() + public async Task<ActionResult<QueryResult<AuthenticationInfo>>> GetKeys() { - var result = _authRepo.Get(new AuthenticationInfoQuery - { - HasUser = false - }); + var keys = await _authenticationManager.GetApiKeys(); - return result; + return new QueryResult<AuthenticationInfo> + { + Items = keys, + TotalRecordCount = keys.Count + }; } /// <summary> @@ -65,17 +55,10 @@ namespace Jellyfin.Api.Controllers [HttpPost("Keys")] [Authorize(Policy = Policies.RequiresElevation)] [ProducesResponseType(StatusCodes.Status204NoContent)] - public ActionResult CreateKey([FromQuery, Required] string app) + public async Task<ActionResult> CreateKey([FromQuery, Required] string app) { - _authRepo.Create(new AuthenticationInfo - { - AppName = app, - AccessToken = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture), - DateCreated = DateTime.UtcNow, - DeviceId = _appHost.SystemId, - DeviceName = _appHost.FriendlyName, - AppVersion = _appHost.ApplicationVersionString - }); + await _authenticationManager.CreateApiKey(app).ConfigureAwait(false); + return NoContent(); } @@ -88,9 +71,10 @@ namespace Jellyfin.Api.Controllers [HttpDelete("Keys/{key}")] [Authorize(Policy = Policies.RequiresElevation)] [ProducesResponseType(StatusCodes.Status204NoContent)] - public ActionResult RevokeKey([FromRoute, Required] string key) + public async Task<ActionResult> RevokeKey([FromRoute, Required] Guid key) { - _sessionManager.RevokeToken(key); + await _authenticationManager.DeleteApiKey(key).ConfigureAwait(false); + return NoContent(); } } diff --git a/Jellyfin.Server.Implementations/Security/AuthenticationManager.cs b/Jellyfin.Server.Implementations/Security/AuthenticationManager.cs new file mode 100644 index 000000000..37b8ee6e0 --- /dev/null +++ b/Jellyfin.Server.Implementations/Security/AuthenticationManager.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Threading.Tasks; +using Jellyfin.Data.Entities.Security; +using MediaBrowser.Controller.Security; +using Microsoft.EntityFrameworkCore; + +namespace Jellyfin.Server.Implementations.Security +{ + /// <inheritdoc /> + public class AuthenticationManager : IAuthenticationManager + { + private readonly JellyfinDbProvider _dbProvider; + + /// <summary> + /// Initializes a new instance of the <see cref="AuthenticationManager"/> class. + /// </summary> + /// <param name="dbProvider">The database provider.</param> + public AuthenticationManager(JellyfinDbProvider dbProvider) + { + _dbProvider = dbProvider; + } + + /// <inheritdoc /> + public async Task CreateApiKey(string name) + { + await using var dbContext = _dbProvider.CreateContext(); + + dbContext.ApiKeys.Add(new ApiKey(name)); + + await dbContext.SaveChangesAsync().ConfigureAwait(false); + } + + /// <inheritdoc /> + public async Task<IReadOnlyList<AuthenticationInfo>> GetApiKeys() + { + await using var dbContext = _dbProvider.CreateContext(); + + return await dbContext.ApiKeys + .AsAsyncEnumerable() + .Select(key => new AuthenticationInfo + { + AppName = key.Name, + AccessToken = key.AccessToken.ToString("N", CultureInfo.InvariantCulture), + DateCreated = key.DateCreated, + DeviceId = string.Empty, + DeviceName = string.Empty, + AppVersion = string.Empty + }).ToListAsync().ConfigureAwait(false); + } + + /// <inheritdoc /> + public async Task DeleteApiKey(Guid id) + { + await using var dbContext = _dbProvider.CreateContext(); + + var key = await dbContext.ApiKeys + .AsQueryable() + .Where(apiKey => apiKey.AccessToken == id) + .FirstOrDefaultAsync(); + + if (key == null) + { + return; + } + + dbContext.Remove(key); + + await dbContext.SaveChangesAsync().ConfigureAwait(false); + } + } +} diff --git a/MediaBrowser.Controller/Security/IAuthenticationManager.cs b/MediaBrowser.Controller/Security/IAuthenticationManager.cs new file mode 100644 index 000000000..46d0c6622 --- /dev/null +++ b/MediaBrowser.Controller/Security/IAuthenticationManager.cs @@ -0,0 +1,34 @@ +#nullable enable + +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace MediaBrowser.Controller.Security +{ + /// <summary> + /// Handles the retrieval and storage of API keys. + /// </summary> + public interface IAuthenticationManager + { + /// <summary> + /// Creates an API key. + /// </summary> + /// <param name="name">The name of the key.</param> + /// <returns>A task representing the creation of the key.</returns> + Task CreateApiKey(string name); + + /// <summary> + /// Gets the API keys. + /// </summary> + /// <returns>A task representing the retrieval of the API keys.</returns> + Task<IReadOnlyList<AuthenticationInfo>> GetApiKeys(); + + /// <summary> + /// Deletes an API key with the provided access token. + /// </summary> + /// <param name="accessToken">The access token.</param> + /// <returns>A task representing the deletion of the API key.</returns> + Task DeleteApiKey(Guid accessToken); + } +} |
