Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/doc_developers/api/test.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ describe('E2E Spec', () => {
let app: INestApplication;
beforeAll(async () => {
// Création de l'app
app = (await Test.createTestingModule({ imports: [AppModule] }).compile()).createNestApplication();
app = (await Test.createTestingModule({ imports: [AppModule.register()] }).compile()).createNestApplication();
// ... Reste de l'initialisation de l'app.
});
GetUserFromIdE2ESpec(() => app);
Expand Down
1 change: 0 additions & 1 deletion migration/etuutt_old/modules/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ export async function migrateUsers(
prisma.user.create({
data: {
login: user.login,
hash: '', // it is not possible to migrate the password, it's not possible to hash a password to an empty string
studentId: user.studentId,
firstName: user.firstName,
lastName: user.lastName,
Expand Down
1 change: 0 additions & 1 deletion prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -543,7 +543,6 @@ model UeWorkTime {
model User {
id String @id @default(uuid())
login String @unique @db.VarChar(50)
hash String?
// TODO : maybe a field accountType (that is either CAS or login-password), but this may be implemented in the centralized authentication
studentId Int?
firstName String
Expand Down
4 changes: 0 additions & 4 deletions prisma/seed/modules/user.seed.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { PrismaClient, Sex } from '@prisma/client';
import { RawUser } from '../../../src/prisma/types';
import { faker } from '@faker-js/faker';
import * as bcrypt from 'bcryptjs';
import { DEFAULT_APPLICATION } from '../utils';
import { AuthService } from '../../../src/auth/auth.service';

Expand All @@ -10,8 +9,6 @@ const FAKER_ROUNDS = 100;
export async function userSeed(prisma: PrismaClient): Promise<RawUser[]> {
console.log('Seeding users...');
const users: Promise<RawUser>[] = [];
const saltRounds = Number.parseInt(process.env.SALT_ROUNDS);
const hash = await bcrypt.hash('etuutt', saltRounds);
for (let i = 0; i < FAKER_ROUNDS; i++) {
const firstName = faker.person.firstName();
const lastName = faker.person.lastName();
Expand All @@ -23,7 +20,6 @@ export async function userSeed(prisma: PrismaClient): Promise<RawUser[]> {
studentId: faker.number.int({ max: 99999 }),
login: i === 0 ? 'student' : faker.internet.username(),
userType: 'STUDENT',
hash,
apiKeys: {
create: {
token: AuthService.generateToken(),
Expand Down
2 changes: 1 addition & 1 deletion scripts/dependency_graph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { SpelunkerModule } from 'nestjs-spelunker';
import * as fs from 'fs';

async function bootstrap() {
const app = await NestFactory.create(AppModule);
const app = await NestFactory.create(AppModule.register());
await generateDependencyGraph(app);
}
bootstrap();
Expand Down
91 changes: 54 additions & 37 deletions src/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Module } from '@nestjs/common';
import { DynamicModule, INestApplication, VersioningType } from '@nestjs/common';
import { AuthModule } from './auth/auth.module';
import { PrismaModule } from './prisma/prisma.module';
import { ProfileModule } from './profile/profile.module';
Expand All @@ -15,40 +15,57 @@ import { BranchModule } from './branch/branch.module';
import { AssosModule } from './assos/assos.module';
import { TranslationInterceptor } from './app.interceptor';
import { SemesterModule } from './semester/semester.module';
import { AppValidationPipe } from './app.pipe';
import { NotFoundFilter } from './exceptions';

@Module({
imports: [
ConfigModule,
HttpModule,
PrismaModule,
SemesterModule,
AuthModule,
ProfileModule,
UsersModule,
UeModule,
TimetableModule,
BranchModule,
AssosModule,
],
// The providers below are used for all the routes of the api.
// For example, the JwtGuard is used for all the routes and checks whether the user is authenticated.
providers: [
{
provide: APP_GUARD,
useClass: JwtGuard,
},
{
provide: APP_GUARD,
useClass: PermissionGuard,
},
{
provide: APP_GUARD,
useClass: RoleGuard,
},
{
provide: APP_INTERCEPTOR,
useClass: TranslationInterceptor,
},
],
})
export class AppModule {}
export class AppModule {
static register(): DynamicModule {
return {
module: AppModule,
imports: [
ConfigModule,
HttpModule,
PrismaModule,
SemesterModule,
AuthModule.register(),
ProfileModule,
UsersModule,
UeModule,
TimetableModule,
BranchModule,
AssosModule,
],
// The providers below are used for all the routes of the api.
// For example, the JwtGuard is used for all the routes and checks whether the user is authenticated.
providers: [
{
provide: APP_GUARD,
useClass: JwtGuard,
},
{
provide: APP_GUARD,
useClass: PermissionGuard,
},
{
provide: APP_GUARD,
useClass: RoleGuard,
},
{
provide: APP_INTERCEPTOR,
useClass: TranslationInterceptor,
},
],
}
}
static initApp(app: INestApplication) {
app.enableVersioning({
type: VersioningType.URI,
defaultVersion: '1',
});
// This env variable is not set in ConfigModule because we use it before modules are loaded
app.setGlobalPrefix(process.env.API_PREFIX);
app.useGlobalPipes(new AppValidationPipe());
app.useGlobalFilters(new NotFoundFilter())
app.enableCors({ origin: '*' });
}
}
3 changes: 1 addition & 2 deletions src/app.pipe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ import {
PipeTransform,
ValidationPipe,
} from '@nestjs/common';
import { AppException, ERROR_CODE } from './exceptions';
import { validationExceptionFactory } from './validation';
import { AppException, ERROR_CODE, validationExceptionFactory } from './exceptions';

/**
* A validating pipe for regex.
Expand Down
4 changes: 4 additions & 0 deletions src/auth/application/application.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,8 @@ export default class ApplicationService {
});
return this.authService.signAuthenticationToken(updatedApiKey.token, tokenExpiresIn);
}

formatRedirectUrl(application: Application, validationToken: string): string {
return `${application.redirectUrl}?${new URLSearchParams({ token: validationToken }).toString()}`;
}
}
72 changes: 72 additions & 0 deletions src/auth/auth-debug.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { IsPublic } from './decorator';
import { Body, Controller, HttpCode, HttpStatus, Post } from '@nestjs/common';
import { ApiCreatedResponse, ApiOkResponse, ApiOperation } from '@nestjs/swagger';
import { AppException, ERROR_CODE } from '../exceptions';
import { ApiAppErrorResponse } from '../app.dto';
import AuthSignUpDebugReqDto from './dto/req/auth-sign-up-debug-req.dto';
import { GetApplication } from './decorator/get-application.decorator';
import { Application } from './application/interfaces/application.interface';
import AuthSignInDebugResDto from './dto/res/auth-sign-in-debug-res.dto';
import AuthSignInDebugReqDto from './dto/req/auth-sign-in-debug-req.dto';
import { AuthService } from './auth.service';
import { ConfigModule } from '../config/config.module';
import ApplicationService from './application/application.service';

@Controller({ path: 'auth', version: 'dev' })
export class AuthDebugController {
constructor(private authService: AuthService, private config: ConfigModule, private applicationService: ApplicationService) {
}

@IsPublic()
@Post('signup')
@ApiOperation({
description: 'Signs up the user, and returns an authentication token. This token should be used as a Bearer token.',
})
@ApiCreatedResponse({
description: 'The account was created successfully, the user is now authenticated and the token is returned.',
type: AuthSignInDebugResDto,
})
@ApiAppErrorResponse(
ERROR_CODE.CREDENTIALS_ALREADY_TAKEN,
'Login, email address or any field that should be unique is already taken',
)
async debugSignUp(@Body() dto: AuthSignUpDebugReqDto, @GetApplication() application: Application): Promise<AuthSignInDebugResDto> {
const token = await this.authService.signUp(dto, application.id, false, dto.tokenExpiresIn);
const redirectUrl = `${application.redirectUrl}/${token}`;
return { signedIn: true, token, redirectUrl };
}

@HttpCode(HttpStatus.OK)
@IsPublic()
@Post('signin')
@ApiOperation({
description: 'Signs in the user, and returns an authentication token. This token should be used as a Bearer token.',
})
@ApiOkResponse({
description: 'The user was successfully authenticated, the token is returned.',
type: AuthSignInDebugResDto,
})
@ApiAppErrorResponse(ERROR_CODE.INVALID_CREDENTIALS, 'Either the login or the password is incorrect')
async debugSignIn(@Body() dto: AuthSignInDebugReqDto, @GetApplication() application: Application): Promise<AuthSignInDebugResDto> {
const res = await this.authService.signInFromLogin(dto.login, application.id);
if (!res) throw new AppException(ERROR_CODE.INVALID_CREDENTIALS);
if (!res.apiKey)
return {
signedIn: false,
token: await this.authService.signRegisterApiKeyToken(res.userId, application.id, dto.tokenExpiresIn),
redirectUrl: null,
};
if (application.id === this.config.ETUUTT_WEBSITE_APPLICATION_ID)
return {
signedIn: true,
token: await this.authService.signApiKey(res.apiKey.id, dto.tokenExpiresIn),
redirectUrl: null,
};
const token = await this.authService.signValidationToken(res.apiKey.id, application.id, dto.tokenExpiresIn);
return {
signedIn: true,
token: null,
redirectUrl: this.applicationService.formatRedirectUrl(application, token),
};
}
}
Loading
Loading