feat: Creating the first user

This commit is contained in:
Aleksi Lassila
2024-06-02 02:59:38 +03:00
parent 052ea44548
commit db21aef3f3
12 changed files with 141 additions and 44 deletions

View File

@@ -8,11 +8,7 @@ import {
} from '@nestjs/common';
import { AuthService } from './auth.service';
import { SignInDto } from '../user/user.dto';
import {
ApiOkResponse,
ApiProperty,
ApiUnauthorizedResponse,
} from '@nestjs/swagger';
import { ApiOkResponse, ApiProperty } from '@nestjs/swagger';
import { ApiException } from '@nanogiants/nestjs-swagger-api-exception-decorator';
export class SignInResponse {

View File

@@ -18,6 +18,12 @@ export const GetUser = createParamDecorator(
},
);
function extractTokenFromHeader(request: Request): string | undefined {
const [type, token] =
(request.headers as any).authorization?.split(' ') ?? [];
return type === 'Bearer' ? token : undefined;
}
@Injectable()
export class AuthGuard implements CanActivate {
constructor(
@@ -27,7 +33,7 @@ export class AuthGuard implements CanActivate {
async canActivate(context: ExecutionContext): Promise<boolean> {
const request = context.switchToHttp().getRequest();
const token = this.extractTokenFromHeader(request);
const token = extractTokenFromHeader(request);
if (!token) {
throw new UnauthorizedException();
}
@@ -46,10 +52,34 @@ export class AuthGuard implements CanActivate {
}
return true;
}
}
private extractTokenFromHeader(request: Request): string | undefined {
const [type, token] =
(request.headers as any).authorization?.split(' ') ?? [];
return type === 'Bearer' ? token : undefined;
@Injectable()
export class OptionalAuthGuard implements CanActivate {
constructor(
private jwtService: JwtService,
private userService: UserService,
) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const request = context.switchToHttp().getRequest();
const token = extractTokenFromHeader(request);
if (!token) {
return true;
}
try {
const payload: AccessTokenPayload = await this.jwtService.verifyAsync(
token,
{
secret: JWT_SECRET,
},
);
if (payload.sub) {
request['user'] = await this.userService.findOne(payload.sub);
}
} catch {
return true;
}
return true;
}
}

View File

@@ -11,7 +11,7 @@ import { JWT_SECRET } from '../consts';
JwtModule.register({
global: true,
secret: JWT_SECRET,
signOptions: { expiresIn: '1d' },
signOptions: { expiresIn: '1y' },
}),
],
controllers: [AuthController],

View File

@@ -1,6 +1,7 @@
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { UserService } from '../user/user.service';
import { JwtService } from '@nestjs/jwt';
import { User } from '../user/user.entity';
export interface AccessTokenPayload {
sub: string;
@@ -19,7 +20,9 @@ export class AuthService {
): Promise<{
token: string;
}> {
const user = await this.userService.findOneByName(name);
let user = await this.userService.findOneByName(name);
if (!user && (await this.userService.noPreviousAdmins()))
user = await this.userService.create(name, password, true);
if (!(user && user.password === password)) {
throw new UnauthorizedException();

View File

@@ -1 +1 @@
export const JWT_SECRET = 'secret';
export const JWT_SECRET = Math.random().toString(36).substring(2, 15);

View File

@@ -8,13 +8,14 @@ import {
Param,
Post,
Put,
UnauthorizedException,
UseGuards,
} from '@nestjs/common';
import { UserService } from './user.service';
import { AuthGuard, GetUser } from '../auth/auth.guard';
import { ApiNotFoundResponse, ApiOkResponse, ApiTags } from '@nestjs/swagger';
import { AuthGuard, GetUser, OptionalAuthGuard } from '../auth/auth.guard';
import { ApiOkResponse, ApiTags } from '@nestjs/swagger';
import { CreateUserDto, UpdateUserDto, UserDto } from './user.dto';
import { Settings, User } from './user.entity';
import { User } from './user.entity';
import { ApiException } from '@nanogiants/nestjs-swagger-api-exception-decorator';
@ApiTags('user')
@@ -57,18 +58,29 @@ export class UserController {
return UserDto.fromEntity(user);
}
// @Get('isSetupDone')
// @ApiOkResponse({ description: 'Setup done', type: Boolean })
// async isSetupDone() {
// return this.userService.noPreviousAdmins();
// }
@UseGuards(OptionalAuthGuard)
@HttpCode(HttpStatus.OK)
@Post()
async create(
@Body()
userCreateDto: CreateUserDto,
@GetUser() callerUser: User | undefined,
) {
const canCreateAdmin = await this.userService.noPreviousAdmins();
const canCreateUser =
(await this.userService.noPreviousAdmins()) || callerUser?.isAdmin;
if (!canCreateUser) throw new UnauthorizedException();
const user = await this.userService.create(
userCreateDto.name,
userCreateDto.password,
canCreateAdmin && userCreateDto.isAdmin,
userCreateDto.isAdmin,
);
return UserDto.fromEntity(user);