mirror of
				https://github.com/immich-app/immich.git
				synced 2025-10-30 18:22:37 -04:00 
			
		
		
		
	refactor: access repository (#15490)
This commit is contained in:
		
							parent
							
								
									318dd32363
								
							
						
					
					
						commit
						b0cdd8f475
					
				| @ -1,53 +0,0 @@ | ||||
| import { AlbumUserRole } from 'src/enum'; | ||||
| 
 | ||||
| export const IAccessRepository = 'IAccessRepository'; | ||||
| 
 | ||||
| export interface IAccessRepository { | ||||
|   activity: { | ||||
|     checkOwnerAccess(userId: string, activityIds: Set<string>): Promise<Set<string>>; | ||||
|     checkAlbumOwnerAccess(userId: string, activityIds: Set<string>): Promise<Set<string>>; | ||||
|     checkCreateAccess(userId: string, albumIds: Set<string>): Promise<Set<string>>; | ||||
|   }; | ||||
| 
 | ||||
|   asset: { | ||||
|     checkOwnerAccess(userId: string, assetIds: Set<string>): Promise<Set<string>>; | ||||
|     checkAlbumAccess(userId: string, assetIds: Set<string>): Promise<Set<string>>; | ||||
|     checkPartnerAccess(userId: string, assetIds: Set<string>): Promise<Set<string>>; | ||||
|     checkSharedLinkAccess(sharedLinkId: string, assetIds: Set<string>): Promise<Set<string>>; | ||||
|   }; | ||||
| 
 | ||||
|   authDevice: { | ||||
|     checkOwnerAccess(userId: string, deviceIds: Set<string>): Promise<Set<string>>; | ||||
|   }; | ||||
| 
 | ||||
|   album: { | ||||
|     checkOwnerAccess(userId: string, albumIds: Set<string>): Promise<Set<string>>; | ||||
|     checkSharedAlbumAccess(userId: string, albumIds: Set<string>, access: AlbumUserRole): Promise<Set<string>>; | ||||
|     checkSharedLinkAccess(sharedLinkId: string, albumIds: Set<string>): Promise<Set<string>>; | ||||
|   }; | ||||
| 
 | ||||
|   timeline: { | ||||
|     checkPartnerAccess(userId: string, partnerIds: Set<string>): Promise<Set<string>>; | ||||
|   }; | ||||
| 
 | ||||
|   memory: { | ||||
|     checkOwnerAccess(userId: string, memoryIds: Set<string>): Promise<Set<string>>; | ||||
|   }; | ||||
| 
 | ||||
|   person: { | ||||
|     checkFaceOwnerAccess(userId: string, assetFaceId: Set<string>): Promise<Set<string>>; | ||||
|     checkOwnerAccess(userId: string, personIds: Set<string>): Promise<Set<string>>; | ||||
|   }; | ||||
| 
 | ||||
|   partner: { | ||||
|     checkUpdateAccess(userId: string, partnerIds: Set<string>): Promise<Set<string>>; | ||||
|   }; | ||||
| 
 | ||||
|   stack: { | ||||
|     checkOwnerAccess(userId: string, stackIds: Set<string>): Promise<Set<string>>; | ||||
|   }; | ||||
| 
 | ||||
|   tag: { | ||||
|     checkOwnerAccess(userId: string, tagIds: Set<string>): Promise<Set<string>>; | ||||
|   }; | ||||
| } | ||||
| @ -1,33 +1,18 @@ | ||||
| import { Injectable } from '@nestjs/common'; | ||||
| import { Kysely, sql } from 'kysely'; | ||||
| import { InjectKysely } from 'nestjs-kysely'; | ||||
| import { DB } from 'src/db'; | ||||
| import { ChunkedSet, DummyValue, GenerateSql } from 'src/decorators'; | ||||
| 
 | ||||
| import { AlbumUserRole } from 'src/enum'; | ||||
| import { IAccessRepository } from 'src/interfaces/access.interface'; | ||||
| import { asUuid } from 'src/utils/database'; | ||||
| 
 | ||||
| type IActivityAccess = IAccessRepository['activity']; | ||||
| type IAlbumAccess = IAccessRepository['album']; | ||||
| type IAssetAccess = IAccessRepository['asset']; | ||||
| type IAuthDeviceAccess = IAccessRepository['authDevice']; | ||||
| type IMemoryAccess = IAccessRepository['memory']; | ||||
| type IPersonAccess = IAccessRepository['person']; | ||||
| type IPartnerAccess = IAccessRepository['partner']; | ||||
| type IStackAccess = IAccessRepository['stack']; | ||||
| type ITagAccess = IAccessRepository['tag']; | ||||
| type ITimelineAccess = IAccessRepository['timeline']; | ||||
| 
 | ||||
| @Injectable() | ||||
| class ActivityAccess implements IActivityAccess { | ||||
| class ActivityAccess { | ||||
|   constructor(private db: Kysely<DB>) {} | ||||
| 
 | ||||
|   @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] }) | ||||
|   @ChunkedSet({ paramIndex: 1 }) | ||||
|   async checkOwnerAccess(userId: string, activityIds: Set<string>): Promise<Set<string>> { | ||||
|   async checkOwnerAccess(userId: string, activityIds: Set<string>) { | ||||
|     if (activityIds.size === 0) { | ||||
|       return new Set(); | ||||
|       return new Set<string>(); | ||||
|     } | ||||
| 
 | ||||
|     return this.db | ||||
| @ -41,9 +26,9 @@ class ActivityAccess implements IActivityAccess { | ||||
| 
 | ||||
|   @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] }) | ||||
|   @ChunkedSet({ paramIndex: 1 }) | ||||
|   async checkAlbumOwnerAccess(userId: string, activityIds: Set<string>): Promise<Set<string>> { | ||||
|   async checkAlbumOwnerAccess(userId: string, activityIds: Set<string>) { | ||||
|     if (activityIds.size === 0) { | ||||
|       return new Set(); | ||||
|       return new Set<string>(); | ||||
|     } | ||||
| 
 | ||||
|     return this.db | ||||
| @ -58,9 +43,9 @@ class ActivityAccess implements IActivityAccess { | ||||
| 
 | ||||
|   @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] }) | ||||
|   @ChunkedSet({ paramIndex: 1 }) | ||||
|   async checkCreateAccess(userId: string, albumIds: Set<string>): Promise<Set<string>> { | ||||
|   async checkCreateAccess(userId: string, albumIds: Set<string>) { | ||||
|     if (albumIds.size === 0) { | ||||
|       return new Set(); | ||||
|       return new Set<string>(); | ||||
|     } | ||||
| 
 | ||||
|     return this.db | ||||
| @ -77,14 +62,14 @@ class ActivityAccess implements IActivityAccess { | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| class AlbumAccess implements IAlbumAccess { | ||||
| class AlbumAccess { | ||||
|   constructor(private db: Kysely<DB>) {} | ||||
| 
 | ||||
|   @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] }) | ||||
|   @ChunkedSet({ paramIndex: 1 }) | ||||
|   async checkOwnerAccess(userId: string, albumIds: Set<string>): Promise<Set<string>> { | ||||
|   async checkOwnerAccess(userId: string, albumIds: Set<string>) { | ||||
|     if (albumIds.size === 0) { | ||||
|       return new Set(); | ||||
|       return new Set<string>(); | ||||
|     } | ||||
| 
 | ||||
|     return this.db | ||||
| @ -99,9 +84,9 @@ class AlbumAccess implements IAlbumAccess { | ||||
| 
 | ||||
|   @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] }) | ||||
|   @ChunkedSet({ paramIndex: 1 }) | ||||
|   async checkSharedAlbumAccess(userId: string, albumIds: Set<string>, access: AlbumUserRole): Promise<Set<string>> { | ||||
|   async checkSharedAlbumAccess(userId: string, albumIds: Set<string>, access: AlbumUserRole) { | ||||
|     if (albumIds.size === 0) { | ||||
|       return new Set(); | ||||
|       return new Set<string>(); | ||||
|     } | ||||
| 
 | ||||
|     const accessRole = | ||||
| @ -122,9 +107,9 @@ class AlbumAccess implements IAlbumAccess { | ||||
| 
 | ||||
|   @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] }) | ||||
|   @ChunkedSet({ paramIndex: 1 }) | ||||
|   async checkSharedLinkAccess(sharedLinkId: string, albumIds: Set<string>): Promise<Set<string>> { | ||||
|   async checkSharedLinkAccess(sharedLinkId: string, albumIds: Set<string>) { | ||||
|     if (albumIds.size === 0) { | ||||
|       return new Set(); | ||||
|       return new Set<string>(); | ||||
|     } | ||||
| 
 | ||||
|     return this.db | ||||
| @ -139,14 +124,14 @@ class AlbumAccess implements IAlbumAccess { | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| class AssetAccess implements IAssetAccess { | ||||
| class AssetAccess { | ||||
|   constructor(private db: Kysely<DB>) {} | ||||
| 
 | ||||
|   @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] }) | ||||
|   @ChunkedSet({ paramIndex: 1 }) | ||||
|   async checkAlbumAccess(userId: string, assetIds: Set<string>): Promise<Set<string>> { | ||||
|   async checkAlbumAccess(userId: string, assetIds: Set<string>) { | ||||
|     if (assetIds.size === 0) { | ||||
|       return new Set(); | ||||
|       return new Set<string>(); | ||||
|     } | ||||
| 
 | ||||
|     return this.db | ||||
| @ -182,9 +167,9 @@ class AssetAccess implements IAssetAccess { | ||||
| 
 | ||||
|   @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] }) | ||||
|   @ChunkedSet({ paramIndex: 1 }) | ||||
|   async checkOwnerAccess(userId: string, assetIds: Set<string>): Promise<Set<string>> { | ||||
|   async checkOwnerAccess(userId: string, assetIds: Set<string>) { | ||||
|     if (assetIds.size === 0) { | ||||
|       return new Set(); | ||||
|       return new Set<string>(); | ||||
|     } | ||||
| 
 | ||||
|     return this.db | ||||
| @ -198,9 +183,9 @@ class AssetAccess implements IAssetAccess { | ||||
| 
 | ||||
|   @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] }) | ||||
|   @ChunkedSet({ paramIndex: 1 }) | ||||
|   async checkPartnerAccess(userId: string, assetIds: Set<string>): Promise<Set<string>> { | ||||
|   async checkPartnerAccess(userId: string, assetIds: Set<string>) { | ||||
|     if (assetIds.size === 0) { | ||||
|       return new Set(); | ||||
|       return new Set<string>(); | ||||
|     } | ||||
| 
 | ||||
|     return this.db | ||||
| @ -221,9 +206,9 @@ class AssetAccess implements IAssetAccess { | ||||
| 
 | ||||
|   @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] }) | ||||
|   @ChunkedSet({ paramIndex: 1 }) | ||||
|   async checkSharedLinkAccess(sharedLinkId: string, assetIds: Set<string>): Promise<Set<string>> { | ||||
|   async checkSharedLinkAccess(sharedLinkId: string, assetIds: Set<string>) { | ||||
|     if (assetIds.size === 0) { | ||||
|       return new Set(); | ||||
|       return new Set<string>(); | ||||
|     } | ||||
| 
 | ||||
|     return this.db | ||||
| @ -273,14 +258,14 @@ class AssetAccess implements IAssetAccess { | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| class AuthDeviceAccess implements IAuthDeviceAccess { | ||||
| class AuthDeviceAccess { | ||||
|   constructor(private db: Kysely<DB>) {} | ||||
| 
 | ||||
|   @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] }) | ||||
|   @ChunkedSet({ paramIndex: 1 }) | ||||
|   async checkOwnerAccess(userId: string, deviceIds: Set<string>): Promise<Set<string>> { | ||||
|   async checkOwnerAccess(userId: string, deviceIds: Set<string>) { | ||||
|     if (deviceIds.size === 0) { | ||||
|       return new Set(); | ||||
|       return new Set<string>(); | ||||
|     } | ||||
| 
 | ||||
|     return this.db | ||||
| @ -293,14 +278,14 @@ class AuthDeviceAccess implements IAuthDeviceAccess { | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| class StackAccess implements IStackAccess { | ||||
| class StackAccess { | ||||
|   constructor(private db: Kysely<DB>) {} | ||||
| 
 | ||||
|   @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] }) | ||||
|   @ChunkedSet({ paramIndex: 1 }) | ||||
|   async checkOwnerAccess(userId: string, stackIds: Set<string>): Promise<Set<string>> { | ||||
|   async checkOwnerAccess(userId: string, stackIds: Set<string>) { | ||||
|     if (stackIds.size === 0) { | ||||
|       return new Set(); | ||||
|       return new Set<string>(); | ||||
|     } | ||||
| 
 | ||||
|     return this.db | ||||
| @ -313,14 +298,14 @@ class StackAccess implements IStackAccess { | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| class TimelineAccess implements ITimelineAccess { | ||||
| class TimelineAccess { | ||||
|   constructor(private db: Kysely<DB>) {} | ||||
| 
 | ||||
|   @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] }) | ||||
|   @ChunkedSet({ paramIndex: 1 }) | ||||
|   async checkPartnerAccess(userId: string, partnerIds: Set<string>): Promise<Set<string>> { | ||||
|   async checkPartnerAccess(userId: string, partnerIds: Set<string>) { | ||||
|     if (partnerIds.size === 0) { | ||||
|       return new Set(); | ||||
|       return new Set<string>(); | ||||
|     } | ||||
| 
 | ||||
|     return this.db | ||||
| @ -333,14 +318,14 @@ class TimelineAccess implements ITimelineAccess { | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| class MemoryAccess implements IMemoryAccess { | ||||
| class MemoryAccess { | ||||
|   constructor(private db: Kysely<DB>) {} | ||||
| 
 | ||||
|   @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] }) | ||||
|   @ChunkedSet({ paramIndex: 1 }) | ||||
|   async checkOwnerAccess(userId: string, memoryIds: Set<string>): Promise<Set<string>> { | ||||
|   async checkOwnerAccess(userId: string, memoryIds: Set<string>) { | ||||
|     if (memoryIds.size === 0) { | ||||
|       return new Set(); | ||||
|       return new Set<string>(); | ||||
|     } | ||||
| 
 | ||||
|     return this.db | ||||
| @ -354,14 +339,14 @@ class MemoryAccess implements IMemoryAccess { | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| class PersonAccess implements IPersonAccess { | ||||
| class PersonAccess { | ||||
|   constructor(private db: Kysely<DB>) {} | ||||
| 
 | ||||
|   @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] }) | ||||
|   @ChunkedSet({ paramIndex: 1 }) | ||||
|   async checkOwnerAccess(userId: string, personIds: Set<string>): Promise<Set<string>> { | ||||
|   async checkOwnerAccess(userId: string, personIds: Set<string>) { | ||||
|     if (personIds.size === 0) { | ||||
|       return new Set(); | ||||
|       return new Set<string>(); | ||||
|     } | ||||
| 
 | ||||
|     return this.db | ||||
| @ -375,9 +360,9 @@ class PersonAccess implements IPersonAccess { | ||||
| 
 | ||||
|   @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] }) | ||||
|   @ChunkedSet({ paramIndex: 1 }) | ||||
|   async checkFaceOwnerAccess(userId: string, assetFaceIds: Set<string>): Promise<Set<string>> { | ||||
|   async checkFaceOwnerAccess(userId: string, assetFaceIds: Set<string>) { | ||||
|     if (assetFaceIds.size === 0) { | ||||
|       return new Set(); | ||||
|       return new Set<string>(); | ||||
|     } | ||||
| 
 | ||||
|     return this.db | ||||
| @ -393,14 +378,14 @@ class PersonAccess implements IPersonAccess { | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| class PartnerAccess implements IPartnerAccess { | ||||
| class PartnerAccess { | ||||
|   constructor(private db: Kysely<DB>) {} | ||||
| 
 | ||||
|   @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] }) | ||||
|   @ChunkedSet({ paramIndex: 1 }) | ||||
|   async checkUpdateAccess(userId: string, partnerIds: Set<string>): Promise<Set<string>> { | ||||
|   async checkUpdateAccess(userId: string, partnerIds: Set<string>) { | ||||
|     if (partnerIds.size === 0) { | ||||
|       return new Set(); | ||||
|       return new Set<string>(); | ||||
|     } | ||||
| 
 | ||||
|     return this.db | ||||
| @ -413,14 +398,14 @@ class PartnerAccess implements IPartnerAccess { | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| class TagAccess implements ITagAccess { | ||||
| class TagAccess { | ||||
|   constructor(private db: Kysely<DB>) {} | ||||
| 
 | ||||
|   @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] }) | ||||
|   @ChunkedSet({ paramIndex: 1 }) | ||||
|   async checkOwnerAccess(userId: string, tagIds: Set<string>): Promise<Set<string>> { | ||||
|   async checkOwnerAccess(userId: string, tagIds: Set<string>) { | ||||
|     if (tagIds.size === 0) { | ||||
|       return new Set(); | ||||
|       return new Set<string>(); | ||||
|     } | ||||
| 
 | ||||
|     return this.db | ||||
| @ -433,17 +418,17 @@ class TagAccess implements ITagAccess { | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| export class AccessRepository implements IAccessRepository { | ||||
|   activity: IActivityAccess; | ||||
|   album: IAlbumAccess; | ||||
|   asset: IAssetAccess; | ||||
|   authDevice: IAuthDeviceAccess; | ||||
|   memory: IMemoryAccess; | ||||
|   person: IPersonAccess; | ||||
|   partner: IPartnerAccess; | ||||
|   stack: IStackAccess; | ||||
|   tag: ITagAccess; | ||||
|   timeline: ITimelineAccess; | ||||
| export class AccessRepository { | ||||
|   activity: ActivityAccess; | ||||
|   album: AlbumAccess; | ||||
|   asset: AssetAccess; | ||||
|   authDevice: AuthDeviceAccess; | ||||
|   memory: MemoryAccess; | ||||
|   person: PersonAccess; | ||||
|   partner: PartnerAccess; | ||||
|   stack: StackAccess; | ||||
|   tag: TagAccess; | ||||
|   timeline: TimelineAccess; | ||||
| 
 | ||||
|   constructor(@InjectKysely() db: Kysely<DB>) { | ||||
|     this.activity = new ActivityAccess(db); | ||||
|  | ||||
| @ -1,4 +1,3 @@ | ||||
| import { IAccessRepository } from 'src/interfaces/access.interface'; | ||||
| import { IAlbumUserRepository } from 'src/interfaces/album-user.interface'; | ||||
| import { IAlbumRepository } from 'src/interfaces/album.interface'; | ||||
| import { IKeyRepository } from 'src/interfaces/api-key.interface'; | ||||
| @ -78,11 +77,11 @@ import { ViewRepository } from 'src/repositories/view-repository'; | ||||
| 
 | ||||
| export const repositories = [ | ||||
|   //
 | ||||
|   AccessRepository, | ||||
|   ActivityRepository, | ||||
| ]; | ||||
| 
 | ||||
| export const providers = [ | ||||
|   { provide: IAccessRepository, useClass: AccessRepository }, | ||||
|   { provide: IAlbumRepository, useClass: AlbumRepository }, | ||||
|   { provide: IAlbumUserRepository, useClass: AlbumUserRepository }, | ||||
|   { provide: IAssetRepository, useClass: AssetRepository }, | ||||
|  | ||||
| @ -6,7 +6,6 @@ import { SALT_ROUNDS } from 'src/constants'; | ||||
| import { StorageCore } from 'src/cores/storage.core'; | ||||
| import { Users } from 'src/db'; | ||||
| import { UserEntity } from 'src/entities/user.entity'; | ||||
| import { IAccessRepository } from 'src/interfaces/access.interface'; | ||||
| import { IAlbumUserRepository } from 'src/interfaces/album-user.interface'; | ||||
| import { IAlbumRepository } from 'src/interfaces/album.interface'; | ||||
| import { IKeyRepository } from 'src/interfaces/api-key.interface'; | ||||
| @ -44,6 +43,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 { AccessRepository } from 'src/repositories/access.repository'; | ||||
| import { ActivityRepository } from 'src/repositories/activity.repository'; | ||||
| import { AccessRequest, checkAccess, requireAccess } from 'src/utils/access'; | ||||
| import { getConfig, updateConfig } from 'src/utils/config'; | ||||
| @ -53,7 +53,7 @@ export class BaseService { | ||||
| 
 | ||||
|   constructor( | ||||
|     @Inject(ILoggerRepository) protected logger: ILoggerRepository, | ||||
|     @Inject(IAccessRepository) protected accessRepository: IAccessRepository, | ||||
|     protected accessRepository: AccessRepository, | ||||
|     protected activityRepository: ActivityRepository, | ||||
|     @Inject(IAuditRepository) protected auditRepository: IAuditRepository, | ||||
|     @Inject(IAlbumRepository) protected albumRepository: IAlbumRepository, | ||||
|  | ||||
| @ -1,5 +1,6 @@ | ||||
| import { UserEntity } from 'src/entities/user.entity'; | ||||
| import { Permission } from 'src/enum'; | ||||
| import { AccessRepository } from 'src/repositories/access.repository'; | ||||
| import { ActivityRepository } from 'src/repositories/activity.repository'; | ||||
| 
 | ||||
| export type AuthApiKey = { | ||||
| @ -12,6 +13,7 @@ export type AuthApiKey = { | ||||
| export type RepositoryInterface<T extends object> = Pick<T, keyof T>; | ||||
| 
 | ||||
| export type IActivityRepository = RepositoryInterface<ActivityRepository>; | ||||
| export type IAccessRepository = { [K in keyof AccessRepository]: RepositoryInterface<AccessRepository[K]> }; | ||||
| 
 | ||||
| export type ActivityItem = | ||||
|   | Awaited<ReturnType<IActivityRepository['create']>> | ||||
|  | ||||
| @ -2,7 +2,7 @@ import { BadRequestException, UnauthorizedException } from '@nestjs/common'; | ||||
| import { AuthDto } from 'src/dtos/auth.dto'; | ||||
| import { SharedLinkEntity } from 'src/entities/shared-link.entity'; | ||||
| import { AlbumUserRole, Permission } from 'src/enum'; | ||||
| import { IAccessRepository } from 'src/interfaces/access.interface'; | ||||
| import { AccessRepository } from 'src/repositories/access.repository'; | ||||
| import { setDifference, setIsEqual, setIsSuperset, setUnion } from 'src/utils/set'; | ||||
| 
 | ||||
| export type GrantedRequest = { | ||||
| @ -34,7 +34,7 @@ export const requireUploadAccess = (auth: AuthDto | null): AuthDto => { | ||||
|   return auth; | ||||
| }; | ||||
| 
 | ||||
| export const requireAccess = async (access: IAccessRepository, request: AccessRequest) => { | ||||
| export const requireAccess = async (access: AccessRepository, request: AccessRequest) => { | ||||
|   const allowedIds = await checkAccess(access, request); | ||||
|   if (!setIsEqual(new Set(request.ids), allowedIds)) { | ||||
|     throw new BadRequestException(`Not found or no ${request.permission} access`); | ||||
| @ -42,7 +42,7 @@ export const requireAccess = async (access: IAccessRepository, request: AccessRe | ||||
| }; | ||||
| 
 | ||||
| export const checkAccess = async ( | ||||
|   access: IAccessRepository, | ||||
|   access: AccessRepository, | ||||
|   { ids, auth, permission }: AccessRequest, | ||||
| ): Promise<Set<string>> => { | ||||
|   const idSet = Array.isArray(ids) ? new Set(ids) : ids; | ||||
| @ -56,7 +56,7 @@ export const checkAccess = async ( | ||||
| }; | ||||
| 
 | ||||
| const checkSharedLinkAccess = async ( | ||||
|   access: IAccessRepository, | ||||
|   access: AccessRepository, | ||||
|   request: SharedLinkAccessRequest, | ||||
| ): Promise<Set<string>> => { | ||||
|   const { sharedLink, permission, ids } = request; | ||||
| @ -102,7 +102,7 @@ const checkSharedLinkAccess = async ( | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| const checkOtherAccess = async (access: IAccessRepository, request: OtherAccessRequest): Promise<Set<string>> => { | ||||
| const checkOtherAccess = async (access: AccessRepository, request: OtherAccessRequest): Promise<Set<string>> => { | ||||
|   const { auth, permission, ids } = request; | ||||
| 
 | ||||
|   switch (permission) { | ||||
|  | ||||
| @ -5,12 +5,12 @@ import { UploadFieldName } from 'src/dtos/asset-media.dto'; | ||||
| import { AuthDto } from 'src/dtos/auth.dto'; | ||||
| import { AssetFileEntity } from 'src/entities/asset-files.entity'; | ||||
| import { AssetFileType, AssetType, Permission } from 'src/enum'; | ||||
| import { IAccessRepository } from 'src/interfaces/access.interface'; | ||||
| import { IAssetRepository } from 'src/interfaces/asset.interface'; | ||||
| import { IEventRepository } from 'src/interfaces/event.interface'; | ||||
| import { IPartnerRepository } from 'src/interfaces/partner.interface'; | ||||
| import { AuthRequest } from 'src/middleware/auth.guard'; | ||||
| import { ImmichFile } from 'src/middleware/file-upload.interceptor'; | ||||
| import { AccessRepository } from 'src/repositories/access.repository'; | ||||
| import { UploadFile } from 'src/services/asset-media.service'; | ||||
| import { checkAccess } from 'src/utils/access'; | ||||
| 
 | ||||
| @ -31,7 +31,7 @@ export const getAssetFiles = (files?: AssetFileEntity[]) => ({ | ||||
| 
 | ||||
| export const addAssets = async ( | ||||
|   auth: AuthDto, | ||||
|   repositories: { access: IAccessRepository; bulk: IBulkAsset }, | ||||
|   repositories: { access: AccessRepository; bulk: IBulkAsset }, | ||||
|   dto: { parentId: string; assetIds: string[] }, | ||||
| ) => { | ||||
|   const { access, bulk } = repositories; | ||||
| @ -71,7 +71,7 @@ export const addAssets = async ( | ||||
| 
 | ||||
| export const removeAssets = async ( | ||||
|   auth: AuthDto, | ||||
|   repositories: { access: IAccessRepository; bulk: IBulkAsset }, | ||||
|   repositories: { access: AccessRepository; bulk: IBulkAsset }, | ||||
|   dto: { parentId: string; assetIds: string[]; canAlwaysRemove: Permission }, | ||||
| ) => { | ||||
|   const { access, bulk } = repositories; | ||||
|  | ||||
| @ -1,18 +1,7 @@ | ||||
| import { IAccessRepository } from 'src/interfaces/access.interface'; | ||||
| import { IAccessRepository } from 'src/types'; | ||||
| import { Mocked, vitest } from 'vitest'; | ||||
| 
 | ||||
| export interface IAccessRepositoryMock { | ||||
|   activity: Mocked<IAccessRepository['activity']>; | ||||
|   asset: Mocked<IAccessRepository['asset']>; | ||||
|   album: Mocked<IAccessRepository['album']>; | ||||
|   authDevice: Mocked<IAccessRepository['authDevice']>; | ||||
|   memory: Mocked<IAccessRepository['memory']>; | ||||
|   person: Mocked<IAccessRepository['person']>; | ||||
|   partner: Mocked<IAccessRepository['partner']>; | ||||
|   stack: Mocked<IAccessRepository['stack']>; | ||||
|   timeline: Mocked<IAccessRepository['timeline']>; | ||||
|   tag: Mocked<IAccessRepository['tag']>; | ||||
| } | ||||
| export type IAccessRepositoryMock = { [K in keyof IAccessRepository]: Mocked<IAccessRepository[K]> }; | ||||
| 
 | ||||
| export const newAccessRepositoryMock = (): IAccessRepositoryMock => { | ||||
|   return { | ||||
|  | ||||
| @ -3,9 +3,10 @@ import { Writable } from 'node:stream'; | ||||
| import { PNG } from 'pngjs'; | ||||
| import { ImmichWorker } from 'src/enum'; | ||||
| import { IMetadataRepository } from 'src/interfaces/metadata.interface'; | ||||
| import { AccessRepository } from 'src/repositories/access.repository'; | ||||
| import { ActivityRepository } from 'src/repositories/activity.repository'; | ||||
| import { BaseService } from 'src/services/base.service'; | ||||
| import { IActivityRepository } from 'src/types'; | ||||
| import { IAccessRepository, IActivityRepository } from 'src/types'; | ||||
| import { newAccessRepositoryMock } from 'test/repositories/access.repository.mock'; | ||||
| import { newActivityRepositoryMock } from 'test/repositories/activity.repository.mock'; | ||||
| import { newAlbumUserRepositoryMock } from 'test/repositories/album-user.repository.mock'; | ||||
| @ -105,7 +106,7 @@ export const newTestService = <T extends BaseService>( | ||||
| 
 | ||||
|   const sut = new Service( | ||||
|     loggerMock, | ||||
|     accessMock, | ||||
|     accessMock as IAccessRepository as AccessRepository, | ||||
|     activityMock as IActivityRepository as ActivityRepository, | ||||
|     auditMock, | ||||
|     albumMock, | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user