refactor(server): narrow auth types (#16066)
This commit is contained in:
@@ -17,12 +17,10 @@ import {
|
||||
mapLoginResponse,
|
||||
} from 'src/dtos/auth.dto';
|
||||
import { UserAdminResponseDto, mapUserAdmin } from 'src/dtos/user.dto';
|
||||
import { SessionEntity } from 'src/entities/session.entity';
|
||||
import { UserEntity } from 'src/entities/user.entity';
|
||||
import { AuthType, ImmichCookie, ImmichHeader, ImmichQuery, Permission } from 'src/enum';
|
||||
import { OAuthProfile } from 'src/repositories/oauth.repository';
|
||||
import { BaseService } from 'src/services/base.service';
|
||||
import { AuthApiKey } from 'src/types';
|
||||
import { isGranted } from 'src/utils/access';
|
||||
import { HumanReadableSize } from 'src/utils/bytes';
|
||||
|
||||
@@ -298,11 +296,11 @@ export class AuthService extends BaseService {
|
||||
|
||||
const bytes = Buffer.from(key, key.length === 100 ? 'hex' : 'base64url');
|
||||
const sharedLink = await this.sharedLinkRepository.getByKey(bytes);
|
||||
if (sharedLink && (!sharedLink.expiresAt || new Date(sharedLink.expiresAt) > new Date())) {
|
||||
const user = sharedLink.user;
|
||||
if (user) {
|
||||
return { user, sharedLink };
|
||||
}
|
||||
if (sharedLink?.user && (!sharedLink.expiresAt || new Date(sharedLink.expiresAt) > new Date())) {
|
||||
return {
|
||||
user: sharedLink.user,
|
||||
sharedLink,
|
||||
};
|
||||
}
|
||||
throw new UnauthorizedException('Invalid share key');
|
||||
}
|
||||
@@ -310,10 +308,10 @@ export class AuthService extends BaseService {
|
||||
private async validateApiKey(key: string): Promise<AuthDto> {
|
||||
const hashedKey = this.cryptoRepository.hashSha256(key);
|
||||
const apiKey = await this.keyRepository.getKey(hashedKey);
|
||||
if (apiKey) {
|
||||
if (apiKey?.user) {
|
||||
return {
|
||||
user: apiKey.user as unknown as UserEntity,
|
||||
apiKey: apiKey as unknown as AuthApiKey,
|
||||
user: apiKey.user,
|
||||
apiKey,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -330,7 +328,6 @@ export class AuthService extends BaseService {
|
||||
private async validateSession(tokenValue: string): Promise<AuthDto> {
|
||||
const hashedToken = this.cryptoRepository.hashSha256(tokenValue);
|
||||
const session = await this.sessionRepository.getByToken(hashedToken);
|
||||
|
||||
if (session?.user) {
|
||||
const now = DateTime.now();
|
||||
const updatedAt = DateTime.fromJSDate(session.updatedAt);
|
||||
@@ -339,7 +336,10 @@ export class AuthService extends BaseService {
|
||||
await this.sessionRepository.update(session.id, { id: session.id, updatedAt: new Date() });
|
||||
}
|
||||
|
||||
return { user: session.user as unknown as UserEntity, session: session as unknown as SessionEntity };
|
||||
return {
|
||||
user: session.user,
|
||||
session,
|
||||
};
|
||||
}
|
||||
|
||||
throw new UnauthorizedException('Invalid user token');
|
||||
|
||||
@@ -19,7 +19,8 @@ export class DownloadService extends BaseService {
|
||||
const archives: DownloadArchiveInfo[] = [];
|
||||
let archive: DownloadArchiveInfo = { size: 0, assetIds: [] };
|
||||
|
||||
const preferences = getPreferences(auth.user);
|
||||
const metadata = await this.userRepository.getMetadata(auth.user.id);
|
||||
const preferences = getPreferences(auth.user.email, metadata);
|
||||
|
||||
const assetPagination = await this.getDownloadAssets(auth, dto);
|
||||
for await (const assets of assetPagination) {
|
||||
|
||||
@@ -276,7 +276,7 @@ export class NotificationService extends BaseService {
|
||||
return JobStatus.SKIPPED;
|
||||
}
|
||||
|
||||
const { emailNotifications } = getPreferences(recipient);
|
||||
const { emailNotifications } = getPreferences(recipient.email, recipient.metadata);
|
||||
|
||||
if (!emailNotifications.enabled || !emailNotifications.albumInvite) {
|
||||
return JobStatus.SKIPPED;
|
||||
@@ -340,7 +340,7 @@ export class NotificationService extends BaseService {
|
||||
continue;
|
||||
}
|
||||
|
||||
const { emailNotifications } = getPreferences(user);
|
||||
const { emailNotifications } = getPreferences(user.email, user.metadata);
|
||||
|
||||
if (!emailNotifications.enabled || !emailNotifications.albumUpdate) {
|
||||
continue;
|
||||
|
||||
@@ -106,21 +106,24 @@ export class UserAdminService extends BaseService {
|
||||
}
|
||||
|
||||
async getPreferences(auth: AuthDto, id: string): Promise<UserPreferencesResponseDto> {
|
||||
const user = await this.findOrFail(id, { withDeleted: false });
|
||||
const preferences = getPreferences(user);
|
||||
const { email } = await this.findOrFail(id, { withDeleted: true });
|
||||
const metadata = await this.userRepository.getMetadata(id);
|
||||
const preferences = getPreferences(email, metadata);
|
||||
return mapPreferences(preferences);
|
||||
}
|
||||
|
||||
async updatePreferences(auth: AuthDto, id: string, dto: UserPreferencesUpdateDto) {
|
||||
const user = await this.findOrFail(id, { withDeleted: false });
|
||||
const preferences = mergePreferences(user, dto);
|
||||
const { email } = await this.findOrFail(id, { withDeleted: false });
|
||||
const metadata = await this.userRepository.getMetadata(id);
|
||||
const preferences = getPreferences(email, metadata);
|
||||
const newPreferences = mergePreferences(preferences, dto);
|
||||
|
||||
await this.userRepository.upsertMetadata(user.id, {
|
||||
await this.userRepository.upsertMetadata(id, {
|
||||
key: UserMetadataKey.PREFERENCES,
|
||||
value: getPreferencesPartial(user, preferences),
|
||||
value: getPreferencesPartial({ email }, newPreferences),
|
||||
});
|
||||
|
||||
return mapPreferences(preferences);
|
||||
return mapPreferences(newPreferences);
|
||||
}
|
||||
|
||||
private async findOrFail(id: string, options: UserFindOptions) {
|
||||
|
||||
@@ -77,9 +77,9 @@ describe(UserService.name, () => {
|
||||
});
|
||||
|
||||
describe('getMe', () => {
|
||||
it("should get the auth user's info", () => {
|
||||
it("should get the auth user's info", async () => {
|
||||
const user = authStub.admin.user;
|
||||
expect(sut.getMe(authStub.admin)).toMatchObject({
|
||||
await expect(sut.getMe(authStub.admin)).resolves.toMatchObject({
|
||||
id: user.id,
|
||||
email: user.email,
|
||||
});
|
||||
|
||||
@@ -22,16 +22,24 @@ export class UserService extends BaseService {
|
||||
async search(auth: AuthDto): Promise<UserResponseDto[]> {
|
||||
const config = await this.getConfig({ withCache: false });
|
||||
|
||||
let users: UserEntity[] = [auth.user];
|
||||
let users;
|
||||
if (auth.user.isAdmin || config.server.publicUsers) {
|
||||
users = await this.userRepository.getList({ withDeleted: false });
|
||||
} else {
|
||||
const authUser = await this.userRepository.get(auth.user.id, {});
|
||||
users = authUser ? [authUser] : [];
|
||||
}
|
||||
|
||||
return users.map((user) => mapUser(user));
|
||||
}
|
||||
|
||||
getMe(auth: AuthDto): UserAdminResponseDto {
|
||||
return mapUserAdmin(auth.user);
|
||||
async getMe(auth: AuthDto): Promise<UserAdminResponseDto> {
|
||||
const user = await this.userRepository.get(auth.user.id, {});
|
||||
if (!user) {
|
||||
throw new BadRequestException('User not found');
|
||||
}
|
||||
|
||||
return mapUserAdmin(user);
|
||||
}
|
||||
|
||||
async updateMe({ user }: AuthDto, dto: UserUpdateMeDto): Promise<UserAdminResponseDto> {
|
||||
@@ -58,20 +66,23 @@ export class UserService extends BaseService {
|
||||
return mapUserAdmin(updatedUser);
|
||||
}
|
||||
|
||||
getMyPreferences({ user }: AuthDto): UserPreferencesResponseDto {
|
||||
const preferences = getPreferences(user);
|
||||
async getMyPreferences(auth: AuthDto): Promise<UserPreferencesResponseDto> {
|
||||
const metadata = await this.userRepository.getMetadata(auth.user.id);
|
||||
const preferences = getPreferences(auth.user.email, metadata);
|
||||
return mapPreferences(preferences);
|
||||
}
|
||||
|
||||
async updateMyPreferences({ user }: AuthDto, dto: UserPreferencesUpdateDto) {
|
||||
const preferences = mergePreferences(user, dto);
|
||||
async updateMyPreferences(auth: AuthDto, dto: UserPreferencesUpdateDto) {
|
||||
const metadata = await this.userRepository.getMetadata(auth.user.id);
|
||||
const current = getPreferences(auth.user.email, metadata);
|
||||
const updated = mergePreferences(current, dto);
|
||||
|
||||
await this.userRepository.upsertMetadata(user.id, {
|
||||
await this.userRepository.upsertMetadata(auth.user.id, {
|
||||
key: UserMetadataKey.PREFERENCES,
|
||||
value: getPreferencesPartial(user, preferences),
|
||||
value: getPreferencesPartial(auth.user, updated),
|
||||
});
|
||||
|
||||
return mapPreferences(preferences);
|
||||
return mapPreferences(updated);
|
||||
}
|
||||
|
||||
async get(id: string): Promise<UserResponseDto> {
|
||||
@@ -120,8 +131,10 @@ export class UserService extends BaseService {
|
||||
});
|
||||
}
|
||||
|
||||
getLicense({ user }: AuthDto): LicenseResponseDto {
|
||||
const license = user.metadata.find(
|
||||
async getLicense(auth: AuthDto): Promise<LicenseResponseDto> {
|
||||
const metadata = await this.userRepository.getMetadata(auth.user.id);
|
||||
|
||||
const license = metadata.find(
|
||||
(item): item is UserMetadataEntity<UserMetadataKey.LICENSE> => item.key === UserMetadataKey.LICENSE,
|
||||
);
|
||||
if (!license) {
|
||||
|
||||
Reference in New Issue
Block a user