diff --git a/src/backend/middlewares/SharingMWs.ts b/src/backend/middlewares/SharingMWs.ts index 0a51fb1f..0895d3a1 100644 --- a/src/backend/middlewares/SharingMWs.ts +++ b/src/backend/middlewares/SharingMWs.ts @@ -1,11 +1,11 @@ -import { NextFunction, Request, Response } from 'express'; -import { CreateSharingDTO, SharingDTO } from '../../common/entities/SharingDTO'; -import { ObjectManagers } from '../model/ObjectManagers'; -import { ErrorCodes, ErrorDTO } from '../../common/entities/Error'; -import { Config } from '../../common/config/private/Config'; -import { QueryParams } from '../../common/QueryParams'; +import {NextFunction, Request, Response} from 'express'; +import {CreateSharingDTO, SharingDTO} from '../../common/entities/SharingDTO'; +import {ObjectManagers} from '../model/ObjectManagers'; +import {ErrorCodes, ErrorDTO} from '../../common/entities/Error'; +import {Config} from '../../common/config/private/Config'; +import {QueryParams} from '../../common/QueryParams'; import * as path from 'path'; -import { UserRoles } from '../../common/entities/UserDTO'; +import {UserRoles} from '../../common/entities/UserDTO'; export class SharingMWs { public static async getSharing( @@ -20,9 +20,7 @@ export class SharingMWs { try { req.resultPipe = - await ObjectManagers.getInstance().SharingManager.findOne({ - sharingKey, - }); + await ObjectManagers.getInstance().SharingManager.findOne(sharingKey); return next(); } catch (err) { return next( @@ -58,9 +56,7 @@ export class SharingMWs { // eslint-disable-next-line no-constant-condition while (true) { try { - await ObjectManagers.getInstance().SharingManager.findOne({ - sharingKey, - }); + await ObjectManagers.getInstance().SharingManager.findOne(sharingKey); sharingKey = this.generateKey(); } catch (err) { break; @@ -173,6 +169,13 @@ export class SharingMWs { const sharingKey: string = req.params['sharingKey']; try { + // Check if user has the right to delete sharing. + if (req.session['user'].role < UserRoles.Admin) { + const s = await ObjectManagers.getInstance().SharingManager.findOne(sharingKey); + if (s.creator.id !== req.session['user'].id) { + return next(new ErrorDTO(ErrorCodes.NOT_AUTHORISED, 'Can\'t delete sharing.')); + } + } req.resultPipe = await ObjectManagers.getInstance().SharingManager.deleteSharing( sharingKey @@ -213,6 +216,36 @@ export class SharingMWs { } } + public static async listSharingForDir( + req: Request, + res: Response, + next: NextFunction + ): Promise { + if (Config.Sharing.enabled === false) { + return next(); + } + + const dir = path.normalize(req.params['directory'] || '/'); + try { + if (req.session['user'].role >= UserRoles.Admin) { + req.resultPipe = + await ObjectManagers.getInstance().SharingManager.listAllForDir(dir); + } else { + req.resultPipe = + await ObjectManagers.getInstance().SharingManager.listAllForDir(dir, req.session['user']); + } + return next(); + } catch (err) { + return next( + new ErrorDTO( + ErrorCodes.GENERAL_ERROR, + 'Error during listing shares', + err + ) + ); + } + } + private static generateKey(): string { function s4(): string { return Math.floor((1 + Math.random()) * 0x10000) diff --git a/src/backend/middlewares/user/AuthenticationMWs.ts b/src/backend/middlewares/user/AuthenticationMWs.ts index 94d97821..68bc569c 100644 --- a/src/backend/middlewares/user/AuthenticationMWs.ts +++ b/src/backend/middlewares/user/AuthenticationMWs.ts @@ -151,11 +151,7 @@ export class AuthenticationMWs { const sharingKey: string = (req.query[QueryParams.gallery.sharingKey_query] as string) || (req.params[QueryParams.gallery.sharingKey_params] as string); - const sharing = await ObjectManagers.getInstance().SharingManager.findOne( - { - sharingKey, - } - ); + const sharing = await ObjectManagers.getInstance().SharingManager.findOne(sharingKey); if ( !sharing || @@ -264,11 +260,7 @@ export class AuthenticationMWs { const sharingKey: string = (req.query[QueryParams.gallery.sharingKey_query] as string) || (req.params[QueryParams.gallery.sharingKey_params] as string); - const sharing = await ObjectManagers.getInstance().SharingManager.findOne( - { - sharingKey, - } - ); + const sharing = await ObjectManagers.getInstance().SharingManager.findOne(sharingKey); if (!sharing || sharing.expires < Date.now()) { return null; } diff --git a/src/backend/model/database/SharingManager.ts b/src/backend/model/database/SharingManager.ts index 5347e548..6c3c7fec 100644 --- a/src/backend/model/database/SharingManager.ts +++ b/src/backend/model/database/SharingManager.ts @@ -3,7 +3,8 @@ import {SQLConnection} from './SQLConnection'; import {SharingEntity} from './enitites/SharingEntity'; import {Config} from '../../../common/config/private/Config'; import {PasswordHelper} from '../PasswordHelper'; -import {DeleteResult, FindOptionsWhere} from 'typeorm'; +import {DeleteResult, SelectQueryBuilder} from 'typeorm'; +import {UserDTO} from '../../../common/entities/UserDTO'; export class SharingManager { private static async removeExpiredLink(): Promise { @@ -34,10 +35,29 @@ export class SharingManager { .getMany(); } - async findOne(filter: FindOptionsWhere): Promise { + + async listAllForDir(dir: string, user?: UserDTO): Promise { await SharingManager.removeExpiredLink(); const connection = await SQLConnection.getConnection(); - return await connection.getRepository(SharingEntity).findOneBy(filter); + const q: SelectQueryBuilder = connection + .getRepository(SharingEntity) + .createQueryBuilder('share') + .leftJoinAndSelect('share.creator', 'creator') + .where('path = :dir', {dir}); + if (user) { + q.andWhere('share.creator = :user', {user: user.id}); + } + return await q.getMany(); + } + + async findOne(sharingKey: string): Promise { + await SharingManager.removeExpiredLink(); + const connection = await SQLConnection.getConnection(); + return await connection.getRepository(SharingEntity) + .createQueryBuilder('share') + .leftJoinAndSelect('share.creator', 'creator') + .where('share.sharingKey = :sharingKey', {sharingKey}) + .getOne(); } async createSharing(sharing: SharingDTO): Promise { diff --git a/src/backend/routes/SharingRouter.ts b/src/backend/routes/SharingRouter.ts index 75c62adb..771977ed 100644 --- a/src/backend/routes/SharingRouter.ts +++ b/src/backend/routes/SharingRouter.ts @@ -14,6 +14,7 @@ export class SharingRouter { this.addCreateSharing(app); this.addUpdateSharing(app); this.addListSharing(app); + this.addListSharingForDir(app); this.addDeleteSharing(app); } @@ -64,7 +65,7 @@ export class SharingRouter { app.delete( [Config.Server.apiPath + '/share/:' + QueryParams.gallery.sharingKey_params], AuthenticationMWs.authenticate, - AuthenticationMWs.authorise(UserRoles.Admin), + AuthenticationMWs.authorise(UserRoles.User), SharingMWs.deleteSharing, ServerTimingMWs.addServerTiming, RenderingMWs.renderResult @@ -73,12 +74,25 @@ export class SharingRouter { private static addListSharing(app: express.Express): void { app.get( - [Config.Server.apiPath + '/share/list'], + [Config.Server.apiPath + '/share/listAll'], AuthenticationMWs.authenticate, - AuthenticationMWs.authorise(UserRoles.User), + AuthenticationMWs.authorise(UserRoles.Admin), SharingMWs.listSharing, ServerTimingMWs.addServerTiming, RenderingMWs.renderSharingList ); } + + private static addListSharingForDir(app: express.Express): void { + app.get( + [Config.Server.apiPath + '/share/list/:directory(*)', + Config.Server.apiPath + '/share/list//', + Config.Server.apiPath + '/share/list'], + AuthenticationMWs.authenticate, + AuthenticationMWs.authorise(UserRoles.User), + SharingMWs.listSharingForDir, + ServerTimingMWs.addServerTiming, + RenderingMWs.renderSharingList + ); + } } diff --git a/src/frontend/app/ui/gallery/share.service.ts b/src/frontend/app/ui/gallery/share.service.ts index 3ac364dc..2d5fce94 100644 --- a/src/frontend/app/ui/gallery/share.service.ts +++ b/src/frontend/app/ui/gallery/share.service.ts @@ -6,6 +6,8 @@ import {BehaviorSubject} from 'rxjs'; import {distinctUntilChanged, filter} from 'rxjs/operators'; import {QueryParams} from '../../../../common/QueryParams'; import {UserDTO} from '../../../../common/entities/UserDTO'; +import {Utils} from '../../../../common/Utils'; +import {Config} from '../../../../common/config/public/Config'; @Injectable() @@ -61,6 +63,11 @@ export class ShareService { }); } + public getUrl(share: SharingDTO): string { + return Utils.concatUrls(Config.Server.publicUrl, '/share/', share.sharingKey); + } + + onNewUser = async (user: UserDTO) => { if (user && !!user.usedSharingKey) { if ( @@ -135,4 +142,23 @@ export class ShareService { console.error(e); } } + + public async getSharingListForDir( + dir: string + ): Promise { + return this.networkService.getJson('/share/list/' + dir); + } + + + + public getSharingList(): Promise { + if (!Config.Sharing.enabled) { + return Promise.resolve([]); + } + return this.networkService.getJson('/share/listAll'); + } + + public deleteSharing(sharing: SharingDTO): Promise { + return this.networkService.deleteJson('/share/' + sharing.sharingKey); + } } diff --git a/src/frontend/app/ui/gallery/share/share.gallery.component.html b/src/frontend/app/ui/gallery/share/share.gallery.component.html index c7b86512..ef342560 100644 --- a/src/frontend/app/ui/gallery/share/share.gallery.component.html +++ b/src/frontend/app/ui/gallery/share/share.gallery.component.html @@ -117,5 +117,32 @@ + +
+ + + + + + + + + + + + + + + + + +
KeyCreatorExpires
{{share.sharingKey}}{{share.creator.name}}{{share.expires | date}} + +
+
diff --git a/src/frontend/app/ui/gallery/share/share.gallery.component.ts b/src/frontend/app/ui/gallery/share/share.gallery.component.ts index c24111b4..705db94b 100644 --- a/src/frontend/app/ui/gallery/share/share.gallery.component.ts +++ b/src/frontend/app/ui/gallery/share/share.gallery.component.ts @@ -37,13 +37,15 @@ export class GalleryShareComponent implements OnInit, OnDestroy { modalRef: BsModalRef; invalidSettings = $localize`Invalid settings`; + activeShares: SharingDTO[] = []; + text = { Yes: 'Yes', No: 'No', }; constructor( - private sharingService: ShareService, + public sharingService: ShareService, public galleryService: ContentService, private notification: NotificationService, private modalService: BsModalService @@ -54,7 +56,8 @@ export class GalleryShareComponent implements OnInit, OnDestroy { ngOnInit(): void { this.contentSubscription = this.galleryService.content.subscribe( - (content: ContentWrapper) => { + async (content: ContentWrapper) => { + this.activeShares = []; this.enabled = !!content.directory; if (!this.enabled) { return; @@ -63,6 +66,7 @@ export class GalleryShareComponent implements OnInit, OnDestroy { content.directory.path, content.directory.name ); + await this.updateActiveSharesList(); } ); } @@ -73,6 +77,21 @@ export class GalleryShareComponent implements OnInit, OnDestroy { } } + + async deleteSharing(sharing: SharingDTO): Promise { + await this.sharingService.deleteSharing(sharing); + await this.updateActiveSharesList(); + } + + private async updateActiveSharesList() { + try { + this.activeShares = await this.sharingService.getSharingListForDir(this.currentDir); + } catch (e) { + this.activeShares = []; + console.error(e); + } + } + calcValidity(): number { switch (parseInt(this.input.valid.type.toString(), 10)) { case ValidityTypes.Minutes: @@ -112,6 +131,7 @@ export class GalleryShareComponent implements OnInit, OnDestroy { this.calcValidity() ); this.url = Utils.concatUrls(Config.Server.publicUrl, '/share/', this.sharing.sharingKey); + await this.updateActiveSharesList(); } async openModal(template: TemplateRef): Promise { diff --git a/src/frontend/app/ui/settings/settings.service.ts b/src/frontend/app/ui/settings/settings.service.ts index 965a1ad4..5df4e9c4 100644 --- a/src/frontend/app/ui/settings/settings.service.ts +++ b/src/frontend/app/ui/settings/settings.service.ts @@ -11,6 +11,9 @@ import {DefaultsJobs} from '../../../../common/entities/job/JobDTO'; import {StatisticDTO} from '../../../../common/entities/settings/StatisticDTO'; import {ScheduledJobsService} from './scheduled-jobs.service'; import {IWebConfigClassPrivate} from '../../../../../node_modules/typeconfig/src/decorators/class/IWebConfigClass'; +import {SharingDTO} from '../../../../common/entities/SharingDTO'; +import {Utils} from '../../../../common/Utils'; +import {Config} from '../../../../common/config/public/Config'; export enum ConfigStyle { diff --git a/src/frontend/app/ui/settings/sharings-list/sharing-list.service.ts b/src/frontend/app/ui/settings/sharings-list/sharing-list.service.ts deleted file mode 100644 index a7ca0938..00000000 --- a/src/frontend/app/ui/settings/sharings-list/sharing-list.service.ts +++ /dev/null @@ -1,26 +0,0 @@ -import {Injectable} from '@angular/core'; -import {SharingDTO} from '../../../../../common/entities/SharingDTO'; -import {NetworkService} from '../../../model/network/network.service'; -import {SettingsService} from '../settings.service'; -import {Config} from '../../../../../common/config/public/Config'; - -@Injectable({ - providedIn: 'root' -}) -export class SharingListService { - - constructor(private networkService: NetworkService) { - } - - - public getSharingList(): Promise { - if (!Config.Sharing.enabled) { - return Promise.resolve([]); - } - return this.networkService.getJson('/share/list'); - } - - public deleteSharing(sharing: SharingDTO): Promise { - return this.networkService.deleteJson('/share/' + sharing.sharingKey); - } -} diff --git a/src/frontend/app/ui/settings/sharings-list/sharings-list.component.html b/src/frontend/app/ui/settings/sharings-list/sharings-list.component.html index f6ca68eb..cae545e0 100644 --- a/src/frontend/app/ui/settings/sharings-list/sharings-list.component.html +++ b/src/frontend/app/ui/settings/sharings-list/sharings-list.component.html @@ -20,7 +20,7 @@ - {{share.sharingKey}} + {{share.sharingKey}} {{share.path}} {{share.creator.name}} {{share.expires | date}} diff --git a/src/frontend/app/ui/settings/sharings-list/sharings-list.component.ts b/src/frontend/app/ui/settings/sharings-list/sharings-list.component.ts index 699b91b5..c3f3fed5 100644 --- a/src/frontend/app/ui/settings/sharings-list/sharings-list.component.ts +++ b/src/frontend/app/ui/settings/sharings-list/sharings-list.component.ts @@ -1,9 +1,7 @@ import {Component, OnInit} from '@angular/core'; import {SharingDTO} from '../../../../../common/entities/SharingDTO'; -import {SharingListService} from './sharing-list.service'; import {SettingsService} from '../settings.service'; -import {Config} from '../../../../../common/config/public/Config'; -import {Utils} from '../../../../../common/Utils'; +import {ShareService} from '../../gallery/share.service'; @Component({ selector: 'app-settigns-sharings-list', @@ -13,10 +11,10 @@ import {Utils} from '../../../../../common/Utils'; export class SharingsListComponent implements OnInit { public shares: SharingDTO[] = []; - public sharingUrl = Utils.concatUrls(Config.Server.publicUrl, '/share') + '/'; - constructor(public sharingList: SharingListService, - private settingsService: SettingsService) { + + constructor(public sharingService: ShareService, + public settingsService: SettingsService) { } @@ -29,13 +27,13 @@ export class SharingsListComponent implements OnInit { } async deleteSharing(sharing: SharingDTO): Promise { - await this.sharingList.deleteSharing(sharing); + await this.sharingService.deleteSharing(sharing); await this.getSharingList(); } private async getSharingList(): Promise { try { - this.shares = await this.sharingList.getSharingList(); + this.shares = await this.sharingService.getSharingList(); } catch (err) { this.shares = []; throw err; diff --git a/test/backend/unit/model/sql/SharingManager.spec.ts b/test/backend/unit/model/sql/SharingManager.spec.ts index b2afc800..fb43bc92 100644 --- a/test/backend/unit/model/sql/SharingManager.spec.ts +++ b/test/backend/unit/model/sql/SharingManager.spec.ts @@ -82,7 +82,7 @@ describe('SharingManager', (sqlHelper: DBTestHelper) => { }; const saved = await sm.createSharing(sharing); - const found = await sm.findOne({sharingKey: 'testKey'}); + const found = await sm.findOne('testKey'); expect(found.id).to.not.equals(null); expect(found.sharingKey).to.equals(sharing.sharingKey);