Skip to content

Commit

Permalink
Merge pull request #337 from aXenDeveloper/files_protected
Browse files Browse the repository at this point in the history
feat(editor): Download secure files form content
  • Loading branch information
aXenDeveloper authored May 6, 2024
2 parents 035348d + b6ceedd commit 9b15930
Show file tree
Hide file tree
Showing 65 changed files with 862 additions and 532 deletions.
15 changes: 8 additions & 7 deletions backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"class-validator": "^0.14.1",
"cookie-parser": "^1.4.6",
"date-fns": "^3.6.0",
"drizzle-orm": "^0.30.9",
"drizzle-orm": "^0.30.10",
"graphql": "^16.8.1",
"lodash": "^4.17.21",
"nodemailer": "^6.9.13",
Expand All @@ -39,7 +39,7 @@
"rxjs": "^7.8.1",
"sharp": "^0.33.3",
"socket.io": "^4.7.5",
"tar": "^7.0.1",
"tar": "^7.1.0",
"ua-parser-js": "^1.0.37"
},
"devDependencies": {
Expand All @@ -62,20 +62,21 @@
"@nestjs/testing": "^10.3.8",
"@types/archiver": "^6.0.2",
"@types/bcrypt": "^5.0.2",
"@types/busboy": "^1.5.3",
"@types/busboy": "^1.5.4",
"@types/cookie-parser": "^1.4.7",
"@types/express": "^4.17.21",
"@types/lodash": "^4.17.0",
"@types/node": "^20.12.7",
"@types/lodash": "^4.17.1",
"@types/node": "^20.12.9",
"@types/nodemailer": "^6.4.15",
"@types/object-path": "^0.11.4",
"@types/pg": "^8.11.5",
"@types/pg": "^8.11.6",
"@types/supertest": "^6.0.2",
"@types/ua-parser-js": "^0.7.39",
"@typescript-eslint/eslint-plugin": "^7.8.0",
"@typescript-eslint/parser": "^7.8.0",
"cross-env": "^7.0.3",
"dotenv": "^16.4.5",
"drizzle-kit": "0.20.17-e38e63d",
"drizzle-kit": "0.20.17-57f44bf",
"eslint": "^8.57.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-import": "^2.29.1",
Expand Down
4 changes: 3 additions & 1 deletion backend/plugins/core/admin/admin.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { AdminNavModule } from "./nav/nav.module";
import { AdminLanguagesModule } from "./languages/languages.module";
import { AdminSettingsModule } from "./settings/settings.module";
import { AdminMetadataModule } from "./metadata/metadata.module";
import { DownloadFilesAdminController } from "./files/download/download.controller";

@Module({
imports: [
Expand All @@ -29,6 +30,7 @@ import { AdminMetadataModule } from "./metadata/metadata.module";
AdminLanguagesModule,
AdminSettingsModule,
AdminMetadataModule
]
],
controllers: [DownloadFilesAdminController]
})
export class AdminModule {}
108 changes: 108 additions & 0 deletions backend/plugins/core/admin/files/download/download.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import { createReadStream, existsSync, unlinkSync } from "fs";
import { join } from "path";

import {
Controller,
Get,
Param,
Req,
Res,
StreamableFile
} from "@nestjs/common";
import { Request, Response } from "express";

import { DatabaseService } from "@/plugins/database/database.service";
import { AuthorizationAdminSessionsService } from "../../sessions/authorization/authorization.service";
import { InternalAuthorizationCoreSessionsService } from "@/plugins/core/sessions/authorization/internal/internal_authorization.service";

