diff options
Diffstat (limited to 'src/api')
-rw-r--r-- | src/api/routes/applications/#id/bot/index.ts | 3 | ||||
-rw-r--r-- | src/api/routes/auth/mfa/totp.ts | 3 | ||||
-rw-r--r-- | src/api/routes/auth/verify/view-backup-codes-challenge.ts | 30 | ||||
-rw-r--r-- | src/api/routes/guilds/#guild_id/member-verification.ts | 14 | ||||
-rw-r--r-- | src/api/routes/guilds/#guild_id/members/#member_id/index.ts | 7 | ||||
-rw-r--r-- | src/api/routes/guilds/#guild_id/profile/index.ts | 30 | ||||
-rw-r--r-- | src/api/routes/guilds/#guild_id/welcome_screen.ts | 2 | ||||
-rw-r--r-- | src/api/routes/policies/stats.ts | 22 | ||||
-rw-r--r-- | src/api/routes/users/#id/profile.ts | 63 | ||||
-rw-r--r-- | src/api/routes/users/@me/mfa/codes-verification.ts | 41 | ||||
-rw-r--r-- | src/api/routes/users/@me/mfa/totp/disable.ts | 3 | ||||
-rw-r--r-- | src/api/routes/users/@me/mfa/totp/enable.ts | 3 |
12 files changed, 206 insertions, 15 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/members/#member_id/index.ts b/src/api/routes/guilds/#guild_id/members/#member_id/index.ts index 57152f9a..06474f3e 100644 --- a/src/api/routes/guilds/#guild_id/members/#member_id/index.ts +++ b/src/api/routes/guilds/#guild_id/members/#member_id/index.ts @@ -6,6 +6,7 @@ import { getRights, Guild, GuildMemberUpdateEvent, + handleFile, Member, MemberChangeSchema, OrmUtils, @@ -30,7 +31,7 @@ router.patch("/", route({ body: "MemberChangeSchema" }), async (req: Request, re if (member_id === "@me") member_id = req.user_id; const body = req.body as MemberChangeSchema; - const member = await Member.findOneOrFail({ where: { id: member_id, guild_id }, relations: ["roles", "user"] }); + let member = await Member.findOneOrFail({ where: { id: member_id, guild_id }, relations: ["roles", "user"] }); const permission = await getPermission(req.user_id, guild_id); const everyone = await Role.findOneOrFail({ where: { guild_id: guild_id, name: "@everyone", position: 0 } }); @@ -41,6 +42,10 @@ router.patch("/", route({ body: "MemberChangeSchema" }), async (req: Request, re member.roles = body.roles.map((x) => OrmUtils.mergeDeep(new Role(), { id: x })); // foreign key constraint will fail if role doesn't exist } + if (body.avatar) body.avatar = await handleFile(`/guilds/${guild_id}/users/${member_id}/avatars`, body.avatar as string); + + member = await OrmUtils.mergeDeep(member, body); + await member.save(); member.roles = member.roles.filter((x) => x.id !== everyone.id); diff --git a/src/api/routes/guilds/#guild_id/profile/index.ts b/src/api/routes/guilds/#guild_id/profile/index.ts new file mode 100644 index 00000000..ddc30943 --- /dev/null +++ b/src/api/routes/guilds/#guild_id/profile/index.ts @@ -0,0 +1,30 @@ +import { route } from "@fosscord/api"; +import { emitEvent, GuildMemberUpdateEvent, handleFile, Member, MemberChangeProfileSchema, OrmUtils } from "@fosscord/util"; +import { Request, Response, Router } from "express"; + +const router = Router(); + +router.patch("/:member_id", route({ body: "MemberChangeProfileSchema" }), async (req: Request, res: Response) => { + let { guild_id, member_id } = req.params; + if (member_id === "@me") member_id = req.user_id; + const body = req.body as MemberChangeProfileSchema; + + let member = await Member.findOneOrFail({ where: { id: req.user_id, guild_id }, relations: ["roles", "user"] }); + + if (body.banner) body.banner = await handleFile(`/guilds/${guild_id}/users/${req.user_id}/avatars`, body.banner as string); + + member = await OrmUtils.mergeDeep(member, body); + + await member.save(); + + // do not use promise.all as we have to first write to db before emitting the event to catch errors + await emitEvent({ + event: "GUILD_MEMBER_UPDATE", + guild_id, + data: { ...member, roles: member.roles.map((x) => x.id) } + } as GuildMemberUpdateEvent); + + res.json(member); +}); + +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 766c9880..541bb66a 100644 --- a/src/api/routes/users/#id/profile.ts +++ b/src/api/routes/users/#id/profile.ts @@ -1,5 +1,16 @@ import { route } from "@fosscord/api"; -import { Member, PublicConnectedAccount, User, UserPublic } from "@fosscord/util"; +import { + emitEvent, + handleFile, + Member, + OrmUtils, + PrivateUserProjection, + PublicConnectedAccount, + User, + UserProfileModifySchema, + UserPublic, + UserUpdateEvent +} from "@fosscord/util"; import { Request, Response, Router } from "express"; const router: Router = Router(); @@ -62,12 +73,18 @@ 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: user.avatar, // TODO - banner: user.banner, // TODO - bio: req.user_bot ? null : user.bio, // TODO - communication_disabled_until: null, // TODO + avatar: guild_member.avatar, + banner: guild_member.banner, + bio: req.user_bot ? null : guild_member.bio, + communication_disabled_until: guild_member.communication_disabled_until, deaf: guild_member.deaf, flags: user.flags, is_pending: guild_member.pending, @@ -81,13 +98,47 @@ router.get("/", route({ test: { response: { body: "UserProfileResponse" } } }), } : undefined; + const guildMemberProfile = { + accent_color: null, + banner: guild_member?.banner || null, + bio: guild_member?.bio || "", + guild_id + }; res.json({ connected_accounts: user.connected_accounts, premium_guild_since: premium_guild_since, // TODO premium_since: user.premium_since, // TODO mutual_guilds: mutual_guilds, // TODO {id: "", nick: null} when ?with_mutual_guilds=true user: userDto, - guild_member: guildMemberDto + user_profile: userProfile, + guild_member: guild_id && guildMemberDto, + guild_member_profile: guild_id && guildMemberProfile + }); +}); + +router.patch("/", route({ body: "UserProfileModifySchema" }), async (req: Request, res: Response) => { + const body = req.body as UserProfileModifySchema; + + if (body.banner) body.banner = await handleFile(`/banners/${req.user_id}`, body.banner as string); + let user = await User.findOneOrFail({ where: { id: req.user_id }, select: [...PrivateUserProjection, "data"] }); + + user = OrmUtils.mergeDeep(user, body); + await user.save(); + + // @ts-ignore + delete user.data; + + // TODO: send update member list event in gateway + await emitEvent({ + event: "USER_UPDATE", + user_id: req.user_id, + data: user + } as UserUpdateEvent); + + res.json({ + accent_color: user.accent_color, + bio: user.bio, + banner: user.banner }); }); 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; |