Use new entities for API key endpoints

This commit is contained in:
Patrick Barron 2021-04-01 17:08:22 -04:00
parent 1c501b17d7
commit 499785bebb
3 changed files with 126 additions and 34 deletions

View File

@ -1,10 +1,8 @@
using System; using System;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.Globalization; using System.Threading.Tasks;
using Jellyfin.Api.Constants; using Jellyfin.Api.Constants;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Security; using MediaBrowser.Controller.Security;
using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Querying; using MediaBrowser.Model.Querying;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
@ -18,24 +16,15 @@ namespace Jellyfin.Api.Controllers
[Route("Auth")] [Route("Auth")]
public class ApiKeyController : BaseJellyfinApiController public class ApiKeyController : BaseJellyfinApiController
{ {
private readonly ISessionManager _sessionManager; private readonly IAuthenticationManager _authenticationManager;
private readonly IServerApplicationHost _appHost;
private readonly IAuthenticationRepository _authRepo;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ApiKeyController"/> class. /// Initializes a new instance of the <see cref="ApiKeyController"/> class.
/// </summary> /// </summary>
/// <param name="sessionManager">Instance of <see cref="ISessionManager"/> interface.</param> /// <param name="authenticationManager">Instance of <see cref="IAuthenticationManager"/> interface.</param>
/// <param name="appHost">Instance of <see cref="IServerApplicationHost"/> interface.</param> public ApiKeyController(IAuthenticationManager authenticationManager)
/// <param name="authRepo">Instance of <see cref="IAuthenticationRepository"/> interface.</param>
public ApiKeyController(
ISessionManager sessionManager,
IServerApplicationHost appHost,
IAuthenticationRepository authRepo)
{ {
_sessionManager = sessionManager; _authenticationManager = authenticationManager;
_appHost = appHost;
_authRepo = authRepo;
} }
/// <summary> /// <summary>
@ -46,14 +35,15 @@ namespace Jellyfin.Api.Controllers
[HttpGet("Keys")] [HttpGet("Keys")]
[Authorize(Policy = Policies.RequiresElevation)] [Authorize(Policy = Policies.RequiresElevation)]
[ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult<QueryResult<AuthenticationInfo>> GetKeys() public async Task<ActionResult<QueryResult<AuthenticationInfo>>> GetKeys()
{ {
var result = _authRepo.Get(new AuthenticationInfoQuery var keys = await _authenticationManager.GetApiKeys();
{
HasUser = false
});
return result; return new QueryResult<AuthenticationInfo>
{
Items = keys,
TotalRecordCount = keys.Count
};
} }
/// <summary> /// <summary>
@ -65,17 +55,10 @@ namespace Jellyfin.Api.Controllers
[HttpPost("Keys")] [HttpPost("Keys")]
[Authorize(Policy = Policies.RequiresElevation)] [Authorize(Policy = Policies.RequiresElevation)]
[ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status204NoContent)]
public ActionResult CreateKey([FromQuery, Required] string app) public async Task<ActionResult> CreateKey([FromQuery, Required] string app)
{ {
_authRepo.Create(new AuthenticationInfo await _authenticationManager.CreateApiKey(app).ConfigureAwait(false);
{
AppName = app,
AccessToken = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture),
DateCreated = DateTime.UtcNow,
DeviceId = _appHost.SystemId,
DeviceName = _appHost.FriendlyName,
AppVersion = _appHost.ApplicationVersionString
});
return NoContent(); return NoContent();
} }
@ -88,9 +71,10 @@ namespace Jellyfin.Api.Controllers
[HttpDelete("Keys/{key}")] [HttpDelete("Keys/{key}")]
[Authorize(Policy = Policies.RequiresElevation)] [Authorize(Policy = Policies.RequiresElevation)]
[ProducesResponseType(StatusCodes.Status204NoContent)] [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(); return NoContent();
} }
} }

View File

@ -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);
}
}
}

View File

@ -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);
}
}