2023-07-09 00:37:40 -04:00
|
|
|
import { BadRequestException, UnauthorizedException } from '@nestjs/common';
|
2023-06-28 09:56:24 -04:00
|
|
|
import { AuthUserDto } from '../auth';
|
2023-10-09 10:25:03 -04:00
|
|
|
import { IAccessRepository } from '../repositories';
|
2023-06-28 09:56:24 -04:00
|
|
|
|
|
|
|
|
export enum Permission {
|
2023-11-01 04:13:34 +01:00
|
|
|
ACTIVITY_CREATE = 'activity.create',
|
|
|
|
|
ACTIVITY_DELETE = 'activity.delete',
|
|
|
|
|
|
2023-06-28 09:56:24 -04:00
|
|
|
// ASSET_CREATE = 'asset.create',
|
|
|
|
|
ASSET_READ = 'asset.read',
|
|
|
|
|
ASSET_UPDATE = 'asset.update',
|
|
|
|
|
ASSET_DELETE = 'asset.delete',
|
2023-10-06 07:01:14 +00:00
|
|
|
ASSET_RESTORE = 'asset.restore',
|
2023-06-28 09:56:24 -04:00
|
|
|
ASSET_SHARE = 'asset.share',
|
|
|
|
|
ASSET_VIEW = 'asset.view',
|
|
|
|
|
ASSET_DOWNLOAD = 'asset.download',
|
2023-10-03 18:36:51 +02:00
|
|
|
ASSET_UPLOAD = 'asset.upload',
|
2023-06-28 09:56:24 -04:00
|
|
|
|
|
|
|
|
// ALBUM_CREATE = 'album.create',
|
2023-08-01 21:29:14 -04:00
|
|
|
ALBUM_READ = 'album.read',
|
2023-06-28 09:56:24 -04:00
|
|
|
ALBUM_UPDATE = 'album.update',
|
|
|
|
|
ALBUM_DELETE = 'album.delete',
|
2023-08-01 21:29:14 -04:00
|
|
|
ALBUM_REMOVE_ASSET = 'album.removeAsset',
|
2023-06-28 09:56:24 -04:00
|
|
|
ALBUM_SHARE = 'album.share',
|
2023-06-30 12:24:28 -04:00
|
|
|
ALBUM_DOWNLOAD = 'album.download',
|
2023-06-28 09:56:24 -04:00
|
|
|
|
2023-10-30 11:48:38 -04:00
|
|
|
AUTH_DEVICE_DELETE = 'authDevice.delete',
|
|
|
|
|
|
2023-08-15 19:02:38 +03:00
|
|
|
ARCHIVE_READ = 'archive.read',
|
|
|
|
|
|
2023-09-20 13:16:33 +02:00
|
|
|
TIMELINE_READ = 'timeline.read',
|
|
|
|
|
TIMELINE_DOWNLOAD = 'timeline.download',
|
|
|
|
|
|
|
|
|
|
LIBRARY_CREATE = 'library.create',
|
2023-06-28 09:56:24 -04:00
|
|
|
LIBRARY_READ = 'library.read',
|
2023-09-20 13:16:33 +02:00
|
|
|
LIBRARY_UPDATE = 'library.update',
|
|
|
|
|
LIBRARY_DELETE = 'library.delete',
|
2023-06-28 09:56:24 -04:00
|
|
|
LIBRARY_DOWNLOAD = 'library.download',
|
2023-09-18 23:22:44 +02:00
|
|
|
|
|
|
|
|
PERSON_READ = 'person.read',
|
|
|
|
|
PERSON_WRITE = 'person.write',
|
|
|
|
|
PERSON_MERGE = 'person.merge',
|
2023-11-11 15:06:19 -06:00
|
|
|
|
|
|
|
|
PARTNER_UPDATE = 'partner.update',
|
2023-06-28 09:56:24 -04:00
|
|
|
}
|
|
|
|
|
|
2023-10-23 14:37:51 +02:00
|
|
|
let instance: AccessCore | null;
|
|
|
|
|
|
2023-06-28 09:56:24 -04:00
|
|
|
export class AccessCore {
|
2023-10-23 14:37:51 +02:00
|
|
|
private constructor(private repository: IAccessRepository) {}
|
|
|
|
|
|
|
|
|
|
static create(repository: IAccessRepository) {
|
|
|
|
|
if (!instance) {
|
|
|
|
|
instance = new AccessCore(repository);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return instance;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static reset() {
|
|
|
|
|
instance = null;
|
|
|
|
|
}
|
2023-06-28 09:56:24 -04:00
|
|
|
|
2023-07-09 00:37:40 -04:00
|
|
|
requireUploadAccess(authUser: AuthUserDto | null): AuthUserDto {
|
|
|
|
|
if (!authUser || (authUser.isPublicUser && !authUser.isAllowUpload)) {
|
|
|
|
|
throw new UnauthorizedException();
|
|
|
|
|
}
|
|
|
|
|
return authUser;
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-22 23:04:52 -05:00
|
|
|
/**
|
|
|
|
|
* Check if user has access to all ids, for the given permission.
|
|
|
|
|
* Throws error if user does not have access to any of the ids.
|
|
|
|
|
*/
|
2023-06-28 09:56:24 -04:00
|
|
|
async requirePermission(authUser: AuthUserDto, permission: Permission, ids: string[] | string) {
|
2023-11-22 23:04:52 -05:00
|
|
|
ids = Array.isArray(ids) ? ids : [ids];
|
|
|
|
|
const allowedIds = await this.checkAccess(authUser, permission, ids);
|
|
|
|
|
if (new Set(ids).size !== allowedIds.size) {
|
2023-06-28 09:56:24 -04:00
|
|
|
throw new BadRequestException(`Not found or no ${permission} access`);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-22 23:04:52 -05:00
|
|
|
/**
|
|
|
|
|
* Return ids that user has access to, for the given permission.
|
|
|
|
|
* Check is done for each id, and only allowed ids are returned.
|
|
|
|
|
*
|
|
|
|
|
* @returns Set<string>
|
|
|
|
|
*/
|
|
|
|
|
async checkAccess(authUser: AuthUserDto, permission: Permission, ids: Set<string> | string[]) {
|
|
|
|
|
const idSet = Array.isArray(ids) ? new Set(ids) : ids;
|
|
|
|
|
if (idSet.size === 0) {
|
|
|
|
|
return new Set();
|
2023-08-01 21:29:14 -04:00
|
|
|
}
|
2023-06-28 09:56:24 -04:00
|
|
|
|
|
|
|
|
const isSharedLink = authUser.isPublicUser ?? false;
|
2023-11-22 23:04:52 -05:00
|
|
|
return isSharedLink
|
|
|
|
|
? await this.checkAccessSharedLink(authUser, permission, idSet)
|
|
|
|
|
: await this.checkAccessOther(authUser, permission, idSet);
|
|
|
|
|
}
|
2023-06-28 09:56:24 -04:00
|
|
|
|
2023-11-22 23:04:52 -05:00
|
|
|
private async checkAccessSharedLink(authUser: AuthUserDto, permission: Permission, ids: Set<string>) {
|
|
|
|
|
const allowedIds = new Set();
|
2023-06-28 09:56:24 -04:00
|
|
|
for (const id of ids) {
|
2023-11-22 23:04:52 -05:00
|
|
|
const hasAccess = await this.hasSharedLinkAccess(authUser, permission, id);
|
|
|
|
|
if (hasAccess) {
|
|
|
|
|
allowedIds.add(id);
|
2023-06-28 09:56:24 -04:00
|
|
|
}
|
|
|
|
|
}
|
2023-11-22 23:04:52 -05:00
|
|
|
return allowedIds;
|
2023-06-28 09:56:24 -04:00
|
|
|
}
|
|
|
|
|
|
2023-11-22 23:04:52 -05:00
|
|
|
// TODO: Migrate logic to checkAccessSharedLink to evaluate permissions in bulk.
|
2023-06-28 09:56:24 -04:00
|
|
|
private async hasSharedLinkAccess(authUser: AuthUserDto, permission: Permission, id: string) {
|
|
|
|
|
const sharedLinkId = authUser.sharedLinkId;
|
|
|
|
|
if (!sharedLinkId) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (permission) {
|
|
|
|
|
case Permission.ASSET_READ:
|
|
|
|
|
return this.repository.asset.hasSharedLinkAccess(sharedLinkId, id);
|
|
|
|
|
|
|
|
|
|
case Permission.ASSET_VIEW:
|
|
|
|
|
return await this.repository.asset.hasSharedLinkAccess(sharedLinkId, id);
|
|
|
|
|
|
|
|
|
|
case Permission.ASSET_DOWNLOAD:
|
|
|
|
|
return !!authUser.isAllowDownload && (await this.repository.asset.hasSharedLinkAccess(sharedLinkId, id));
|
|
|
|
|
|
2023-10-03 18:36:51 +02:00
|
|
|
case Permission.ASSET_UPLOAD:
|
|
|
|
|
return authUser.isAllowUpload;
|
|
|
|
|
|
2023-06-28 09:56:24 -04:00
|
|
|
case Permission.ASSET_SHARE:
|
|
|
|
|
// TODO: fix this to not use authUser.id for shared link access control
|
|
|
|
|
return this.repository.asset.hasOwnerAccess(authUser.id, id);
|
|
|
|
|
|
2023-08-01 21:29:14 -04:00
|
|
|
case Permission.ALBUM_READ:
|
|
|
|
|
return this.repository.album.hasSharedLinkAccess(sharedLinkId, id);
|
2023-06-30 12:24:28 -04:00
|
|
|
|
2023-08-01 21:29:14 -04:00
|
|
|
case Permission.ALBUM_DOWNLOAD:
|
|
|
|
|
return !!authUser.isAllowDownload && (await this.repository.album.hasSharedLinkAccess(sharedLinkId, id));
|
2023-06-28 09:56:24 -04:00
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-22 23:04:52 -05:00
|
|
|
private async checkAccessOther(authUser: AuthUserDto, permission: Permission, ids: Set<string>) {
|
|
|
|
|
const allowedIds = new Set();
|
|
|
|
|
for (const id of ids) {
|
|
|
|
|
const hasAccess = await this.hasOtherAccess(authUser, permission, id);
|
|
|
|
|
if (hasAccess) {
|
|
|
|
|
allowedIds.add(id);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return allowedIds;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO: Migrate logic to checkAccessOther to evaluate permissions in bulk.
|
2023-06-28 09:56:24 -04:00
|
|
|
private async hasOtherAccess(authUser: AuthUserDto, permission: Permission, id: string) {
|
|
|
|
|
switch (permission) {
|
2023-11-01 04:13:34 +01:00
|
|
|
// uses album id
|
|
|
|
|
case Permission.ACTIVITY_CREATE:
|
2023-11-07 05:37:21 +01:00
|
|
|
return await this.repository.activity.hasCreateAccess(authUser.id, id);
|
2023-11-01 04:13:34 +01:00
|
|
|
|
|
|
|
|
// uses activity id
|
|
|
|
|
case Permission.ACTIVITY_DELETE:
|
|
|
|
|
return (
|
|
|
|
|
(await this.repository.activity.hasOwnerAccess(authUser.id, id)) ||
|
|
|
|
|
(await this.repository.activity.hasAlbumOwnerAccess(authUser.id, id))
|
|
|
|
|
);
|
|
|
|
|
|
2023-06-28 09:56:24 -04:00
|
|
|
case Permission.ASSET_READ:
|
|
|
|
|
return (
|
|
|
|
|
(await this.repository.asset.hasOwnerAccess(authUser.id, id)) ||
|
|
|
|
|
(await this.repository.asset.hasAlbumAccess(authUser.id, id)) ||
|
|
|
|
|
(await this.repository.asset.hasPartnerAccess(authUser.id, id))
|
|
|
|
|
);
|
|
|
|
|
case Permission.ASSET_UPDATE:
|
|
|
|
|
return this.repository.asset.hasOwnerAccess(authUser.id, id);
|
|
|
|
|
|
|
|
|
|
case Permission.ASSET_DELETE:
|
|
|
|
|
return this.repository.asset.hasOwnerAccess(authUser.id, id);
|
|
|
|
|
|
2023-10-06 07:01:14 +00:00
|
|
|
case Permission.ASSET_RESTORE:
|
|
|
|
|
return this.repository.asset.hasOwnerAccess(authUser.id, id);
|
|
|
|
|
|
2023-06-28 09:56:24 -04:00
|
|
|
case Permission.ASSET_SHARE:
|
|
|
|
|
return (
|
|
|
|
|
(await this.repository.asset.hasOwnerAccess(authUser.id, id)) ||
|
|
|
|
|
(await this.repository.asset.hasPartnerAccess(authUser.id, id))
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
case Permission.ASSET_VIEW:
|
|
|
|
|
return (
|
|
|
|
|
(await this.repository.asset.hasOwnerAccess(authUser.id, id)) ||
|
|
|
|
|
(await this.repository.asset.hasAlbumAccess(authUser.id, id)) ||
|
|
|
|
|
(await this.repository.asset.hasPartnerAccess(authUser.id, id))
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
case Permission.ASSET_DOWNLOAD:
|
|
|
|
|
return (
|
|
|
|
|
(await this.repository.asset.hasOwnerAccess(authUser.id, id)) ||
|
|
|
|
|
(await this.repository.asset.hasAlbumAccess(authUser.id, id)) ||
|
|
|
|
|
(await this.repository.asset.hasPartnerAccess(authUser.id, id))
|
|
|
|
|
);
|
|
|
|
|
|
2023-08-01 21:29:14 -04:00
|
|
|
case Permission.ALBUM_READ:
|
|
|
|
|
return (
|
|
|
|
|
(await this.repository.album.hasOwnerAccess(authUser.id, id)) ||
|
|
|
|
|
(await this.repository.album.hasSharedAlbumAccess(authUser.id, id))
|
|
|
|
|
);
|
2023-06-28 09:56:24 -04:00
|
|
|
|
|
|
|
|
case Permission.ALBUM_UPDATE:
|
|
|
|
|
return this.repository.album.hasOwnerAccess(authUser.id, id);
|
|
|
|
|
|
|
|
|
|
case Permission.ALBUM_DELETE:
|
|
|
|
|
return this.repository.album.hasOwnerAccess(authUser.id, id);
|
|
|
|
|
|
|
|
|
|
case Permission.ALBUM_SHARE:
|
|
|
|
|
return this.repository.album.hasOwnerAccess(authUser.id, id);
|
|
|
|
|
|
2023-06-30 12:24:28 -04:00
|
|
|
case Permission.ALBUM_DOWNLOAD:
|
|
|
|
|
return (
|
|
|
|
|
(await this.repository.album.hasOwnerAccess(authUser.id, id)) ||
|
|
|
|
|
(await this.repository.album.hasSharedAlbumAccess(authUser.id, id))
|
|
|
|
|
);
|
|
|
|
|
|
2023-10-03 18:36:51 +02:00
|
|
|
case Permission.ASSET_UPLOAD:
|
|
|
|
|
return this.repository.library.hasOwnerAccess(authUser.id, id);
|
|
|
|
|
|
2023-08-01 21:29:14 -04:00
|
|
|
case Permission.ALBUM_REMOVE_ASSET:
|
|
|
|
|
return this.repository.album.hasOwnerAccess(authUser.id, id);
|
|
|
|
|
|
2023-08-15 19:02:38 +03:00
|
|
|
case Permission.ARCHIVE_READ:
|
|
|
|
|
return authUser.id === id;
|
|
|
|
|
|
2023-10-30 11:48:38 -04:00
|
|
|
case Permission.AUTH_DEVICE_DELETE:
|
|
|
|
|
return this.repository.authDevice.hasOwnerAccess(authUser.id, id);
|
|
|
|
|
|
2023-09-20 13:16:33 +02:00
|
|
|
case Permission.TIMELINE_READ:
|
|
|
|
|
return authUser.id === id || (await this.repository.timeline.hasPartnerAccess(authUser.id, id));
|
2023-06-28 09:56:24 -04:00
|
|
|
|
2023-09-20 13:16:33 +02:00
|
|
|
case Permission.TIMELINE_DOWNLOAD:
|
2023-06-28 09:56:24 -04:00
|
|
|
return authUser.id === id;
|
|
|
|
|
|
2023-09-20 13:16:33 +02:00
|
|
|
case Permission.LIBRARY_READ:
|
|
|
|
|
return (
|
|
|
|
|
(await this.repository.library.hasOwnerAccess(authUser.id, id)) ||
|
|
|
|
|
(await this.repository.library.hasPartnerAccess(authUser.id, id))
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
case Permission.LIBRARY_UPDATE:
|
|
|
|
|
return this.repository.library.hasOwnerAccess(authUser.id, id);
|
|
|
|
|
|
|
|
|
|
case Permission.LIBRARY_DELETE:
|
|
|
|
|
return this.repository.library.hasOwnerAccess(authUser.id, id);
|
|
|
|
|
|
2023-09-18 23:22:44 +02:00
|
|
|
case Permission.PERSON_READ:
|
|
|
|
|
return this.repository.person.hasOwnerAccess(authUser.id, id);
|
|
|
|
|
|
|
|
|
|
case Permission.PERSON_WRITE:
|
|
|
|
|
return this.repository.person.hasOwnerAccess(authUser.id, id);
|
|
|
|
|
|
|
|
|
|
case Permission.PERSON_MERGE:
|
|
|
|
|
return this.repository.person.hasOwnerAccess(authUser.id, id);
|
|
|
|
|
|
2023-11-11 15:06:19 -06:00
|
|
|
case Permission.PARTNER_UPDATE:
|
|
|
|
|
return this.repository.partner.hasUpdateAccess(authUser.id, id);
|
|
|
|
|
|
2023-08-01 21:29:14 -04:00
|
|
|
default:
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2023-06-28 09:56:24 -04:00
|
|
|
}
|
|
|
|
|
}
|