Skip to content

Commit 8daa807

Browse files
authored
refactor(server): add base methods for access checks (immich-app#13349)
1 parent 97edf90 commit 8daa807

17 files changed

+84
-90
lines changed

server/src/services/activity.service.ts

+4-5
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,11 @@ import { AuthDto } from 'src/dtos/auth.dto';
1414
import { ActivityEntity } from 'src/entities/activity.entity';
1515
import { Permission } from 'src/enum';
1616
import { BaseService } from 'src/services/base.service';
17-
import { requireAccess } from 'src/utils/access';
1817

1918
@Injectable()
2019
export class ActivityService extends BaseService {
2120
async getAll(auth: AuthDto, dto: ActivitySearchDto): Promise<ActivityResponseDto[]> {
22-
await requireAccess(this.accessRepository, { auth, permission: Permission.ALBUM_READ, ids: [dto.albumId] });
21+
await this.requireAccess({ auth, permission: Permission.ALBUM_READ, ids: [dto.albumId] });
2322
const activities = await this.activityRepository.search({
2423
userId: dto.userId,
2524
albumId: dto.albumId,
@@ -31,12 +30,12 @@ export class ActivityService extends BaseService {
3130
}
3231

3332
async getStatistics(auth: AuthDto, dto: ActivityDto): Promise<ActivityStatisticsResponseDto> {
34-
await requireAccess(this.accessRepository, { auth, permission: Permission.ALBUM_READ, ids: [dto.albumId] });
33+
await this.requireAccess({ auth, permission: Permission.ALBUM_READ, ids: [dto.albumId] });
3534
return { comments: await this.activityRepository.getStatistics(dto.assetId, dto.albumId) };
3635
}
3736

3837
async create(auth: AuthDto, dto: ActivityCreateDto): Promise<MaybeDuplicate<ActivityResponseDto>> {
39-
await requireAccess(this.accessRepository, { auth, permission: Permission.ACTIVITY_CREATE, ids: [dto.albumId] });
38+
await this.requireAccess({ auth, permission: Permission.ACTIVITY_CREATE, ids: [dto.albumId] });
4039

4140
const common = {
4241
userId: auth.user.id,
@@ -70,7 +69,7 @@ export class ActivityService extends BaseService {
7069
}
7170

7271
async delete(auth: AuthDto, id: string): Promise<void> {
73-
await requireAccess(this.accessRepository, { auth, permission: Permission.ACTIVITY_DELETE, ids: [id] });
72+
await this.requireAccess({ auth, permission: Permission.ACTIVITY_DELETE, ids: [id] });
7473
await this.activityRepository.delete(id);
7574
}
7675
}

server/src/services/album.service.ts

+9-10
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ import { AssetEntity } from 'src/entities/asset.entity';
1919
import { Permission } from 'src/enum';
2020
import { AlbumAssetCount, AlbumInfoOptions } from 'src/interfaces/album.interface';
2121
import { BaseService } from 'src/services/base.service';
22-
import { checkAccess, requireAccess } from 'src/utils/access';
2322
import { addAssets, removeAssets } from 'src/utils/asset.util';
2423

2524
@Injectable()
@@ -82,7 +81,7 @@ export class AlbumService extends BaseService {
8281
}
8382

8483
async get(auth: AuthDto, id: string, dto: AlbumInfoDto): Promise<AlbumResponseDto> {
85-
await requireAccess(this.accessRepository, { auth, permission: Permission.ALBUM_READ, ids: [id] });
84+
await this.requireAccess({ auth, permission: Permission.ALBUM_READ, ids: [id] });
8685
await this.albumRepository.updateThumbnails();
8786
const withAssets = dto.withoutAssets === undefined ? true : !dto.withoutAssets;
8887
const album = await this.findOrFail(id, { withAssets });
@@ -106,7 +105,7 @@ export class AlbumService extends BaseService {
106105
}
107106
}
108107

109-
const allowedAssetIdsSet = await checkAccess(this.accessRepository, {
108+
const allowedAssetIdsSet = await this.checkAccess({
110109
auth,
111110
permission: Permission.ASSET_SHARE,
112111
ids: dto.assetIds || [],
@@ -130,7 +129,7 @@ export class AlbumService extends BaseService {
130129
}
131130

132131
async update(auth: AuthDto, id: string, dto: UpdateAlbumDto): Promise<AlbumResponseDto> {
133-
await requireAccess(this.accessRepository, { auth, permission: Permission.ALBUM_UPDATE, ids: [id] });
132+
await this.requireAccess({ auth, permission: Permission.ALBUM_UPDATE, ids: [id] });
134133

135134
const album = await this.findOrFail(id, { withAssets: true });
136135

@@ -153,13 +152,13 @@ export class AlbumService extends BaseService {
153152
}
154153

155154
async delete(auth: AuthDto, id: string): Promise<void> {
156-
await requireAccess(this.accessRepository, { auth, permission: Permission.ALBUM_DELETE, ids: [id] });
155+
await this.requireAccess({ auth, permission: Permission.ALBUM_DELETE, ids: [id] });
157156
await this.albumRepository.delete(id);
158157
}
159158

160159
async addAssets(auth: AuthDto, id: string, dto: BulkIdsDto): Promise<BulkIdResponseDto[]> {
161160
const album = await this.findOrFail(id, { withAssets: false });
162-
await requireAccess(this.accessRepository, { auth, permission: Permission.ALBUM_ADD_ASSET, ids: [id] });
161+
await this.requireAccess({ auth, permission: Permission.ALBUM_ADD_ASSET, ids: [id] });
163162

164163
const results = await addAssets(
165164
auth,
@@ -182,7 +181,7 @@ export class AlbumService extends BaseService {
182181
}
183182

184183
async removeAssets(auth: AuthDto, id: string, dto: BulkIdsDto): Promise<BulkIdResponseDto[]> {
185-
await requireAccess(this.accessRepository, { auth, permission: Permission.ALBUM_REMOVE_ASSET, ids: [id] });
184+
await this.requireAccess({ auth, permission: Permission.ALBUM_REMOVE_ASSET, ids: [id] });
186185

187186
const album = await this.findOrFail(id, { withAssets: false });
188187
const results = await removeAssets(
@@ -203,7 +202,7 @@ export class AlbumService extends BaseService {
203202
}
204203

205204
async addUsers(auth: AuthDto, id: string, { albumUsers }: AddUsersDto): Promise<AlbumResponseDto> {
206-
await requireAccess(this.accessRepository, { auth, permission: Permission.ALBUM_SHARE, ids: [id] });
205+
await this.requireAccess({ auth, permission: Permission.ALBUM_SHARE, ids: [id] });
207206

208207
const album = await this.findOrFail(id, { withAssets: false });
209208

@@ -247,14 +246,14 @@ export class AlbumService extends BaseService {
247246

248247
// non-admin can remove themselves
249248
if (auth.user.id !== userId) {
250-
await requireAccess(this.accessRepository, { auth, permission: Permission.ALBUM_SHARE, ids: [id] });
249+
await this.requireAccess({ auth, permission: Permission.ALBUM_SHARE, ids: [id] });
251250
}
252251

253252
await this.albumUserRepository.delete({ albumId: id, userId });
254253
}
255254

256255
async updateUser(auth: AuthDto, id: string, userId: string, dto: Partial<AlbumUserEntity>): Promise<void> {
257-
await requireAccess(this.accessRepository, { auth, permission: Permission.ALBUM_SHARE, ids: [id] });
256+
await this.requireAccess({ auth, permission: Permission.ALBUM_SHARE, ids: [id] });
258257
await this.albumUserRepository.update({ albumId: id, userId }, { role: dto.role });
259258
}
260259

server/src/services/asset-media.service.ts

+6-6
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import { ASSET_CHECKSUM_CONSTRAINT, AssetEntity } from 'src/entities/asset.entit
2424
import { AssetStatus, AssetType, CacheControl, Permission, StorageFolder } from 'src/enum';
2525
import { JobName } from 'src/interfaces/job.interface';
2626
import { BaseService } from 'src/services/base.service';
27-
import { requireAccess, requireUploadAccess } from 'src/utils/access';
27+
import { requireUploadAccess } from 'src/utils/access';
2828
import { getAssetFiles, onBeforeLink } from 'src/utils/asset.util';
2929
import { ImmichFileResponse } from 'src/utils/file';
3030
import { mimeTypes } from 'src/utils/mime-types';
@@ -125,7 +125,7 @@ export class AssetMediaService extends BaseService {
125125
sidecarFile?: UploadFile,
126126
): Promise<AssetMediaResponseDto> {
127127
try {
128-
await requireAccess(this.accessRepository, {
128+
await this.requireAccess({
129129
auth,
130130
permission: Permission.ASSET_UPLOAD,
131131
// do not need an id here, but the interface requires it
@@ -159,7 +159,7 @@ export class AssetMediaService extends BaseService {
159159
sidecarFile?: UploadFile,
160160
): Promise<AssetMediaResponseDto> {
161161
try {
162-
await requireAccess(this.accessRepository, { auth, permission: Permission.ASSET_UPDATE, ids: [id] });
162+
await this.requireAccess({ auth, permission: Permission.ASSET_UPDATE, ids: [id] });
163163
const asset = (await this.assetRepository.getById(id)) as AssetEntity;
164164

165165
this.requireQuota(auth, file.size);
@@ -182,7 +182,7 @@ export class AssetMediaService extends BaseService {
182182
}
183183

184184
async downloadOriginal(auth: AuthDto, id: string): Promise<ImmichFileResponse> {
185-
await requireAccess(this.accessRepository, { auth, permission: Permission.ASSET_DOWNLOAD, ids: [id] });
185+
await this.requireAccess({ auth, permission: Permission.ASSET_DOWNLOAD, ids: [id] });
186186

187187
const asset = await this.findOrFail(id);
188188

@@ -194,7 +194,7 @@ export class AssetMediaService extends BaseService {
194194
}
195195

196196
async viewThumbnail(auth: AuthDto, id: string, dto: AssetMediaOptionsDto): Promise<ImmichFileResponse> {
197-
await requireAccess(this.accessRepository, { auth, permission: Permission.ASSET_VIEW, ids: [id] });
197+
await this.requireAccess({ auth, permission: Permission.ASSET_VIEW, ids: [id] });
198198

199199
const asset = await this.findOrFail(id);
200200
const size = dto.size ?? AssetMediaSize.THUMBNAIL;
@@ -217,7 +217,7 @@ export class AssetMediaService extends BaseService {
217217
}
218218

219219
async playbackVideo(auth: AuthDto, id: string): Promise<ImmichFileResponse> {
220-
await requireAccess(this.accessRepository, { auth, permission: Permission.ASSET_VIEW, ids: [id] });
220+
await this.requireAccess({ auth, permission: Permission.ASSET_VIEW, ids: [id] });
221221

222222
const asset = await this.findOrFail(id);
223223

server/src/services/asset.service.ts

+5-6
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ import {
2929
JobStatus,
3030
} from 'src/interfaces/job.interface';
3131
import { BaseService } from 'src/services/base.service';
32-
import { requireAccess } from 'src/utils/access';
3332
import { getAssetFiles, getMyPartnerIds, onAfterUnlink, onBeforeLink, onBeforeUnlink } from 'src/utils/asset.util';
3433
import { usePagination } from 'src/utils/pagination';
3534

@@ -86,7 +85,7 @@ export class AssetService extends BaseService {
8685
}
8786

8887
async get(auth: AuthDto, id: string): Promise<AssetResponseDto | SanitizedAssetResponseDto> {
89-
await requireAccess(this.accessRepository, { auth, permission: Permission.ASSET_READ, ids: [id] });
88+
await this.requireAccess({ auth, permission: Permission.ASSET_READ, ids: [id] });
9089

9190
const asset = await this.assetRepository.getById(
9291
id,
@@ -135,7 +134,7 @@ export class AssetService extends BaseService {
135134
}
136135

137136
async update(auth: AuthDto, id: string, dto: UpdateAssetDto): Promise<AssetResponseDto> {
138-
await requireAccess(this.accessRepository, { auth, permission: Permission.ASSET_UPDATE, ids: [id] });
137+
await this.requireAccess({ auth, permission: Permission.ASSET_UPDATE, ids: [id] });
139138

140139
const { description, dateTimeOriginal, latitude, longitude, rating, ...rest } = dto;
141140
const repos = { asset: this.assetRepository, event: this.eventRepository };
@@ -178,7 +177,7 @@ export class AssetService extends BaseService {
178177

179178
async updateAll(auth: AuthDto, dto: AssetBulkUpdateDto): Promise<void> {
180179
const { ids, dateTimeOriginal, latitude, longitude, ...options } = dto;
181-
await requireAccess(this.accessRepository, { auth, permission: Permission.ASSET_UPDATE, ids });
180+
await this.requireAccess({ auth, permission: Permission.ASSET_UPDATE, ids });
182181

183182
for (const id of ids) {
184183
await this.updateMetadata({ id, dateTimeOriginal, latitude, longitude });
@@ -275,7 +274,7 @@ export class AssetService extends BaseService {
275274
async deleteAll(auth: AuthDto, dto: AssetBulkDeleteDto): Promise<void> {
276275
const { ids, force } = dto;
277276

278-
await requireAccess(this.accessRepository, { auth, permission: Permission.ASSET_DELETE, ids });
277+
await this.requireAccess({ auth, permission: Permission.ASSET_DELETE, ids });
279278
await this.assetRepository.updateAll(ids, {
280279
deletedAt: new Date(),
281280
status: force ? AssetStatus.DELETED : AssetStatus.TRASHED,
@@ -284,7 +283,7 @@ export class AssetService extends BaseService {
284283
}
285284

286285
async run(auth: AuthDto, dto: AssetJobsDto) {
287-
await requireAccess(this.accessRepository, { auth, permission: Permission.ASSET_UPDATE, ids: dto.assetIds });
286+
await this.requireAccess({ auth, permission: Permission.ASSET_UPDATE, ids: dto.assetIds });
288287

289288
const jobs: JobItem[] = [];
290289

server/src/services/audit.service.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import {
2323
} from 'src/enum';
2424
import { JOBS_ASSET_PAGINATION_SIZE, JobStatus } from 'src/interfaces/job.interface';
2525
import { BaseService } from 'src/services/base.service';
26-
import { requireAccess } from 'src/utils/access';
2726
import { getAssetFiles } from 'src/utils/asset.util';
2827
import { usePagination } from 'src/utils/pagination';
2928

@@ -36,7 +35,7 @@ export class AuditService extends BaseService {
3635

3736
async getDeletes(auth: AuthDto, dto: AuditDeletesDto): Promise<AuditDeletesResponseDto> {
3837
const userId = dto.userId || auth.user.id;
39-
await requireAccess(this.accessRepository, { auth, permission: Permission.TIMELINE_READ, ids: [userId] });
38+
await this.requireAccess({ auth, permission: Permission.TIMELINE_READ, ids: [userId] });
4039

4140
const audits = await this.auditRepository.getAfter(dto.after, {
4241
userIds: [userId],

server/src/services/base.service.ts

+12-3
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import { ITrashRepository } from 'src/interfaces/trash.interface';
3838
import { IUserRepository } from 'src/interfaces/user.interface';
3939
import { IVersionHistoryRepository } from 'src/interfaces/version-history.interface';
4040
import { IViewRepository } from 'src/interfaces/view.interface';
41+
import { AccessRequest, checkAccess, requireAccess } from 'src/utils/access';
4142
import { getConfig, updateConfig } from 'src/utils/config';
4243

4344
export class BaseService {
@@ -95,7 +96,7 @@ export class BaseService {
9596
);
9697
}
9798

98-
private get repos() {
99+
private get configRepos() {
99100
return {
100101
configRepo: this.configRepository,
101102
metadataRepo: this.systemMetadataRepository,
@@ -104,10 +105,18 @@ export class BaseService {
104105
}
105106

106107
getConfig(options: { withCache: boolean }) {
107-
return getConfig(this.repos, options);
108+
return getConfig(this.configRepos, options);
108109
}
109110

110111
updateConfig(newConfig: SystemConfig) {
111-
return updateConfig(this.repos, newConfig);
112+
return updateConfig(this.configRepos, newConfig);
113+
}
114+
115+
requireAccess(request: AccessRequest) {
116+
return requireAccess(this.accessRepository, request);
117+
}
118+
119+
checkAccess(request: AccessRequest) {
120+
return checkAccess(this.accessRepository, request);
112121
}
113122
}

server/src/services/download.service.ts

+4-5
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import { AssetEntity } from 'src/entities/asset.entity';
88
import { Permission } from 'src/enum';
99
import { ImmichReadStream } from 'src/interfaces/storage.interface';
1010
import { BaseService } from 'src/services/base.service';
11-
import { requireAccess } from 'src/utils/access';
1211
import { HumanReadableSize } from 'src/utils/bytes';
1312
import { usePagination } from 'src/utils/pagination';
1413
import { getPreferences } from 'src/utils/preferences';
@@ -62,7 +61,7 @@ export class DownloadService extends BaseService {
6261
}
6362

6463
async downloadArchive(auth: AuthDto, dto: AssetIdsDto): Promise<ImmichReadStream> {
65-
await requireAccess(this.accessRepository, { auth, permission: Permission.ASSET_DOWNLOAD, ids: dto.assetIds });
64+
await this.requireAccess({ auth, permission: Permission.ASSET_DOWNLOAD, ids: dto.assetIds });
6665

6766
const zip = this.storageRepository.createZipStream();
6867
const assets = await this.assetRepository.getByIds(dto.assetIds);
@@ -105,20 +104,20 @@ export class DownloadService extends BaseService {
105104

106105
if (dto.assetIds) {
107106
const assetIds = dto.assetIds;
108-
await requireAccess(this.accessRepository, { auth, permission: Permission.ASSET_DOWNLOAD, ids: assetIds });
107+
await this.requireAccess({ auth, permission: Permission.ASSET_DOWNLOAD, ids: assetIds });
109108
const assets = await this.assetRepository.getByIds(assetIds, { exifInfo: true });
110109
return usePagination(PAGINATION_SIZE, () => ({ hasNextPage: false, items: assets }));
111110
}
112111

113112
if (dto.albumId) {
114113
const albumId = dto.albumId;
115-
await requireAccess(this.accessRepository, { auth, permission: Permission.ALBUM_DOWNLOAD, ids: [albumId] });
114+
await this.requireAccess({ auth, permission: Permission.ALBUM_DOWNLOAD, ids: [albumId] });
116115
return usePagination(PAGINATION_SIZE, (pagination) => this.assetRepository.getByAlbumId(pagination, albumId));
117116
}
118117

119118
if (dto.userId) {
120119
const userId = dto.userId;
121-
await requireAccess(this.accessRepository, { auth, permission: Permission.TIMELINE_DOWNLOAD, ids: [userId] });
120+
await this.requireAccess({ auth, permission: Permission.TIMELINE_DOWNLOAD, ids: [userId] });
122121
return usePagination(PAGINATION_SIZE, (pagination) =>
123122
this.assetRepository.getByUserId(pagination, userId, { isVisible: true }),
124123
);

server/src/services/memory.service.ts

+6-7
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import { MemoryCreateDto, MemoryResponseDto, MemoryUpdateDto, mapMemory } from '
55
import { AssetEntity } from 'src/entities/asset.entity';
66
import { Permission } from 'src/enum';
77
import { BaseService } from 'src/services/base.service';
8-
import { checkAccess, requireAccess } from 'src/utils/access';
98
import { addAssets, removeAssets } from 'src/utils/asset.util';
109

1110
@Injectable()
@@ -16,7 +15,7 @@ export class MemoryService extends BaseService {
1615
}
1716

1817
async get(auth: AuthDto, id: string): Promise<MemoryResponseDto> {
19-
await requireAccess(this.accessRepository, { auth, permission: Permission.MEMORY_READ, ids: [id] });
18+
await this.requireAccess({ auth, permission: Permission.MEMORY_READ, ids: [id] });
2019
const memory = await this.findOrFail(id);
2120
return mapMemory(memory);
2221
}
@@ -25,7 +24,7 @@ export class MemoryService extends BaseService {
2524
// TODO validate type/data combination
2625

2726
const assetIds = dto.assetIds || [];
28-
const allowedAssetIds = await checkAccess(this.accessRepository, {
27+
const allowedAssetIds = await this.checkAccess({
2928
auth,
3029
permission: Permission.ASSET_SHARE,
3130
ids: assetIds,
@@ -44,7 +43,7 @@ export class MemoryService extends BaseService {
4443
}
4544

4645
async update(auth: AuthDto, id: string, dto: MemoryUpdateDto): Promise<MemoryResponseDto> {
47-
await requireAccess(this.accessRepository, { auth, permission: Permission.MEMORY_UPDATE, ids: [id] });
46+
await this.requireAccess({ auth, permission: Permission.MEMORY_UPDATE, ids: [id] });
4847

4948
const memory = await this.memoryRepository.update({
5049
id,
@@ -57,12 +56,12 @@ export class MemoryService extends BaseService {
5756
}
5857

5958
async remove(auth: AuthDto, id: string): Promise<void> {
60-
await requireAccess(this.accessRepository, { auth, permission: Permission.MEMORY_DELETE, ids: [id] });
59+
await this.requireAccess({ auth, permission: Permission.MEMORY_DELETE, ids: [id] });
6160
await this.memoryRepository.delete(id);
6261
}
6362

6463
async addAssets(auth: AuthDto, id: string, dto: BulkIdsDto): Promise<BulkIdResponseDto[]> {
65-
await requireAccess(this.accessRepository, { auth, permission: Permission.MEMORY_READ, ids: [id] });
64+
await this.requireAccess({ auth, permission: Permission.MEMORY_READ, ids: [id] });
6665

6766
const repos = { access: this.accessRepository, bulk: this.memoryRepository };
6867
const results = await addAssets(auth, repos, { parentId: id, assetIds: dto.ids });
@@ -76,7 +75,7 @@ export class MemoryService extends BaseService {
7675
}
7776

7877
async removeAssets(auth: AuthDto, id: string, dto: BulkIdsDto): Promise<BulkIdResponseDto[]> {
79-
await requireAccess(this.accessRepository, { auth, permission: Permission.MEMORY_UPDATE, ids: [id] });
78+
await this.requireAccess({ auth, permission: Permission.MEMORY_UPDATE, ids: [id] });
8079

8180
const repos = { access: this.accessRepository, bulk: this.memoryRepository };
8281
const results = await removeAssets(auth, repos, {

0 commit comments

Comments
 (0)