Migrate API to ASP.NET Core Auth services + refactor
This commit is contained in:
32
PluralKit.API/Authentication/AuthExt.cs
Normal file
32
PluralKit.API/Authentication/AuthExt.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using System.Security.Claims;
|
||||
|
||||
using PluralKit.Core;
|
||||
|
||||
namespace PluralKit.API
|
||||
{
|
||||
public static class AuthExt
|
||||
{
|
||||
public static SystemId CurrentSystem(this ClaimsPrincipal user)
|
||||
{
|
||||
var claim = user.FindFirst(PKClaims.SystemId);
|
||||
if (claim == null) throw new ArgumentException("User is unauthorized");
|
||||
|
||||
if (int.TryParse(claim.Value, out var id))
|
||||
return new SystemId(id);
|
||||
throw new ArgumentException("User has non-integer system ID claim");
|
||||
}
|
||||
|
||||
public static LookupContext ContextFor(this ClaimsPrincipal user, PKSystem system)
|
||||
{
|
||||
if (!user.Identity.IsAuthenticated) return LookupContext.API;
|
||||
return system.Id == user.CurrentSystem() ? LookupContext.ByOwner : LookupContext.API;
|
||||
}
|
||||
|
||||
public static LookupContext ContextFor(this ClaimsPrincipal user, PKMember member)
|
||||
{
|
||||
if (!user.Identity.IsAuthenticated) return LookupContext.API;
|
||||
return member.System == user.CurrentSystem() ? LookupContext.ByOwner : LookupContext.API;
|
||||
}
|
||||
}
|
||||
}
|
7
PluralKit.API/Authentication/PKClaims.cs
Normal file
7
PluralKit.API/Authentication/PKClaims.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace PluralKit.API
|
||||
{
|
||||
public class PKClaims
|
||||
{
|
||||
public const string SystemId = "PluralKit:SystemId";
|
||||
}
|
||||
}
|
@@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Text.Encodings.Web;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Dapper;
|
||||
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
using PluralKit.Core;
|
||||
|
||||
namespace PluralKit.API
|
||||
{
|
||||
public class SystemTokenAuthenticationHandler: AuthenticationHandler<SystemTokenAuthenticationHandler.Opts>
|
||||
{
|
||||
private readonly IDatabase _db;
|
||||
|
||||
public SystemTokenAuthenticationHandler(IOptionsMonitor<Opts> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock, IDatabase db): base(options, logger, encoder, clock)
|
||||
{
|
||||
_db = db;
|
||||
}
|
||||
|
||||
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
|
||||
{
|
||||
if (!Request.Headers.ContainsKey("Authorization"))
|
||||
return AuthenticateResult.NoResult();
|
||||
|
||||
var token = Request.Headers["Authorization"].FirstOrDefault();
|
||||
var systemId = await _db.Execute(c => c.QuerySingleOrDefaultAsync<SystemId?>("select id from systems where token = @token", new { token }));
|
||||
if (systemId == null) return AuthenticateResult.Fail("Invalid system token");
|
||||
|
||||
var claims = new[] {new Claim(PKClaims.SystemId, systemId.Value.Value.ToString())};
|
||||
var identity = new ClaimsIdentity(claims, Scheme.Name);
|
||||
var principal = new ClaimsPrincipal(identity);
|
||||
var ticket = new AuthenticationTicket(principal, Scheme.Name);
|
||||
ticket.Properties.IsPersistent = false;
|
||||
ticket.Properties.AllowRefresh = false;
|
||||
return AuthenticateResult.Success(ticket);
|
||||
}
|
||||
|
||||
public class Opts: AuthenticationSchemeOptions
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
19
PluralKit.API/Authorization/MemberOwnerHandler.cs
Normal file
19
PluralKit.API/Authorization/MemberOwnerHandler.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
||||
using PluralKit.Core;
|
||||
|
||||
namespace PluralKit.API
|
||||
{
|
||||
public class MemberOwnerHandler: AuthorizationHandler<OwnSystemRequirement, PKMember> {
|
||||
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
|
||||
OwnSystemRequirement requirement, PKMember resource)
|
||||
{
|
||||
if (!context.User.Identity.IsAuthenticated) return Task.CompletedTask;
|
||||
if (resource.System == context.User.CurrentSystem())
|
||||
context.Succeed(requirement);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
21
PluralKit.API/Authorization/MemberPrivacyHandler.cs
Normal file
21
PluralKit.API/Authorization/MemberPrivacyHandler.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
||||
using PluralKit.Core;
|
||||
|
||||
namespace PluralKit.API
|
||||
{
|
||||
public class MemberPrivacyHandler: AuthorizationHandler<PrivacyRequirement<PKMember>, PKMember>
|
||||
{
|
||||
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
|
||||
PrivacyRequirement<PKMember> requirement, PKMember resource)
|
||||
{
|
||||
var level = requirement.Mapper(resource);
|
||||
var ctx = context.User.ContextFor(resource);
|
||||
if (level.CanAccess(ctx))
|
||||
context.Succeed(requirement);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
6
PluralKit.API/Authorization/OwnSystemRequirement.cs
Normal file
6
PluralKit.API/Authorization/OwnSystemRequirement.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
||||
namespace PluralKit.API
|
||||
{
|
||||
public class OwnSystemRequirement: IAuthorizationRequirement { }
|
||||
}
|
18
PluralKit.API/Authorization/PrivacyRequirement.cs
Normal file
18
PluralKit.API/Authorization/PrivacyRequirement.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using System;
|
||||
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
||||
using PluralKit.Core;
|
||||
|
||||
namespace PluralKit.API
|
||||
{
|
||||
public class PrivacyRequirement<T>: IAuthorizationRequirement
|
||||
{
|
||||
public readonly Func<T, PrivacyLevel> Mapper;
|
||||
|
||||
public PrivacyRequirement(Func<T, PrivacyLevel> mapper)
|
||||
{
|
||||
Mapper = mapper;
|
||||
}
|
||||
}
|
||||
}
|
20
PluralKit.API/Authorization/SystemOwnerHandler.cs
Normal file
20
PluralKit.API/Authorization/SystemOwnerHandler.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
||||
using PluralKit.Core;
|
||||
|
||||
namespace PluralKit.API
|
||||
{
|
||||
public class SystemOwnerHandler: AuthorizationHandler<OwnSystemRequirement, PKSystem>
|
||||
{
|
||||
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
|
||||
OwnSystemRequirement requirement, PKSystem resource)
|
||||
{
|
||||
if (!context.User.Identity.IsAuthenticated) return Task.CompletedTask;
|
||||
if (resource.Id == context.User.CurrentSystem())
|
||||
context.Succeed(requirement);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
21
PluralKit.API/Authorization/SystemPrivacyHandler.cs
Normal file
21
PluralKit.API/Authorization/SystemPrivacyHandler.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
||||
using PluralKit.Core;
|
||||
|
||||
namespace PluralKit.API
|
||||
{
|
||||
public class SystemPrivacyHandler: AuthorizationHandler<PrivacyRequirement<PKSystem>, PKSystem>
|
||||
{
|
||||
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
|
||||
PrivacyRequirement<PKSystem> requirement, PKSystem resource)
|
||||
{
|
||||
var level = requirement.Mapper(resource);
|
||||
var ctx = context.User.ContextFor(resource);
|
||||
if (level.CanAccess(ctx))
|
||||
context.Succeed(requirement);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
@@ -15,12 +15,10 @@ namespace PluralKit.API
|
||||
public class AccountController: ControllerBase
|
||||
{
|
||||
private IDataStore _data;
|
||||
private TokenAuthService _auth;
|
||||
|
||||
public AccountController(IDataStore data, TokenAuthService auth)
|
||||
public AccountController(IDataStore data)
|
||||
{
|
||||
_data = data;
|
||||
_auth = auth;
|
||||
}
|
||||
|
||||
[HttpGet("{aid}")]
|
||||
@@ -29,7 +27,7 @@ namespace PluralKit.API
|
||||
var system = await _data.GetSystemByAccount(aid);
|
||||
if (system == null) return NotFound("Account not found.");
|
||||
|
||||
return Ok(system.ToJson(_auth.ContextFor(system)));
|
||||
return Ok(system.ToJson(User.ContextFor(system)));
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,5 +1,6 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
using Newtonsoft.Json.Linq;
|
||||
@@ -15,9 +16,9 @@ namespace PluralKit.API
|
||||
public class MemberController: ControllerBase
|
||||
{
|
||||
private IDataStore _data;
|
||||
private TokenAuthService _auth;
|
||||
private IAuthorizationService _auth;
|
||||
|
||||
public MemberController(IDataStore data, TokenAuthService auth)
|
||||
public MemberController(IDataStore data, IAuthorizationService auth)
|
||||
{
|
||||
_data = data;
|
||||
_auth = auth;
|
||||
@@ -29,15 +30,15 @@ namespace PluralKit.API
|
||||
var member = await _data.GetMemberByHid(hid);
|
||||
if (member == null) return NotFound("Member not found.");
|
||||
|
||||
return Ok(member.ToJson(_auth.ContextFor(member)));
|
||||
return Ok(member.ToJson(User.ContextFor(member)));
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[RequiresSystem]
|
||||
[Authorize]
|
||||
public async Task<ActionResult<JObject>> PostMember([FromBody] JObject properties)
|
||||
{
|
||||
var system = _auth.CurrentSystem;
|
||||
|
||||
var system = User.CurrentSystem();
|
||||
|
||||
if (!properties.ContainsKey("name"))
|
||||
return BadRequest("Member name must be specified.");
|
||||
|
||||
@@ -57,17 +58,18 @@ namespace PluralKit.API
|
||||
}
|
||||
|
||||
await _data.SaveMember(member);
|
||||
return Ok(member.ToJson(_auth.ContextFor(member)));
|
||||
return Ok(member.ToJson(User.ContextFor(member)));
|
||||
}
|
||||
|
||||
[HttpPatch("{hid}")]
|
||||
[RequiresSystem]
|
||||
[Authorize]
|
||||
public async Task<ActionResult<JObject>> PatchMember(string hid, [FromBody] JObject changes)
|
||||
{
|
||||
var member = await _data.GetMemberByHid(hid);
|
||||
if (member == null) return NotFound("Member not found.");
|
||||
|
||||
if (member.System != _auth.CurrentSystem.Id) return Unauthorized($"Member '{hid}' is not part of your system.");
|
||||
|
||||
var res = await _auth.AuthorizeAsync(User, member, "EditMember");
|
||||
if (!res.Succeeded) return Unauthorized($"Member '{hid}' is not part of your system.");
|
||||
|
||||
try
|
||||
{
|
||||
@@ -79,17 +81,18 @@ namespace PluralKit.API
|
||||
}
|
||||
|
||||
await _data.SaveMember(member);
|
||||
return Ok(member.ToJson(_auth.ContextFor(member)));
|
||||
return Ok(member.ToJson(User.ContextFor(member)));
|
||||
}
|
||||
|
||||
[HttpDelete("{hid}")]
|
||||
[RequiresSystem]
|
||||
[Authorize]
|
||||
public async Task<ActionResult> DeleteMember(string hid)
|
||||
{
|
||||
var member = await _data.GetMemberByHid(hid);
|
||||
if (member == null) return NotFound("Member not found.");
|
||||
|
||||
if (member.System != _auth.CurrentSystem.Id) return Unauthorized($"Member '{hid}' is not part of your system.");
|
||||
var res = await _auth.AuthorizeAsync(User, member, "EditMember");
|
||||
if (!res.Succeeded) return Unauthorized($"Member '{hid}' is not part of your system.");
|
||||
|
||||
await _data.DeleteMember(member);
|
||||
return Ok();
|
@@ -30,12 +30,10 @@ namespace PluralKit.API
|
||||
public class MessageController: ControllerBase
|
||||
{
|
||||
private IDataStore _data;
|
||||
private TokenAuthService _auth;
|
||||
|
||||
public MessageController(IDataStore _data, TokenAuthService auth)
|
||||
public MessageController(IDataStore _data)
|
||||
{
|
||||
this._data = _data;
|
||||
_auth = auth;
|
||||
}
|
||||
|
||||
[HttpGet("{mid}")]
|
||||
@@ -50,8 +48,8 @@ namespace PluralKit.API
|
||||
Id = msg.Message.Mid.ToString(),
|
||||
Channel = msg.Message.Channel.ToString(),
|
||||
Sender = msg.Message.Sender.ToString(),
|
||||
Member = msg.Member.ToJson(_auth.ContextFor(msg.System)),
|
||||
System = msg.System.ToJson(_auth.ContextFor(msg.System)),
|
||||
Member = msg.Member.ToJson(User.ContextFor(msg.System)),
|
||||
System = msg.System.ToJson(User.ContextFor(msg.System)),
|
||||
Original = msg.Message.OriginalMid?.ToString()
|
||||
};
|
||||
}
|
@@ -4,6 +4,7 @@ using System.Threading.Tasks;
|
||||
|
||||
using Dapper;
|
||||
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
@@ -41,9 +42,9 @@ namespace PluralKit.API
|
||||
{
|
||||
private IDataStore _data;
|
||||
private IDatabase _conn;
|
||||
private TokenAuthService _auth;
|
||||
private IAuthorizationService _auth;
|
||||
|
||||
public SystemController(IDataStore data, IDatabase conn, TokenAuthService auth)
|
||||
public SystemController(IDataStore data, IDatabase conn, IAuthorizationService auth)
|
||||
{
|
||||
_data = data;
|
||||
_conn = conn;
|
||||
@@ -51,10 +52,11 @@ namespace PluralKit.API
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[RequiresSystem]
|
||||
public Task<ActionResult<JObject>> GetOwnSystem()
|
||||
[Authorize]
|
||||
public async Task<ActionResult<JObject>> GetOwnSystem()
|
||||
{
|
||||
return Task.FromResult<ActionResult<JObject>>(Ok(_auth.CurrentSystem.ToJson(_auth.ContextFor(_auth.CurrentSystem))));
|
||||
var system = await _conn.Execute(c => c.QuerySystem(User.CurrentSystem()));
|
||||
return system.ToJson(User.ContextFor(system));
|
||||
}
|
||||
|
||||
[HttpGet("{hid}")]
|
||||
@@ -62,7 +64,7 @@ namespace PluralKit.API
|
||||
{
|
||||
var system = await _data.GetSystemByHid(hid);
|
||||
if (system == null) return NotFound("System not found.");
|
||||
return Ok(system.ToJson(_auth.ContextFor(system)));
|
||||
return Ok(system.ToJson(User.ContextFor(system)));
|
||||
}
|
||||
|
||||
[HttpGet("{hid}/members")]
|
||||
@@ -71,13 +73,13 @@ namespace PluralKit.API
|
||||
var system = await _data.GetSystemByHid(hid);
|
||||
if (system == null) return NotFound("System not found.");
|
||||
|
||||
if (!system.MemberListPrivacy.CanAccess(_auth.ContextFor(system)))
|
||||
if (!system.MemberListPrivacy.CanAccess(User.ContextFor(system)))
|
||||
return StatusCode(StatusCodes.Status403Forbidden, "Unauthorized to view member list.");
|
||||
|
||||
var members = _data.GetSystemMembers(system);
|
||||
return Ok(await members
|
||||
.Where(m => m.MemberPrivacy.CanAccess(_auth.ContextFor(system)))
|
||||
.Select(m => m.ToJson(_auth.ContextFor(system)))
|
||||
.Where(m => m.MemberPrivacy.CanAccess(User.ContextFor(system)))
|
||||
.Select(m => m.ToJson(User.ContextFor(system)))
|
||||
.ToListAsync());
|
||||
}
|
||||
|
||||
@@ -88,9 +90,9 @@ namespace PluralKit.API
|
||||
|
||||
var system = await _data.GetSystemByHid(hid);
|
||||
if (system == null) return NotFound("System not found.");
|
||||
|
||||
if (!system.FrontHistoryPrivacy.CanAccess(_auth.ContextFor(system)))
|
||||
return StatusCode(StatusCodes.Status403Forbidden, "Unauthorized to view front history.");
|
||||
|
||||
var auth = await _auth.AuthorizeAsync(User, system, "ViewFrontHistory");
|
||||
if (!auth.Succeeded) return StatusCode(StatusCodes.Status403Forbidden, "Unauthorized to view front history.");
|
||||
|
||||
using (var conn = await _conn.Obtain())
|
||||
{
|
||||
@@ -112,26 +114,25 @@ namespace PluralKit.API
|
||||
var system = await _data.GetSystemByHid(hid);
|
||||
if (system == null) return NotFound("System not found.");
|
||||
|
||||
if (!system.FrontPrivacy.CanAccess(_auth.ContextFor(system)))
|
||||
return StatusCode(StatusCodes.Status403Forbidden, "Unauthorized to view fronter.");
|
||||
var auth = await _auth.AuthorizeAsync(User, system, "ViewFront");
|
||||
if (!auth.Succeeded) return StatusCode(StatusCodes.Status403Forbidden, "Unauthorized to view fronter.");
|
||||
|
||||
var sw = await _data.GetLatestSwitch(system);
|
||||
var sw = await _data.GetLatestSwitch(system.Id);
|
||||
if (sw == null) return NotFound("System has no registered switches.");
|
||||
|
||||
var members = _data.GetSwitchMembers(sw);
|
||||
return Ok(new FrontersReturn
|
||||
{
|
||||
Timestamp = sw.Timestamp,
|
||||
Members = await members.Select(m => m.ToJson(_auth.ContextFor(system))).ToListAsync()
|
||||
Members = await members.Select(m => m.ToJson(User.ContextFor(system))).ToListAsync()
|
||||
});
|
||||
}
|
||||
|
||||
[HttpPatch]
|
||||
[RequiresSystem]
|
||||
[Authorize]
|
||||
public async Task<ActionResult<JObject>> EditSystem([FromBody] JObject changes)
|
||||
{
|
||||
var system = _auth.CurrentSystem;
|
||||
|
||||
var system = await _conn.Execute(c => c.QuerySystem(User.CurrentSystem()));
|
||||
try
|
||||
{
|
||||
system.ApplyJson(changes);
|
||||
@@ -142,18 +143,18 @@ namespace PluralKit.API
|
||||
}
|
||||
|
||||
await _data.SaveSystem(system);
|
||||
return Ok(system.ToJson(_auth.ContextFor(system)));
|
||||
return Ok(system.ToJson(User.ContextFor(system)));
|
||||
}
|
||||
|
||||
[HttpPost("switches")]
|
||||
[RequiresSystem]
|
||||
[Authorize]
|
||||
public async Task<IActionResult> PostSwitch([FromBody] PostSwitchParams param)
|
||||
{
|
||||
if (param.Members.Distinct().Count() != param.Members.Count())
|
||||
if (param.Members.Distinct().Count() != param.Members.Count)
|
||||
return BadRequest("Duplicate members in member list.");
|
||||
|
||||
// We get the current switch, if it exists
|
||||
var latestSwitch = await _data.GetLatestSwitch(_auth.CurrentSystem);
|
||||
var latestSwitch = await _data.GetLatestSwitch(User.CurrentSystem());
|
||||
if (latestSwitch != null)
|
||||
{
|
||||
var latestSwitchMembers = _data.GetSwitchMembers(latestSwitch);
|
||||
@@ -169,7 +170,7 @@ namespace PluralKit.API
|
||||
membersList = (await conn.QueryAsync<PKMember>("select * from members where hid = any(@Hids)", new {Hids = param.Members})).ToList();
|
||||
|
||||
foreach (var member in membersList)
|
||||
if (member.System != _auth.CurrentSystem.Id)
|
||||
if (member.System != User.CurrentSystem())
|
||||
return BadRequest($"Cannot switch to member '{member.Hid}' not in system.");
|
||||
|
||||
// membersList is in DB order, and we want it in actual input order
|
||||
@@ -185,7 +186,7 @@ namespace PluralKit.API
|
||||
}
|
||||
|
||||
// Finally, log the switch (yay!)
|
||||
await _data.AddSwitch(_auth.CurrentSystem, membersInOrder);
|
||||
await _data.AddSwitch(User.CurrentSystem(), membersInOrder);
|
||||
return NoContent();
|
||||
}
|
||||
}
|
@@ -6,8 +6,6 @@ namespace PluralKit.API
|
||||
{
|
||||
protected override void Load(ContainerBuilder builder)
|
||||
{
|
||||
// Lifetime scope so the service, RequiresSystem, and handler itself all get the same value
|
||||
builder.RegisterType<TokenAuthService>().AsSelf().InstancePerLifetimeScope();
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,41 +0,0 @@
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Dapper;
|
||||
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
using PluralKit.Core;
|
||||
|
||||
namespace PluralKit.API
|
||||
{
|
||||
public class TokenAuthService: IMiddleware
|
||||
{
|
||||
public PKSystem CurrentSystem { get; set; }
|
||||
|
||||
private readonly IDatabase _db;
|
||||
|
||||
public TokenAuthService(IDatabase db)
|
||||
{
|
||||
_db = db;
|
||||
}
|
||||
|
||||
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
|
||||
{
|
||||
var token = context.Request.Headers["Authorization"].FirstOrDefault();
|
||||
if (token != null)
|
||||
{
|
||||
CurrentSystem = await _db.Execute(c => c.QueryFirstOrDefaultAsync("select * from systems where token = @token", new { token }));
|
||||
}
|
||||
|
||||
await next.Invoke(context);
|
||||
CurrentSystem = null;
|
||||
}
|
||||
|
||||
public LookupContext ContextFor(PKSystem system) =>
|
||||
system.Id == CurrentSystem?.Id ? LookupContext.ByOwner : LookupContext.API;
|
||||
|
||||
public LookupContext ContextFor(PKMember member) =>
|
||||
member.System == CurrentSystem?.Id ? LookupContext.ByOwner : LookupContext.API;
|
||||
}
|
||||
}
|
@@ -4,6 +4,8 @@ using System.Reflection;
|
||||
|
||||
using Autofac;
|
||||
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
@@ -13,6 +15,7 @@ using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.OpenApi.Models;
|
||||
|
||||
using PluralKit.API;
|
||||
using PluralKit.Core;
|
||||
|
||||
namespace PluralKit.API
|
||||
@@ -30,6 +33,23 @@ namespace PluralKit.API
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
services.AddCors();
|
||||
services.AddAuthentication("SystemToken")
|
||||
.AddScheme<SystemTokenAuthenticationHandler.Opts, SystemTokenAuthenticationHandler>("SystemToken", null);
|
||||
|
||||
services.AddAuthorization(options =>
|
||||
{
|
||||
options.AddPolicy("EditSystem", p => p.RequireAuthenticatedUser().AddRequirements(new OwnSystemRequirement()));
|
||||
options.AddPolicy("EditMember", p => p.RequireAuthenticatedUser().AddRequirements(new OwnSystemRequirement()));
|
||||
|
||||
options.AddPolicy("ViewMembers", p => p.AddRequirements(new PrivacyRequirement<PKSystem>(s => s.MemberListPrivacy)));
|
||||
options.AddPolicy("ViewFront", p => p.AddRequirements(new PrivacyRequirement<PKSystem>(s => s.FrontPrivacy)));
|
||||
options.AddPolicy("ViewFrontHistory", p => p.AddRequirements(new PrivacyRequirement<PKSystem>(s => s.FrontHistoryPrivacy)));
|
||||
});
|
||||
services.AddSingleton<IAuthenticationHandler, SystemTokenAuthenticationHandler>();
|
||||
services.AddSingleton<IAuthorizationHandler, MemberOwnerHandler>();
|
||||
services.AddSingleton<IAuthorizationHandler, SystemOwnerHandler>();
|
||||
services.AddSingleton<IAuthorizationHandler, SystemPrivacyHandler>();
|
||||
|
||||
services.AddControllers()
|
||||
.SetCompatibilityVersion(CompatibilityVersion.Latest)
|
||||
.AddNewtonsoftJson(); // sorry MS, this just does *more*
|
||||
@@ -105,9 +125,10 @@ namespace PluralKit.API
|
||||
|
||||
//app.UseHttpsRedirection();
|
||||
app.UseCors(opts => opts.AllowAnyMethod().AllowAnyOrigin().WithHeaders("Content-Type", "Authorization"));
|
||||
app.UseMiddleware<TokenAuthService>();
|
||||
|
||||
app.UseRouting();
|
||||
app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
app.UseEndpoints(endpoints => endpoints.MapControllers());
|
||||
}
|
||||
}
|
||||
|
@@ -1,23 +0,0 @@
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace PluralKit.API
|
||||
{
|
||||
public class RequiresSystemAttribute: ActionFilterAttribute
|
||||
{
|
||||
|
||||
public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
|
||||
{
|
||||
var auth = context.HttpContext.RequestServices.GetRequiredService<TokenAuthService>();
|
||||
if (auth.CurrentSystem == null)
|
||||
{
|
||||
context.Result = new UnauthorizedObjectResult("Invalid or missing token in Authorization header.");
|
||||
return;
|
||||
}
|
||||
|
||||
await base.OnActionExecutionAsync(context, next);
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user