Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 1 commit into from
Oct 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions server/src/services/activity.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand Down Expand Up @@ -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);
}
}
19 changes: 9 additions & 10 deletions server/src/services/album.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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 });
Expand All @@ -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 || [],
Expand All @@ -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 });

Expand All @@ -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,
Expand All @@ -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(
Expand All @@ -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 });

Expand Down Expand Up @@ -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 });
}

Expand Down
12 changes: 6 additions & 6 deletions server/src/services/asset-media.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand All @@ -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);

Expand All @@ -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;
Expand All @@ -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);

Expand Down
11 changes: 5 additions & 6 deletions server/src/services/asset.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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 };
Expand Down Expand Up @@ -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 });
Expand Down Expand Up @@ -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,
Expand All @@ -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[] = [];

Expand Down
3 changes: 1 addition & 2 deletions server/src/services/audit.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -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],
Expand Down
15 changes: 12 additions & 3 deletions server/src/services/base.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -95,7 +96,7 @@ export class BaseService {
);
}

private get repos() {
private get configRepos() {
return {
configRepo: this.configRepository,
metadataRepo: this.systemMetadataRepository,
Expand All @@ -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);
}
}
9 changes: 4 additions & 5 deletions server/src/services/download.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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 }),
);
Expand Down
13 changes: 6 additions & 7 deletions server/src/services/memory.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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);
}
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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 });
Expand All @@ -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, {
Expand Down
Loading
Loading