diff --git a/PluralKit.API/Controllers/v2/DiscordControllerV2.cs b/PluralKit.API/Controllers/v2/DiscordControllerV2.cs index d636fa64..7d830da2 100644 --- a/PluralKit.API/Controllers/v2/DiscordControllerV2.cs +++ b/PluralKit.API/Controllers/v2/DiscordControllerV2.cs @@ -14,10 +14,13 @@ public class DiscordControllerV2: PKControllerBase public DiscordControllerV2(IServiceProvider svc) : base(svc) { } - [HttpGet("systems/@me/guilds/{guild_id}")] - public async Task SystemGuildGet(ulong guild_id) + [HttpGet("systems/{systemRef}/guilds/{guild_id}")] + public async Task SystemGuildGet(string systemRef, ulong guild_id) { - var system = await ResolveSystem("@me"); + var system = await ResolveSystem(systemRef); + if (ContextFor(system) != LookupContext.ByOwner) + throw Errors.GenericMissingPermissions; + var settings = await _repo.GetSystemGuild(guild_id, system.Id, false); if (settings == null) throw Errors.SystemGuildNotFound; @@ -29,10 +32,13 @@ public class DiscordControllerV2: PKControllerBase return Ok(settings.ToJson(member?.Hid)); } - [HttpPatch("systems/@me/guilds/{guild_id}")] - public async Task DoSystemGuildPatch(ulong guild_id, [FromBody] JObject data) + [HttpPatch("systems/{systemRef}/guilds/{guild_id}")] + public async Task DoSystemGuildPatch(string systemRef, ulong guild_id, [FromBody] JObject data) { - var system = await ResolveSystem("@me"); + var system = await ResolveSystem(systemRef); + if (ContextFor(system) != LookupContext.ByOwner) + throw Errors.GenericMissingPermissions; + var settings = await _repo.GetSystemGuild(guild_id, system.Id, false); if (settings == null) throw Errors.SystemGuildNotFound; diff --git a/PluralKit.API/Controllers/v2/SwitchControllerV2.cs b/PluralKit.API/Controllers/v2/SwitchControllerV2.cs index 3a48e7f5..459f310c 100644 --- a/PluralKit.API/Controllers/v2/SwitchControllerV2.cs +++ b/PluralKit.API/Controllers/v2/SwitchControllerV2.cs @@ -77,14 +77,16 @@ public class SwitchControllerV2: PKControllerBase } - [HttpPost("systems/@me/switches")] - public async Task SwitchCreate([FromBody] PostSwitchParams data) + [HttpPost("systems/{systemRef}/switches")] + public async Task SwitchCreate(string systemRef, [FromBody] PostSwitchParams data) { + var system = await ResolveSystem(systemRef); + if (ContextFor(system) != LookupContext.ByOwner) + throw Errors.GenericMissingPermissions; + if (data.Members.Distinct().Count() != data.Members.Count) throw Errors.DuplicateMembersInList; - var system = await ResolveSystem("@me"); - if (data.Timestamp != null && await _repo.GetSwitches(system.Id).Select(x => x.Timestamp) .ContainsAsync(data.Timestamp.Value)) throw Errors.SameSwitchTimestampError; @@ -155,11 +157,15 @@ public class SwitchControllerV2: PKControllerBase }); } - [HttpPatch("systems/@me/switches/{switchRef}")] - public async Task SwitchPatch(string switchRef, [FromBody] JObject data) + [HttpPatch("systems/{systemRef}/switches/{switchRef}")] + public async Task SwitchPatch(string systemRef, string switchRef, [FromBody] JObject data) { // for now, don't need to make a PatchObject for this, since it's only one param + var system = await ResolveSystem(systemRef); + if (ContextFor(system) != LookupContext.ByOwner) + throw Errors.GenericMissingPermissions; + if (!Guid.TryParse(switchRef, out var switchId)) throw Errors.InvalidSwitchId; @@ -169,10 +175,6 @@ public class SwitchControllerV2: PKControllerBase var value = Instant.FromDateTimeOffset(DateTime.Parse(valueStr).ToUniversalTime()); - var system = await ResolveSystem("@me"); - if (system == null) - throw Errors.SystemNotFound; - var sw = await _repo.GetSwitchByUuid(switchId); if (sw == null || system.Id != sw.System) throw Errors.SwitchNotFoundPublic; @@ -191,15 +193,18 @@ public class SwitchControllerV2: PKControllerBase }); } - [HttpPatch("systems/@me/switches/{switchRef}/members")] - public async Task SwitchMemberPatch(string switchRef, [FromBody] JArray data) + [HttpPatch("systems/{systemRef}/switches/{switchRef}/members")] + public async Task SwitchMemberPatch(string systemRef, string switchRef, [FromBody] JArray data) { + var system = await ResolveSystem(systemRef); + if (ContextFor(system) != LookupContext.ByOwner) + throw Errors.GenericMissingPermissions; + if (!Guid.TryParse(switchRef, out var switchId)) + throw Errors.SwitchNotFound; - if (data.Distinct().Count() != data.Count) - throw Errors.DuplicateMembersInList; - - var system = await ResolveSystem("@me"); + if (data.Distinct().Count() != data.Count) + throw Errors.DuplicateMembersInList; var sw = await _repo.GetSwitchByUuid(switchId); if (sw == null) @@ -235,13 +240,16 @@ public class SwitchControllerV2: PKControllerBase }); } - [HttpDelete("systems/@me/switches/{switchRef}")] - public async Task SwitchDelete(string switchRef) + [HttpDelete("systems/{systemRef}/switches/{switchRef}")] + public async Task SwitchDelete(string systemRef, string switchRef) { + var system = await ResolveSystem(systemRef); + if (ContextFor(system) != LookupContext.ByOwner) + throw Errors.GenericMissingPermissions; + if (!Guid.TryParse(switchRef, out var switchId)) throw Errors.InvalidSwitchId; - var system = await ResolveSystem("@me"); var sw = await _repo.GetSwitchByUuid(switchId); if (sw == null || system.Id != sw.System) throw Errors.SwitchNotFoundPublic; diff --git a/PluralKit.API/Errors.cs b/PluralKit.API/Errors.cs index 6e0ad639..261ac47d 100644 --- a/PluralKit.API/Errors.cs +++ b/PluralKit.API/Errors.cs @@ -72,6 +72,7 @@ public static class Errors { public static PKError GenericBadRequest = new(400, 0, "400: Bad Request"); public static PKError GenericAuthError = new(401, 0, "401: Missing or invalid Authorization header"); + public static PKError GenericMissingPermissions = new(403, 0, "403: Missing permissions to access this resource"); public static PKError SystemNotFound = new(404, 20001, "System not found."); public static PKError MemberNotFound = new(404, 20002, "Member not found."); diff --git a/docs/content/api/endpoints.md b/docs/content/api/endpoints.md index 0c950f53..8d24b09f 100644 --- a/docs/content/api/endpoints.md +++ b/docs/content/api/endpoints.md @@ -204,7 +204,8 @@ Takes an array of member references as input. (An empty list is accepted.) Retur --- ## Switches -*`switchRef` must be a switch's UUID. On POST/PATCH/DELETE endpoints, `systemRef` must be `@me`.* +*`switchRef` must be a switch's UUID. `systemRef` can be a system's short (5-character) ID, a system's UUID, the ID of a Discord account linked to the system, or the string `@me` to refer to the currently authenticated system.* + ### Get System Switches