Unverified Commit 61af958f authored by Hong Minhee's avatar Hong Minhee
Browse files

More tests

parent 4f453692
Loading
Loading
Loading
Loading
+4 −1
Original line number Diff line number Diff line
@@ -22,7 +22,10 @@
  },
  "[typescript]": {
    "editor.defaultFormatter": "denoland.vscode-deno",
    "editor.formatOnSave": true
    "editor.formatOnSave": true,
    "editor.codeActionsOnSave": {
      "source.organizeImports": "explicit"
    }
  },
  "cSpell.words": [
    "bccs",
+12 −26
Original line number Diff line number Diff line
import { accepts } from "jsr:@std/http@^0.218.2";
import { doesActorOwnKey, verify } from "../httpsig/mod.ts";
import { DocumentLoader } from "../runtime/docloader.ts";
import {
  Activity,
  Link,
  Object,
  OrderedCollection,
  OrderedCollectionPage,
} from "../vocab/vocab.ts";
import {
  ActorDispatcher,
  CollectionCounter,
@@ -7,16 +16,6 @@ import {
  InboxListener,
} from "./callback.ts";
import { RequestContext } from "./context.ts";
import { verify } from "../httpsig/mod.ts";
import { DocumentLoader } from "../runtime/docloader.ts";
import { isActor } from "../vocab/actor.ts";
import {
  Activity,
  Link,
  Object,
  OrderedCollection,
  OrderedCollectionPage,
} from "../vocab/mod.ts";

function acceptsJsonLd(request: Request): boolean {
  const types = accepts(request);
@@ -250,8 +249,8 @@ export async function handleInbox<TContextData>(
      return response instanceof Promise ? await response : response;
    }
  }
  const keyId = await verify(request, documentLoader);
  if (keyId == null) {
  const key = await verify(request, documentLoader);
  if (key == null) {
    const response = new Response("Failed to verify the request signature.", {
      status: 401,
      headers: { "Content-Type": "text/plain; charset=utf-8" },
@@ -300,7 +299,7 @@ export async function handleInbox<TContextData>(
    });
    return response;
  }
  if (!await doesActorOwnKey(activity, keyId)) {
  if (!await doesActorOwnKey(activity, key, documentLoader)) {
    const response = new Response("The signer and the actor do not match.", {
      status: 401,
      headers: { "Content-Type": "text/plain; charset=utf-8" },
@@ -341,16 +340,3 @@ export async function handleInbox<TContextData>(
    headers: { "Content-Type": "text/plain; charset=utf-8" },
  });
}

async function doesActorOwnKey(
  activity: Activity,
  keyId: URL,
): Promise<boolean> {
  if (activity.actorId?.href === keyId.href.replace(/#.*$/, "")) return true;
  const actor = await activity.getActor();
  if (actor == null || !isActor(actor)) return false;
  for (const publicKeyId of actor.publicKeyIds) {
    if (publicKeyId.href === keyId.href) return true;
  }
  return false;
}
+54 −0
Original line number Diff line number Diff line
import { assertEquals } from "jsr:@std/assert@^0.218.2";
import { Service } from "../mod.ts";
import { Actor } from "../vocab/actor.ts";
import { Application, Endpoints, Group, Person } from "../vocab/vocab.ts";
import { extractInboxes } from "./send.ts";

Deno.test("extractInboxes()", () => {
  const recipients: Actor[] = [
    new Person({
      id: new URL("https://example.com/alice"),
      inbox: new URL("https://example.com/alice/inbox"),
      endpoints: new Endpoints({
        sharedInbox: new URL("https://example.com/inbox"),
      }),
    }),
    new Application({
      id: new URL("https://example.com/app"),
      inbox: new URL("https://example.com/app/inbox"),
      endpoints: new Endpoints({
        sharedInbox: new URL("https://example.com/inbox"),
      }),
    }),
    new Group({
      id: new URL("https://example.org/group"),
      inbox: new URL("https://example.org/group/inbox"),
    }),
    new Service({
      id: new URL("https://example.net/service"),
      inbox: new URL("https://example.net/service/inbox"),
      endpoints: new Endpoints({
        sharedInbox: new URL("https://example.net/inbox"),
      }),
    }),
  ];
  let inboxes = extractInboxes({ recipients });
  assertEquals(
    inboxes,
    new Set([
      new URL("https://example.com/alice/inbox"),
      new URL("https://example.com/app/inbox"),
      new URL("https://example.org/group/inbox"),
      new URL("https://example.net/service/inbox"),
    ]),
  );
  inboxes = extractInboxes({ recipients, preferSharedInbox: true });
  assertEquals(
    inboxes,
    new Set([
      new URL("https://example.com/inbox"),
      new URL("https://example.org/group/inbox"),
      new URL("https://example.net/inbox"),
    ]),
  );
});
+3 −3
Original line number Diff line number Diff line
@@ -28,14 +28,14 @@ export interface ExtractInboxesParameters {
export function extractInboxes(
  { recipients, preferSharedInbox }: ExtractInboxesParameters,
): Set<URL> {
  const inboxes = new Set<URL>();
  const inboxes: Record<string, URL> = {};
  for (const recipient of recipients) {
    const inbox = preferSharedInbox
      ? recipient.endpoints?.sharedInbox ?? recipient.inboxId
      : recipient.inboxId;
    if (inbox != null) inboxes.add(inbox);
    if (inbox != null) inboxes[inbox.href] = inbox;
  }
  return inboxes;
  return new Set(Object.values(inboxes));
}

/**

httpsig/key.test.ts

0 → 100644
+60 −0
Original line number Diff line number Diff line
import { assertThrows } from "jsr:@std/assert@^0.218.2";
import { validateCryptoKey } from "./key.ts";

Deno.test("validateCryptoKey()", async () => {
  const pkcs1v15 = await crypto.subtle.generateKey(
    {
      name: "RSASSA-PKCS1-v1_5",
      modulusLength: 2048,
      publicExponent: new Uint8Array([1, 0, 1]),
      hash: "SHA-256",
    },
    true,
    ["sign", "verify"],
  );
  validateCryptoKey(pkcs1v15.privateKey, "private");
  validateCryptoKey(pkcs1v15.privateKey);
  validateCryptoKey(pkcs1v15.publicKey, "public");
  validateCryptoKey(pkcs1v15.publicKey);

  assertThrows(
    () => validateCryptoKey(pkcs1v15.privateKey, "public"),
    TypeError,
    "The key is not a public key.",
  );
  assertThrows(
    () => validateCryptoKey(pkcs1v15.publicKey, "private"),
    TypeError,
    "The key is not a private key.",
  );

  const ecdsa = await crypto.subtle.generateKey(
    {
      name: "ECDSA",
      namedCurve: "P-256",
    },
    true,
    ["sign", "verify"],
  );
  assertThrows(
    () => validateCryptoKey(ecdsa.publicKey),
    TypeError,
    "only RSASSA-PKCS1-v1_5",
  );

  const pkcs1v15Sha512 = await crypto.subtle.generateKey(
    {
      name: "RSASSA-PKCS1-v1_5",
      modulusLength: 2048,
      publicExponent: new Uint8Array([1, 0, 1]),
      hash: "SHA-512",
    },
    true,
    ["sign", "verify"],
  );
  assertThrows(
    () => validateCryptoKey(pkcs1v15Sha512.privateKey),
    TypeError,
    "hash algorithm must be SHA-256",
  );
});
Loading