summary refs log tree commit diff
path: root/src/util/entities
diff options
context:
space:
mode:
authorMadeline <46743919+MaddyUnderStars@users.noreply.github.com>2023-09-26 01:22:40 +1000
committerMadeline <46743919+MaddyUnderStars@users.noreply.github.com>2023-09-26 01:22:40 +1000
commitc237247f896cce6e7262c13a874fb0a609cb0d86 (patch)
tree1a3eaa02dbfa8faa159db2e87fcd75807ead7ec7 /src/util/entities
parentMerge branch 'master' of github.com:spacebarchat/server (diff)
downloadserver-c237247f896cce6e7262c13a874fb0a609cb0d86.tar.xz
initial bullshit for federation v2
Diffstat (limited to 'src/util/entities')
-rw-r--r--src/util/entities/BaseClass.ts6
-rw-r--r--src/util/entities/Channel.ts15
-rw-r--r--src/util/entities/FederationKeys.ts67
-rw-r--r--src/util/entities/User.ts29
-rw-r--r--src/util/entities/index.ts1
5 files changed, 114 insertions, 4 deletions
diff --git a/src/util/entities/BaseClass.ts b/src/util/entities/BaseClass.ts
index f4b3cf59..d72c778d 100644
--- a/src/util/entities/BaseClass.ts
+++ b/src/util/entities/BaseClass.ts
@@ -24,9 +24,9 @@ import {
 	ObjectIdColumn,
 	PrimaryColumn,
 } from "typeorm";
-import { Snowflake } from "../util/Snowflake";
-import { getDatabase } from "../util/Database";
 import { OrmUtils } from "../imports/OrmUtils";
+import { Datasource } from "../util/Datasource";
+import { Snowflake } from "../util/Snowflake";
 
 export class BaseClassWithoutId extends BaseEntity {
 	private get construct() {
@@ -34,7 +34,7 @@ export class BaseClassWithoutId extends BaseEntity {
 	}
 
 	private get metadata() {
-		return getDatabase()?.getMetadata(this.construct);
+		return Datasource.getMetadata(this.construct);
 	}
 
 	assign(props: object) {
diff --git a/src/util/entities/Channel.ts b/src/util/entities/Channel.ts
index 9f7041d4..63e320a8 100644
--- a/src/util/entities/Channel.ts
+++ b/src/util/entities/Channel.ts
@@ -28,6 +28,7 @@ import {
 import { DmChannelDTO } from "../dtos";
 import { ChannelCreateEvent, ChannelRecipientRemoveEvent } from "../interfaces";
 import {
+	Config,
 	InvisibleCharacters,
 	Snowflake,
 	containsAll,
@@ -36,6 +37,7 @@ import {
 	trimSpecial,
 } from "../util";
 import { BaseClass } from "./BaseClass";
+import { ActorType, FederationKey } from "./FederationKeys";
 import { Guild } from "./Guild";
 import { Invite } from "./Invite";
 import { Message } from "./Message";
@@ -193,6 +195,9 @@ export class Channel extends BaseClass {
 	@Column()
 	default_thread_rate_limit_per_user: number = 0;
 
+	@Column({ nullable: true, type: String, select: false })
+	domain: string | null; // federation. if null, we own this channel
+
 	// TODO: DM channel
 	static async createChannel(
 		channel: Partial<Channel>,
@@ -316,6 +321,16 @@ export class Channel extends BaseClass {
 				: Promise.resolve(),
 		]);
 
+		// If federation is enabled, generate signing keys for this actor.
+		setImmediate(
+			async () =>
+				Config.get().federation.enabled &&
+				(await FederationKey.generateSigningKeys(
+					ret.id,
+					ActorType.CHANNEL,
+				)),
+		);
+
 		return ret;
 	}
 
diff --git a/src/util/entities/FederationKeys.ts b/src/util/entities/FederationKeys.ts
new file mode 100644
index 00000000..f3f15de5
--- /dev/null
+++ b/src/util/entities/FederationKeys.ts
@@ -0,0 +1,67 @@
+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 remote ID ( actor URL ) of this user */
+	@Column()
+	federatedId: 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 keys = FederationKey.create({
+			actorId,
+			type,
+			domain: Config.get().federation.accountDomain,
+			...(await generateKeyPair("rsa", {
+				modulusLength: 4096,
+				publicKeyEncoding: {
+					type: "spki",
+					format: "pem",
+				},
+				privateKeyEncoding: {
+					type: "pkcs8",
+					format: "pem",
+				},
+			})),
+		});
+
+		return await keys.save();
+	};
+}
diff --git a/src/util/entities/User.ts b/src/util/entities/User.ts
index c6582b00..c08ee95f 100644
--- a/src/util/entities/User.ts
+++ b/src/util/entities/User.ts
@@ -25,7 +25,15 @@ import {
 	OneToMany,
 	OneToOne,
 } from "typeorm";
-import { Config, Email, FieldErrors, Snowflake, trimSpecial } from "..";
+import {
+	ActorType,
+	Config,
+	Email,
+	FederationKey,
+	FieldErrors,
+	Snowflake,
+	trimSpecial,
+} from "..";
 import { BitField } from "../util/BitField";
 import { BaseClass } from "./BaseClass";
 import { ConnectedAccount } from "./ConnectedAccount";
@@ -182,6 +190,15 @@ export class User extends BaseClass {
 	@Column({ type: "bigint" })
 	rights: string;
 
+	@Column({ nullable: true, type: String, select: false })
+	domain: string | null; // Federation. null means this user is our own
+
+	@Column({ nullable: true, type: String, select: false })
+	privateKey: string | null; // No private key if federation is disabled
+
+	@Column({ nullable: true, type: String, select: false })
+	publicKey: string | null; // No public key if federation is disabled
+
 	@OneToMany(() => Session, (session: Session) => session.user)
 	sessions: Session[];
 
@@ -406,6 +423,16 @@ export class User extends BaseClass {
 			}
 		});
 
+		// If federation is enabled, generate signing keys for this actor.
+		setImmediate(
+			async () =>
+				Config.get().federation.enabled &&
+				(await FederationKey.generateSigningKeys(
+					user.id,
+					ActorType.USER,
+				)),
+		);
+
 		return user;
 	}
 }
diff --git a/src/util/entities/index.ts b/src/util/entities/index.ts
index aa943dca..708f2801 100644
--- a/src/util/entities/index.ts
+++ b/src/util/entities/index.ts
@@ -31,6 +31,7 @@ export * from "./ConnectionConfigEntity";
 export * from "./EmbedCache";
 export * from "./Emoji";
 export * from "./Encryption";
+export * from "./FederationKeys";
 export * from "./Guild";
 export * from "./Invite";
 export * from "./Member";