summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/api/routes/applications/#id/bot/index.ts3
-rw-r--r--src/api/routes/auth/mfa/totp.ts3
-rw-r--r--src/api/routes/auth/verify/view-backup-codes-challenge.ts30
-rw-r--r--src/api/routes/guilds/#guild_id/member-verification.ts14
-rw-r--r--src/api/routes/guilds/#guild_id/welcome_screen.ts2
-rw-r--r--src/api/routes/policies/stats.ts22
-rw-r--r--src/api/routes/users/#id/profile.ts11
-rw-r--r--src/api/routes/users/@me/mfa/codes-verification.ts41
-rw-r--r--src/api/routes/users/@me/mfa/totp/disable.ts3
-rw-r--r--src/api/routes/users/@me/mfa/totp/enable.ts3
-rw-r--r--src/util/config/types/SecurityConfiguration.ts1
-rw-r--r--src/util/entities/Guild.ts2
-rw-r--r--src/util/entities/Member.ts3
-rw-r--r--src/util/interfaces/Event.ts1
-rw-r--r--src/util/migrations/mariadb/1661273147273-test.ts71
-rw-r--r--src/util/migrations/mariadb/1661273179287-test2.ts15
-rw-r--r--src/util/migrations/postgres/1661885830688-guild-member-profiles.ts2
-rw-r--r--src/util/schemas/BackupCodesChallengeSchema.ts3
-rw-r--r--src/util/schemas/CodesVerificationSchema.ts5
-rw-r--r--src/util/schemas/index.ts4
-rw-r--r--src/util/util/Database.ts2
-rw-r--r--src/util/util/Rights.ts6
22 files changed, 187 insertions, 60 deletions
diff --git a/src/api/routes/applications/#id/bot/index.ts b/src/api/routes/applications/#id/bot/index.ts

