diff options
author | Madeline <46743919+MaddyUnderStars@users.noreply.github.com> | 2023-09-26 01:22:40 +1000 |
---|---|---|
committer | Madeline <46743919+MaddyUnderStars@users.noreply.github.com> | 2023-09-26 01:22:40 +1000 |
commit | c237247f896cce6e7262c13a874fb0a609cb0d86 (patch) | |
tree | 1a3eaa02dbfa8faa159db2e87fcd75807ead7ec7 /src/util/entities | |
parent | Merge branch 'master' of github.com:spacebarchat/server (diff) | |
download | server-c237247f896cce6e7262c13a874fb0a609cb0d86.tar.xz |
initial bullshit for federation v2
Diffstat (limited to 'src/util/entities')
-rw-r--r-- | src/util/entities/BaseClass.ts | 6 | ||||
-rw-r--r-- | src/util/entities/Channel.ts | 15 | ||||
-rw-r--r-- | src/util/entities/FederationKeys.ts | 67 | ||||
-rw-r--r-- | src/util/entities/User.ts | 29 | ||||
-rw-r--r-- | src/util/entities/index.ts | 1 |
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"; |