🛠 Allow applications to manage the drive (#59)
* 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
This commit is contained in:
@@ -45,7 +45,7 @@ export default class LocalConnectorService implements StorageConnectorAPI {
|
||||
async read(path: string): Promise<Readable> {
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ export class ApplicationServiceImpl implements TdriveServiceProvider, Initializa
|
||||
}
|
||||
|
||||
async get(id: string, _: ExecutionContext): Promise<Application> {
|
||||
return (await this.list(_)).find((app: Application) => app.id === id);
|
||||
return (await this.list(_)).find((app: Application) => app?.id === id);
|
||||
}
|
||||
|
||||
async list(_: ExecutionContext): Promise<Application[]> {
|
||||
|
||||
@@ -18,19 +18,28 @@ export class CompanyApplicationServiceImpl implements TdriveServiceProvider, Ini
|
||||
pk: Pick<CompanyApplicationPrimaryKey, "company_id" | "application_id"> & { id?: string },
|
||||
context?: CompanyExecutionContext,
|
||||
): Promise<CompanyApplicationWithApplication> {
|
||||
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<ListOptions>(
|
||||
|
||||
@@ -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<DriveFileAccessLevel | "none" | null> => {
|
||||
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.
|
||||
*
|
||||
|
||||
@@ -162,6 +162,20 @@ export const hasAccessLevel = (
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
export const isCompanyGuest = async (context: CompanyExecutionContext): Promise<boolean> => {
|
||||
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<boolean>}
|
||||
*/
|
||||
export const isCompanyAdmin = async (context: CompanyExecutionContext): Promise<boolean> => {
|
||||
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) {
|
||||
|
||||
@@ -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
|
||||
*
|
||||
|
||||
@@ -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`,
|
||||
|
||||
Reference in New Issue
Block a user