1
0
mirror of https://github.com/immich-app/immich.git synced 2025-01-01 11:37:06 +02:00

refactor(server): add base methods for access checks (#13349)

This commit is contained in:
Jason Rasmussen 2024-10-10 11:53:53 -04:00 committed by GitHub
parent 97edf90889
commit 8daa8073ae
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 84 additions and 90 deletions

View File

@ -14,12 +14,11 @@ import { AuthDto } from 'src/dtos/auth.dto';
import { ActivityEntity } from 'src/entities/activity.entity';
import { Permission } from 'src/enum';
import { BaseService } from 'src/services/base.service';
import { requireAccess } from 'src/utils/access';
@Injectable()
export class ActivityService extends BaseService {
async getAll(auth: AuthDto, dto: ActivitySearchDto): Promise<ActivityResponseDto[]> {
await requireAccess(this.accessRepository, { auth, permission: Permission.ALBUM_READ, ids: [dto.albumId] });
await this.requireAccess({ auth, permission: Permission.ALBUM_READ, ids: [dto.albumId] });
const activities = await this.activityRepository.search({
userId: dto.userId,
albumId: dto.albumId,
@ -31,12 +30,12 @@ export class ActivityService extends BaseService {
}
async getStatistics(auth: AuthDto, dto: ActivityDto): Promise<ActivityStatisticsResponseDto> {
await requireAccess(this.accessRepository, { auth, permission: Permission.ALBUM_READ, ids: [dto.albumId] });
await this.requireAccess({ auth, permission: Permission.ALBUM_READ, ids: [dto.albumId] });
return { comments: await this.activityRepository.getStatistics(dto.assetId, dto.albumId) };
}
async create(auth: AuthDto, dto: ActivityCreateDto): Promise<MaybeDuplicate<ActivityResponseDto>> {
await requireAccess(this.accessRepository, { auth, permission: Permission.ACTIVITY_CREATE, ids: [dto.albumId] });
await this.requireAccess({ auth, permission: Permission.ACTIVITY_CREATE, ids: [dto.albumId] });
const common = {
userId: auth.user.id,
@ -70,7 +69,7 @@ export class ActivityService extends BaseService {
}
async delete(auth: AuthDto, id: string): Promise<void> {
await requireAccess(this.accessRepository, { auth, permission: Permission.ACTIVITY_DELETE, ids: [id] });
await this.requireAccess({ auth, permission: Permission.ACTIVITY_DELETE, ids: [id] });
await this.activityRepository.delete(id);
}
}

View File

@ -19,7 +19,6 @@ import { AssetEntity } from 'src/entities/asset.entity';
import { Permission } from 'src/enum';
import { AlbumAssetCount, AlbumInfoOptions } from 'src/interfaces/album.interface';
import { BaseService } from 'src/services/base.service';
import { checkAccess, requireAccess } from 'src/utils/access';
import { addAssets, removeAssets } from 'src/utils/asset.util';
@Injectable()
@ -82,7 +81,7 @@ export class AlbumService extends BaseService {
}
async get(auth: AuthDto, id: string, dto: AlbumInfoDto): Promise<AlbumResponseDto> {
await requireAccess(this.accessRepository, { auth, permission: Permission.ALBUM_READ, ids: [id] });
await this.requireAccess({ auth, permission: Permission.ALBUM_READ, ids: [id] });
await this.albumRepository.updateThumbnails();
const withAssets = dto.withoutAssets === undefined ? true : !dto.withoutAssets;
const album = await this.findOrFail(id, { withAssets });
@ -106,7 +105,7 @@ export class AlbumService extends BaseService {
}
}
const allowedAssetIdsSet = await checkAccess(this.accessRepository, {
const allowedAssetIdsSet = await this.checkAccess({
auth,
permission: Permission.ASSET_SHARE,
ids: dto.assetIds || [],
@ -130,7 +129,7 @@ export class AlbumService extends BaseService {
}
async update(auth: AuthDto, id: string, dto: UpdateAlbumDto): Promise<AlbumResponseDto> {
await requireAccess(this.accessRepository, { auth, permission: Permission.ALBUM_UPDATE, ids: [id] });
await this.requireAccess({ auth, permission: Permission.ALBUM_UPDATE, ids: [id] });
const album = await this.findOrFail(id, { withAssets: true });
@ -153,13 +152,13 @@ export class AlbumService extends BaseService {
}
async delete(auth: AuthDto, id: string): Promise<void> {
await requireAccess(this.accessRepository, { auth, permission: Permission.ALBUM_DELETE, ids: [id] });
await this.requireAccess({ auth, permission: Permission.ALBUM_DELETE, ids: [id] });
await this.albumRepository.delete(id);
}
async addAssets(auth: AuthDto, id: string, dto: BulkIdsDto): Promise<BulkIdResponseDto[]> {
const album = await this.findOrFail(id, { withAssets: false });
await requireAccess(this.accessRepository, { auth, permission: Permission.ALBUM_ADD_ASSET, ids: [id] });
await this.requireAccess({ auth, permission: Permission.ALBUM_ADD_ASSET, ids: [id] });
const results = await addAssets(
auth,
@ -182,7 +181,7 @@ export class AlbumService extends BaseService {
}
async removeAssets(auth: AuthDto, id: string, dto: BulkIdsDto): Promise<BulkIdResponseDto[]> {
await requireAccess(this.accessRepository, { auth, permission: Permission.ALBUM_REMOVE_ASSET, ids: [id] });
await this.requireAccess({ auth, permission: Permission.ALBUM_REMOVE_ASSET, ids: [id] });
const album = await this.findOrFail(id, { withAssets: false });
const results = await removeAssets(
@ -203,7 +202,7 @@ export class AlbumService extends BaseService {
}
async addUsers(auth: AuthDto, id: string, { albumUsers }: AddUsersDto): Promise<AlbumResponseDto> {
await requireAccess(this.accessRepository, { auth, permission: Permission.ALBUM_SHARE, ids: [id] });
await this.requireAccess({ auth, permission: Permission.ALBUM_SHARE, ids: [id] });
const album = await this.findOrFail(id, { withAssets: false });
@ -247,14 +246,14 @@ export class AlbumService extends BaseService {
// non-admin can remove themselves
if (auth.user.id !== userId) {
await requireAccess(this.accessRepository, { auth, permission: Permission.ALBUM_SHARE, ids: [id] });
await this.requireAccess({ auth, permission: Permission.ALBUM_SHARE, ids: [id] });
}
await this.albumUserRepository.delete({ albumId: id, userId });
}
async updateUser(auth: AuthDto, id: string, userId: string, dto: Partial<AlbumUserEntity>): Promise<void> {
await requireAccess(this.accessRepository, { auth, permission: Permission.ALBUM_SHARE, ids: [id] });
await this.requireAccess({ auth, permission: Permission.ALBUM_SHARE, ids: [id] });
await this.albumUserRepository.update({ albumId: id, userId }, { role: dto.role });
}

View File

@ -24,7 +24,7 @@ import { ASSET_CHECKSUM_CONSTRAINT, AssetEntity } from 'src/entities/asset.entit
import { AssetStatus, AssetType, CacheControl, Permission, StorageFolder } from 'src/enum';
import { JobName } from 'src/interfaces/job.interface';
import { BaseService } from 'src/services/base.service';
import { requireAccess, requireUploadAccess } from 'src/utils/access';
import { requireUploadAccess } from 'src/utils/access';
import { getAssetFiles, onBeforeLink } from 'src/utils/asset.util';
import { ImmichFileResponse } from 'src/utils/file';
import { mimeTypes } from 'src/utils/mime-types';
@ -125,7 +125,7 @@ export class AssetMediaService extends BaseService {
sidecarFile?: UploadFile,
): Promise<AssetMediaResponseDto> {
try {
await requireAccess(this.accessRepository, {
await this.requireAccess({
auth,
permission: Permission.ASSET_UPLOAD,
// do not need an id here, but the interface requires it
@ -159,7 +159,7 @@ export class AssetMediaService extends BaseService {
sidecarFile?: UploadFile,
): Promise<AssetMediaResponseDto> {
try {
await requireAccess(this.accessRepository, { auth, permission: Permission.ASSET_UPDATE, ids: [id] });
await this.requireAccess({ auth, permission: Permission.ASSET_UPDATE, ids: [id] });
const asset = (await this.assetRepository.getById(id)) as AssetEntity;
this.requireQuota(auth, file.size);
@ -182,7 +182,7 @@ export class AssetMediaService extends BaseService {
}
async downloadOriginal(auth: AuthDto, id: string): Promise<ImmichFileResponse> {
await requireAccess(this.accessRepository, { auth, permission: Permission.ASSET_DOWNLOAD, ids: [id] });
await this.requireAccess({ auth, permission: Permission.ASSET_DOWNLOAD, ids: [id] });
const asset = await this.findOrFail(id);
@ -194,7 +194,7 @@ export class AssetMediaService extends BaseService {
}
async viewThumbnail(auth: AuthDto, id: string, dto: AssetMediaOptionsDto): Promise<ImmichFileResponse> {
await requireAccess(this.accessRepository, { auth, permission: Permission.ASSET_VIEW, ids: [id] });
await this.requireAccess({ auth, permission: Permission.ASSET_VIEW, ids: [id] });
const asset = await this.findOrFail(id);
const size = dto.size ?? AssetMediaSize.THUMBNAIL;
@ -217,7 +217,7 @@ export class AssetMediaService extends BaseService {
}
async playbackVideo(auth: AuthDto, id: string): Promise<ImmichFileResponse> {
await requireAccess(this.accessRepository, { auth, permission: Permission.ASSET_VIEW, ids: [id] });
await this.requireAccess({ auth, permission: Permission.ASSET_VIEW, ids: [id] });
const asset = await this.findOrFail(id);

View File

@ -29,7 +29,6 @@ import {
JobStatus,
} from 'src/interfaces/job.interface';
import { BaseService } from 'src/services/base.service';
import { requireAccess } from 'src/utils/access';
import { getAssetFiles, getMyPartnerIds, onAfterUnlink, onBeforeLink, onBeforeUnlink } from 'src/utils/asset.util';
import { usePagination } from 'src/utils/pagination';
@ -86,7 +85,7 @@ export class AssetService extends BaseService {
}
async get(auth: AuthDto, id: string): Promise<AssetResponseDto | SanitizedAssetResponseDto> {
await requireAccess(this.accessRepository, { auth, permission: Permission.ASSET_READ, ids: [id] });
await this.requireAccess({ auth, permission: Permission.ASSET_READ, ids: [id] });
const asset = await this.assetRepository.getById(
id,
@ -135,7 +134,7 @@ export class AssetService extends BaseService {
}
async update(auth: AuthDto, id: string, dto: UpdateAssetDto): Promise<AssetResponseDto> {
await requireAccess(this.accessRepository, { auth, permission: Permission.ASSET_UPDATE, ids: [id] });
await this.requireAccess({ auth, permission: Permission.ASSET_UPDATE, ids: [id] });
const { description, dateTimeOriginal, latitude, longitude, rating, ...rest } = dto;
const repos = { asset: this.assetRepository, event: this.eventRepository };
@ -178,7 +177,7 @@ export class AssetService extends BaseService {
async updateAll(auth: AuthDto, dto: AssetBulkUpdateDto): Promise<void> {
const { ids, dateTimeOriginal, latitude, longitude, ...options } = dto;
await requireAccess(this.accessRepository, { auth, permission: Permission.ASSET_UPDATE, ids });
await this.requireAccess({ auth, permission: Permission.ASSET_UPDATE, ids });
for (const id of ids) {
await this.updateMetadata({ id, dateTimeOriginal, latitude, longitude });
@ -275,7 +274,7 @@ export class AssetService extends BaseService {
async deleteAll(auth: AuthDto, dto: AssetBulkDeleteDto): Promise<void> {
const { ids, force } = dto;
await requireAccess(this.accessRepository, { auth, permission: Permission.ASSET_DELETE, ids });
await this.requireAccess({ auth, permission: Permission.ASSET_DELETE, ids });
await this.assetRepository.updateAll(ids, {
deletedAt: new Date(),
status: force ? AssetStatus.DELETED : AssetStatus.TRASHED,
@ -284,7 +283,7 @@ export class AssetService extends BaseService {
}
async run(auth: AuthDto, dto: AssetJobsDto) {
await requireAccess(this.accessRepository, { auth, permission: Permission.ASSET_UPDATE, ids: dto.assetIds });
await this.requireAccess({ auth, permission: Permission.ASSET_UPDATE, ids: dto.assetIds });
const jobs: JobItem[] = [];

View File

@ -23,7 +23,6 @@ import {
} from 'src/enum';
import { JOBS_ASSET_PAGINATION_SIZE, JobStatus } from 'src/interfaces/job.interface';
import { BaseService } from 'src/services/base.service';
import { requireAccess } from 'src/utils/access';
import { getAssetFiles } from 'src/utils/asset.util';
import { usePagination } from 'src/utils/pagination';
@ -36,7 +35,7 @@ export class AuditService extends BaseService {
async getDeletes(auth: AuthDto, dto: AuditDeletesDto): Promise<AuditDeletesResponseDto> {
const userId = dto.userId || auth.user.id;
await requireAccess(this.accessRepository, { auth, permission: Permission.TIMELINE_READ, ids: [userId] });
await this.requireAccess({ auth, permission: Permission.TIMELINE_READ, ids: [userId] });
const audits = await this.auditRepository.getAfter(dto.after, {
userIds: [userId],

View File

@ -38,6 +38,7 @@ import { ITrashRepository } from 'src/interfaces/trash.interface';
import { IUserRepository } from 'src/interfaces/user.interface';
import { IVersionHistoryRepository } from 'src/interfaces/version-history.interface';
import { IViewRepository } from 'src/interfaces/view.interface';
import { AccessRequest, checkAccess, requireAccess } from 'src/utils/access';
import { getConfig, updateConfig } from 'src/utils/config';
export class BaseService {
@ -95,7 +96,7 @@ export class BaseService {
);
}
private get repos() {
private get configRepos() {
return {
configRepo: this.configRepository,
metadataRepo: this.systemMetadataRepository,
@ -104,10 +105,18 @@ export class BaseService {
}
getConfig(options: { withCache: boolean }) {
return getConfig(this.repos, options);
return getConfig(this.configRepos, options);
}
updateConfig(newConfig: SystemConfig) {
return updateConfig(this.repos, newConfig);
return updateConfig(this.configRepos, newConfig);
}
requireAccess(request: AccessRequest) {
return requireAccess(this.accessRepository, request);
}
checkAccess(request: AccessRequest) {
return checkAccess(this.accessRepository, request);
}
}

View File

@ -8,7 +8,6 @@ import { AssetEntity } from 'src/entities/asset.entity';
import { Permission } from 'src/enum';
import { ImmichReadStream } from 'src/interfaces/storage.interface';
import { BaseService } from 'src/services/base.service';
import { requireAccess } from 'src/utils/access';
import { HumanReadableSize } from 'src/utils/bytes';
import { usePagination } from 'src/utils/pagination';
import { getPreferences } from 'src/utils/preferences';
@ -62,7 +61,7 @@ export class DownloadService extends BaseService {
}
async downloadArchive(auth: AuthDto, dto: AssetIdsDto): Promise<ImmichReadStream> {
await requireAccess(this.accessRepository, { auth, permission: Permission.ASSET_DOWNLOAD, ids: dto.assetIds });
await this.requireAccess({ auth, permission: Permission.ASSET_DOWNLOAD, ids: dto.assetIds });
const zip = this.storageRepository.createZipStream();
const assets = await this.assetRepository.getByIds(dto.assetIds);
@ -105,20 +104,20 @@ export class DownloadService extends BaseService {
if (dto.assetIds) {
const assetIds = dto.assetIds;
await requireAccess(this.accessRepository, { auth, permission: Permission.ASSET_DOWNLOAD, ids: assetIds });
await this.requireAccess({ auth, permission: Permission.ASSET_DOWNLOAD, ids: assetIds });
const assets = await this.assetRepository.getByIds(assetIds, { exifInfo: true });
return usePagination(PAGINATION_SIZE, () => ({ hasNextPage: false, items: assets }));
}
if (dto.albumId) {
const albumId = dto.albumId;
await requireAccess(this.accessRepository, { auth, permission: Permission.ALBUM_DOWNLOAD, ids: [albumId] });
await this.requireAccess({ auth, permission: Permission.ALBUM_DOWNLOAD, ids: [albumId] });
return usePagination(PAGINATION_SIZE, (pagination) => this.assetRepository.getByAlbumId(pagination, albumId));
}
if (dto.userId) {
const userId = dto.userId;
await requireAccess(this.accessRepository, { auth, permission: Permission.TIMELINE_DOWNLOAD, ids: [userId] });
await this.requireAccess({ auth, permission: Permission.TIMELINE_DOWNLOAD, ids: [userId] });
return usePagination(PAGINATION_SIZE, (pagination) =>
this.assetRepository.getByUserId(pagination, userId, { isVisible: true }),
);

View File

@ -5,7 +5,6 @@ import { MemoryCreateDto, MemoryResponseDto, MemoryUpdateDto, mapMemory } from '
import { AssetEntity } from 'src/entities/asset.entity';
import { Permission } from 'src/enum';
import { BaseService } from 'src/services/base.service';
import { checkAccess, requireAccess } from 'src/utils/access';
import { addAssets, removeAssets } from 'src/utils/asset.util';
@Injectable()
@ -16,7 +15,7 @@ export class MemoryService extends BaseService {
}
async get(auth: AuthDto, id: string): Promise<MemoryResponseDto> {
await requireAccess(this.accessRepository, { auth, permission: Permission.MEMORY_READ, ids: [id] });
await this.requireAccess({ auth, permission: Permission.MEMORY_READ, ids: [id] });
const memory = await this.findOrFail(id);
return mapMemory(memory);
}
@ -25,7 +24,7 @@ export class MemoryService extends BaseService {
// TODO validate type/data combination
const assetIds = dto.assetIds || [];
const allowedAssetIds = await checkAccess(this.accessRepository, {
const allowedAssetIds = await this.checkAccess({
auth,
permission: Permission.ASSET_SHARE,
ids: assetIds,
@ -44,7 +43,7 @@ export class MemoryService extends BaseService {
}
async update(auth: AuthDto, id: string, dto: MemoryUpdateDto): Promise<MemoryResponseDto> {
await requireAccess(this.accessRepository, { auth, permission: Permission.MEMORY_UPDATE, ids: [id] });
await this.requireAccess({ auth, permission: Permission.MEMORY_UPDATE, ids: [id] });
const memory = await this.memoryRepository.update({
id,
@ -57,12 +56,12 @@ export class MemoryService extends BaseService {
}
async remove(auth: AuthDto, id: string): Promise<void> {
await requireAccess(this.accessRepository, { auth, permission: Permission.MEMORY_DELETE, ids: [id] });
await this.requireAccess({ auth, permission: Permission.MEMORY_DELETE, ids: [id] });
await this.memoryRepository.delete(id);
}
async addAssets(auth: AuthDto, id: string, dto: BulkIdsDto): Promise<BulkIdResponseDto[]> {
await requireAccess(this.accessRepository, { auth, permission: Permission.MEMORY_READ, ids: [id] });
await this.requireAccess({ auth, permission: Permission.MEMORY_READ, ids: [id] });
const repos = { access: this.accessRepository, bulk: this.memoryRepository };
const results = await addAssets(auth, repos, { parentId: id, assetIds: dto.ids });
@ -76,7 +75,7 @@ export class MemoryService extends BaseService {
}
async removeAssets(auth: AuthDto, id: string, dto: BulkIdsDto): Promise<BulkIdResponseDto[]> {
await requireAccess(this.accessRepository, { auth, permission: Permission.MEMORY_UPDATE, ids: [id] });
await this.requireAccess({ auth, permission: Permission.MEMORY_UPDATE, ids: [id] });
const repos = { access: this.accessRepository, bulk: this.memoryRepository };
const results = await removeAssets(auth, repos, {

View File

@ -6,7 +6,6 @@ import { PartnerEntity } from 'src/entities/partner.entity';
import { Permission } from 'src/enum';
import { PartnerDirection, PartnerIds } from 'src/interfaces/partner.interface';
import { BaseService } from 'src/services/base.service';
import { requireAccess } from 'src/utils/access';
@Injectable()
export class PartnerService extends BaseService {
@ -41,7 +40,7 @@ export class PartnerService extends BaseService {
}
async update(auth: AuthDto, sharedById: string, dto: UpdatePartnerDto): Promise<PartnerResponseDto> {
await requireAccess(this.accessRepository, { auth, permission: Permission.PARTNER_UPDATE, ids: [sharedById] });
await this.requireAccess({ auth, permission: Permission.PARTNER_UPDATE, ids: [sharedById] });
const partnerId: PartnerIds = { sharedById, sharedWithId: auth.user.id };
const entity = await this.partnerRepository.update({ ...partnerId, inTimeline: dto.inTimeline });

View File

@ -47,7 +47,6 @@ import { BoundingBox } from 'src/interfaces/machine-learning.interface';
import { CropOptions, ImageDimensions, InputDimensions } from 'src/interfaces/media.interface';
import { UpdateFacesData } from 'src/interfaces/person.interface';
import { BaseService } from 'src/services/base.service';
import { checkAccess, requireAccess } from 'src/utils/access';
import { getAssetFiles } from 'src/utils/asset.util';
import { ImmichFileResponse } from 'src/utils/file';
import { mimeTypes } from 'src/utils/mime-types';
@ -80,7 +79,7 @@ export class PersonService extends BaseService {
}
async reassignFaces(auth: AuthDto, personId: string, dto: AssetFaceUpdateDto): Promise<PersonResponseDto[]> {
await requireAccess(this.accessRepository, { auth, permission: Permission.PERSON_UPDATE, ids: [personId] });
await this.requireAccess({ auth, permission: Permission.PERSON_UPDATE, ids: [personId] });
const person = await this.findOrFail(personId);
const result: PersonResponseDto[] = [];
const changeFeaturePhoto: string[] = [];
@ -88,7 +87,7 @@ export class PersonService extends BaseService {
const faces = await this.personRepository.getFacesByIds([{ personId: data.personId, assetId: data.assetId }]);
for (const face of faces) {
await requireAccess(this.accessRepository, { auth, permission: Permission.PERSON_CREATE, ids: [face.id] });
await this.requireAccess({ auth, permission: Permission.PERSON_CREATE, ids: [face.id] });
if (person.faceAssetId === null) {
changeFeaturePhoto.push(person.id);
}
@ -109,8 +108,8 @@ export class PersonService extends BaseService {
}
async reassignFacesById(auth: AuthDto, personId: string, dto: FaceDto): Promise<PersonResponseDto> {
await requireAccess(this.accessRepository, { auth, permission: Permission.PERSON_UPDATE, ids: [personId] });
await requireAccess(this.accessRepository, { auth, permission: Permission.PERSON_CREATE, ids: [dto.id] });
await this.requireAccess({ auth, permission: Permission.PERSON_UPDATE, ids: [personId] });
await this.requireAccess({ auth, permission: Permission.PERSON_CREATE, ids: [dto.id] });
const face = await this.personRepository.getFaceById(dto.id);
const person = await this.findOrFail(personId);
@ -126,7 +125,7 @@ export class PersonService extends BaseService {
}
async getFacesById(auth: AuthDto, dto: FaceDto): Promise<AssetFaceResponseDto[]> {
await requireAccess(this.accessRepository, { auth, permission: Permission.ASSET_READ, ids: [dto.id] });
await this.requireAccess({ auth, permission: Permission.ASSET_READ, ids: [dto.id] });
const faces = await this.personRepository.getFaces(dto.id);
return faces.map((asset) => mapFaces(asset, auth));
}
@ -150,17 +149,17 @@ export class PersonService extends BaseService {
}
async getById(auth: AuthDto, id: string): Promise<PersonResponseDto> {
await requireAccess(this.accessRepository, { auth, permission: Permission.PERSON_READ, ids: [id] });
await this.requireAccess({ auth, permission: Permission.PERSON_READ, ids: [id] });
return this.findOrFail(id).then(mapPerson);
}
async getStatistics(auth: AuthDto, id: string): Promise<PersonStatisticsResponseDto> {
await requireAccess(this.accessRepository, { auth, permission: Permission.PERSON_READ, ids: [id] });
await this.requireAccess({ auth, permission: Permission.PERSON_READ, ids: [id] });
return this.personRepository.getStatistics(id);
}
async getThumbnail(auth: AuthDto, id: string): Promise<ImmichFileResponse> {
await requireAccess(this.accessRepository, { auth, permission: Permission.PERSON_READ, ids: [id] });
await this.requireAccess({ auth, permission: Permission.PERSON_READ, ids: [id] });
const person = await this.personRepository.getById(id);
if (!person || !person.thumbnailPath) {
throw new NotFoundException();
@ -183,13 +182,13 @@ export class PersonService extends BaseService {
}
async update(auth: AuthDto, id: string, dto: PersonUpdateDto): Promise<PersonResponseDto> {
await requireAccess(this.accessRepository, { auth, permission: Permission.PERSON_UPDATE, ids: [id] });
await this.requireAccess({ auth, permission: Permission.PERSON_UPDATE, ids: [id] });
const { name, birthDate, isHidden, featureFaceAssetId: assetId } = dto;
// TODO: set by faceId directly
let faceId: string | undefined = undefined;
if (assetId) {
await requireAccess(this.accessRepository, { auth, permission: Permission.ASSET_READ, ids: [assetId] });
await this.requireAccess({ auth, permission: Permission.ASSET_READ, ids: [assetId] });
const [face] = await this.personRepository.getFacesByIds([{ personId: id, assetId }]);
if (!face) {
throw new BadRequestException('Invalid assetId for feature face');
@ -584,13 +583,13 @@ export class PersonService extends BaseService {
throw new BadRequestException('Cannot merge a person into themselves');
}
await requireAccess(this.accessRepository, { auth, permission: Permission.PERSON_UPDATE, ids: [id] });
await this.requireAccess({ auth, permission: Permission.PERSON_UPDATE, ids: [id] });
let primaryPerson = await this.findOrFail(id);
const primaryName = primaryPerson.name || primaryPerson.id;
const results: BulkIdResponseDto[] = [];
const allowedIds = await checkAccess(this.accessRepository, {
const allowedIds = await this.checkAccess({
auth,
permission: Permission.PERSON_MERGE,
ids: mergeIds,

View File

@ -5,7 +5,6 @@ import { SessionResponseDto, mapSession } from 'src/dtos/session.dto';
import { Permission } from 'src/enum';
import { JobStatus } from 'src/interfaces/job.interface';
import { BaseService } from 'src/services/base.service';
import { requireAccess } from 'src/utils/access';
@Injectable()
export class SessionService extends BaseService {
@ -34,7 +33,7 @@ export class SessionService extends BaseService {
}
async delete(auth: AuthDto, id: string): Promise<void> {
await requireAccess(this.accessRepository, { auth, permission: Permission.AUTH_DEVICE_DELETE, ids: [id] });
await this.requireAccess({ auth, permission: Permission.AUTH_DEVICE_DELETE, ids: [id] });
await this.sessionRepository.delete(id);
}

View File

@ -15,7 +15,6 @@ import { AssetEntity } from 'src/entities/asset.entity';
import { SharedLinkEntity } from 'src/entities/shared-link.entity';
import { Permission, SharedLinkType } from 'src/enum';
import { BaseService } from 'src/services/base.service';
import { checkAccess, requireAccess } from 'src/utils/access';
import { OpenGraphTags } from 'src/utils/misc';
@Injectable()
@ -49,7 +48,7 @@ export class SharedLinkService extends BaseService {
if (!dto.albumId) {
throw new BadRequestException('Invalid albumId');
}
await requireAccess(this.accessRepository, { auth, permission: Permission.ALBUM_SHARE, ids: [dto.albumId] });
await this.requireAccess({ auth, permission: Permission.ALBUM_SHARE, ids: [dto.albumId] });
break;
}
@ -58,7 +57,7 @@ export class SharedLinkService extends BaseService {
throw new BadRequestException('Invalid assetIds');
}
await requireAccess(this.accessRepository, { auth, permission: Permission.ASSET_SHARE, ids: dto.assetIds });
await this.requireAccess({ auth, permission: Permission.ASSET_SHARE, ids: dto.assetIds });
break;
}
@ -119,7 +118,7 @@ export class SharedLinkService extends BaseService {
const existingAssetIds = new Set(sharedLink.assets.map((asset) => asset.id));
const notPresentAssetIds = dto.assetIds.filter((assetId) => !existingAssetIds.has(assetId));
const allowedAssetIds = await checkAccess(this.accessRepository, {
const allowedAssetIds = await this.checkAccess({
auth,
permission: Permission.ASSET_SHARE,
ids: notPresentAssetIds,

View File

@ -4,7 +4,6 @@ import { AuthDto } from 'src/dtos/auth.dto';
import { StackCreateDto, StackResponseDto, StackSearchDto, StackUpdateDto, mapStack } from 'src/dtos/stack.dto';
import { Permission } from 'src/enum';
import { BaseService } from 'src/services/base.service';
import { requireAccess } from 'src/utils/access';
@Injectable()
export class StackService extends BaseService {
@ -18,7 +17,7 @@ export class StackService extends BaseService {
}
async create(auth: AuthDto, dto: StackCreateDto): Promise<StackResponseDto> {
await requireAccess(this.accessRepository, { auth, permission: Permission.ASSET_UPDATE, ids: dto.assetIds });
await this.requireAccess({ auth, permission: Permission.ASSET_UPDATE, ids: dto.assetIds });
const stack = await this.stackRepository.create({ ownerId: auth.user.id, assetIds: dto.assetIds });
@ -28,13 +27,13 @@ export class StackService extends BaseService {
}
async get(auth: AuthDto, id: string): Promise<StackResponseDto> {
await requireAccess(this.accessRepository, { auth, permission: Permission.STACK_READ, ids: [id] });
await this.requireAccess({ auth, permission: Permission.STACK_READ, ids: [id] });
const stack = await this.findOrFail(id);
return mapStack(stack, { auth });
}
async update(auth: AuthDto, id: string, dto: StackUpdateDto): Promise<StackResponseDto> {
await requireAccess(this.accessRepository, { auth, permission: Permission.STACK_UPDATE, ids: [id] });
await this.requireAccess({ auth, permission: Permission.STACK_UPDATE, ids: [id] });
const stack = await this.findOrFail(id);
if (dto.primaryAssetId && !stack.assets.some(({ id }) => id === dto.primaryAssetId)) {
throw new BadRequestException('Primary asset must be in the stack');
@ -48,13 +47,13 @@ export class StackService extends BaseService {
}
async delete(auth: AuthDto, id: string): Promise<void> {
await requireAccess(this.accessRepository, { auth, permission: Permission.STACK_DELETE, ids: [id] });
await this.requireAccess({ auth, permission: Permission.STACK_DELETE, ids: [id] });
await this.stackRepository.delete(id);
await this.eventRepository.emit('stack.delete', { stackId: id, userId: auth.user.id });
}
async deleteAll(auth: AuthDto, dto: BulkIdsDto): Promise<void> {
await requireAccess(this.accessRepository, { auth, permission: Permission.STACK_DELETE, ids: dto.ids });
await this.requireAccess({ auth, permission: Permission.STACK_DELETE, ids: dto.ids });
await this.stackRepository.deleteAll(dto.ids);
await this.eventRepository.emit('stacks.delete', { stackIds: dto.ids, userId: auth.user.id });
}

View File

@ -5,7 +5,6 @@ import { AuthDto } from 'src/dtos/auth.dto';
import { AssetDeltaSyncDto, AssetDeltaSyncResponseDto, AssetFullSyncDto } from 'src/dtos/sync.dto';
import { DatabaseAction, EntityType, Permission } from 'src/enum';
import { BaseService } from 'src/services/base.service';
import { requireAccess } from 'src/utils/access';
import { getMyPartnerIds } from 'src/utils/asset.util';
import { setIsEqual } from 'src/utils/set';
@ -15,7 +14,7 @@ export class SyncService extends BaseService {
async getFullSync(auth: AuthDto, dto: AssetFullSyncDto): Promise<AssetResponseDto[]> {
// mobile implementation is faster if this is a single id
const userId = dto.userId || auth.user.id;
await requireAccess(this.accessRepository, { auth, permission: Permission.TIMELINE_READ, ids: [userId] });
await this.requireAccess({ auth, permission: Permission.TIMELINE_READ, ids: [userId] });
const assets = await this.assetRepository.getAllForUserFullSync({
ownerId: userId,
updatedUntil: dto.updatedUntil,
@ -39,7 +38,7 @@ export class SyncService extends BaseService {
return FULL_SYNC;
}
await requireAccess(this.accessRepository, { auth, permission: Permission.TIMELINE_READ, ids: dto.userIds });
await this.requireAccess({ auth, permission: Permission.TIMELINE_READ, ids: dto.userIds });
const limit = 10_000;
const upserted = await this.assetRepository.getChangedDeltaSync({ limit, updatedAfter: dto.updatedAfter, userIds });

View File

@ -15,7 +15,6 @@ import { Permission } from 'src/enum';
import { JobStatus } from 'src/interfaces/job.interface';
import { AssetTagItem } from 'src/interfaces/tag.interface';
import { BaseService } from 'src/services/base.service';
import { checkAccess, requireAccess } from 'src/utils/access';
import { addAssets, removeAssets } from 'src/utils/asset.util';
import { upsertTags } from 'src/utils/tag';
@ -27,7 +26,7 @@ export class TagService extends BaseService {
}
async get(auth: AuthDto, id: string): Promise<TagResponseDto> {
await requireAccess(this.accessRepository, { auth, permission: Permission.TAG_READ, ids: [id] });
await this.requireAccess({ auth, permission: Permission.TAG_READ, ids: [id] });
const tag = await this.findOrFail(id);
return mapTag(tag);
}
@ -35,7 +34,7 @@ export class TagService extends BaseService {
async create(auth: AuthDto, dto: TagCreateDto) {
let parent: TagEntity | undefined;
if (dto.parentId) {
await requireAccess(this.accessRepository, { auth, permission: Permission.TAG_READ, ids: [dto.parentId] });
await this.requireAccess({ auth, permission: Permission.TAG_READ, ids: [dto.parentId] });
parent = (await this.tagRepository.get(dto.parentId)) || undefined;
if (!parent) {
throw new BadRequestException('Tag not found');
@ -55,7 +54,7 @@ export class TagService extends BaseService {
}
async update(auth: AuthDto, id: string, dto: TagUpdateDto): Promise<TagResponseDto> {
await requireAccess(this.accessRepository, { auth, permission: Permission.TAG_UPDATE, ids: [id] });
await this.requireAccess({ auth, permission: Permission.TAG_UPDATE, ids: [id] });
const { color } = dto;
const tag = await this.tagRepository.update({ id, color });
@ -68,7 +67,7 @@ export class TagService extends BaseService {
}
async remove(auth: AuthDto, id: string): Promise<void> {
await requireAccess(this.accessRepository, { auth, permission: Permission.TAG_DELETE, ids: [id] });
await this.requireAccess({ auth, permission: Permission.TAG_DELETE, ids: [id] });
// TODO sync tag changes for affected assets
@ -77,8 +76,8 @@ export class TagService extends BaseService {
async bulkTagAssets(auth: AuthDto, dto: TagBulkAssetsDto): Promise<TagBulkAssetsResponseDto> {
const [tagIds, assetIds] = await Promise.all([
checkAccess(this.accessRepository, { auth, permission: Permission.TAG_ASSET, ids: dto.tagIds }),
checkAccess(this.accessRepository, { auth, permission: Permission.ASSET_UPDATE, ids: dto.assetIds }),
this.checkAccess({ auth, permission: Permission.TAG_ASSET, ids: dto.tagIds }),
this.checkAccess({ auth, permission: Permission.ASSET_UPDATE, ids: dto.assetIds }),
]);
const items: AssetTagItem[] = [];
@ -97,7 +96,7 @@ export class TagService extends BaseService {
}
async addAssets(auth: AuthDto, id: string, dto: BulkIdsDto): Promise<BulkIdResponseDto[]> {
await requireAccess(this.accessRepository, { auth, permission: Permission.TAG_ASSET, ids: [id] });
await this.requireAccess({ auth, permission: Permission.TAG_ASSET, ids: [id] });
const results = await addAssets(
auth,
@ -115,7 +114,7 @@ export class TagService extends BaseService {
}
async removeAssets(auth: AuthDto, id: string, dto: BulkIdsDto): Promise<BulkIdResponseDto[]> {
await requireAccess(this.accessRepository, { auth, permission: Permission.TAG_ASSET, ids: [id] });
await this.requireAccess({ auth, permission: Permission.TAG_ASSET, ids: [id] });
const results = await removeAssets(
auth,

View File

@ -5,7 +5,6 @@ import { TimeBucketAssetDto, TimeBucketDto, TimeBucketResponseDto } from 'src/dt
import { Permission } from 'src/enum';
import { TimeBucketOptions } from 'src/interfaces/asset.interface';
import { BaseService } from 'src/services/base.service';
import { requireAccess } from 'src/utils/access';
import { getMyPartnerIds } from 'src/utils/asset.util';
export class TimelineService extends BaseService {
@ -48,20 +47,20 @@ export class TimelineService extends BaseService {
private async timeBucketChecks(auth: AuthDto, dto: TimeBucketDto) {
if (dto.albumId) {
await requireAccess(this.accessRepository, { auth, permission: Permission.ALBUM_READ, ids: [dto.albumId] });
await this.requireAccess({ auth, permission: Permission.ALBUM_READ, ids: [dto.albumId] });
} else {
dto.userId = dto.userId || auth.user.id;
}
if (dto.userId) {
await requireAccess(this.accessRepository, { auth, permission: Permission.TIMELINE_READ, ids: [dto.userId] });
await this.requireAccess({ auth, permission: Permission.TIMELINE_READ, ids: [dto.userId] });
if (dto.isArchived !== false) {
await requireAccess(this.accessRepository, { auth, permission: Permission.ARCHIVE_READ, ids: [dto.userId] });
await this.requireAccess({ auth, permission: Permission.ARCHIVE_READ, ids: [dto.userId] });
}
}
if (dto.tagId) {
await requireAccess(this.accessRepository, { auth, permission: Permission.TAG_READ, ids: [dto.tagId] });
await this.requireAccess({ auth, permission: Permission.TAG_READ, ids: [dto.tagId] });
}
if (dto.withPartners) {

View File

@ -5,7 +5,6 @@ import { TrashResponseDto } from 'src/dtos/trash.dto';
import { Permission } from 'src/enum';
import { JOBS_ASSET_PAGINATION_SIZE, JobName, JobStatus } from 'src/interfaces/job.interface';
import { BaseService } from 'src/services/base.service';
import { requireAccess } from 'src/utils/access';
import { usePagination } from 'src/utils/pagination';
export class TrashService extends BaseService {
@ -15,7 +14,7 @@ export class TrashService extends BaseService {
return { count: 0 };
}
await requireAccess(this.accessRepository, { auth, permission: Permission.ASSET_DELETE, ids });
await this.requireAccess({ auth, permission: Permission.ASSET_DELETE, ids });
await this.trashRepository.restoreAll(ids);
await this.eventRepository.emit('assets.restore', { assetIds: ids, userId: auth.user.id });