diff --git a/PluralKit.API/Controllers/MemberController.cs b/PluralKit.API/Controllers/MemberController.cs new file mode 100644 index 00000000..e2d4b678 --- /dev/null +++ b/PluralKit.API/Controllers/MemberController.cs @@ -0,0 +1,66 @@ +using System.Data; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using PluralKit.Core; + +namespace PluralKit.API.Controllers +{ + [ApiController] + [Route("m")] + public class MemberController: ControllerBase + { + private MemberStore _members; + private IDbConnection _conn; + private TokenAuthService _auth; + + public MemberController(MemberStore members, IDbConnection conn, TokenAuthService auth) + { + _members = members; + _conn = conn; + _auth = auth; + } + + [HttpGet("{hid}")] + public async Task> GetMember(string hid) + { + var member = await _members.GetByHid(hid); + if (member == null) return NotFound("Member not found."); + + return Ok(member); + } + + [HttpPatch("{hid}")] + [RequiresSystem] + public async Task> PatchMember(string hid, [FromBody] PKMember newMember) + { + var member = await _members.GetByHid(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."); + + // Explicit bounds checks + if (newMember.Name.Length > Limits.MaxMemberNameLength) + return BadRequest($"Member name too long ({newMember.Name.Length} > {Limits.MaxMemberNameLength}."); + if (newMember.Pronouns.Length > Limits.MaxPronounsLength) + return BadRequest($"Member pronouns too long ({newMember.Pronouns.Length} > {Limits.MaxPronounsLength}."); + if (newMember.Description.Length > Limits.MaxDescriptionLength) + return BadRequest($"Member descriptions too long ({newMember.Description.Length} > {Limits.MaxDescriptionLength}."); + + // Sanity bounds checks + if (newMember.AvatarUrl.Length > 1000 || newMember.Prefix.Length > 1000 || newMember.Suffix.Length > 1000) + return BadRequest(); + + member.Name = newMember.Name; + member.Color = newMember.Color; + member.AvatarUrl = newMember.AvatarUrl; + member.Birthday = newMember.Birthday; + member.Pronouns = newMember.Pronouns; + member.Description = newMember.Description; + member.Prefix = newMember.Prefix; + member.Suffix = newMember.Suffix; + await _members.Save(member); + + return Ok(); + } + } +} \ No newline at end of file diff --git a/PluralKit.API/Controllers/SystemController.cs b/PluralKit.API/Controllers/SystemController.cs index 73d1fb14..3cedf147 100644 --- a/PluralKit.API/Controllers/SystemController.cs +++ b/PluralKit.API/Controllers/SystemController.cs @@ -99,9 +99,9 @@ namespace PluralKit.API.Controllers } [HttpPatch] + [RequiresSystem] public async Task> EditSystem([FromBody] PKSystem newSystem) { - if (_auth.CurrentSystem == null) return Unauthorized("No token specified in Authorization header."); var system = _auth.CurrentSystem; system.Name = newSystem.Name; @@ -115,10 +115,9 @@ namespace PluralKit.API.Controllers } [HttpPost("switches")] + [RequiresSystem] public async Task PostSwitch([FromBody] PostSwitchParams param) { - if (_auth.CurrentSystem == null) return Unauthorized("No token specified in Authorization header."); - if (param.Members.Distinct().Count() != param.Members.Count()) return BadRequest("Duplicate members in member list."); diff --git a/PluralKit.API/RequiresSystemAttribute.cs b/PluralKit.API/RequiresSystemAttribute.cs new file mode 100644 index 00000000..381cf29e --- /dev/null +++ b/PluralKit.API/RequiresSystemAttribute.cs @@ -0,0 +1,23 @@ +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(); + if (auth.CurrentSystem == null) + { + context.Result = new UnauthorizedObjectResult("Invalid or missing token in Authorization header."); + return; + } + + await base.OnActionExecutionAsync(context, next); + } + } +} \ No newline at end of file diff --git a/PluralKit.Bot/Commands/MemberCommands.cs b/PluralKit.Bot/Commands/MemberCommands.cs index 968c5fc2..a2ad4867 100644 --- a/PluralKit.Bot/Commands/MemberCommands.cs +++ b/PluralKit.Bot/Commands/MemberCommands.cs @@ -6,6 +6,7 @@ using System.Threading.Tasks; using Discord; using Discord.Commands; using NodaTime; +using PluralKit.Core; using Image = SixLabors.ImageSharp.Image; namespace PluralKit.Bot.Commands diff --git a/PluralKit.Bot/Commands/SystemCommands.cs b/PluralKit.Bot/Commands/SystemCommands.cs index 6a964324..477c02c3 100644 --- a/PluralKit.Bot/Commands/SystemCommands.cs +++ b/PluralKit.Bot/Commands/SystemCommands.cs @@ -9,6 +9,7 @@ using NodaTime; using NodaTime.Extensions; using NodaTime.Text; using NodaTime.TimeZones; +using PluralKit.Core; namespace PluralKit.Bot.Commands { diff --git a/PluralKit.Bot/Errors.cs b/PluralKit.Bot/Errors.cs index 5cd76df9..6e346fe7 100644 --- a/PluralKit.Bot/Errors.cs +++ b/PluralKit.Bot/Errors.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Net; using Humanizer; using NodaTime; +using PluralKit.Core; namespace PluralKit.Bot { public static class Errors { diff --git a/PluralKit.Bot/Utils.cs b/PluralKit.Bot/Utils.cs index e1fc6531..f55458ed 100644 --- a/PluralKit.Bot/Utils.cs +++ b/PluralKit.Bot/Utils.cs @@ -12,6 +12,7 @@ using Discord.Commands.Builders; using Discord.WebSocket; using Microsoft.Extensions.DependencyInjection; using NodaTime; +using PluralKit.Core; using Image = SixLabors.ImageSharp.Image; namespace PluralKit.Bot diff --git a/PluralKit.Bot/Limits.cs b/PluralKit.Core/Limits.cs similarity index 94% rename from PluralKit.Bot/Limits.cs rename to PluralKit.Core/Limits.cs index 3f7b3ec5..37f56ea7 100644 --- a/PluralKit.Bot/Limits.cs +++ b/PluralKit.Core/Limits.cs @@ -1,4 +1,4 @@ -namespace PluralKit.Bot { +namespace PluralKit.Core { public static class Limits { public static readonly int MaxSystemNameLength = 100; public static readonly int MaxSystemTagLength = 31;