summary refs log tree commit diff
path: root/src/util/entities/FederationKeys.ts
blob: 86e72bd273e27287ebc0e78fadd5c54cac62d3a8 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
import { Column, Entity, PrimaryColumn } from "typeorm";
import { BaseClassWithoutId } from "./BaseClass";

import crypto from "crypto";
import { promisify } from "util";
const generateKeyPair = promisify(crypto.generateKeyPair);

export enum ActorType {
	USER = "users",
	CHANNEL = "channels",
	GUILD = "guilds",
}

@Entity("federation_keys")
export class FederationKey extends BaseClassWithoutId {
	/** The ID of this actor. */
	@PrimaryColumn()
	actorId: string;

	/** The type of this actor. I.e. User, Channel, Guild */
	@Column()
	type: ActorType;

	/** The domain of this actor. I.e. spacebar.chat */
	@Column()
	domain: string;

	/** The federated preferred username of remote users. Local usernames are null. */
	@Column({ nullable: true, type: String })
	username: string | null;

	/** The remote ID ( actor URL ) of this user */
	@Column()
	federatedId: string;

	/** The inbox of the remote user */
	@Column()
	inbox: string;

	/** The outbox of the remote user */
	@Column()
	outbox: string;

	/** The following collection of this user */
	@Column()
	following: string;

	/** The followers collection of this user */
	@Column()
	followers: string;

	/** The public key of this actor. Public keys of remote actors are cached. */
	@Column()
	publicKey: string;

	/** Will only have a private key if this actor is ours */
	@Column({ nullable: true, type: String })
	privateKey: string | null;

	/** Create a new FederationKey for an actor */
	static generateSigningKeys = async (actorId: string, type: ActorType) => {
		const existing = await FederationKey.findOne({ where: { actorId } });
		if (existing) return existing;

		// Lazy loading config to prevent circular dep
		const { Config } = await import("../util/Config");

		const { accountDomain, host } = Config.get().federation;

		const keys = FederationKey.create({
			actorId,
			type,
			federatedId: `https://${host}/federation/${type}/${actorId}`,
			inbox: `https://${host}/federation/${type}/${actorId}/inbox`,
			outbox: `https://${host}/federation/${type}/${actorId}/outbox`,
			followers: `https://${host}/federation/${type}/${actorId}/followers`,
			following: `https://${host}/federation/${type}/${actorId}/following`,
			domain: accountDomain,
			...(await generateKeyPair("rsa", {
				modulusLength: 4096,
				publicKeyEncoding: {
					type: "spki",
					format: "pem",
				},
				privateKeyEncoding: {
					type: "pkcs8",
					format: "pem",
				},
			})),
		});

		return await keys.save();
	};
}