@Controller("files")
export class DownloadFilesAdminController {
constructor(
private service: InternalAuthorizationCoreSessionsService,
private readonly serviceAdmin: AuthorizationAdminSessionsService,
private databaseService: DatabaseService
) {}

@Get(":file")
async getFile(
@Res({ passthrough: true }) res: Response,
@Req() req: Request,
@Param() { file }: { file: string }
): Promise<StreamableFile> {
const path = join(process.cwd(), "temp", file);
if (!existsSync(path)) {
res.status(404);

return;
}
const userId = file.split(".")[0].split("--")[1].split("-")[0];
const currentFile = {
name: file.split("--")[0],
type: file.split(".")[1]
};

const user = await this.databaseService.db.query.core_users.findFirst({
where: (table, { eq }) => eq(table.id, +userId)
});

if (!user) {
res.status(404);

return;
}

const isAdmin =
await this.databaseService.db.query.core_admin_permissions.findFirst({
where: (table, { eq, or }) =>
or(eq(table.user_id, +userId), eq(table.group_id, user.group_id))
});

if (isAdmin) {
try {
const data = await this.serviceAdmin.initialAuthorization({
req,
res
});

if (+userId !== data.id) {
res.status(404);

return;
}
} catch (e) {
res.status(404);

return;
}
} else {
try {
const data = await this.service.authorization({
req,
res
});

if (+userId !== data.id) {
res.status(404);

return;
}
} catch (e) {
res.status(404);

return;
}
}

const streamFile = createReadStream(path);
res.set({
"Content-Type": `application/${currentFile.type}`,
"Content-Disposition": `attachment; filename="${currentFile.name}.${currentFile.type}"`
});

streamFile.on("close", () => {
unlinkSync(path);
});

return new StreamableFile(streamFile);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ export class DownloadAdminPluginsService {

try {
await execShellCommand(
`npx drizzle-kit generate --out --config plugins/${code}/admin/database/drizzle.config.ts`
`npx drizzle-kit generate --config plugins/${code}/admin/database/drizzle.config.ts`
);
} catch (err) {
throw new CustomError({
Expand Down
94 changes: 17 additions & 77 deletions backend/plugins/core/files/download/download.controller.ts
Original file line number Diff line number Diff line change
@@ -1,106 +1,46 @@
import { createReadStream, existsSync, unlinkSync } from "fs";
import { createReadStream } from "fs";
import { join } from "path";

import {
Controller,
Get,
Param,
Req,
Res,
StreamableFile
StreamableFile,
Query
} from "@nestjs/common";
import { Request, Response } from "express";
import { Response } from "express";

import { InternalAuthorizationCoreSessionsService } from "../../sessions/authorization/internal/internal_authorization.service";
import { DatabaseService } from "@/plugins/database/database.service";
import { AuthorizationAdminSessionsService } from "../../admin/sessions/authorization/authorization.service";

@Controller("files")
export class DownloadFilesController {
constructor(
private service: InternalAuthorizationCoreSessionsService,
private readonly serviceAdmin: AuthorizationAdminSessionsService,
private databaseService: DatabaseService
) {}
@Controller("secure_files")
export class DownloadSecureFilesController {
constructor(private databaseService: DatabaseService) {}

@Get(":file")
@Get(":id")
async getFile(
@Res({ passthrough: true }) res: Response,
@Req() req: Request,
@Param() { file }: { file: string }
@Param() { id }: { id: string },
@Query() { security_key }: { security_key: string }
): Promise<StreamableFile> {
const path = join(process.cwd(), "temp", file);
if (!existsSync(path)) {
res.status(404);

return;
}
const userId = file.split(".")[0].split("--")[1].split("-")[0];
const currentFile = {
name: file.split("--")[0],
type: file.split(".")[1]
};

const user = await this.databaseService.db.query.core_users.findFirst({
where: (table, { eq }) => eq(table.id, +userId)
const file = await this.databaseService.db.query.core_files.findFirst({
where: (table, { eq }) => eq(table.id, +id)
});

if (!user) {
if (!file || file.security_key !== security_key) {
res.status(404);

return;
}

const isAdmin =
await this.databaseService.db.query.core_admin_permissions.findFirst({
where: (table, { eq, or }) =>
or(eq(table.user_id, +userId), eq(table.group_id, user.group_id))
});

if (isAdmin) {
try {
const data = await this.serviceAdmin.initialAuthorization({
req,
res
});

if (+userId !== data.id) {
res.status(404);
const path = join(process.cwd(), file.dir_folder, file.file_name);

return;
}
} catch (e) {
res.status(404);

return;
}
} else {
try {
const data = await this.service.authorization({
req,
res
});

if (+userId !== data.id) {
res.status(404);

return;
}
} catch (e) {
res.status(404);

return;
}
}
const mediaType = file.mimetype.split("/")[0];

const streamFile = createReadStream(path);
res.set({
"Content-Type": `application/${currentFile.type}`,
"Content-Disposition": `attachment; filename="${currentFile.name}.${currentFile.type}"`
});

streamFile.on("close", () => {
unlinkSync(path);
"Content-Type": `application/${mediaType}`,
"Content-Disposition": `attachment; filename="${file.file_name}"`
});

return new StreamableFile(streamFile);
Expand Down
4 changes: 2 additions & 2 deletions backend/plugins/core/files/files.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ import { Global, Module } from "@nestjs/common";

import { UploadCoreFilesService } from "./helpers/upload/upload.service";
import { DeleteCoreFilesService } from "./helpers/delete/delete.service";
import { DownloadFilesController } from "./download/download.controller";
import { AuthorizationAdminSessionsService } from "../admin/sessions/authorization/authorization.service";
import { ShowCoreFilesService } from "./show/show.service";
import { ShowCoreFilesResolver } from "./show/show.resolver";
import { CoreFilesCron } from "./files.cron";
import { DownloadSecureFilesController } from "./download/download.controller";

@Module({
providers: [ShowCoreFilesService, ShowCoreFilesResolver, CoreFilesCron]
Expand All @@ -21,6 +21,6 @@ export class CoreFilesModule {}
AuthorizationAdminSessionsService
],
exports: [UploadCoreFilesService, DeleteCoreFilesService],
controllers: [DownloadFilesController]
controllers: [DownloadSecureFilesController]
})
export class CoreFilesModuleGlobal {}
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ export class ParserTextLanguageCoreHelpersService extends HelpersParserTextLangu
return update[0];
}

await this.databaseService.db
const data = await this.databaseService.db
.insert(database)
.values({ ...item, item_id } as {
[Key in keyof PgTableWithColumns<T>["$inferInsert"]]:
Expand All @@ -113,6 +113,8 @@ export class ParserTextLanguageCoreHelpersService extends HelpersParserTextLangu
| PgTableWithColumns<T>["$inferInsert"][Key];
})
.returning();

return data[0];
})
);

Expand Down
7 changes: 2 additions & 5 deletions backend/plugins/forum/admin/forums/create/create.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,7 @@ export class CreateForumForumsService {
? theMostHighestPosition.position + 1
: 0,
parent_id,
can_all_create: permissions.can_all_create,
can_all_read: permissions.can_all_read,
can_all_reply: permissions.can_all_reply,
can_all_view: permissions.can_all_view
...permissions
})
.returning();

Expand All @@ -81,7 +78,7 @@ export class CreateForumForumsService {
await this.databaseService.db.insert(forum_forums_permissions).values(
permissions.groups.map(item => ({
forum_id: data[0].id,
group_id: item.id,
group_id: item.group_id,
...item
}))
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
@InputType()
class GroupsPermissionsCreateForumForums {
@Field(() => Int)
id: number;
group_id: number;

@Field(() => Boolean)
can_view: boolean;
Expand Down
Loading

0 comments on commit 9b15930

Please sign in to comment.