2023-01-24 05:13:42 +01:00
|
|
|
import { SystemConfig } from '@app/infra/db/entities';
|
|
|
|
import {
|
|
|
|
BadRequestException,
|
|
|
|
Inject,
|
|
|
|
Injectable,
|
|
|
|
InternalServerErrorException,
|
|
|
|
Logger,
|
|
|
|
UnauthorizedException,
|
|
|
|
} from '@nestjs/common';
|
|
|
|
import { IncomingHttpHeaders } from 'http';
|
|
|
|
import { OAuthCore } from '../oauth/oauth.core';
|
|
|
|
import { INITIAL_SYSTEM_CONFIG, ISystemConfigRepository } from '../system-config';
|
2023-01-27 21:50:07 +01:00
|
|
|
import { IUserRepository, UserCore } from '../user';
|
|
|
|
import { AuthType } from './auth.constant';
|
2023-01-24 05:13:42 +01:00
|
|
|
import { AuthCore } from './auth.core';
|
|
|
|
import { ICryptoRepository } from './crypto.repository';
|
2023-01-27 21:50:07 +01:00
|
|
|
import { AuthUserDto, ChangePasswordDto, LoginCredentialDto, SignUpDto } from './dto';
|
2023-01-24 05:13:42 +01:00
|
|
|
import { AdminSignupResponseDto, LoginResponseDto, LogoutResponseDto, mapAdminSignupResponse } from './response-dto';
|
2023-01-27 21:50:07 +01:00
|
|
|
import { IUserTokenRepository, UserTokenCore } from '@app/domain/user-token';
|
2023-01-24 05:13:42 +01:00
|
|
|
|
|
|
|
@Injectable()
|
|
|
|
export class AuthService {
|
2023-01-27 21:50:07 +01:00
|
|
|
private userTokenCore: UserTokenCore;
|
2023-01-24 05:13:42 +01:00
|
|
|
private authCore: AuthCore;
|
|
|
|
private oauthCore: OAuthCore;
|
|
|
|
private userCore: UserCore;
|
|
|
|
|
|
|
|
private logger = new Logger(AuthService.name);
|
|
|
|
|
|
|
|
constructor(
|
|
|
|
@Inject(ICryptoRepository) private cryptoRepository: ICryptoRepository,
|
|
|
|
@Inject(ISystemConfigRepository) configRepository: ISystemConfigRepository,
|
|
|
|
@Inject(IUserRepository) userRepository: IUserRepository,
|
2023-01-27 21:50:07 +01:00
|
|
|
@Inject(IUserTokenRepository) userTokenRepository: IUserTokenRepository,
|
|
|
|
@Inject(INITIAL_SYSTEM_CONFIG)
|
|
|
|
initialConfig: SystemConfig,
|
2023-01-24 05:13:42 +01:00
|
|
|
) {
|
2023-01-27 21:50:07 +01:00
|
|
|
this.userTokenCore = new UserTokenCore(cryptoRepository, userTokenRepository);
|
|
|
|
this.authCore = new AuthCore(cryptoRepository, configRepository, userTokenRepository, initialConfig);
|
2023-01-24 05:13:42 +01:00
|
|
|
this.oauthCore = new OAuthCore(configRepository, initialConfig);
|
2023-01-27 21:50:07 +01:00
|
|
|
this.userCore = new UserCore(userRepository, cryptoRepository);
|
2023-01-24 05:13:42 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public async login(
|
|
|
|
loginCredential: LoginCredentialDto,
|
|
|
|
clientIp: string,
|
|
|
|
isSecure: boolean,
|
|
|
|
): Promise<{ response: LoginResponseDto; cookie: string[] }> {
|
|
|
|
if (!this.authCore.isPasswordLoginEnabled()) {
|
|
|
|
throw new UnauthorizedException('Password login has been disabled');
|
|
|
|
}
|
|
|
|
|
|
|
|
let user = await this.userCore.getByEmail(loginCredential.email, true);
|
|
|
|
if (user) {
|
2023-01-27 21:50:07 +01:00
|
|
|
const isAuthenticated = this.authCore.validatePassword(loginCredential.password, user);
|
2023-01-24 05:13:42 +01:00
|
|
|
if (!isAuthenticated) {
|
|
|
|
user = null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!user) {
|
|
|
|
this.logger.warn(`Failed login attempt for user ${loginCredential.email} from ip address ${clientIp}`);
|
|
|
|
throw new BadRequestException('Incorrect email or password');
|
|
|
|
}
|
|
|
|
|
|
|
|
return this.authCore.createLoginResponse(user, AuthType.PASSWORD, isSecure);
|
|
|
|
}
|
|
|
|
|
|
|
|
public async logout(authType: AuthType): Promise<LogoutResponseDto> {
|
|
|
|
if (authType === AuthType.OAUTH) {
|
|
|
|
const url = await this.oauthCore.getLogoutEndpoint();
|
|
|
|
if (url) {
|
|
|
|
return { successful: true, redirectUri: url };
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return { successful: true, redirectUri: '/auth/login?autoLaunch=0' };
|
|
|
|
}
|
|
|
|
|
|
|
|
public async changePassword(authUser: AuthUserDto, dto: ChangePasswordDto) {
|
|
|
|
const { password, newPassword } = dto;
|
|
|
|
const user = await this.userCore.getByEmail(authUser.email, true);
|
|
|
|
if (!user) {
|
|
|
|
throw new UnauthorizedException();
|
|
|
|
}
|
|
|
|
|
2023-01-27 21:50:07 +01:00
|
|
|
const valid = this.authCore.validatePassword(password, user);
|
2023-01-24 05:13:42 +01:00
|
|
|
if (!valid) {
|
|
|
|
throw new BadRequestException('Wrong password');
|
|
|
|
}
|
|
|
|
|
|
|
|
return this.userCore.updateUser(authUser, authUser.id, { password: newPassword });
|
|
|
|
}
|
|
|
|
|
|
|
|
public async adminSignUp(dto: SignUpDto): Promise<AdminSignupResponseDto> {
|
|
|
|
const adminUser = await this.userCore.getAdmin();
|
|
|
|
|
|
|
|
if (adminUser) {
|
|
|
|
throw new BadRequestException('The server already has an admin');
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
const admin = await this.userCore.createUser({
|
|
|
|
isAdmin: true,
|
|
|
|
email: dto.email,
|
|
|
|
firstName: dto.firstName,
|
|
|
|
lastName: dto.lastName,
|
|
|
|
password: dto.password,
|
|
|
|
});
|
|
|
|
|
|
|
|
return mapAdminSignupResponse(admin);
|
|
|
|
} catch (error) {
|
|
|
|
this.logger.error(`Unable to register admin user: ${error}`, (error as Error).stack);
|
|
|
|
throw new InternalServerErrorException('Failed to register new admin user');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-28 06:12:11 +01:00
|
|
|
public async validate(headers: IncomingHttpHeaders): Promise<AuthUserDto | null> {
|
2023-01-27 21:50:07 +01:00
|
|
|
const tokenValue = this.extractTokenFromHeader(headers);
|
|
|
|
if (!tokenValue) {
|
2023-01-28 06:12:11 +01:00
|
|
|
return null;
|
2023-01-24 05:13:42 +01:00
|
|
|
}
|
|
|
|
|
2023-01-27 21:50:07 +01:00
|
|
|
const hashedToken = this.cryptoRepository.hashSha256(tokenValue);
|
|
|
|
const user = await this.userTokenCore.getUserByToken(hashedToken);
|
|
|
|
if (user) {
|
|
|
|
return {
|
|
|
|
...user,
|
|
|
|
isPublicUser: false,
|
|
|
|
isAllowUpload: true,
|
|
|
|
isAllowDownload: true,
|
|
|
|
isShowExif: true,
|
|
|
|
};
|
2023-01-24 05:13:42 +01:00
|
|
|
}
|
|
|
|
|
2023-01-28 06:12:11 +01:00
|
|
|
return null;
|
2023-01-24 05:13:42 +01:00
|
|
|
}
|
|
|
|
|
2023-01-27 21:50:07 +01:00
|
|
|
extractTokenFromHeader(headers: IncomingHttpHeaders) {
|
|
|
|
return this.authCore.extractTokenFromHeader(headers);
|
2023-01-24 05:13:42 +01:00
|
|
|
}
|
|
|
|
}
|