From e14cf0812c0f261ca1a6b54bd3d97877d5d63296 Mon Sep 17 00:00:00 2001 From: Grant Date: Fri, 23 May 2025 17:00:12 -0600 Subject: [PATCH 1/2] [wip] move away from handles --- .../src/components/PixelWhoisSidebar.tsx | 10 ++------- .../src/components/Profile/ProfileModal.tsx | 14 +++++++----- .../src/components/Profile/UserCard.tsx | 22 ++++--------------- packages/lib/src/net.ts | 8 +++++++ .../migration.sql | 8 +++++++ .../prisma/migrations/migration_lock.toml | 2 +- packages/server/prisma/schema.prisma | 3 ++- packages/server/src/api/client.ts | 2 ++ .../src/controllers/OpenIDController.ts | 12 ++++++---- packages/server/src/models/User.ts | 4 +--- 10 files changed, 44 insertions(+), 41 deletions(-) create mode 100644 packages/server/prisma/migrations/20250520015553_migrate_to_uri_users/migration.sql diff --git a/packages/client/src/components/PixelWhoisSidebar.tsx b/packages/client/src/components/PixelWhoisSidebar.tsx index faa5465..64560b8 100644 --- a/packages/client/src/components/PixelWhoisSidebar.tsx +++ b/packages/client/src/components/PixelWhoisSidebar.tsx @@ -16,6 +16,7 @@ interface IPixel { interface IUser { sub: string; + username: string; display_name?: string; picture_url?: string; profile_url?: string; @@ -179,12 +180,5 @@ const SmallCanvas = ({ } }, [surrounding]); - return ( - (canvasRef.current = r)} - {...props} - /> - ); + return ; }; diff --git a/packages/client/src/components/Profile/ProfileModal.tsx b/packages/client/src/components/Profile/ProfileModal.tsx index c2b17e6..7f74b5b 100644 --- a/packages/client/src/components/Profile/ProfileModal.tsx +++ b/packages/client/src/components/Profile/ProfileModal.tsx @@ -21,13 +21,15 @@ export const ProfileModal = () => { return; } - api<{ user: IUser }>("/api/user/" + profile).then(({ status, data }) => { - if (status === 200 && data.success) { - setUser(data.user); - } else { - handleError({ status, data }); + api<{ user: IUser }>("/api/user/" + encodeURIComponent(profile)).then( + ({ status, data }) => { + if (status === 200 && data.success) { + setUser(data.user); + } else { + handleError({ status, data }); + } } - }); + ); }, [profile]); return ( diff --git a/packages/client/src/components/Profile/UserCard.tsx b/packages/client/src/components/Profile/UserCard.tsx index 4939aae..56ca72e 100644 --- a/packages/client/src/components/Profile/UserCard.tsx +++ b/packages/client/src/components/Profile/UserCard.tsx @@ -2,12 +2,13 @@ import { faMessage, faWarning } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { Button, Link, Spinner, User } from "@nextui-org/react"; import { ClientConfig } from "@sc07-canvas/lib/src/net"; -import { MouseEvent, useEffect, useMemo, useState } from "react"; +import { MouseEvent, useEffect, useState } from "react"; import { toast } from "react-toastify"; import { useAppContext } from "../../contexts/AppContext"; export interface IUser { sub: string; + username: string; display_name?: string; picture_url?: string; profile_url?: string; @@ -39,7 +40,7 @@ export const UserCard = ({ user }: { user: IUser }) => { setMessageStatus("loading"); fetch( - `https://${config.chat.matrix_homeserver}/_matrix/client/v3/profile/${encodeURIComponent(`@${user.sub.replace("@", "=40")}:${config.chat.matrix_homeserver}`)}` + `https://${config.chat.matrix_homeserver}/_matrix/client/v3/profile/${encodeURIComponent(`@${user.username.replace("@", "=40")}:${config.chat.matrix_homeserver}`)}` ) .then((req) => { if (req.status === 200) { @@ -72,26 +73,11 @@ export const UserCard = ({ user }: { user: IUser }) => { setProfile(user.sub); }; - const name = useMemo(() => { - if (!user || !user.sub) { - return "Unknown"; - } - - const regex = /^(.*)@/; - const match = user.sub.match(regex); - - if (match) { - return match[1]; - } - - return "Unknown"; - }, [user]); - return (
{ user: user && { sub: user.sub, display_name: user.display_name, + username: user.username, picture_url: user.picture_url, profile_url: user.profile_url, isAdmin: user.isAdmin, @@ -91,6 +92,7 @@ app.get("/user/:sub", RateLimiter.HIGH, async (req, res) => { success: true, user: { sub: user.sub, + username: user.username, display_name: user.display_name, picture_url: user.picture_url, profile_url: user.profile_url, diff --git a/packages/server/src/controllers/OpenIDController.ts b/packages/server/src/controllers/OpenIDController.ts index 3fe44b5..cb9f857 100644 --- a/packages/server/src/controllers/OpenIDController.ts +++ b/packages/server/src/controllers/OpenIDController.ts @@ -176,7 +176,7 @@ export class OpenIDController { return; } - const sub = [username, hostname].join("@"); + const sub = `https://${hostname}/users/${username}`; await prisma.user.upsert({ where: { sub, @@ -186,6 +186,7 @@ export class OpenIDController { }, create: { sub, + username: `${username}@${hostname}`, }, }); @@ -200,6 +201,7 @@ export class OpenIDController { }, }, user: { + sub, username, }, }; @@ -270,7 +272,7 @@ export class OpenIDController { }; }>(exchange.access_token, exchange.claims()!.sub); - const [username, hostname] = whoami.sub.split("@"); + const [username, hostname] = whoami.preferred_username!.split("@"); const instance = await Instance.fromAuth( hostname, @@ -284,7 +286,7 @@ export class OpenIDController { return; } - const sub = [username, hostname].join("@"); + const sub = whoami.sub; await prisma.user.upsert({ where: { sub, @@ -297,6 +299,7 @@ export class OpenIDController { }, create: { sub, + username: username + "@" + hostname, display_name: whoami.name, picture_url: whoami.picture, profile_url: whoami.profile, @@ -312,8 +315,9 @@ export class OpenIDController { }, }, user: { - picture_url: whoami.picture, + sub: whoami.sub, username, + picture_url: whoami.picture, }, }; req.session.save(); diff --git a/packages/server/src/models/User.ts b/packages/server/src/models/User.ts index 38d8c80..7258007 100644 --- a/packages/server/src/models/User.ts +++ b/packages/server/src/models/User.ts @@ -353,9 +353,7 @@ export class User { static async fromAuthSession(auth: AuthSession): Promise { try { - const user = await this.fromSub( - auth.user.username + "@" + auth.service.instance.hostname - ); + const user = await this.fromSub(auth.user.sub); user.authSession = auth; return user; } catch (e) { -- GitLab From 8e6177c49a85920e53a6cf3b5d849e443629b51c Mon Sep 17 00:00:00 2001 From: Grant Date: Sun, 25 May 2025 18:23:56 -0600 Subject: [PATCH 2/2] Update tests --- .../server/src/__test__/api/client.test.ts | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/packages/server/src/__test__/api/client.test.ts b/packages/server/src/__test__/api/client.test.ts index 7e71932..ce4405f 100644 --- a/packages/server/src/__test__/api/client.test.ts +++ b/packages/server/src/__test__/api/client.test.ts @@ -239,7 +239,8 @@ describe("Client endpoints /api", () => { .spyOn(OpenIDController.prototype, "userInfo") .mockImplementationOnce(async () => { return { - sub: "grant@local.host", + sub: "https://local.host/users/grant", + preferred_username: "grant@local.host", instance: { instance: {}, software: { @@ -298,7 +299,8 @@ describe("Client endpoints /api", () => { .spyOn(OpenIDController.prototype, "userInfo") .mockImplementationOnce(async () => { return { - sub: "grant@local.host", + sub: "https://local.host/users/grant", + preferred_username: "grant@local.host", instance: { instance: {}, software: { @@ -422,7 +424,8 @@ describe("Client endpoints /api", () => { .spyOn(OpenIDController.prototype, "userInfo") .mockImplementation(async () => { return { - sub: "grant@local.host", + sub: "https://local.host/users/grant", + preferred_username: "grant@local.host", instance: { instance: {}, software: { @@ -524,7 +527,8 @@ describe("Client endpoints /api", () => { }); prismaMock.pixel.count.mockResolvedValue(4); prismaMock.user.findFirst.mockResolvedValue({ - sub: "grant@local.host", + sub: "https://local.host/users/grant", + username: "grant@local.host", display_name: null, isAdmin: false, isModerator: false, @@ -546,7 +550,7 @@ describe("Client endpoints /api", () => { expect(response.body.pixel.id).toBe(0); expect(response.body.otherPixels).toBe(3); - expect(response.body.user.sub).toBe("grant@local.host"); + expect(response.body.user.sub).toBe("https://local.host/users/grant"); expect(response.body.instance.hostname).toBe("local.host"); }); }); @@ -637,7 +641,8 @@ describe("Client endpoints /api", () => { it("should return 200 & user data on existant user", async () => { mockReset(prismaMock); prismaMock.user.findFirst.mockResolvedValue({ - sub: "grant@local.host", + sub: "https://local.host/users/grant", + username: "grant@local.host", display_name: null, isAdmin: false, isModerator: false, @@ -651,7 +656,7 @@ describe("Client endpoints /api", () => { const response = await request(app) .get("/api/user/grant@local.host") .expect(200); - expect(response.body.user.sub).toBe("grant@local.host"); + expect(response.body.user.sub).toBe("https://local.host/users/grant"); }); }); }); -- GitLab