diff --git a/app/client/src/modules/admin/components/users/user-form/user-form.component.tsx b/app/client/src/modules/admin/components/users/user-form/user-form.component.tsx index a2eeb0fd..2e7b2c32 100644 --- a/app/client/src/modules/admin/components/users/user-form/user-form.component.tsx +++ b/app/client/src/modules/admin/components/users/user-form/user-form.component.tsx @@ -13,6 +13,7 @@ import { User } from "shared/types"; import dayjs from "dayjs"; import { useAdmin } from "shared/hooks"; import styles from "./user-form.module.scss"; +import { RestrictionCode } from "shared/enums"; type Props = { user: User; @@ -41,6 +42,8 @@ export const UserFormComponent: React.FC = ({ user, setUser }) => { createdAt: dayjs(createdAt).valueOf(), admin: user.admin, languages: user.languages, + restrictions: user.restrictions, + blocked: user.blocked, }; await updateUser($user); @@ -66,6 +69,25 @@ export const UserFormComponent: React.FC = ({ user, setUser }) => { adminOptions.find(({ key }) => (user?.admin ? "true" : "false" === key)), [user, adminOptions], ); + const restrictionOptions = useMemo( + () => + Object.keys(RestrictionCode).map((value) => ({ + value, + key: RestrictionCode[value], + })), + [], + ); + + const selectedRestrictionOption = useMemo( + () => + restrictionOptions.find(({ key }) => + user?.restrictions?.[0] ? user.restrictions[0][1] === key : null, + ), + [user, restrictionOptions], + ); + + const isBlocked = useMemo(() => user?.blocked, [user]); + const $onRemoveAccount = useCallback(async () => { await deleteUser(user); fetchUsers(); @@ -131,6 +153,34 @@ export const UserFormComponent: React.FC = ({ user, setUser }) => { } /> +
+
+ + setUser({ + ...user, + restrictions: option + ? [Date.now(), option.key as RestrictionCode] + : undefined, + }) + } + /> + + setUser({ + ...user, + blocked: !user.blocked, + }) + } + > + {isBlocked ? "UnBlock" : "Block"} + +
{user.admin ? null : ( { const title = $row[$column.key]; let value = title; + if ($column.key === "restrictions") + value = title[1] ? "🚫" : title[0] ? "⚠️" : null; if ($column.key === "email") value = getCensoredEmail(title); if ($column.key === "accountId") value = value.substring(0, 12) + "..."; @@ -62,6 +64,7 @@ export const AdminUsersComponent = () => { return { ...user, + restrictions: [user.restrictions, user.blocked], otp: user.otp ? "✅" : "❌", verified: user.verified ? "✅" @@ -71,6 +74,10 @@ export const AdminUsersComponent = () => { }; })} columns={[ + { + key: "restrictions", + label: "-", + }, { key: "accountId", label: "Account Id", diff --git a/app/client/src/shared/enums/index.ts b/app/client/src/shared/enums/index.ts index a34bdf67..8c3d206d 100644 --- a/app/client/src/shared/enums/index.ts +++ b/app/client/src/shared/enums/index.ts @@ -1,2 +1,3 @@ export * from "./request.enums"; export * from "./hotels.enums"; +export * from "./restriction.enums"; diff --git a/app/client/src/shared/enums/restriction.enums.ts b/app/client/src/shared/enums/restriction.enums.ts new file mode 100644 index 00000000..f32ccbd5 --- /dev/null +++ b/app/client/src/shared/enums/restriction.enums.ts @@ -0,0 +1,5 @@ +export enum RestrictionCode { + GENERIC = "0x1000", + HATE_SPEECH = "0x6001", + RACISM = "0x6002", +} diff --git a/app/client/src/shared/types/user.types.ts b/app/client/src/shared/types/user.types.ts index 84f63c59..f4ff1b70 100644 --- a/app/client/src/shared/types/user.types.ts +++ b/app/client/src/shared/types/user.types.ts @@ -1,3 +1,5 @@ +import { RestrictionCode } from "shared/enums"; + export type User = { createdAt: number; accountId: string; @@ -9,4 +11,8 @@ export type User = { otp?: boolean; verified?: boolean; githubLogin?: string; + + // [from, kind] + restrictions?: [number, RestrictionCode]; + blocked?: boolean; }; diff --git a/app/server/__tests__/tests/A-admin.test.ts b/app/server/__tests__/tests/A-admin.test.ts index 3f52f0ff..f4b1d18a 100644 --- a/app/server/__tests__/tests/A-admin.test.ts +++ b/app/server/__tests__/tests/A-admin.test.ts @@ -103,6 +103,8 @@ describe("10. admin", () => { assertEquals(user1.otp, false); assertEquals(user1.username, USER_2.username); assertEquals(user1.verified, true); + assertEquals(user1.restrictions, undefined); + assertEquals(user1.blocked, undefined); assertExists(user1.createdAt); assertExists(user1.updatedAt); @@ -114,6 +116,8 @@ describe("10. admin", () => { assertEquals(user2.otp, false); assertEquals(user2.username, USER_1.username); assertEquals(user2.verified, true); + assertEquals(user1.restrictions, undefined); + assertEquals(user2.blocked, undefined); assertExists(user2.createdAt); assertExists(user2.updatedAt); }); @@ -152,29 +156,17 @@ describe("10. admin", () => { assertEquals(user.verified, true); assertEquals(user.createdAt, new Date(1994, 3, 19).getTime()); assertExists(user.updatedAt); - }); - it("delete user", async () => { - const { accountId } = STATE.getUser(USER_2.email); - const { status } = await fetcher(`/admin/user`, { - method: "DELETE", + + const { status: status2 } = await fetcher(`/admin/user`, { + method: "PATCH", headers: STATE.getSessionHeaders(USER_1.email), body: JSON.stringify({ accountId, + username: "test", + email: USER_2.email, + createdAt: new Date(1994, 3, 19).getTime(), }), }); - assertEquals(status, 200); - - const response = await fetcher(`/admin/users`, { - method: "GET", - headers: STATE.getSessionHeaders(USER_1.email), - }); - assertEquals(response.status, 200); - assertEquals(response.data.users.length, 1); - - const user = response.data.users.find( - (user: any) => user.accountId === accountId, - ); - - assertEquals(user, undefined); + assertEquals(status2, 200); }); }); diff --git a/app/server/__tests__/tests/C-login-blocked-account.test.ts b/app/server/__tests__/tests/C-login-blocked-account.test.ts new file mode 100644 index 00000000..497e3218 --- /dev/null +++ b/app/server/__tests__/tests/C-login-blocked-account.test.ts @@ -0,0 +1,111 @@ +import { describe, it } from "jsr:@std/testing/bdd"; +import { assertEquals, assertExists } from "jsr:@std/assert"; +import { STATE } from "../state.ts"; +import { USER_1, USER_2 } from "../consts.ts"; +import { fetcher } from "../utils.ts"; + +describe("12. login with a blocked account", () => { + it("login with a non blocked account", async () => { + const { status, data } = await fetcher("/account/login", { + method: "POST", + headers: STATE.getSessionHeaders(USER_2.email), + body: JSON.stringify({ + email: USER_2.email, + password: USER_2.password, + }), + }); + assertEquals(status, 200); + assertExists(data.accountId); + assertExists(data.durations); + assertExists(data.refreshToken); + assertExists(data.token); + }); + it("blocks user", async () => { + const { accountId } = STATE.getUser(USER_2.email); + const { status } = await fetcher(`/admin/user`, { + method: "PATCH", + headers: STATE.getSessionHeaders(USER_1.email), + body: JSON.stringify({ + accountId, + username: "test", + email: "test@test.com", + createdAt: new Date(1994, 3, 19).getTime(), + blocked: true, + }), + }); + assertEquals(status, 200); + + const response = await fetcher(`/admin/users`, { + method: "GET", + headers: STATE.getSessionHeaders(USER_1.email), + }); + assertEquals(response.status, 200); + assertEquals(response.data.users.length, 2); + + const user = response.data.users.find( + (user: any) => user.accountId === accountId, + ); + + assertEquals(Object.keys(user).length, 10); + assertEquals(user.accountId, STATE.getUser(USER_2.email).accountId); + assertEquals(user.blocked, true); + assertExists(user.updatedAt); + }); + it("try to login with a blocked account", async () => { + const { status } = await fetcher("/account/login", { + method: "POST", + headers: STATE.getSessionHeaders(USER_2.email), + body: JSON.stringify({ + email: USER_2.email, + password: USER_2.password, + }), + }); + assertEquals(status, 400); + }); + it("unblock user", async () => { + const { accountId } = STATE.getUser(USER_2.email); + const { status } = await fetcher(`/admin/user`, { + method: "PATCH", + headers: STATE.getSessionHeaders(USER_1.email), + body: JSON.stringify({ + accountId, + username: "test", + email: USER_2.email, + createdAt: new Date(1994, 3, 19).getTime(), + blocked: false, + }), + }); + assertEquals(status, 200); + + const response = await fetcher(`/admin/users`, { + method: "GET", + headers: STATE.getSessionHeaders(USER_1.email), + }); + assertEquals(response.status, 200); + assertEquals(response.data.users.length, 2); + + const user = response.data.users.find( + (user: any) => user.accountId === accountId, + ); + + assertEquals(Object.keys(user).length, 10); + assertEquals(user.accountId, STATE.getUser(USER_2.email).accountId); + assertEquals(user.blocked, false); + assertExists(user.updatedAt); + }); + it("login with a non blocked account", async () => { + const { status, data } = await fetcher("/account/login", { + method: "POST", + headers: STATE.getSessionHeaders(USER_2.email), + body: JSON.stringify({ + email: USER_2.email, + password: USER_2.password, + }), + }); + assertEquals(status, 200); + assertExists(data.accountId); + assertExists(data.durations); + assertExists(data.refreshToken); + assertExists(data.token); + }); +}); diff --git a/app/server/__tests__/tests/D-account-with-restrictions.test.ts b/app/server/__tests__/tests/D-account-with-restrictions.test.ts new file mode 100644 index 00000000..7d0fafff --- /dev/null +++ b/app/server/__tests__/tests/D-account-with-restrictions.test.ts @@ -0,0 +1,136 @@ +import { describe, it } from "jsr:@std/testing/bdd"; +import { assertEquals, assertExists } from "jsr:@std/assert"; +import { STATE } from "../state.ts"; +import { HOTEL_1, USER_1, USER_2 } from "../consts.ts"; +import { fetcher } from "../utils.ts"; + +describe("13. account with restrictions", () => { + it("adds restrictions to user", async () => { + const currentDate = Date.now(); + + const { accountId } = STATE.getUser(USER_2.email); + const { status } = await fetcher(`/admin/user`, { + method: "PATCH", + headers: STATE.getSessionHeaders(USER_1.email), + body: JSON.stringify({ + accountId, + username: "test", + email: USER_2.email, + createdAt: new Date(1994, 3, 19).getTime(), + restrictions: [currentDate, "0x6001"], + }), + }); + assertEquals(status, 200); + + const response = await fetcher(`/admin/users`, { + method: "GET", + headers: STATE.getSessionHeaders(USER_1.email), + }); + assertEquals(response.status, 200); + assertEquals(response.data.users.length, 2); + + const user = response.data.users.find( + (user: any) => user.accountId === accountId, + ); + + assertEquals(Object.keys(user).length, 10); + assertEquals(user.accountId, STATE.getUser(USER_2.email).accountId); + assertEquals(user.restrictions, [currentDate, "0x6001"]); + assertExists(user.updatedAt); + }); + describe("otp", () => { + it("fail to retrieve the otp verifier", async () => { + const { status } = await fetcher("/account/otp", { + method: "GET", + headers: STATE.getSessionHeaders(USER_2.email), + }); + assertEquals(status, 403); + }); + it("fail to verify otp token", async () => { + const { status } = await fetcher("/account/otp/verify?token=000000", { + method: "GET", + headers: STATE.getSessionHeaders(USER_2.email), + }); + assertEquals(status, 403); + }); + it("fail to delete the otp verifier", async () => { + const { status } = await fetcher("/account/otp", { + method: "DELETE", + headers: STATE.getSessionHeaders(USER_2.email), + }); + assertEquals(status, 403); + }); + }); + describe("account", () => { + it("fail to recover password", async () => { + const { status } = await fetcher("/account/recover-password", { + method: "POST", + body: JSON.stringify({ + email: USER_2.email, + }), + }); + assertEquals(status, 429); + }); + }); + describe("hotels", () => { + it("fails to get hotel list", async () => { + const { status } = await fetcher("/user/@me/hotel", { + method: "GET", + headers: STATE.getSessionHeaders(USER_2.email), + }); + assertEquals(status, 403); + }); + it("fails to create hotel", async () => { + const { status } = await fetcher("/user/@me/hotel", { + method: "POST", + headers: STATE.getSessionHeaders(USER_2.email), + body: JSON.stringify({ + name: HOTEL_1.name, + public: false, + }), + }); + assertEquals(status, 403); + }); + }); + describe("connections", () => { + it("fail to retrieve user connection", async () => { + const hotel = STATE.getHotel(HOTEL_1.name); + + const { status } = await fetcher( + `/user/@me/connection?hotelId=${hotel.hotelId}&integrationId=${hotel.integrationId}`, + { + method: "GET", + headers: STATE.getSessionHeaders(USER_2.email), + }, + ); + assertEquals(status, 403); + }); + it("fail to create user connection", async () => { + const hotel = STATE.getHotel(HOTEL_1.name); + + const { status } = await fetcher(`/user/@me/connection`, { + method: "POST", + headers: STATE.getSessionHeaders(USER_2.email), + body: JSON.stringify({ + hotelId: hotel.hotelId, + integrationId: hotel.integrationId, + scopes: USER_2.connectionScopes, + state: "RANDOM_STATE", + }), + }); + assertEquals(status, 403); + }); + it("fail to delete connection", async () => { + const hotel = STATE.getHotel(HOTEL_1.name); + + const { status } = await fetcher( + `/user/@me/connection?hotelId=${hotel.hotelId}&integrationId=${hotel.integrationId}`, + { + method: "DELETE", + headers: STATE.getSessionHeaders(USER_2.email), + }, + ); + assertEquals(status, 403); + }); + }); +}); diff --git a/app/server/__tests__/tests/C-delete-account.test.ts b/app/server/__tests__/tests/E-delete-account.test.ts similarity index 79% rename from app/server/__tests__/tests/C-delete-account.test.ts rename to app/server/__tests__/tests/E-delete-account.test.ts index 3099a7e9..f64d593b 100644 --- a/app/server/__tests__/tests/C-delete-account.test.ts +++ b/app/server/__tests__/tests/E-delete-account.test.ts @@ -6,7 +6,32 @@ import { HOTEL_1, USER_1, USER_2 } from "../consts.ts"; import { STATE } from "../state.ts"; -describe("12. delete account", () => { +describe("14. delete account", () => { + it("delete user", async () => { + const { accountId } = STATE.getUser(USER_2.email); + const { status } = await fetcher(`/admin/user`, { + method: "DELETE", + headers: STATE.getSessionHeaders(USER_1.email), + body: JSON.stringify({ + accountId, + }), + }); + assertEquals(status, 200); + + const response = await fetcher(`/admin/users`, { + method: "GET", + headers: STATE.getSessionHeaders(USER_1.email), + }); + assertEquals(response.status, 200); + assertEquals(response.data.users.length, 1); + + const user = response.data.users.find( + (user: any) => user.accountId === accountId, + ); + + assertEquals(user, undefined); + }); + it("register a new user", async () => { const { status, data, message } = await fetcher("/account/register", { method: "POST", diff --git a/app/server/src/modules/api/v3/account/change-password.request.ts b/app/server/src/modules/api/v3/account/change-password.request.ts index 80193369..afd02ffa 100644 --- a/app/server/src/modules/api/v3/account/change-password.request.ts +++ b/app/server/src/modules/api/v3/account/change-password.request.ts @@ -45,6 +45,9 @@ export const changePasswordPostRequest: RequestType = { }); } + if (account.getObject().restrictions) + return getResponse(HttpStatusCode.LOCKED); + await account.update({ password, }); diff --git a/app/server/src/modules/api/v3/account/login.request.ts b/app/server/src/modules/api/v3/account/login.request.ts index 5bfbe1d8..9eb4829d 100644 --- a/app/server/src/modules/api/v3/account/login.request.ts +++ b/app/server/src/modules/api/v3/account/login.request.ts @@ -27,7 +27,7 @@ export const loginPostRequest: RequestType = { const account = await System.accounts.getAccount({ email }); - if (!account) + if (!account || account.getObject().blocked) return getResponse(HttpStatusCode.BAD_REQUEST, { message: "Email or password not valid!", }); diff --git a/app/server/src/modules/api/v3/account/misc/github.request.ts b/app/server/src/modules/api/v3/account/misc/github.request.ts index 36bb4e08..98f7ad8c 100644 --- a/app/server/src/modules/api/v3/account/misc/github.request.ts +++ b/app/server/src/modules/api/v3/account/misc/github.request.ts @@ -24,6 +24,9 @@ export const githubGetRequest: RequestType = { const account = await System.accounts.getAccount({ request }); + if (account.getObject().restrictions) + return getResponse(HttpStatusCode.LOCKED); + return getResponse(HttpStatusCode.OK, { url: await account.github.generateUri(), }); diff --git a/app/server/src/modules/api/v3/account/otp/delete.request.ts b/app/server/src/modules/api/v3/account/otp/delete.request.ts index 1df5595d..c85d7ee2 100644 --- a/app/server/src/modules/api/v3/account/otp/delete.request.ts +++ b/app/server/src/modules/api/v3/account/otp/delete.request.ts @@ -15,6 +15,9 @@ export const deleteRequest: RequestType = { const account = await System.accounts.getAccount({ request }); await account.otp.remove(); + if (account.getObject().restrictions) + return getResponse(HttpStatusCode.LOCKED); + return getResponse(HttpStatusCode.OK); }, }; diff --git a/app/server/src/modules/api/v3/account/otp/get.request.ts b/app/server/src/modules/api/v3/account/otp/get.request.ts index 3b8817e4..ad15cd12 100644 --- a/app/server/src/modules/api/v3/account/otp/get.request.ts +++ b/app/server/src/modules/api/v3/account/otp/get.request.ts @@ -14,6 +14,9 @@ export const getRequest: RequestType = { func: async (request: Request) => { const account = await System.accounts.getAccount({ request }); + if (account.getObject().restrictions) + return getResponse(HttpStatusCode.LOCKED); + if (await account.otp.isVerified()) return getResponse(HttpStatusCode.CONFLICT); diff --git a/app/server/src/modules/api/v3/account/otp/verify.request.ts b/app/server/src/modules/api/v3/account/otp/verify.request.ts index af939727..b4d8c6d9 100644 --- a/app/server/src/modules/api/v3/account/otp/verify.request.ts +++ b/app/server/src/modules/api/v3/account/otp/verify.request.ts @@ -17,6 +17,9 @@ export const verifyGetRequest: RequestType = { const account = await System.accounts.getAccount({ request }); + if (account.getObject().restrictions) + return getResponse(HttpStatusCode.LOCKED); + if (await account.otp.isVerified()) return getResponse(HttpStatusCode.CONFLICT); if (!(await account.otp.check(token, true))) diff --git a/app/server/src/modules/api/v3/admin/user.request.ts b/app/server/src/modules/api/v3/admin/user.request.ts index 46278595..b6456220 100644 --- a/app/server/src/modules/api/v3/admin/user.request.ts +++ b/app/server/src/modules/api/v3/admin/user.request.ts @@ -28,8 +28,15 @@ export const userPatchRequest: RequestType = { kind: RequestKind.ADMIN, func: async (request: Request) => { try { - let { accountId, username, email, createdAt, admin } = - await request.json(); + let { + accountId, + username, + email, + createdAt, + admin, + restrictions, + blocked, + } = await request.json(); if (!accountId || !username || !email || !createdAt) return getResponse(HttpStatusCode.FORBIDDEN, { @@ -67,6 +74,8 @@ export const userPatchRequest: RequestType = { email: accountByEmail ? null : email, username: accountByUsername ? null : username, createdAt, + restrictions, + blocked, }); if ((await account.isAdmin()) !== admin) await account.setAdmin(admin); diff --git a/app/server/src/modules/api/v3/user/@me/connection/main.request.ts b/app/server/src/modules/api/v3/user/@me/connection/main.request.ts index 4c142ad2..648230ed 100644 --- a/app/server/src/modules/api/v3/user/@me/connection/main.request.ts +++ b/app/server/src/modules/api/v3/user/@me/connection/main.request.ts @@ -15,6 +15,11 @@ export const mainPostRequest: RequestType = { func: async (request: Request) => { const { state, scopes, hotelId, integrationId } = await request.json(); + const account = await System.accounts.getAccount({ request }); + + if (account.getObject().restrictions) + return getResponse(HttpStatusCode.LOCKED); + if (!hotelId || !integrationId) return getResponse(HttpStatusCode.BAD_REQUEST, { message: `Missing inputs!`, @@ -36,8 +41,6 @@ export const mainPostRequest: RequestType = { const integration = hotel.getIntegration({ integrationId }); if (!integration) return getResponse(HttpStatusCode.BAD_REQUEST); - const account = await System.accounts.getAccount({ request }); - const redirectUrl = await account.connections.active.create({ request, @@ -68,6 +71,9 @@ export const mainGetRequest: RequestType = { const account = await System.accounts.getAccount({ request }); + if (account.getObject().restrictions) + return getResponse(HttpStatusCode.LOCKED); + const $connections = await account.connections.getConnections(); if (!$connections) return getResponse(HttpStatusCode.NOT_FOUND); diff --git a/app/server/src/modules/api/v3/user/@me/connection/ping.request.ts b/app/server/src/modules/api/v3/user/@me/connection/ping.request.ts index 933c0e81..9784a241 100644 --- a/app/server/src/modules/api/v3/user/@me/connection/ping.request.ts +++ b/app/server/src/modules/api/v3/user/@me/connection/ping.request.ts @@ -20,6 +20,9 @@ export const pingGetRequest: RequestType = { }); if (!account) return getResponse(HttpStatusCode.FORBIDDEN); + if (account.getObject().restrictions) + return getResponse(HttpStatusCode.LOCKED); + const pingResult = await account.connections.active.ping( connectionId, request, diff --git a/app/server/src/modules/api/v3/user/@me/hotel/integration/main.request.ts b/app/server/src/modules/api/v3/user/@me/hotel/integration/main.request.ts index 0113158a..4dc9dedb 100644 --- a/app/server/src/modules/api/v3/user/@me/hotel/integration/main.request.ts +++ b/app/server/src/modules/api/v3/user/@me/hotel/integration/main.request.ts @@ -17,6 +17,9 @@ export const mainPostRequest: RequestType = { const account = await System.accounts.getAccount({ request }); const hotel = await account.getHotel({ hotelId }); + if (account.getObject().restrictions) + return getResponse(HttpStatusCode.LOCKED); + if (!hotel || hotel.getObject().blocked) return getResponse(HttpStatusCode.BAD_REQUEST); @@ -44,6 +47,10 @@ export const mainGetRequest: RequestType = { return getResponse(HttpStatusCode.BAD_REQUEST); const account = await System.accounts.getAccount({ request }); + + if (account.getObject().restrictions) + return getResponse(HttpStatusCode.LOCKED); + const hotel = await account.getHotel({ hotelId }); if (!hotel || hotel.getObject().blocked) return getResponse(HttpStatusCode.BAD_REQUEST); @@ -70,6 +77,9 @@ export const mainDeleteRequest: RequestType = { const account = await System.accounts.getAccount({ request }); + if (account.getObject().restrictions) + return getResponse(HttpStatusCode.LOCKED); + const hotel = await account.getHotel({ hotelId }); if (!hotel || hotel.getObject().blocked) diff --git a/app/server/src/modules/api/v3/user/@me/hotel/main.request.ts b/app/server/src/modules/api/v3/user/@me/hotel/main.request.ts index 6fe03e35..116608d8 100644 --- a/app/server/src/modules/api/v3/user/@me/hotel/main.request.ts +++ b/app/server/src/modules/api/v3/user/@me/hotel/main.request.ts @@ -13,6 +13,10 @@ export const mainGetRequest: RequestType = { kind: RequestKind.ACCOUNT, func: async (request: Request) => { const account = await System.accounts.getAccount({ request }); + + const { restrictions, blocked } = account.getObject(); + if (restrictions || blocked) return getResponse(HttpStatusCode.LOCKED); + const $hotels = await account.getHotels(); const hotels = ( @@ -58,6 +62,10 @@ export const mainPostRequest: RequestType = { if (!name) return getResponse(HttpStatusCode.BAD_REQUEST); const account = await System.accounts.getAccount({ request }); + + const { restrictions, blocked } = account.getObject(); + if (restrictions || blocked) return getResponse(HttpStatusCode.LOCKED); + const hotelId = await account.createHotel({ name, public: Boolean($public), @@ -87,6 +95,9 @@ export const mainPatchRequest: RequestType = { const account = await System.accounts.getAccount({ request }); + const { restrictions, blocked } = account.getObject(); + if (restrictions || blocked) return getResponse(HttpStatusCode.LOCKED); + const hotel = await account.getHotel({ hotelId }); if (!hotel || hotel.getObject().blocked) return getResponse(HttpStatusCode.BAD_REQUEST); @@ -110,6 +121,9 @@ export const mainDeleteRequest: RequestType = { const account = await System.accounts.getAccount({ request }); + if (account.getObject().restrictions) + return getResponse(HttpStatusCode.LOCKED); + const hotel = await account.getHotel({ hotelId }); if (!hotel || hotel.getObject().blocked) return getResponse(HttpStatusCode.BAD_REQUEST); diff --git a/app/server/src/modules/system/accounts/main.ts b/app/server/src/modules/system/accounts/main.ts index bc463702..f1bc1807 100644 --- a/app/server/src/modules/system/accounts/main.ts +++ b/app/server/src/modules/system/accounts/main.ts @@ -554,6 +554,8 @@ export const accounts = () => { verified: account.verified, languages: account.languages, githubLogin: account.githubLogin, + restrictions: account.restrictions, + blocked: account.blocked, }); const update = async ($account: AccountUpdate) => { @@ -693,6 +695,9 @@ export const accounts = () => { const accountData = account.getObject(); + if (accountData.blocked || accountData.restrictions) + return HttpStatusCode.TOO_MANY_REQUESTS; + const verifyToken = getRandomString(16); const { url: rootUrl, version } = System.getConfig(); diff --git a/app/server/src/shared/enums/restrictions.enums.ts b/app/server/src/shared/enums/restrictions.enums.ts new file mode 100644 index 00000000..f32ccbd5 --- /dev/null +++ b/app/server/src/shared/enums/restrictions.enums.ts @@ -0,0 +1,5 @@ +export enum RestrictionCode { + GENERIC = "0x1000", + HATE_SPEECH = "0x6001", + RACISM = "0x6002", +} diff --git a/app/server/src/shared/types/account.types.ts b/app/server/src/shared/types/account.types.ts index 10d84530..675cdc89 100644 --- a/app/server/src/shared/types/account.types.ts +++ b/app/server/src/shared/types/account.types.ts @@ -5,6 +5,7 @@ import { } from "shared/types/hotel.types.ts"; import { Scope } from "shared/enums/scopes.enums.ts"; import { DbHotelIntegrationType } from "shared/enums/hotel.enums.ts"; +import { RestrictionCode } from "shared/enums/restrictions.enums.ts"; export type DbAccount = { accountId: string; @@ -21,6 +22,9 @@ export type DbAccount = { updatedAt: number; githubLogin?: string; + + restrictions?: [number, RestrictionCode]; + blocked?: boolean; }; export type DbAccountIntegrationConnection = { @@ -55,6 +59,8 @@ export type PublicAccount = { verified: boolean; languages: string[]; githubLogin?: string; + restrictions?: [number, RestrictionCode]; + blocked?: boolean; }; export type AccountCreation = {