112 lines
2.8 KiB
TypeScript
112 lines
2.8 KiB
TypeScript
import {
|
|
BadRequestException,
|
|
Injectable,
|
|
UnauthorizedException
|
|
} from '@nestjs/common';
|
|
import { InjectRepository } from '@nestjs/typeorm';
|
|
import { JWTToken, User, UserRole } from '../../entities';
|
|
import { LessThanOrEqual, Repository } from 'typeorm';
|
|
import * as argon2 from 'argon2';
|
|
import FileSystemService from '../filesystem';
|
|
import * as jwt from 'jsonwebtoken';
|
|
|
|
export const jwtSecret = 'CUM';
|
|
|
|
export interface jwtPayload {
|
|
sub: number;
|
|
jti: number;
|
|
exp?: number;
|
|
iat?: number;
|
|
}
|
|
|
|
@Injectable()
|
|
export default class BaseAuthService {
|
|
constructor(
|
|
@InjectRepository(User)
|
|
protected userRepo: Repository<User>,
|
|
@InjectRepository(JWTToken)
|
|
protected tokenRepo: Repository<JWTToken>,
|
|
protected fsService: FileSystemService
|
|
) {}
|
|
|
|
async getUser(userId: number): Promise<User | null> {
|
|
return this.userRepo.findOneBy({
|
|
id: userId
|
|
});
|
|
}
|
|
|
|
async findUser(username: string, gitlab: boolean): Promise<User | null> {
|
|
return this.userRepo.findOneBy({
|
|
name: username,
|
|
isGitlabUser: gitlab
|
|
});
|
|
}
|
|
|
|
async getToken(tokenId: number): Promise<JWTToken | null> {
|
|
return this.tokenRepo.findOneBy({
|
|
id: tokenId
|
|
});
|
|
}
|
|
|
|
async validateUser(username: string, pass: string): Promise<User | null> {
|
|
const user = await this.findUser(username, false);
|
|
if (!user)
|
|
throw new UnauthorizedException('Invalid username or password');
|
|
if (!(await argon2.verify(user.password, pass)))
|
|
throw new UnauthorizedException('Invalid username or password');
|
|
if (user.role == UserRole.DISABLED)
|
|
throw new UnauthorizedException('Account is disabled');
|
|
return user;
|
|
}
|
|
|
|
async cleanupTokens(): Promise<void> {
|
|
await this.tokenRepo.delete({
|
|
exp: LessThanOrEqual(Math.floor(Date.now() / 1000))
|
|
});
|
|
}
|
|
|
|
async login(req: Request, user: User) {
|
|
const token = new JWTToken();
|
|
token.ownerId = user.id;
|
|
const db_token = await this.tokenRepo.save(token);
|
|
const payload: jwtPayload = {
|
|
sub: user.id,
|
|
jti: db_token.id
|
|
};
|
|
const jwtToken = jwt.sign(payload, jwtSecret, {
|
|
mutatePayload: true,
|
|
expiresIn: '1d'
|
|
});
|
|
db_token.exp = payload.exp;
|
|
await this.tokenRepo.save(db_token);
|
|
return jwtToken;
|
|
}
|
|
|
|
async singupInternal(user: User): Promise<User> {
|
|
const root = await this.fsService.generateRoot(user);
|
|
user.rootId = root.id;
|
|
return this.userRepo.save(user);
|
|
}
|
|
|
|
async signup(username: string, password: string) {
|
|
if (await this.findUser(username, false))
|
|
throw new BadRequestException('User already exists');
|
|
const user = new User();
|
|
user.name = username;
|
|
user.password = await argon2.hash(password);
|
|
await this.singupInternal(await this.userRepo.save(user));
|
|
}
|
|
|
|
async revoke(token: JWTToken) {
|
|
await this.tokenRepo.delete({
|
|
id: token.id
|
|
});
|
|
}
|
|
|
|
async revokeAll(user: User) {
|
|
await this.tokenRepo.delete({
|
|
ownerId: user.id
|
|
});
|
|
}
|
|
}
|