diff --git a/tdrive/backend/node/src/core/platform/services/admin/web/index.ts b/tdrive/backend/node/src/core/platform/services/admin/web/index.ts index 4bdb1a9d..dfadecfa 100644 --- a/tdrive/backend/node/src/core/platform/services/admin/web/index.ts +++ b/tdrive/backend/node/src/core/platform/services/admin/web/index.ts @@ -1,9 +1,11 @@ import type { FastifyInstance, FastifyRegisterOptions } from "fastify"; import deleteUserRoutes from "./delete-user-routes"; +import updateUserRoutes from "./update-user-routes"; export default ( fastify: FastifyInstance, opts: FastifyRegisterOptions<{ prefix: string }>, ): void => { fastify.register(deleteUserRoutes, opts); + fastify.register(updateUserRoutes, opts); }; diff --git a/tdrive/backend/node/src/core/platform/services/admin/web/update-user-routes.ts b/tdrive/backend/node/src/core/platform/services/admin/web/update-user-routes.ts new file mode 100644 index 00000000..f14d112c --- /dev/null +++ b/tdrive/backend/node/src/core/platform/services/admin/web/update-user-routes.ts @@ -0,0 +1,37 @@ +import type { FastifyInstance, FastifyPluginCallback, FastifyReply, FastifyRequest } from "fastify"; +import { getConfig } from "../../../framework/api/admin"; +import gr from "../../../../../services/global-resolver"; +import { CrudException } from "../../../framework/api/crud-service"; +import { UpdateUser } from "../../../../../services/user/services/users/types"; + +const config = getConfig(); + +function authenticateAdminQuery(request: FastifyRequest, reply: FastifyReply) { + const secret = request.headers["secret"]; + if (secret !== config.endpointSecret) { + reply.status(403).send(); + return false; + } + return true; +} + +const routes: FastifyPluginCallback = async (fastify: FastifyInstance, _opts, next) => { + const urlRoot = "/api/user/update"; + const config = getConfig(); + if (config?.endpointSecret?.length) { + fastify.put(`${urlRoot}/:id`, async (request, reply) => { + try { + if (!authenticateAdminQuery(request, reply)) { + throw CrudException.unauthorized("Not allow to update user"); + } + + return await gr.services.users.update(request.params["id"], request.body as UpdateUser); + } catch (error) { + return error; + } + }); + } + next(); +}; + +export default routes; diff --git a/tdrive/backend/node/src/services/user/services/users/service.ts b/tdrive/backend/node/src/services/user/services/users/service.ts index a64647e0..cc83de3c 100644 --- a/tdrive/backend/node/src/services/user/services/users/service.ts +++ b/tdrive/backend/node/src/services/user/services/users/service.ts @@ -110,34 +110,43 @@ export class UserServiceImpl { } async update( - instance: User, - user: UpdateUser, + id: string, + body: UpdateUser, context?: ExecutionContext, ): Promise> { + const user = await gr.services.users.get({ id }); + if (!user) { + throw CrudException.notFound(`User ${id} not found`); + } + + if (user.email_canonical !== body.email) { + const userByEmail = await gr.services.users.getByEmail(body.email, context); + if (userByEmail) { + throw CrudException.badRequest(`Email ${body.email} is existed`); + } + } + let isChangeEmail = false; - if (instance.email_canonical !== user.email) { + if (user.email_canonical !== body.email) { isChangeEmail = true; } - instance.email_canonical = user.email || instance.email_canonical; - instance.first_name = user.first_name || instance.first_name; - instance.last_name = user.last_name || instance.last_name; - instance.picture = user.picture || instance.picture; - instance.creation_date = !isNumber(instance.creation_date) - ? Date.now() - : instance.creation_date; + user.email_canonical = body.email || user.email_canonical; + user.first_name = body.first_name || user.first_name; + user.last_name = body.last_name || user.last_name; + user.picture = body.picture || user.picture; + user.creation_date = !isNumber(user.creation_date) ? Date.now() : user.creation_date; if (isChangeEmail) { - instance.username_canonical = formatUsername(user.email); - instance.identity_provider_id = - instance.identity_provider_id !== "null" ? user.email : "null"; + user.username_canonical = formatUsername(body.email); + user.identity_provider_id = user.identity_provider_id !== "null" ? body.email : "null"; } - await this.repository.save(instance, context); + await this.repository.save(user, context); if (isChangeEmail) { - await this.updateExtRepositoryInCaseChangeEmail(instance, context); + await this.updateExtRepositoryInCaseChangeEmail(user, context); } - return new UpdateResult("user", instance); + return new UpdateResult("user", user); } async save(user: User, context?: ExecutionContext): Promise> { diff --git a/tdrive/backend/node/src/services/user/web/controller.ts b/tdrive/backend/node/src/services/user/web/controller.ts index f6f55ea9..84ca76a5 100644 --- a/tdrive/backend/node/src/services/user/web/controller.ts +++ b/tdrive/backend/node/src/services/user/web/controller.ts @@ -373,47 +373,37 @@ export class UsersCrudController request: FastifyRequest<{ Body: UpdateUser; Params: UserParameters }>, reply: FastifyReply, ): Promise> { - const id = request.params.id; - const context = getExecutionContext(request); + try { + const id = request.params.id; + const context = getExecutionContext(request); - const [currentUserCompanies, requestedUserCompanies] = await Promise.all( - [context.user.id, request.params.id].map(userId => - gr.services.users.getUserCompanies({ id: userId }), - ), - ); - const currentUserCompaniesIds = new Set(currentUserCompanies.map(a => a.group_id)); - const sameCompanies = requestedUserCompanies.filter(a => - currentUserCompaniesIds.has(a.group_id), - ); - const roles = await Promise.all( - sameCompanies.map(a => gr.services.companies.getUserRole(a.group_id, context.user?.id)), - ); + const [currentUserCompanies, requestedUserCompanies] = await Promise.all( + [context.user.id, request.params.id].map(userId => + gr.services.users.getUserCompanies({ id: userId }), + ), + ); + const currentUserCompaniesIds = new Set(currentUserCompanies.map(a => a.group_id)); + const sameCompanies = requestedUserCompanies.filter(a => + currentUserCompaniesIds.has(a.group_id), + ); + const roles = await Promise.all( + sameCompanies.map(a => gr.services.companies.getUserRole(a.group_id, context.user?.id)), + ); - if (!roles.some(role => hasCompanyAdminLevel(role) === true)) { - reply.unauthorized(`User ${context.user?.id} is not allowed to update user ${id}`); - return; - } - - const user = await gr.services.users.get({ id }); - if (!user) { - reply.notFound(`User ${id} not found`); - return; - } - - const body = { ...request.body }; - if (user.email_canonical !== body.email) { - const userByEmail = await gr.services.users.getByEmail(body.email, context); - if (userByEmail) { - reply.conflict(`Email ${body.email} is existed`); + if (!roles.some(role => hasCompanyAdminLevel(role) === true)) { + reply.unauthorized(`User ${context.user?.id} is not allowed to update user ${id}`); return; } + + const body = { ...request.body }; + const user = await gr.services.users.update(id, body, context); + + return { + resource: await formatUser(user.entity), + }; + } catch (error) { + return error; } - - await gr.services.users.update(user, body, context); - - return { - resource: await formatUser(user), - }; } }