From d6d25004b9e9ee953d2949b38c4db061e55b65d4 Mon Sep 17 00:00:00 2001 From: Akhil Meka Date: Tue, 6 May 2025 17:25:13 +0530 Subject: [PATCH] add organization access to clients --- src/auth/auth.route.ts | 6 ++-- src/organization/organization.controller.ts | 6 ++-- src/organization/organization.service.ts | 34 ++++++++++++++++++--- src/user/user.controller.ts | 2 +- src/user/user.service.ts | 30 ++++++++++++++++-- src/utils/roles.ts | 1 + 6 files changed, 65 insertions(+), 14 deletions(-) diff --git a/src/auth/auth.route.ts b/src/auth/auth.route.ts index 0ad0f0d..39c0907 100644 --- a/src/auth/auth.route.ts +++ b/src/auth/auth.route.ts @@ -1,6 +1,6 @@ import { FastifyInstance, FastifyReply, FastifyRequest } from "fastify"; -import { getUserByEmail, updateUser } from "../user/user.service"; -import { createSession, deleteSession, getSession } from "./auth.service"; +import { getUserByEmail, updateUserInternal } from "../user/user.service"; +import { createSession, deleteSession } from "./auth.service"; export async function authRoutes(fastify: FastifyInstance) { fastify.get( @@ -24,7 +24,7 @@ export async function authRoutes(fastify: FastifyInstance) { if (userInDb == null) return res.code(401).send({ error: "not_allowed" }); - await updateUser(userInDb.pid, { + await updateUserInternal(userInDb.pid, { firstName: user.givenname, lastName: user.familyname, email: user.email, diff --git a/src/organization/organization.controller.ts b/src/organization/organization.controller.ts index 657b505..d1a5fb6 100644 --- a/src/organization/organization.controller.ts +++ b/src/organization/organization.controller.ts @@ -42,8 +42,7 @@ export async function listOrgsHandler(req: FastifyRequest, res: FastifyReply) { const queryParams = req.query as PageQueryParams; try { - const authUser = req.user; - const orgList = await listOrgs(queryParams, authUser.tenantId); + const orgList = await listOrgs(queryParams, req.user); return res.code(200).send(orgList); } catch (err) { return err; @@ -84,8 +83,7 @@ export async function searchOrgHandler(req: FastifyRequest, res: FastifyReply) { const queryParams = req.query as PageQueryParams; try { - const authUser = req.user; - const orgList = await searchOrgs(queryParams, authUser.tenantId); + const orgList = await searchOrgs(queryParams, req.user); return res.code(200).send(orgList); } catch (err) { return err; diff --git a/src/organization/organization.service.ts b/src/organization/organization.service.ts index dd0181e..4cacd37 100644 --- a/src/organization/organization.service.ts +++ b/src/organization/organization.service.ts @@ -1,3 +1,5 @@ +import mongoose from "mongoose"; +import { AuthenticatedUser } from "../auth"; import { getFilterObject, getSortObject, PageQueryParams } from "../pagination"; import { ChangeEvent, dbEvents } from "../realtime"; import { generateId } from "../utils/id"; @@ -35,14 +37,26 @@ export async function getOrg(orgId: string, tenantId: string) { }); } -export async function listOrgs(params: PageQueryParams, tenantId: string) { +export async function listOrgs( + params: PageQueryParams, + user: AuthenticatedUser +) { const page = params.page || 1; const pageSize = params.pageSize || 10; const sortObj = getSortObject(params, orgFields); const filterObj = getFilterObject(params) || []; + if (user.role === "client") { + filterObj.push({ + $or: [ + { type: "county" }, + { _id: new mongoose.Types.ObjectId(user.orgId) }, + ], + }); + } + const orgs = await orgModel.aggregate([ - { $match: { $and: [{ tenantId: tenantId }, ...filterObj] } }, + { $match: { $and: [{ tenantId: user.tenantId }, ...filterObj] } }, { $facet: { metadata: [{ $count: "count" }], @@ -118,19 +132,31 @@ export async function deleteOrg(orgId: string, tenantId: string) { return res; } -export async function searchOrgs(params: PageQueryParams, tenantId: string) { +export async function searchOrgs( + params: PageQueryParams, + user: AuthenticatedUser +) { const page = params.page || 1; const pageSize = params.pageSize || 10; const sortObj = getSortObject(params, orgFields); const filterObj = getFilterObject(params) || []; + if (user.role === "client") { + filterObj.push({ + $or: [ + { type: "county" }, + { _id: new mongoose.Types.ObjectId(user.orgId) }, + ], + }); + } + if (!params.searchToken) return { orgs: [], metadata: { count: 0, page, pageSize } }; const regex = new RegExp(params.searchToken, "i"); const orgs = await orgModel.aggregate([ - { $match: { $and: [{ tenantId: tenantId }, ...filterObj] } }, + { $match: { $and: [{ tenantId: user.tenantId }, ...filterObj] } }, { $match: { $or: [{ name: { $regex: regex } }, { domain: { $regex: regex } }], diff --git a/src/user/user.controller.ts b/src/user/user.controller.ts index 0268501..4d0ae06 100644 --- a/src/user/user.controller.ts +++ b/src/user/user.controller.ts @@ -80,7 +80,7 @@ export async function updateUserHandler( const { userId } = req.params as { userId: string }; try { - const updatedUser = await updateUser(userId, input); + const updatedUser = await updateUser(userId, input, req.user); if (!updateUser) return res.code(404).send({ error: "resource not found" }); return res.code(200).send(updatedUser); diff --git a/src/user/user.service.ts b/src/user/user.service.ts index eaffbbd..bfc9e45 100644 --- a/src/user/user.service.ts +++ b/src/user/user.service.ts @@ -13,7 +13,10 @@ export async function createUser( input: CreateUserInput, user: AuthenticatedUser ) { - if (input.role == "admin" && user.role != "superAdmin") { + if ( + input.role === "superAdmin" || + (input.role == "admin" && user.role != "superAdmin") + ) { throw ErrOpNotValid; } @@ -82,7 +85,30 @@ export async function listUsers(tenantId: string) { ); } -export async function updateUser(userId: string, input: UpdateUserInput) { +export async function updateUser( + userId: string, + input: UpdateUserInput, + user: AuthenticatedUser +) { + if ( + input.role === "superAdmin" || + (input.role == "admin" && user.role != "superAdmin") + ) { + throw ErrOpNotValid; + } + return await userModel + .findOneAndUpdate({ pid: userId }, input, { + new: true, + }) + .select( + "_id pid orgId firstName lastName name email role avatar status createdAt createdBy lastLogin" + ); +} + +export async function updateUserInternal( + userId: string, + input: UpdateUserInput +) { return await userModel .findOneAndUpdate({ pid: userId }, input, { new: true, diff --git a/src/utils/roles.ts b/src/utils/roles.ts index 6216873..aba9058 100644 --- a/src/utils/roles.ts +++ b/src/utils/roles.ts @@ -107,6 +107,7 @@ export const rules: Record< "permit:read", "file:upload", "file:download", + "org:read", "rts:read", "rts:write", "view:read",