From 62f3b562c09e5473e9b81fccf7eeb251a1aa422d Mon Sep 17 00:00:00 2001 From: Romaric Mourgues Date: Mon, 22 May 2023 13:16:51 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=9B=A0=20Allow=20applications=20to=20mana?= =?UTF-8?q?ge=20the=20drive=20(#59)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Now applications can manage the drive as they wish * Fix typo * Fix the access computation * Test if my code is the issue * Fix test if app exists * Fix test if app exists * Fix test if app exists --- .../storage/connectors/local/service.ts | 2 +- .../applications/services/applications.ts | 2 +- .../services/company-applications.ts | 33 +++++++++----- .../src/services/documents/services/index.ts | 23 ++++++++++ .../node/src/services/documents/utils.ts | 44 ++++++++++++++++++- .../documents/web/controllers/documents.ts | 28 ++++++++++++ .../node/src/services/documents/web/routes.ts | 7 +++ 7 files changed, 124 insertions(+), 15 deletions(-) diff --git a/tdrive/backend/node/src/core/platform/services/storage/connectors/local/service.ts b/tdrive/backend/node/src/core/platform/services/storage/connectors/local/service.ts index e68f0759..249ed14a 100644 --- a/tdrive/backend/node/src/core/platform/services/storage/connectors/local/service.ts +++ b/tdrive/backend/node/src/core/platform/services/storage/connectors/local/service.ts @@ -45,7 +45,7 @@ export default class LocalConnectorService implements StorageConnectorAPI { async read(path: string): Promise { const fullPath = this.getFullPath(path); if (!fs.existsSync(fullPath)) { - throw new Error("File doesn't not exists"); + throw new Error("File doesn't exists"); } return createReadStream(fullPath); } diff --git a/tdrive/backend/node/src/services/applications/services/applications.ts b/tdrive/backend/node/src/services/applications/services/applications.ts index 8cb8b5ef..971da724 100644 --- a/tdrive/backend/node/src/services/applications/services/applications.ts +++ b/tdrive/backend/node/src/services/applications/services/applications.ts @@ -11,7 +11,7 @@ export class ApplicationServiceImpl implements TdriveServiceProvider, Initializa } async get(id: string, _: ExecutionContext): Promise { - return (await this.list(_)).find((app: Application) => app.id === id); + return (await this.list(_)).find((app: Application) => app?.id === id); } async list(_: ExecutionContext): Promise { diff --git a/tdrive/backend/node/src/services/applications/services/company-applications.ts b/tdrive/backend/node/src/services/applications/services/company-applications.ts index 28444d11..ee1ed321 100644 --- a/tdrive/backend/node/src/services/applications/services/company-applications.ts +++ b/tdrive/backend/node/src/services/applications/services/company-applications.ts @@ -18,19 +18,28 @@ export class CompanyApplicationServiceImpl implements TdriveServiceProvider, Ini pk: Pick & { id?: string }, context?: CompanyExecutionContext, ): Promise { - const application = await gr.services.applications.marketplaceApps.get( - pk.application_id, - context, - ); + try { + const application = await gr.services.applications.marketplaceApps.get( + pk.application_id, + context, + ); - return { - ...{ - id: pk.application_id, - company_id: pk.company_id, - application_id: pk.application_id, - }, - application: application, - }; + if (!application?.id) { + return null; + } + + return { + ...{ + id: pk.application_id, + company_id: pk.company_id, + application_id: pk.application_id, + }, + application: application, + }; + } catch (err) { + console.error(err); + return null; + } } async list( diff --git a/tdrive/backend/node/src/services/documents/services/index.ts b/tdrive/backend/node/src/services/documents/services/index.ts index 1cd993b4..e1836b68 100644 --- a/tdrive/backend/node/src/services/documents/services/index.ts +++ b/tdrive/backend/node/src/services/documents/services/index.ts @@ -21,6 +21,7 @@ import { TrashType, CompanyExecutionContext, DriveTdriveTab, + DriveFileAccessLevel, } from "../types"; import { addDriveItemToArchive, @@ -183,6 +184,28 @@ export class DocumentsService { }; }; + getAccess = async ( + id: string, + userId: string, + context: DriveExecutionContext, + ): Promise => { + if (!context) { + this.logger.error("invalid context"); + return null; + } + + id = id || this.ROOT; + + //Get requested entity + const myAccessLevel = await getAccessLevel(id, null, this.repository, context); + + if (myAccessLevel !== "none") { + return await getAccessLevel(id, null, this.repository, { ...context, user: { id: userId } }); + } + + return null; + }; + /** * Creates a DriveFile item. * diff --git a/tdrive/backend/node/src/services/documents/utils.ts b/tdrive/backend/node/src/services/documents/utils.ts index c126d27e..9915821a 100644 --- a/tdrive/backend/node/src/services/documents/utils.ts +++ b/tdrive/backend/node/src/services/documents/utils.ts @@ -162,6 +162,20 @@ export const hasAccessLevel = ( * @returns {Promise} */ export const isCompanyGuest = async (context: CompanyExecutionContext): Promise => { + if (context.user?.application_id) { + //Applications do everything (if they are added to the company) + if ( + !!( + await globalResolver.services.applications.companyApps.get({ + company_id: context.company.id, + application_id: context.user?.application_id, + }) + )?.application?.id + ) { + return false; + } + } + const userRole = await globalResolver.services.companies.getUserRole( context.company.id, context.user?.id, @@ -177,6 +191,20 @@ export const isCompanyGuest = async (context: CompanyExecutionContext): Promise< * @returns {Promise} */ export const isCompanyAdmin = async (context: CompanyExecutionContext): Promise => { + if (context.user?.application_id) { + //Applications do everything (if they are added to the company) + if ( + !!( + await globalResolver.services.applications.companyApps.get({ + company_id: context.company.id, + application_id: context.user?.application_id, + }) + )?.application?.id + ) { + return true; + } + } + const userRole = await globalResolver.services.companies.getUserRole( context.company.id, context.user?.id, @@ -373,6 +401,20 @@ export const getAccessLevel = async ( throw Error("Drive item doesn't exist"); } + if (context.user?.application_id) { + //Applications do everything (if they are added to the company) + if ( + !!( + await globalResolver.services.applications.companyApps.get({ + company_id: context.company.id, + application_id: context.user?.application_id, + }) + )?.application?.id + ) { + return "manage"; + } + } + /* * Specific user or channel rule is applied first. Then less restrictive level will be chosen * between the parent folder and company accesses. @@ -673,7 +715,7 @@ export const getFileMetadata = async ( company_id: context.company.id, }, context, - { ...(context.user.server_request ? {} : { waitForThumbnail: true }) }, + { ...(context.user?.server_request ? {} : { waitForThumbnail: true }) }, ); if (!file) { diff --git a/tdrive/backend/node/src/services/documents/web/controllers/documents.ts b/tdrive/backend/node/src/services/documents/web/controllers/documents.ts index 3253a21c..8daad7da 100644 --- a/tdrive/backend/node/src/services/documents/web/controllers/documents.ts +++ b/tdrive/backend/node/src/services/documents/web/controllers/documents.ts @@ -10,6 +10,7 @@ import { FileVersion } from "../../entities/file-version"; import { CompanyExecutionContext, DriveExecutionContext, + DriveFileAccessLevel, DriveItemDetails, DriveTdriveTab, ItemRequestParams, @@ -133,6 +134,33 @@ export class DocumentsController { }; }; + /** + * Return access level of a given user on a given item + */ + getAccess = async ( + request: FastifyRequest<{ + Params: ItemRequestParams & { user_id: string }; + }>, + ): Promise<{ access: DriveFileAccessLevel | "none" }> => { + const context = getDriveExecutionContext(request); + const { id } = request.params; + const { user_id } = request.params; + + const access = await globalResolver.services.documents.documents.getAccess( + id, + user_id, + context, + ); + + if (!access) { + throw new CrudException("Item not found", 404); + } + + return { + access, + }; + }; + /** * Update drive item * diff --git a/tdrive/backend/node/src/services/documents/web/routes.ts b/tdrive/backend/node/src/services/documents/web/routes.ts index 0bcf1aff..29427e39 100644 --- a/tdrive/backend/node/src/services/documents/web/routes.ts +++ b/tdrive/backend/node/src/services/documents/web/routes.ts @@ -44,6 +44,13 @@ const routes: FastifyPluginCallback = (fastify: FastifyInstance, _options, next) handler: documentsController.delete.bind(documentsController), }); + fastify.route({ + method: "GET", + url: `${serviceUrl}/:id/user/:user_id/access`, + preValidation: [fastify.authenticateOptional], + handler: documentsController.getAccess.bind(documentsController), + }); + fastify.route({ method: "POST", url: `${serviceUrl}/:id/version`,