Skip to content
Snippets Groups Projects
Commit 95ed7eca authored by Hugo SUBTIL's avatar Hugo SUBTIL
Browse files

feat: add admin module + add validation for claiming structures

parent acb79f25
No related branches found
No related tags found
3 merge requests!27Recette,!26Dev,!13feat(admin) : update model + add get endpoint
import { Test, TestingModule } from '@nestjs/testing';
import { AdminController } from './admin.controller';
describe('AdminController', () => {
let controller: AdminController;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [AdminController],
}).compile();
controller = module.get<AdminController>(AdminController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});
import { Body } from '@nestjs/common';
import { Controller, Get, Post, UseGuards } from '@nestjs/common';
import { ApiOperation } from '@nestjs/swagger';
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
import { Roles } from '../users/decorators/roles.decorator';
import { RolesGuard } from '../users/guards/roles.guard';
import { UsersService } from '../users/users.service';
import { PendingStructureDto } from './dto/pending-structure.dto';
@Controller('admin')
export class AdminController {
constructor(private usersService: UsersService) {}
@UseGuards(JwtAuthGuard, RolesGuard)
@Roles('admin')
@Get('pendingStructures')
@ApiOperation({ description: 'Get pending structre for validation' })
public getPendingAttachments() {
return this.usersService.getPendingStructures();
}
@UseGuards(JwtAuthGuard, RolesGuard)
@Roles('admin')
@Post('validatePendingStructure')
@ApiOperation({ description: 'Validate structure ownership' })
public validatePendingStructure(@Body() pendingStructureDto: PendingStructureDto) {
return this.usersService.validatePendingStructure(
pendingStructureDto.userEmail,
pendingStructureDto.structureId,
true
);
}
@UseGuards(JwtAuthGuard, RolesGuard)
@Roles('admin')
@Post('rejectPendingStructure')
@ApiOperation({ description: 'Refuse structure ownership' })
public refusePendingStructure(@Body() pendingStructureDto: PendingStructureDto) {
return this.usersService.validatePendingStructure(
pendingStructureDto.userEmail,
pendingStructureDto.structureId,
false
);
}
}
import { Module } from '@nestjs/common';
import { UsersModule } from '../users/users.module';
import { UsersService } from '../users/users.service';
import { AdminController } from './admin.controller';
import { AdminService } from './admin.service';
@Module({
imports: [UsersModule],
controllers: [AdminController],
providers: [AdminService],
})
export class AdminModule {}
import { Test, TestingModule } from '@nestjs/testing';
import { AdminService } from './admin.service';
describe('AdminService', () => {
let service: AdminService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [AdminService],
}).compile();
service = module.get<AdminService>(AdminService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});
import { Injectable } from '@nestjs/common';
@Injectable()
export class AdminService {}
import { ApiProperty } from '@nestjs/swagger';
import { IsEmail, IsNotEmpty, IsNumber } from 'class-validator';
export class PendingStructureDto {
@IsNotEmpty()
@IsEmail()
@ApiProperty({ type: String })
readonly userEmail: string;
@IsNotEmpty()
@IsNumber()
@ApiProperty({ type: Number })
readonly structureId: number;
}
......@@ -8,6 +8,7 @@ import { AuthModule } from './auth/auth.module';
import { UsersModule } from './users/users.module';
import { MailerModule } from './mailer/mailer.module';
import { TclModule } from './tcl/tcl.module';
import { AdminModule } from './admin/admin.module';
@Module({
imports: [
ConfigurationModule,
......@@ -20,6 +21,7 @@ import { TclModule } from './tcl/tcl.module';
UsersModule,
MailerModule,
TclModule,
AdminModule,
],
controllers: [AppController],
})
......
......@@ -21,5 +21,9 @@ export const config = {
ejs: 'resetPassword.ejs',
json: 'resetPassword.json',
},
adminStructureClaim: {
ejs: 'adminStructureClaim.ejs',
json: 'adminStructureClaim.json',
},
},
};
Bonjour<br />
<br />
Une nouvelle structure a été revendiquée. Pour valider ou refuser la demande, merci de vous rendre sur
<a href="<%= config.protocol %>://<%= config.host %><%= config.port ? ':' + config.port : '' %>/admin">ce lien</a>.
<br />
Ce mail est un mail automatique. Merci de ne pas y répondre.
{
"subject": "Nouvelle demande de revendication de structure"
}
......@@ -22,6 +22,7 @@ export class StructuresController {
}
@Put(':id')
//TODO: protect, only structure owner can edit it
public async update(@Param('id') id: number, @Body() body: structureDto) {
return this.structureService.update(id, body);
}
......
......@@ -26,6 +26,7 @@ export class StructuresService {
createdStructure.save();
user.structuresLink.push(createdStructure.id);
user.save();
return createdStructure;
}
......
......@@ -7,8 +7,6 @@ import { CreateUserDto } from './dto/create-user.dto';
import { PasswordResetApplyDto } from './dto/reset-password-apply.dto';
import { PasswordResetDto } from './dto/reset-password.dto';
import { UsersService } from './users.service';
import { RolesGuard } from './guards/roles.guard';
import { Roles } from './decorators/roles.decorator';
@Controller('users')
export class UsersController {
......@@ -87,11 +85,4 @@ export class UsersController {
public async resetPasswordApply(@Body() passwordResetApplyDto: PasswordResetApplyDto) {
return this.usersService.validatePasswordResetToken(passwordResetApplyDto.password, passwordResetApplyDto.token);
}
@UseGuards(JwtAuthGuard, RolesGuard)
@Roles('admin')
@Get('pendingStructures')
public getPendingAttachments() {
return this.usersService.getPendingStructures();
}
}
......@@ -36,6 +36,7 @@ export class UsersService {
// Send verification email
createUser = await this.verifyUserMail(createUser);
createUser.save();
this.sendAdminStructureValidationMail();
return await this.findOne(createUserDto.email);
}
......@@ -124,6 +125,25 @@ export class UsersService {
return user;
}
/**
* Generate activation token and send it to user by email, in order to validate
* a new account.
* @param user User
*/
private async sendAdminStructureValidationMail(): Promise<any> {
const config = this.mailerService.config;
const ejsPath = this.mailerService.getTemplateLocation(config.templates.adminStructureClaim.ejs);
const jsonConfig = this.mailerService.loadJsonConfig(config.templates.adminStructureClaim.json);
const html = await ejs.renderFile(ejsPath, {
config,
});
const admins = await this.getAdmins();
admins.forEach((admin) => {
this.mailerService.send(admin.email, jsonConfig.subject, html);
});
}
/**
* Check that the given token is associated to userId. If it's true, validate user account.
* @param userId string
......@@ -241,11 +261,16 @@ export class UsersService {
throw new HttpException('Invalid token', HttpStatus.UNAUTHORIZED);
}
public async getAdmins(): Promise<User[]> {
return this.userModel.find({ role: 1 }).exec();
}
public async updateStructureLinked(userEmail: string, idStructure: number): Promise<any> {
const user = await this.findOne(userEmail, true);
if (user) {
user.pendingStructuresLink.push(idStructure);
user.save();
this.sendAdminStructureValidationMail();
return user.pendingStructuresLink;
}
throw new HttpException('Invalid user', HttpStatus.NOT_FOUND);
......@@ -268,4 +293,32 @@ export class UsersService {
});
return structuresPending;
}
/**
* Validate or refuse a pending structure given a email and structure id
*/
public async validatePendingStructure(
userEmail: string,
structureId: number,
validate: boolean
): Promise<{ userEmail: string; structureId: number }[]> {
const users = await this.findOne(userEmail);
if (!users) {
throw new HttpException('User not found', HttpStatus.NOT_FOUND);
}
if (users.pendingStructuresLink.includes(structureId)) {
users.pendingStructuresLink = users.pendingStructuresLink.filter((item) => item !== structureId);
// If it's a validation case, push structureId into validated user structures
if (validate) {
users.structuresLink.push(structureId);
}
await users.save();
return this.getPendingStructures();
} else {
throw new HttpException(
'Cannot validate strucutre. It might have been already validate, or the structure does`nt belong to the user',
HttpStatus.NOT_FOUND
);
}
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment