summary refs log tree commit diff
path: root/src/util
diff options
context:
space:
mode:
authorTheArcaneBrony <myrainbowdash949@gmail.com>2022-09-17 23:35:31 +0200
committerTheArcaneBrony <myrainbowdash949@gmail.com>2022-09-17 23:35:31 +0200
commit258b96757f2d30f68ce873be04b5169de1e1eb9b (patch)
tree97cd6fe041c820d7d61a419b1b424af109e85aee /src/util
parentPartially refactor code to use localization (diff)
downloadserver-258b96757f2d30f68ce873be04b5169de1e1eb9b.tar.xz
Cryptographically secure invites, add generation of tokens
Diffstat (limited to '')
-rw-r--r--src/util/config/types/GeneralConfiguration.ts1
-rw-r--r--src/util/config/types/RegisterConfiguration.ts1
-rw-r--r--src/util/config/types/SecurityConfiguration.ts1
-rw-r--r--src/util/entities/Attachment.ts2
-rw-r--r--src/util/entities/Invite.ts2
-rw-r--r--src/util/entities/ValidRegistrationTokens.ts12
-rw-r--r--src/util/entities/index.ts1
-rw-r--r--src/util/migrations/mariadb/1663440589234-registration_tokens.ts31
-rw-r--r--src/util/migrations/mariadb/1663448562034-drop_id_for_registration_tokens.ts33
-rw-r--r--src/util/migrations/postgres/1663440587650-registration_tokens.ts33
-rw-r--r--src/util/migrations/postgres/1663448561249-drop_id_for_registration_tokens.ts33
-rw-r--r--src/util/migrations/sqlite/1663440585960-registration_tokens.ts246
-rw-r--r--src/util/migrations/sqlite/1663448560501-drop_id_for_registration_tokens.ts97
-rw-r--r--src/util/util/Base64.ts (renamed from src/api/util/utility/Base64.ts)0
-rw-r--r--src/util/util/CDN.ts (renamed from src/util/util/cdn.ts)0
-rw-r--r--src/util/util/Captcha.ts (renamed from src/api/util/utility/captcha.ts)0
-rw-r--r--src/util/util/IPAddress.ts (renamed from src/api/util/utility/ipAddress.ts)0
-rw-r--r--src/util/util/PasswordStrength.ts (renamed from src/api/util/utility/passwordStrength.ts)0
-rw-r--r--src/util/util/RandomInviteID.ts (renamed from src/api/util/utility/RandomInviteID.ts)6
-rw-r--r--src/util/util/String.ts18
-rw-r--r--src/util/util/index.ts5
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