index e663059e..2ac3523b 100644 --- a/src/api/routes/applications/#id/bot/index.ts +++ b/src/api/routes/applications/#id/bot/index.ts
@@ -1,7 +1,6 @@ import { route } from "@fosscord/api"; -import { Application, Config, FieldErrors, generateToken, handleFile, OrmUtils, trimSpecial, User } from "@fosscord/util"; +import { Application, Config, FieldErrors, generateToken, handleFile, OrmUtils, trimSpecial, User, HTTPError } from "@fosscord/util"; import { Request, Response, Router } from "express"; -import { HTTPError } from "lambert-server"; import { verifyToken } from "node-2fa"; const router: Router = Router(); diff --git a/src/api/routes/auth/mfa/totp.ts b/src/api/routes/auth/mfa/totp.ts
index 9938569e..4b080af6 100644 --- a/src/api/routes/auth/mfa/totp.ts +++ b/src/api/routes/auth/mfa/totp.ts
@@ -1,7 +1,6 @@ import { route } from "@fosscord/api"; -import { BackupCode, generateToken, TotpSchema, User } from "@fosscord/util"; +import { BackupCode, generateToken, TotpSchema, User, HTTPError } from "@fosscord/util"; import { Request, Response, Router } from "express"; -import { HTTPError } from "lambert-server"; import { verifyToken } from "node-2fa"; const router = Router(); diff --git a/src/api/routes/auth/verify/view-backup-codes-challenge.ts b/src/api/routes/auth/verify/view-backup-codes-challenge.ts new file mode 100644
index 00000000..d524e0f7 --- /dev/null +++ b/src/api/routes/auth/verify/view-backup-codes-challenge.ts
@@ -0,0 +1,30 @@ +import { Router, Request, Response } from "express"; +import { route } from "@fosscord/api"; +import { FieldErrors, User, BackupCodesChallengeSchema } from "@fosscord/util"; + +let bcrypt: any; +try { + bcrypt = require("bcrypt"); +} catch { + bcrypt = require("bcryptjs"); + console.log("Warning: using bcryptjs because bcrypt is not installed! Performance will be affected."); +} + +const router = Router(); + +router.post("/", route({ body: "BackupCodesChallengeSchema" }), async (req: Request, res: Response) => { + const { password } = req.body as BackupCodesChallengeSchema; + + const user = await User.findOneOrFail({ where: { id: req.user_id }, select: ["data"] }); + + if (!await bcrypt.compare(password, user.data.hash || "")) { + throw FieldErrors({ password: { message: req.t("auth:login.INVALID_PASSWORD"), code: "INVALID_PASSWORD" } }); + } + + return res.json({ + nonce: "NoncePlaceholder", + regenerate_nonce: "RegenNoncePlaceholder", + }); +}); + +export default router; diff --git a/src/api/routes/guilds/#guild_id/member-verification.ts b/src/api/routes/guilds/#guild_id/member-verification.ts new file mode 100644
index 00000000..265a1b35 --- /dev/null +++ b/src/api/routes/guilds/#guild_id/member-verification.ts
@@ -0,0 +1,14 @@ +import { Router, Request, Response } from "express"; +import { route } from "@fosscord/api"; +const router = Router(); + +router.get("/",route({}), async (req: Request, res: Response) => { + // TODO: member verification + + res.status(404).json({ + message: "Unknown Guild Member Verification Form", + code: 10068 + }); +}); + +export default router; diff --git a/src/api/routes/guilds/#guild_id/welcome_screen.ts b/src/api/routes/guilds/#guild_id/welcome_screen.ts
index 85c22a19..7e955c56 100644 --- a/src/api/routes/guilds/#guild_id/welcome_screen.ts +++ b/src/api/routes/guilds/#guild_id/welcome_screen.ts
@@ -24,6 +24,8 @@ router.patch("/", route({ body: "GuildUpdateWelcomeScreenSchema", permission: "M if (body.description) guild.welcome_screen.description = body.description; if (body.enabled != null) guild.welcome_screen.enabled = body.enabled; + await guild.save(); + res.sendStatus(204); }); diff --git a/src/api/routes/policies/stats.ts b/src/api/routes/policies/stats.ts new file mode 100644
index 00000000..d3aad2ec --- /dev/null +++ b/src/api/routes/policies/stats.ts
@@ -0,0 +1,22 @@ +import { route } from "@fosscord/api"; +import { Config, getRights, Guild, Member, Message, User } from "@fosscord/util"; +import { Request, Response, Router } from "express"; +const router = Router(); + +router.get("/", route({}), async (req: Request, res: Response) => { + let users, guilds, msgs, memberships; + // needs to be let otherwise we can't for + + let config = Config.get(); + if (!config.security.statsWorldReadable) { + let rights = await getRights(req.user_id); + rights.hasThrow("VIEW_SERVER_STATS"); + } + users = await User.count(); + guilds = await Guild.count(); + msgs = await Message.count(); + memberships = await Member.count(); + res.json({ user_count: users, guild_count: guilds, msg_count: msgs, membership_rels: memberships }); +}); + +export default router; diff --git a/src/api/routes/users/#id/profile.ts b/src/api/routes/users/#id/profile.ts
index 0b9107e2..541bb66a 100644 --- a/src/api/routes/users/#id/profile.ts +++ b/src/api/routes/users/#id/profile.ts
@@ -73,6 +73,12 @@ router.get("/", route({ test: { response: { body: "UserProfileResponse" } } }), bot: user.bot }; + const userProfile = { + bio: req.user_bot ? null : user.bio, + accent_color: user.accent_color, + banner: user.banner + }; + const guildMemberDto = guild_member ? { avatar: guild_member.avatar, @@ -104,8 +110,9 @@ router.get("/", route({ test: { response: { body: "UserProfileResponse" } } }), premium_since: user.premium_since, // TODO mutual_guilds: mutual_guilds, // TODO {id: "", nick: null} when ?with_mutual_guilds=true user: userDto, - guild_member: guildMemberDto, - guild_member_profile: guildMemberProfile + user_profile: userProfile, + guild_member: guild_id && guildMemberDto, + guild_member_profile: guild_id && guildMemberProfile }); }); diff --git a/src/api/routes/users/@me/mfa/codes-verification.ts b/src/api/routes/users/@me/mfa/codes-verification.ts new file mode 100644
index 00000000..071c71fa --- /dev/null +++ b/src/api/routes/users/@me/mfa/codes-verification.ts
@@ -0,0 +1,41 @@ +import { Router, Request, Response } from "express"; +import { route } from "@fosscord/api"; +import { BackupCode, generateMfaBackupCodes, User, CodesVerificationSchema } from "@fosscord/util"; + +const router = Router(); + +router.post("/", route({ body: "CodesVerificationSchema" }), async (req: Request, res: Response) => { + const { key, nonce, regenerate } = req.body as CodesVerificationSchema; + + // TODO: We don't have email/etc etc, so can't send a verification code. + // Once that's done, this route can verify `key` + + const user = await User.findOneOrFail({ where: { id: req.user_id } }); + + var codes: BackupCode[]; + if (regenerate) { + await BackupCode.update( + { user: { id: req.user_id } }, + { expired: true } + ); + + codes = generateMfaBackupCodes(req.user_id); + await Promise.all(codes.map(x => x.save())); + } + else { + codes = await BackupCode.find({ + where: { + user: { + id: req.user_id, + }, + expired: false, + } + }); + } + + return res.json({ + backup_codes: codes.map(x => ({ ...x, expired: undefined })), + }); +}); + +export default router; diff --git a/src/api/routes/users/@me/mfa/totp/disable.ts b/src/api/routes/users/@me/mfa/totp/disable.ts
index 6bc9a5c7..07fdbb05 100644 --- a/src/api/routes/users/@me/mfa/totp/disable.ts +++ b/src/api/routes/users/@me/mfa/totp/disable.ts
@@ -1,7 +1,6 @@ import { route } from "@fosscord/api"; -import { BackupCode, generateToken, TotpDisableSchema, User } from "@fosscord/util"; +import { BackupCode, generateToken, TotpDisableSchema, User, HTTPError } from "@fosscord/util"; import { Request, Response, Router } from "express"; -import { HTTPError } from "lambert-server"; import { verifyToken } from "node-2fa"; const router = Router(); diff --git a/src/api/routes/users/@me/mfa/totp/enable.ts b/src/api/routes/users/@me/mfa/totp/enable.ts
index f3a73c28..adf51d6e 100644 --- a/src/api/routes/users/@me/mfa/totp/enable.ts +++ b/src/api/routes/users/@me/mfa/totp/enable.ts
@@ -1,7 +1,6 @@ import { route } from "@fosscord/api"; -import { BackupCode, Config, generateMfaBackupCodes, generateToken, TotpEnableSchema, User } from "@fosscord/util"; +import { BackupCode, Config, generateMfaBackupCodes, generateToken, TotpEnableSchema, User, HTTPError } from "@fosscord/util"; import { Request, Response, Router } from "express"; -import { HTTPError } from "lambert-server"; import { verifyToken } from "node-2fa"; let bcrypt: any; diff --git a/src/util/config/types/SecurityConfiguration.ts b/src/util/config/types/SecurityConfiguration.ts
index a2cebbd3..5a3d5aa6 100644 --- a/src/util/config/types/SecurityConfiguration.ts +++ b/src/util/config/types/SecurityConfiguration.ts
@@ -16,4 +16,5 @@ export class SecurityConfiguration { ipdataApiKey: string | null = "eca677b284b3bac29eb72f5e496aa9047f26543605efe99ff2ce35c9"; mfaBackupCodeCount: number = 10; mfaBackupCodeBytes: number = 4; + statsWorldReadable: boolean = true; } diff --git a/src/util/entities/Guild.ts b/src/util/entities/Guild.ts
index 015c6d04..cd7fa561 100644 --- a/src/util/entities/Guild.ts +++ b/src/util/entities/Guild.ts
@@ -241,7 +241,7 @@ export class Guild extends BaseClass { welcome_channels: { description: string; emoji_id?: string; - emoji_name: string; + emoji_name?: string; channel_id: string; }[]; }; diff --git a/src/util/entities/Member.ts b/src/util/entities/Member.ts
index a2a7b8cb..1b947a08 100644 --- a/src/util/entities/Member.ts +++ b/src/util/entities/Member.ts
@@ -300,7 +300,8 @@ export class Member extends BaseClassWithoutId { joined_at: member.joined_at, presences: [], stage_instances: [], - threads: [] + threads: [], + embedded_activities: [], }, user_id } as GuildCreateEvent) diff --git a/src/util/interfaces/Event.ts b/src/util/interfaces/Event.ts
index f97f4615..2631ef13 100644 --- a/src/util/interfaces/Event.ts +++ b/src/util/interfaces/Event.ts
@@ -155,6 +155,7 @@ export interface GuildCreateEvent extends Event { presences: never[]; stage_instances: never[]; threads: never[]; + embedded_activities: never[]; }; } diff --git a/src/util/migrations/mariadb/1661273147273-test.ts b/src/util/migrations/mariadb/1661273147273-test.ts
index 4e077a11..0090e2aa 100644 --- a/src/util/migrations/mariadb/1661273147273-test.ts +++ b/src/util/migrations/mariadb/1661273147273-test.ts
@@ -1,23 +1,23 @@ import { MigrationInterface, QueryRunner } from "typeorm"; export class test1661273147273 implements MigrationInterface { - name = 'test1661273147273' + name = "test1661273147273"; - public async up(queryRunner: QueryRunner): Promise<void> { - await queryRunner.query(` + public async up(queryRunner: QueryRunner): Promise<void> { + await queryRunner.query(` ALTER TABLE \`invites\` DROP FOREIGN KEY \`FK_15c35422032e0b22b4ada95f48f\` `); - await queryRunner.query(` + await queryRunner.query(` DROP INDEX \`IDX_2ce5a55796fe4c2f77ece57a64\` ON \`applications\` `); - await queryRunner.query(` + await queryRunner.query(` CREATE TABLE \`plugin_config\` ( \`key\` varchar(255) NOT NULL, \`value\` text NULL, PRIMARY KEY (\`key\`) ) ENGINE = InnoDB `); - await queryRunner.query(` + await queryRunner.query(` CREATE TABLE \`user_settings\` ( \`id\` varchar(255) NOT NULL, \`afk_timeout\` int NULL, @@ -54,96 +54,95 @@ export class test1661273147273 implements MigrationInterface { PRIMARY KEY (\`id\`) ) ENGINE = InnoDB `); - await queryRunner.query(` + await queryRunner.query(` ALTER TABLE \`users\` DROP COLUMN \`settings\` `); - await queryRunner.query(` + await queryRunner.query(` ALTER TABLE \`users\` ADD \`settingsId\` varchar(255) NULL `); - await queryRunner.query(` + await queryRunner.query(` ALTER TABLE \`users\` ADD UNIQUE INDEX \`IDX_76ba283779c8441fd5ff819c8c\` (\`settingsId\`) `); - await queryRunner.query(` + await queryRunner.query(` ALTER TABLE \`channels\` ADD \`flags\` int NULL `); - await queryRunner.query(` + await queryRunner.query(` ALTER TABLE \`channels\` ADD \`default_thread_rate_limit_per_user\` int NULL `); - await queryRunner.query(` + await queryRunner.query(` ALTER TABLE \`guilds\` ADD \`premium_progress_bar_enabled\` tinyint NULL `); - await queryRunner.query(` + await queryRunner.query(` ALTER TABLE \`users\` CHANGE \`bio\` \`bio\` varchar(255) NULL `); - await queryRunner.query(` + await queryRunner.query(` ALTER TABLE \`users\` CHANGE \`mfa_enabled\` \`mfa_enabled\` tinyint NULL `); - await queryRunner.query(` + await queryRunner.query(` CREATE UNIQUE INDEX \`REL_76ba283779c8441fd5ff819c8c\` ON \`users\` (\`settingsId\`) `); - await queryRunner.query(` + await queryRunner.query(` ALTER TABLE \`users\` ADD CONSTRAINT \`FK_76ba283779c8441fd5ff819c8cf\` FOREIGN KEY (\`settingsId\`) REFERENCES \`user_settings\`(\`id\`) ON DELETE NO ACTION ON UPDATE NO ACTION `); - await queryRunner.query(` + await queryRunner.query(` ALTER TABLE \`invites\` ADD CONSTRAINT \`FK_15c35422032e0b22b4ada95f48f\` FOREIGN KEY (\`inviter_id\`) REFERENCES \`users\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION `); - } + } - public async down(queryRunner: QueryRunner): Promise<void> { - await queryRunner.query(` + public async down(queryRunner: QueryRunner): Promise<void> { + await queryRunner.query(` ALTER TABLE \`invites\` DROP FOREIGN KEY \`FK_15c35422032e0b22b4ada95f48f\` `); - await queryRunner.query(` + await queryRunner.query(` ALTER TABLE \`users\` DROP FOREIGN KEY \`FK_76ba283779c8441fd5ff819c8cf\` `); - await queryRunner.query(` + await queryRunner.query(` DROP INDEX \`REL_76ba283779c8441fd5ff819c8c\` ON \`users\` `); - await queryRunner.query(` + await queryRunner.query(` ALTER TABLE \`users\` CHANGE \`mfa_enabled\` \`mfa_enabled\` tinyint NOT NULL `); - await queryRunner.query(` + await queryRunner.query(` ALTER TABLE \`users\` CHANGE \`bio\` \`bio\` varchar(255) NOT NULL `); - await queryRunner.query(` + await queryRunner.query(` ALTER TABLE \`guilds\` DROP COLUMN \`premium_progress_bar_enabled\` `); - await queryRunner.query(` + await queryRunner.query(` ALTER TABLE \`channels\` DROP COLUMN \`default_thread_rate_limit_per_user\` `); - await queryRunner.query(` + await queryRunner.query(` ALTER TABLE \`channels\` DROP COLUMN \`flags\` `); - await queryRunner.query(` + await queryRunner.query(` ALTER TABLE \`users\` DROP INDEX \`IDX_76ba283779c8441fd5ff819c8c\` `); - await queryRunner.query(` + await queryRunner.query(` ALTER TABLE \`users\` DROP COLUMN \`settingsId\` `); - await queryRunner.query(` + await queryRunner.query(` ALTER TABLE \`users\` ADD \`settings\` text NOT NULL `); - await queryRunner.query(` + await queryRunner.query(` DROP TABLE \`user_settings\` `); - await queryRunner.query(` + await queryRunner.query(` DROP TABLE \`plugin_config\` `); - await queryRunner.query(` + await queryRunner.query(` CREATE UNIQUE INDEX \`IDX_2ce5a55796fe4c2f77ece57a64\` ON \`applications\` (\`bot_user_id\`) `); - await queryRunner.query(` + await queryRunner.query(` ALTER TABLE \`invites\` ADD CONSTRAINT \`FK_15c35422032e0b22b4ada95f48f\` FOREIGN KEY (\`inviter_id\`) REFERENCES \`users\`(\`id\`) ON DELETE NO ACTION ON UPDATE NO ACTION `); - } - + } } diff --git a/src/util/migrations/mariadb/1661273179287-test2.ts b/src/util/migrations/mariadb/1661273179287-test2.ts
index 0f77f284..973d8124 100644 --- a/src/util/migrations/mariadb/1661273179287-test2.ts +++ b/src/util/migrations/mariadb/1661273179287-test2.ts
@@ -1,18 +1,17 @@ import { MigrationInterface, QueryRunner } from "typeorm"; export class test21661273179287 implements MigrationInterface { - name = 'test21661273179287' + name = "test21661273179287"; - public async up(queryRunner: QueryRunner): Promise<void> { - await queryRunner.query(` + public async up(queryRunner: QueryRunner): Promise<void> { + await queryRunner.query(` DROP INDEX \`IDX_76ba283779c8441fd5ff819c8c\` ON \`users\` `); - } + } - public async down(queryRunner: QueryRunner): Promise<void> { - await queryRunner.query(` + public async down(queryRunner: QueryRunner): Promise<void> { + await queryRunner.query(` CREATE UNIQUE INDEX \`IDX_76ba283779c8441fd5ff819c8c\` ON \`users\` (\`settingsId\`) `); - } - + } } diff --git a/src/util/migrations/postgres/1661885830688-guild-member-profiles.ts b/src/util/migrations/postgres/1661885830688-guild-member-profiles.ts
index b774743b..81671250 100644 --- a/src/util/migrations/postgres/1661885830688-guild-member-profiles.ts +++ b/src/util/migrations/postgres/1661885830688-guild-member-profiles.ts
@@ -14,7 +14,7 @@ export class guildMemberProfiles1661885830688 implements MigrationInterface { `); await queryRunner.query(` ALTER TABLE "members" - ADD "bio" character varying NOT NULL + ADD "bio" character varying NOT NULL default '' `); await queryRunner.query(` ALTER TABLE "members" diff --git a/src/util/schemas/BackupCodesChallengeSchema.ts b/src/util/schemas/BackupCodesChallengeSchema.ts new file mode 100644
index 00000000..d6b519b7 --- /dev/null +++ b/src/util/schemas/BackupCodesChallengeSchema.ts
@@ -0,0 +1,3 @@ +export interface BackupCodesChallengeSchema { + password: string; +} \ No newline at end of file diff --git a/src/util/schemas/CodesVerificationSchema.ts b/src/util/schemas/CodesVerificationSchema.ts new file mode 100644
index 00000000..e8e2e7b4 --- /dev/null +++ b/src/util/schemas/CodesVerificationSchema.ts
@@ -0,0 +1,5 @@ +export interface CodesVerificationSchema { + key: string; + nonce: string; + regenerate?: boolean; +} \ No newline at end of file diff --git a/src/util/schemas/index.ts b/src/util/schemas/index.ts
index 3770daf0..320593ec 100644 --- a/src/util/schemas/index.ts +++ b/src/util/schemas/index.ts
@@ -1,4 +1,5 @@ export * from "./ActivitySchema"; +export * from "./BackupCodesChallengeSchema"; export * from "./BanCreateSchema"; export * from "./BanModeratorSchema"; export * from "./BanRegistrySchema"; @@ -6,6 +7,7 @@ export * from "./BulkDeleteSchema"; export * from "./ChannelModifySchema"; export * from "./ChannelPermissionOverwriteSchema"; export * from "./ChannelReorderSchema"; +export * from "./CodesVerificationSchema"; export * from "./DmChannelCreateSchema"; export * from "./EmojiCreateSchema"; export * from "./EmojiModifySchema"; @@ -42,4 +44,4 @@ export * from "./UserSettingsSchema"; export * from "./VanityUrlSchema"; export * from "./VoiceStateUpdateSchema"; export * from "./WebhookCreateSchema"; -export * from "./WidgetModifySchema"; +export * from "./WidgetModifySchema"; \ No newline at end of file diff --git a/src/util/util/Database.ts b/src/util/util/Database.ts
index b9f8365e..647de26a 100644 --- a/src/util/util/Database.ts +++ b/src/util/util/Database.ts
@@ -96,7 +96,7 @@ function getDataSourceOptions(): DataSourceOptions { name: "default", migrations: synchronizeInsteadOfMigrations ? [] : [path.join(__dirname, "..", "migrations", type, "*.js")], migrationsRun: !synchronizeInsteadOfMigrations, - applicationName: `Fosscord Server`, + applicationName: `Fosscord Server` } as DataSourceOptions; } diff --git a/src/util/util/Rights.ts b/src/util/util/Rights.ts
index 51bb098c..236bfea7 100644 --- a/src/util/util/Rights.ts +++ b/src/util/util/Rights.ts
@@ -63,7 +63,11 @@ export class Rights extends BitField { RESPOND_TO_INTERACTIONS: BitFlag(41), // can respond to interactions SEND_BACKDATED_EVENTS: BitFlag(42), // can send backdated events USE_MASS_INVITES: BitFlag(43), // added per @xnacly's request — can accept mass invites - ACCEPT_INVITES: BitFlag(44) // added per @xnacly's request — can accept user-specific invites and DM requests + ACCEPT_INVITES: BitFlag(44), // added per @xnacly's request — can accept user-specific invites and DM requests + SELF_EDIT_FLAGS: BitFlag(45), // can modify own flags + EDIT_FLAGS: BitFlag(46), // can set others' flags + MANAGE_GROUPS: BitFlag(47), // can manage others' groups + VIEW_SERVER_STATS: BitFlag(48) // added per @chrischrome's request — can view server stats) }; any(permission: RightResolvable, checkOperator = true) {