diff options
author | TheArcaneBrony <myrainbowdash949@gmail.com> | 2022-09-17 23:35:31 +0200 |
---|---|---|
committer | TheArcaneBrony <myrainbowdash949@gmail.com> | 2022-09-17 23:35:31 +0200 |
commit | 258b96757f2d30f68ce873be04b5169de1e1eb9b (patch) | |
tree | 97cd6fe041c820d7d61a419b1b424af109e85aee /src/util | |
parent | Partially refactor code to use localization (diff) | |
download | server-258b96757f2d30f68ce873be04b5169de1e1eb9b.tar.xz |
Cryptographically secure invites, add generation of tokens
Diffstat (limited to '')
21 files changed, 515 insertions, 7 deletions
diff --git a/src/util/config/types/GeneralConfiguration.ts b/src/util/config/types/GeneralConfiguration.ts index 5cb8df89..6d030645 100644 --- a/src/util/config/types/GeneralConfiguration.ts +++ b/src/util/config/types/GeneralConfiguration.ts @@ -3,6 +3,7 @@ import { Snowflake } from "../../util"; export class GeneralConfiguration { instanceName: string = "Fosscord Instance"; instanceDescription: string | null = "This is a Fosscord instance made in the pre-release days"; + publicUrl: string = "http://localhost:3001"; frontPage: string | null = null; tosPage: string | null = null; correspondenceEmail: string | null = "noreply@localhost.local"; diff --git a/src/util/config/types/RegisterConfiguration.ts b/src/util/config/types/RegisterConfiguration.ts index 68946272..caeab123 100644 --- a/src/util/config/types/RegisterConfiguration.ts +++ b/src/util/config/types/RegisterConfiguration.ts @@ -12,7 +12,6 @@ export class RegisterConfiguration { allowGuests: boolean = true; guestsRequireInvite: boolean = true; allowNewRegistration: boolean = true; - allowMultipleAccounts: boolean = true; blockProxies: boolean = true; incrementingDiscriminators: boolean = false; // random otherwise defaultRights: string = "0"; diff --git a/src/util/config/types/SecurityConfiguration.ts b/src/util/config/types/SecurityConfiguration.ts index 5a3d5aa6..229587c3 100644 --- a/src/util/config/types/SecurityConfiguration.ts +++ b/src/util/config/types/SecurityConfiguration.ts @@ -17,4 +17,5 @@ export class SecurityConfiguration { mfaBackupCodeCount: number = 10; mfaBackupCodeBytes: number = 4; statsWorldReadable: boolean = true; + defaultRegistrationTokenExpiration: number = 1000 * 60 * 60 * 24 * 7; //1 week } diff --git a/src/util/entities/Attachment.ts b/src/util/entities/Attachment.ts index 8392f415..c0ea3dec 100644 --- a/src/util/entities/Attachment.ts +++ b/src/util/entities/Attachment.ts @@ -1,6 +1,6 @@ import { BeforeRemove, Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; import { URL } from "url"; -import { deleteFile } from "../util/cdn"; +import { deleteFile } from "../util/CDN"; import { BaseClass } from "./BaseClass"; @Entity("attachments") diff --git a/src/util/entities/Invite.ts b/src/util/entities/Invite.ts index f6ba85d7..151fcc59 100644 --- a/src/util/entities/Invite.ts +++ b/src/util/entities/Invite.ts @@ -1,4 +1,4 @@ -import { random } from "@fosscord/api"; +import { random } from "@fosscord/util"; import { Column, Entity, JoinColumn, ManyToOne, PrimaryColumn, RelationId } from "typeorm"; import { BaseClassWithoutId } from "./BaseClass"; import { Channel } from "./Channel"; diff --git a/src/util/entities/ValidRegistrationTokens.ts b/src/util/entities/ValidRegistrationTokens.ts new file mode 100644 index 00000000..5d0747b8 --- /dev/null +++ b/src/util/entities/ValidRegistrationTokens.ts @@ -0,0 +1,12 @@ +import { BaseEntity, Column, Entity, PrimaryColumn } from "typeorm"; +import { Config } from ".."; + +@Entity("valid_registration_tokens") +export class ValidRegistrationToken extends BaseEntity { + @PrimaryColumn() + token: string; + @Column() + created_at: Date = new Date(); + @Column() + expires_at: Date = new Date(Date.now() + Config.get().security.defaultRegistrationTokenExpiration); +} diff --git a/src/util/entities/index.ts b/src/util/entities/index.ts index 2b91c2ba..673aac36 100644 --- a/src/util/entities/index.ts +++ b/src/util/entities/index.ts @@ -31,3 +31,4 @@ export * from "./User"; export * from "./UserSettings"; export * from "./VoiceState"; export * from "./Webhook"; +export * from "./ValidRegistrationTokens"; \ No newline at end of file diff --git a/src/util/migrations/mariadb/1663440589234-registration_tokens.ts b/src/util/migrations/mariadb/1663440589234-registration_tokens.ts new file mode 100644 index 00000000..12690ac4 --- /dev/null +++ b/src/util/migrations/mariadb/1663440589234-registration_tokens.ts @@ -0,0 +1,31 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class registrationTokens1663440589234 implements MigrationInterface { + name = 'registrationTokens1663440589234' + + public async up(queryRunner: QueryRunner): Promise<void> { + await queryRunner.query(` + CREATE TABLE \`valid_registration_tokens\` ( + \`id\` varchar(255) NOT NULL, + \`token\` varchar(255) NOT NULL, + \`created_at\` datetime NOT NULL, + \`expires_at\` datetime NOT NULL, + PRIMARY KEY (\`id\`) + ) ENGINE = InnoDB + `); + await queryRunner.query(` + ALTER TABLE \`users\` DROP COLUMN \`notes\` + `); + } + + public async down(queryRunner: QueryRunner): Promise<void> { + await queryRunner.query(` + ALTER TABLE \`users\` + ADD \`notes\` text NOT NULL + `); + await queryRunner.query(` + DROP TABLE \`valid_registration_tokens\` + `); + } + +} diff --git a/src/util/migrations/mariadb/1663448562034-drop_id_for_registration_tokens.ts b/src/util/migrations/mariadb/1663448562034-drop_id_for_registration_tokens.ts new file mode 100644 index 00000000..d4b13abb --- /dev/null +++ b/src/util/migrations/mariadb/1663448562034-drop_id_for_registration_tokens.ts @@ -0,0 +1,33 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class dropIdForRegistrationTokens1663448562034 implements MigrationInterface { + name = 'dropIdForRegistrationTokens1663448562034' + + public async up(queryRunner: QueryRunner): Promise<void> { + await queryRunner.query(` + ALTER TABLE \`valid_registration_tokens\` DROP PRIMARY KEY + `); + await queryRunner.query(` + ALTER TABLE \`valid_registration_tokens\` DROP COLUMN \`id\` + `); + await queryRunner.query(` + ALTER TABLE \`valid_registration_tokens\` + ADD PRIMARY KEY (\`token\`) + `); + } + + public async down(queryRunner: QueryRunner): Promise<void> { + await queryRunner.query(` + ALTER TABLE \`valid_registration_tokens\` DROP PRIMARY KEY + `); + await queryRunner.query(` + ALTER TABLE \`valid_registration_tokens\` + ADD \`id\` varchar(255) NOT NULL + `); + await queryRunner.query(` + ALTER TABLE \`valid_registration_tokens\` + ADD PRIMARY KEY (\`id\`) + `); + } + +} diff --git a/src/util/migrations/postgres/1663440587650-registration_tokens.ts b/src/util/migrations/postgres/1663440587650-registration_tokens.ts new file mode 100644 index 00000000..a794262c --- /dev/null +++ b/src/util/migrations/postgres/1663440587650-registration_tokens.ts @@ -0,0 +1,33 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class registrationTokens1663440587650 implements MigrationInterface { + name = 'registrationTokens1663440587650' + + public async up(queryRunner: QueryRunner): Promise<void> { + await queryRunner.query(` + CREATE TABLE "valid_registration_tokens" ( + "id" character varying NOT NULL, + "token" character varying NOT NULL, + "created_at" TIMESTAMP NOT NULL, + "expires_at" TIMESTAMP NOT NULL, + CONSTRAINT "PK_aac42a46cd46369450217de1c8a" PRIMARY KEY ("id") + ) + `); + await queryRunner.query(` + ALTER TABLE "members" + ALTER COLUMN "bio" DROP DEFAULT + `); + } + + public async down(queryRunner: QueryRunner): Promise<void> { + await queryRunner.query(` + ALTER TABLE "members" + ALTER COLUMN "bio" + SET DEFAULT '' + `); + await queryRunner.query(` + DROP TABLE "valid_registration_tokens" + `); + } + +} diff --git a/src/util/migrations/postgres/1663448561249-drop_id_for_registration_tokens.ts b/src/util/migrations/postgres/1663448561249-drop_id_for_registration_tokens.ts new file mode 100644 index 00000000..ce4b72f4 --- /dev/null +++ b/src/util/migrations/postgres/1663448561249-drop_id_for_registration_tokens.ts @@ -0,0 +1,33 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class dropIdForRegistrationTokens1663448561249 implements MigrationInterface { + name = 'dropIdForRegistrationTokens1663448561249' + + public async up(queryRunner: QueryRunner): Promise<void> { + await queryRunner.query(` + ALTER TABLE "valid_registration_tokens" DROP CONSTRAINT "PK_aac42a46cd46369450217de1c8a" + `); + await queryRunner.query(` + ALTER TABLE "valid_registration_tokens" DROP COLUMN "id" + `); + await queryRunner.query(` + ALTER TABLE "valid_registration_tokens" + ADD CONSTRAINT "PK_e0f5c8e3fcefe3134a092c50485" PRIMARY KEY ("token") + `); + } + + public async down(queryRunner: QueryRunner): Promise<void> { + await queryRunner.query(` + ALTER TABLE "valid_registration_tokens" DROP CONSTRAINT "PK_e0f5c8e3fcefe3134a092c50485" + `); + await queryRunner.query(` + ALTER TABLE "valid_registration_tokens" + ADD "id" character varying NOT NULL + `); + await queryRunner.query(` + ALTER TABLE "valid_registration_tokens" + ADD CONSTRAINT "PK_aac42a46cd46369450217de1c8a" PRIMARY KEY ("id") + `); + } + +} diff --git a/src/util/migrations/sqlite/1663440585960-registration_tokens.ts b/src/util/migrations/sqlite/1663440585960-registration_tokens.ts new file mode 100644 index 00000000..daf76be6 --- /dev/null +++ b/src/util/migrations/sqlite/1663440585960-registration_tokens.ts @@ -0,0 +1,246 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class registrationTokens1663440585960 implements MigrationInterface { + name = 'registrationTokens1663440585960' + + public async up(queryRunner: QueryRunner): Promise<void> { + await queryRunner.query(` + CREATE TABLE "valid_registration_tokens" ( + "id" varchar PRIMARY KEY NOT NULL, + "token" varchar NOT NULL, + "created_at" datetime NOT NULL, + "expires_at" datetime NOT NULL + ) + `); + await queryRunner.query(` + CREATE TABLE "temporary_users" ( + "id" varchar PRIMARY KEY NOT NULL, + "username" varchar NOT NULL, + "discriminator" varchar NOT NULL, + "avatar" varchar, + "accent_color" integer, + "banner" varchar, + "phone" varchar, + "desktop" boolean NOT NULL, + "mobile" boolean NOT NULL, + "premium" boolean NOT NULL, + "premium_type" integer NOT NULL, + "bot" boolean NOT NULL, + "bio" varchar, + "system" boolean NOT NULL, + "nsfw_allowed" boolean NOT NULL, + "mfa_enabled" boolean, + "totp_secret" varchar, + "totp_last_ticket" varchar, + "created_at" datetime NOT NULL, + "premium_since" datetime, + "verified" boolean NOT NULL, + "disabled" boolean NOT NULL, + "deleted" boolean NOT NULL, + "email" varchar, + "flags" varchar NOT NULL, + "public_flags" integer NOT NULL, + "rights" bigint NOT NULL, + "data" text NOT NULL, + "fingerprints" text NOT NULL, + "extended_settings" text NOT NULL, + "settingsId" varchar, + CONSTRAINT "UQ_b1dd13b6ed980004a795ca184a6" UNIQUE ("settingsId"), + CONSTRAINT "FK_76ba283779c8441fd5ff819c8cf" FOREIGN KEY ("settingsId") REFERENCES "user_settings" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION + ) + `); + await queryRunner.query(` + INSERT INTO "temporary_users"( + "id", + "username", + "discriminator", + "avatar", + "accent_color", + "banner", + "phone", + "desktop", + "mobile", + "premium", + "premium_type", + "bot", + "bio", + "system", + "nsfw_allowed", + "mfa_enabled", + "totp_secret", + "totp_last_ticket", + "created_at", + "premium_since", + "verified", + "disabled", + "deleted", + "email", + "flags", + "public_flags", + "rights", + "data", + "fingerprints", + "extended_settings", + "settingsId" + ) + SELECT "id", + "username", + "discriminator", + "avatar", + "accent_color", + "banner", + "phone", + "desktop", + "mobile", + "premium", + "premium_type", + "bot", + "bio", + "system", + "nsfw_allowed", + "mfa_enabled", + "totp_secret", + "totp_last_ticket", + "created_at", + "premium_since", + "verified", + "disabled", + "deleted", + "email", + "flags", + "public_flags", + "rights", + "data", + "fingerprints", + "extended_settings", + "settingsId" + FROM "users" + `); + await queryRunner.query(` + DROP TABLE "users" + `); + await queryRunner.query(` + ALTER TABLE "temporary_users" + RENAME TO "users" + `); + } + + public async down(queryRunner: QueryRunner): Promise<void> { + await queryRunner.query(` + ALTER TABLE "users" + RENAME TO "temporary_users" + `); + await queryRunner.query(` + CREATE TABLE "users" ( + "id" varchar PRIMARY KEY NOT NULL, + "username" varchar NOT NULL, + "discriminator" varchar NOT NULL, + "avatar" varchar, + "accent_color" integer, + "banner" varchar, + "phone" varchar, + "desktop" boolean NOT NULL, + "mobile" boolean NOT NULL, + "premium" boolean NOT NULL, + "premium_type" integer NOT NULL, + "bot" boolean NOT NULL, + "bio" varchar, + "system" boolean NOT NULL, + "nsfw_allowed" boolean NOT NULL, + "mfa_enabled" boolean, + "totp_secret" varchar, + "totp_last_ticket" varchar, + "created_at" datetime NOT NULL, + "premium_since" datetime, + "verified" boolean NOT NULL, + "disabled" boolean NOT NULL, + "deleted" boolean NOT NULL, + "email" varchar, + "flags" varchar NOT NULL, + "public_flags" integer NOT NULL, + "rights" bigint NOT NULL, + "data" text NOT NULL, + "fingerprints" text NOT NULL, + "extended_settings" text NOT NULL, + "notes" text NOT NULL, + "settingsId" varchar, + CONSTRAINT "UQ_b1dd13b6ed980004a795ca184a6" UNIQUE ("settingsId"), + CONSTRAINT "FK_76ba283779c8441fd5ff819c8cf" FOREIGN KEY ("settingsId") REFERENCES "user_settings" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION + ) + `); + await queryRunner.query(` + INSERT INTO "users"( + "id", + "username", + "discriminator", + "avatar", + "accent_color", + "banner", + "phone", + "desktop", + "mobile", + "premium", + "premium_type", + "bot", + "bio", + "system", + "nsfw_allowed", + "mfa_enabled", + "totp_secret", + "totp_last_ticket", + "created_at", + "premium_since", + "verified", + "disabled", + "deleted", + "email", + "flags", + "public_flags", + "rights", + "data", + "fingerprints", + "extended_settings", + "settingsId" + ) + SELECT "id", + "username", + "discriminator", + "avatar", + "accent_color", + "banner", + "phone", + "desktop", + "mobile", + "premium", + "premium_type", + "bot", + "bio", + "system", + "nsfw_allowed", + "mfa_enabled", + "totp_secret", + "totp_last_ticket", + "created_at", + "premium_since", + "verified", + "disabled", + "deleted", + "email", + "flags", + "public_flags", + "rights", + "data", + "fingerprints", + "extended_settings", + "settingsId" + FROM "temporary_users" + `); + await queryRunner.query(` + DROP TABLE "temporary_users" + `); + await queryRunner.query(` + DROP TABLE "valid_registration_tokens" + `); + } + +} diff --git a/src/util/migrations/sqlite/1663448560501-drop_id_for_registration_tokens.ts b/src/util/migrations/sqlite/1663448560501-drop_id_for_registration_tokens.ts new file mode 100644 index 00000000..087cc81f --- /dev/null +++ b/src/util/migrations/sqlite/1663448560501-drop_id_for_registration_tokens.ts @@ -0,0 +1,97 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class dropIdForRegistrationTokens1663448560501 implements MigrationInterface { + name = 'dropIdForRegistrationTokens1663448560501' + + public async up(queryRunner: QueryRunner): Promise<void> { + await queryRunner.query(` + CREATE TABLE "temporary_valid_registration_tokens" ( + "token" varchar NOT NULL, + "created_at" datetime NOT NULL, + "expires_at" datetime NOT NULL + ) + `); + await queryRunner.query(` + INSERT INTO "temporary_valid_registration_tokens"("token", "created_at", "expires_at") + SELECT "token", + "created_at", + "expires_at" + FROM "valid_registration_tokens" + `); + await queryRunner.query(` + DROP TABLE "valid_registration_tokens" + `); + await queryRunner.query(` + ALTER TABLE "temporary_valid_registration_tokens" + RENAME TO "valid_registration_tokens" + `); + await queryRunner.query(` + CREATE TABLE "temporary_valid_registration_tokens" ( + "token" varchar PRIMARY KEY NOT NULL, + "created_at" datetime NOT NULL, + "expires_at" datetime NOT NULL + ) + `); + await queryRunner.query(` + INSERT INTO "temporary_valid_registration_tokens"("token", "created_at", "expires_at") + SELECT "token", + "created_at", + "expires_at" + FROM "valid_registration_tokens" + `); + await queryRunner.query(` + DROP TABLE "valid_registration_tokens" + `); + await queryRunner.query(` + ALTER TABLE "temporary_valid_registration_tokens" + RENAME TO "valid_registration_tokens" + `); + } + + public async down(queryRunner: QueryRunner): Promise<void> { + await queryRunner.query(` + ALTER TABLE "valid_registration_tokens" + RENAME TO "temporary_valid_registration_tokens" + `); + await queryRunner.query(` + CREATE TABLE "valid_registration_tokens" ( + "token" varchar NOT NULL, + "created_at" datetime NOT NULL, + "expires_at" datetime NOT NULL + ) + `); + await queryRunner.query(` + INSERT INTO "valid_registration_tokens"("token", "created_at", "expires_at") + SELECT "token", + "created_at", + "expires_at" + FROM "temporary_valid_registration_tokens" + `); + await queryRunner.query(` + DROP TABLE "temporary_valid_registration_tokens" + `); + await queryRunner.query(` + ALTER TABLE "valid_registration_tokens" + RENAME TO "temporary_valid_registration_tokens" + `); + await queryRunner.query(` + CREATE TABLE "valid_registration_tokens" ( + "id" varchar PRIMARY KEY NOT NULL, + "token" varchar NOT NULL, + "created_at" datetime NOT NULL, + "expires_at" datetime NOT NULL + ) + `); + await queryRunner.query(` + INSERT INTO "valid_registration_tokens"("token", "created_at", "expires_at") + SELECT "token", + "created_at", + "expires_at" + FROM "temporary_valid_registration_tokens" + `); + await queryRunner.query(` + DROP TABLE "temporary_valid_registration_tokens" + `); + } + +} diff --git a/src/api/util/utility/Base64.ts b/src/util/util/Base64.ts index 46cff77a..46cff77a 100644 --- a/src/api/util/utility/Base64.ts +++ b/src/util/util/Base64.ts diff --git a/src/util/util/cdn.ts b/src/util/util/CDN.ts index 5573b848..5573b848 100644 --- a/src/util/util/cdn.ts +++ b/src/util/util/CDN.ts diff --git a/src/api/util/utility/captcha.ts b/src/util/util/Captcha.ts index 02983f3f..02983f3f 100644 --- a/src/api/util/utility/captcha.ts +++ b/src/util/util/Captcha.ts diff --git a/src/api/util/utility/ipAddress.ts b/src/util/util/IPAddress.ts index c96feb9e..c96feb9e 100644 --- a/src/api/util/utility/ipAddress.ts +++ b/src/util/util/IPAddress.ts diff --git a/src/api/util/utility/passwordStrength.ts b/src/util/util/PasswordStrength.ts index ff83d3df..ff83d3df 100644 --- a/src/api/util/utility/passwordStrength.ts +++ b/src/util/util/PasswordStrength.ts diff --git a/src/api/util/utility/RandomInviteID.ts b/src/util/util/RandomInviteID.ts index feebfd3d..49302916 100644 --- a/src/api/util/utility/RandomInviteID.ts +++ b/src/util/util/RandomInviteID.ts @@ -1,13 +1,13 @@ import { Snowflake } from "@fosscord/util"; +import crypto from "crypto"; -export function random(length = 6) { +export function random(length = 6, chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") { // Declare all characters - let chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; // Pick characers randomly let str = ""; for (let i = 0; i < length; i++) { - str += chars.charAt(Math.floor(Math.random() * chars.length)); + str += chars.charAt(Math.floor(crypto.randomInt(chars.length))); } return str; diff --git a/src/util/util/String.ts b/src/util/util/String.ts index 55f11e8d..cd5cb4f2 100644 --- a/src/util/util/String.ts +++ b/src/util/util/String.ts @@ -1,4 +1,22 @@ import { SPECIAL_CHAR } from "./Regex"; +import { FieldErrors } from "@fosscord/util"; +import { Request } from "express"; +import { ntob } from "./Base64"; + +export function checkLength(str: string, min: number, max: number, key: string, req: Request) { + if (str.length < min || str.length > max) { + throw FieldErrors({ + [key]: { + code: "BASE_TYPE_BAD_LENGTH", + message: req.t("common:field.BASE_TYPE_BAD_LENGTH", { length: `${min} - ${max}` }) + } + }); + } +} + +export function generateCode() { + return ntob(Date.now() + Math.randomIntBetween(0, 10000)); +} export function trimSpecial(str?: string): string { // @ts-ignore diff --git a/src/util/util/index.ts b/src/util/util/index.ts index 11f0b72a..1ef7467c 100644 --- a/src/util/util/index.ts +++ b/src/util/util/index.ts @@ -2,7 +2,7 @@ export * from "./ApiError"; export * from "./Array"; export * from "./BitField"; //export * from "./Categories"; -export * from "./cdn"; +export * from "./CDN"; export * from "./Config"; export * from "./Constants"; export * from "./Database"; @@ -23,3 +23,6 @@ export * from "./Snowflake"; export * from "./String"; export * from "./Token"; export * from "./TraverseDirectory"; +export * from "./IPAddress"; +export * from "./RandomInviteID"; +export * from "./Captcha"; \ No newline at end of file |