SlotPostEndpoint.vala 2.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768
  1. using Invercargill;
  2. using Invercargill.DataStructures;
  3. using InvercargillJson;
  4. using Inversion;
  5. using Astralis;
  6. namespace Statum {
  7. /**
  8. * `POST /_statum/slots` — refreshes snapshots whose `transmit_after` window
  9. * has elapsed, and is also the target of a `transmit(retry)` directive.
  10. *
  11. * The client POSTs a JSON array of Statum frames it holds. For each frame
  12. * this endpoint verifies the signature: on success it
  13. * {@link StateService.restore_slot}s the slot into the cache, re-signs it
  14. * (issuing a fresh `as_at`/`transmit_after`), and returns a `set` directive
  15. * so the client refreshes its local snapshot; on failure it returns an
  16. * `invalidate` for that frame's signature so the client drops the slot whose
  17. * frame the server could not trust.
  18. */
  19. public class SlotPostEndpoint : Object, Endpoint {
  20. private StateService state_service = inject<StateService>();
  21. private Cryptography.SigningProvider signing_provider = inject<Cryptography.SigningProvider>();
  22. public async HttpResult handle_request(HttpContext http_context, RouteContext route_context) throws GLib.Error {
  23. var body = yield http_context.request.request_body.read_all();
  24. var body_str = body.to_raw_string();
  25. var directives = new Series<Model.DirectiveDto>();
  26. var invalid_signatures = new Series<string>();
  27. if (body_str.strip().length > 0) {
  28. var body_element = new JsonElement.from_string(body_str);
  29. var array = body_element.as<JsonArray>();
  30. foreach (var item in array) {
  31. var frame = Model.FrameDto.get_mapper().materialise(item.as<JsonObject>());
  32. try {
  33. signing_provider.verify_frame(frame);
  34. } catch (GLib.Error e) {
  35. // Signature did not verify: tell the client to drop the
  36. // slot holding this frame, identified by signature.
  37. if (frame.signature != null && frame.signature.length > 0) {
  38. invalid_signatures.add(frame.signature);
  39. }
  40. continue;
  41. }
  42. var content_element = new JsonElement.from_string(frame.content);
  43. var content_object = content_element.as<JsonObject>();
  44. var snapshot = Model.SnapshotDto.get_mapper().materialise(content_object);
  45. state_service.restore_slot(frame);
  46. var refreshed = state_service.sign_slot(snapshot.slot.key);
  47. if (refreshed != null) {
  48. directives.add(new Model.SetDirectiveDto.with_frame((!)refreshed));
  49. }
  50. }
  51. }
  52. return StatumDirectives.to_result(directives, invalid_signatures);
  53. }
  54. }
  55. }