147 lines
3.8 KiB
TypeScript
147 lines
3.8 KiB
TypeScript
import { Inject, Injectable } from '@nestjs/common';
|
|
import { Repository } from 'typeorm';
|
|
import { UserToken, UserTokenType } from './user-token.entity';
|
|
import { UserTOTPToken } from './user-totp-token.entity';
|
|
import { User } from './user.entity';
|
|
import speakeasy from '@levminer/speakeasy';
|
|
|
|
import * as bcrypt from 'bcrypt';
|
|
import { TokenService } from 'src/modules/utility/services/token.service';
|
|
|
|
@Injectable()
|
|
export class UserService {
|
|
constructor(
|
|
@Inject('USER_REPOSITORY')
|
|
private userRepository: Repository<User>,
|
|
@Inject('USER_TOKEN_REPOSITORY')
|
|
private userTokenRepository: Repository<UserToken>,
|
|
@Inject('USER_TOTP_TOKEN_REPOSITORY')
|
|
private userTOTPTokenRepository: Repository<UserTOTPToken>,
|
|
private token: TokenService,
|
|
) {}
|
|
|
|
public async getById(id: number): Promise<User> {
|
|
return this.userRepository.findOne({ id });
|
|
}
|
|
|
|
public async getByUUID(uuid: string): Promise<User> {
|
|
return this.userRepository.findOne({ uuid });
|
|
}
|
|
|
|
public async getByEmail(email: string): Promise<User> {
|
|
return this.userRepository.findOne({ email });
|
|
}
|
|
|
|
public async getByUsername(username: string): Promise<User> {
|
|
return this.userRepository.findOne({ username });
|
|
}
|
|
|
|
public async get(input: string | number): Promise<User> {
|
|
if (typeof input === 'number') {
|
|
return this.getById(input);
|
|
}
|
|
|
|
if (input.includes('@')) {
|
|
return this.getByEmail(input);
|
|
}
|
|
|
|
if (input.length === 36 && input.includes('-')) {
|
|
return this.getByUUID(input);
|
|
}
|
|
|
|
return this.getByUsername(input);
|
|
}
|
|
|
|
public async comparePasswords(
|
|
hash: string,
|
|
password: string,
|
|
): Promise<boolean> {
|
|
return bcrypt.compare(password, hash);
|
|
}
|
|
|
|
public async hashPassword(password: string): Promise<string> {
|
|
const salt = await bcrypt.genSalt(10);
|
|
return bcrypt.hash(password, salt);
|
|
}
|
|
|
|
/**
|
|
* Check if the user has TOTP enabled
|
|
* @param user User object
|
|
* @returns true if the user has TOTP enabled
|
|
*/
|
|
public async userHasTOTP(user: User): Promise<boolean> {
|
|
return !!(await this.getUserTOTP(user));
|
|
}
|
|
|
|
/**
|
|
* Get the TOTP token of a user
|
|
* @param user User object
|
|
* @returns TOTP token
|
|
*/
|
|
public async getUserTOTP(user: User): Promise<UserTOTPToken> {
|
|
return this.userTOTPTokenRepository.findOne({
|
|
user,
|
|
activated: true,
|
|
});
|
|
}
|
|
|
|
public validateTOTP(secret: string, token: string): boolean {
|
|
return speakeasy.totp.verify({
|
|
secret,
|
|
encoding: 'base32',
|
|
token,
|
|
window: 6,
|
|
});
|
|
}
|
|
|
|
public async createUserToken(
|
|
user: User,
|
|
type: UserTokenType,
|
|
expiresAt?: Date,
|
|
): Promise<UserToken> {
|
|
const token = new UserToken();
|
|
token.token = this.token.generateString(64);
|
|
token.user = user;
|
|
token.type = type;
|
|
token.expires_at = expiresAt;
|
|
await this.userTokenRepository.save(token);
|
|
return token;
|
|
}
|
|
|
|
public async sendActivationEmail(user: User): Promise<void> {
|
|
const activationToken = await this.createUserToken(
|
|
user,
|
|
UserTokenType.ACTIVATION,
|
|
new Date(Date.now() + 3600 * 1000),
|
|
);
|
|
}
|
|
|
|
public async userRegistration(newUserInfo: {
|
|
username: string;
|
|
display_name: string;
|
|
email: string;
|
|
password: string;
|
|
}): Promise<User> {
|
|
if (!!(await this.getByEmail(newUserInfo.email))) {
|
|
throw new Error('Email is already in use!');
|
|
}
|
|
|
|
if (!!(await this.getByUsername(newUserInfo.username))) {
|
|
throw new Error('Username is already in use!');
|
|
}
|
|
|
|
const hashword = await this.hashPassword(newUserInfo.password);
|
|
const user = new User();
|
|
user.email = newUserInfo.email;
|
|
user.uuid = this.token.createUUID();
|
|
user.username = newUserInfo.username;
|
|
user.display_name = newUserInfo.display_name;
|
|
user.password = hashword;
|
|
await this.userRepository.insert(user);
|
|
|
|
// TODO: activation email
|
|
|
|
return user;
|
|
}
|
|
}
|