🛠 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> {
|
async read(path: string): Promise<Readable> {
|
||||||
const fullPath = this.getFullPath(path);
|
const fullPath = this.getFullPath(path);
|
||||||
if (!fs.existsSync(fullPath)) {
|
if (!fs.existsSync(fullPath)) {
|
||||||
throw new Error("File doesn't not exists");
|
throw new Error("File doesn't exists");
|
||||||
}
|
}
|
||||||
return createReadStream(fullPath);
|
return createReadStream(fullPath);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export class ApplicationServiceImpl implements TdriveServiceProvider, Initializa
|
|||||||
}
|
}
|
||||||
|
|
||||||
async get(id: string, _: ExecutionContext): Promise<Application> {
|
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[]> {
|
async list(_: ExecutionContext): Promise<Application[]> {
|
||||||
|
|||||||
@@ -18,19 +18,28 @@ export class CompanyApplicationServiceImpl implements TdriveServiceProvider, Ini
|
|||||||
pk: Pick<CompanyApplicationPrimaryKey, "company_id" | "application_id"> & { id?: string },
|
pk: Pick<CompanyApplicationPrimaryKey, "company_id" | "application_id"> & { id?: string },
|
||||||
context?: CompanyExecutionContext,
|
context?: CompanyExecutionContext,
|
||||||
): Promise<CompanyApplicationWithApplication> {
|
): Promise<CompanyApplicationWithApplication> {
|
||||||
const application = await gr.services.applications.marketplaceApps.get(
|
try {
|
||||||
pk.application_id,
|
const application = await gr.services.applications.marketplaceApps.get(
|
||||||
context,
|
pk.application_id,
|
||||||
);
|
context,
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
if (!application?.id) {
|
||||||
...{
|
return null;
|
||||||
id: pk.application_id,
|
}
|
||||||
company_id: pk.company_id,
|
|
||||||
application_id: pk.application_id,
|
return {
|
||||||
},
|
...{
|
||||||
application: application,
|
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>(
|
async list<ListOptions>(
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import {
|
|||||||
TrashType,
|
TrashType,
|
||||||
CompanyExecutionContext,
|
CompanyExecutionContext,
|
||||||
DriveTdriveTab,
|
DriveTdriveTab,
|
||||||
|
DriveFileAccessLevel,
|
||||||
} from "../types";
|
} from "../types";
|
||||||
import {
|
import {
|
||||||
addDriveItemToArchive,
|
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.
|
* Creates a DriveFile item.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -162,6 +162,20 @@ export const hasAccessLevel = (
|
|||||||
* @returns {Promise<boolean>}
|
* @returns {Promise<boolean>}
|
||||||
*/
|
*/
|
||||||
export const isCompanyGuest = async (context: CompanyExecutionContext): 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(
|
const userRole = await globalResolver.services.companies.getUserRole(
|
||||||
context.company.id,
|
context.company.id,
|
||||||
context.user?.id,
|
context.user?.id,
|
||||||
@@ -177,6 +191,20 @@ export const isCompanyGuest = async (context: CompanyExecutionContext): Promise<
|
|||||||
* @returns {Promise<boolean>}
|
* @returns {Promise<boolean>}
|
||||||
*/
|
*/
|
||||||
export const isCompanyAdmin = async (context: CompanyExecutionContext): 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(
|
const userRole = await globalResolver.services.companies.getUserRole(
|
||||||
context.company.id,
|
context.company.id,
|
||||||
context.user?.id,
|
context.user?.id,
|
||||||
@@ -373,6 +401,20 @@ export const getAccessLevel = async (
|
|||||||
throw Error("Drive item doesn't exist");
|
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
|
* Specific user or channel rule is applied first. Then less restrictive level will be chosen
|
||||||
* between the parent folder and company accesses.
|
* between the parent folder and company accesses.
|
||||||
@@ -673,7 +715,7 @@ export const getFileMetadata = async (
|
|||||||
company_id: context.company.id,
|
company_id: context.company.id,
|
||||||
},
|
},
|
||||||
context,
|
context,
|
||||||
{ ...(context.user.server_request ? {} : { waitForThumbnail: true }) },
|
{ ...(context.user?.server_request ? {} : { waitForThumbnail: true }) },
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!file) {
|
if (!file) {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import { FileVersion } from "../../entities/file-version";
|
|||||||
import {
|
import {
|
||||||
CompanyExecutionContext,
|
CompanyExecutionContext,
|
||||||
DriveExecutionContext,
|
DriveExecutionContext,
|
||||||
|
DriveFileAccessLevel,
|
||||||
DriveItemDetails,
|
DriveItemDetails,
|
||||||
DriveTdriveTab,
|
DriveTdriveTab,
|
||||||
ItemRequestParams,
|
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
|
* Update drive item
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -44,6 +44,13 @@ const routes: FastifyPluginCallback = (fastify: FastifyInstance, _options, next)
|
|||||||
handler: documentsController.delete.bind(documentsController),
|
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({
|
fastify.route({
|
||||||
method: "POST",
|
method: "POST",
|
||||||
url: `${serviceUrl}/:id/version`,
|
url: `${serviceUrl}/:id/version`,
|
||||||
|
|||||||
Reference in New Issue
Block a user