Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • web-et-numerique/factory/pamn_plateforme-des-acteurs-de-la-mediation-numerique/pamn_server
1 result
Show changes
Commits on Source (6)
Showing
with 172 additions and 94 deletions
## Description du problème
_Donnez une briève description du problème_
## L'environnement
#### Utilisez vous l'application sur :
- [ ] Mobile
- [ ] Ordinateur
##### En cas de mobile
###### Quel type de mobile utilisez-vous?
- [ ] Android
- [ ] Iphone
###### Quel navigateur utilisez-vous?
- [ ] Chrome
- [ ] Safari
- [ ] Autre
##### En cas d'ordinateur
###### Quel navigateur utilisez-vous?
- [ ] Chrome
- [ ] Firefox
- [ ] Safari
- [ ] Autre
## Le bug
#### Quelles sont les étapes qui ont menées au problème?
_Donnez une description des étapes, il est fortemment conseillé de l'accompagner par des captures d'écran_
#### Quel est le comportement obtenu?
_Donnez une description du comportement obtenu, il est fortemment conseillé de l'accompagner par des captures d'écran_
#### Quel est le comportement attendu?
_Donnez une description du comportement attendu_
/title [Scope] Description
[[_TOC_]]
### Résumé du problème
## Contexte
_Donnez une description briève du problème._
## Objectif
### Les étapes pour reproduire le bug
## Ressources
_Listez les étapes qui vous permettent de reproduire ce bug, cette étape est très importante._
### Décrivez le comportement du bug ?
### Quel serez le comportement attendu ?
### Logs et/ou screenshots
### Possible fixes
/label ~"type::bug"
## Tâches
......@@ -361,4 +361,10 @@ describe('AdminController', () => {
it('should find unverified users', async () => {
expect((await controller.findUnVerifiedUsers()).length).toBe(0);
});
describe('should test getDeletedStructures()', () => {
it('should find getDeletedStructures', async () => {
expect((await controller.getDeletedStructures()).length).toBeGreaterThan(0);
});
});
});
......@@ -309,4 +309,16 @@ export class AdminController {
await this.usersService.updateUserEmployer(userDocument._id, employerDocument);
return this.usersService.findById(setUserEmployer.userId);
}
@UseGuards(JwtAuthGuard, RolesGuard)
@Roles('admin')
@ApiBearerAuth('JWT')
@ApiOperation({ description: 'Get deleted structures' })
@Get('getDeletedStructures')
public async getDeletedStructures() {
this.logger.debug('getDeletedStructures');
const deletedStructures = await this.structuresService.findAll(true);
this.logger.debug(`Found ${deletedStructures.length} deleted structures`);
return deletedStructures;
}
}
......@@ -6,6 +6,7 @@ import { UsersModule } from '../users/users.module';
import { AuthController } from './auth.controller';
import { AuthService } from './auth.service';
import { JwtStrategy } from './strategy/jwt.strategy';
import { AnonymousStrategy } from './strategy/anonymous.strategy';
config();
@Module({
......@@ -17,7 +18,7 @@ config();
signOptions: { expiresIn: '86400s' }, // 24h validity
}),
],
providers: [AuthService, JwtStrategy],
providers: [AuthService, JwtStrategy, AnonymousStrategy],
controllers: [AuthController],
})
export class AuthModule {}
import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { Strategy } from 'passport';
@Injectable()
export class AnonymousStrategy extends PassportStrategy(Strategy, 'anonymous') {
constructor() {
super();
}
authenticate() {
return this.success({});
}
}
......@@ -23,7 +23,7 @@ export function rewriteGhostImgUrl(configService: ConfigurationService, itemData
}
export function hasAdminRole(user: User): boolean {
return user.role === UserRole.admin;
return user?.role === UserRole.admin;
}
const analyzer: Record<string, AnalysisAnalyzer> = {
......
......@@ -427,11 +427,15 @@ describe('StructuresService', () => {
expect(service.findAll()).toBeTruthy();
});
it('should find all deleted structures', () => {
expect(service.findAll(true)).toBeTruthy();
});
it('should find all unclaimed structures', () => {
expect(service.findAllUnclaimed()).toBeTruthy();
});
it('should find all formated structures', () => {
it('should find all formatted structures', () => {
expect(service.findAllFormated(null)).toBeTruthy();
});
});
......
......@@ -220,10 +220,13 @@ export class StructuresService {
});
}
public async findAll(): Promise<StructureDocument[]> {
/**
* @param deleted boolean to get deleted structures
*/
public async findAll(deleted = false): Promise<StructureDocument[]> {
this.logger.debug('findAll');
return this.structureModel
.find({ deletedAt: { $exists: false }, accountVerified: true })
.find({ deletedAt: { $exists: deleted }, accountVerified: true })
.populate('structureType')
.exec();
}
......@@ -891,18 +894,12 @@ export class StructuresService {
return structure;
}
public async findWithOwners(
idStructure: string,
emailUser: string
): Promise<{ structure: Structure; owners: IUser[] }> {
public async findWithOwners(idStructure: string): Promise<{ structure: Structure; owners: IUser[] }> {
const structure = await this.findOne(idStructure);
if (!structure) {
throw new HttpException('Invalid structure id', HttpStatus.BAD_REQUEST);
}
const owners =
typeof emailUser == 'undefined'
? await this.userService.getStructureOwnersDetails(structure._id, '')
: await this.userService.getStructureOwnersDetails(structure._id, emailUser);
const owners = await this.userService.getStructureOwnersDetails(structure._id);
return { structure: structure, owners: owners };
}
......@@ -911,7 +908,7 @@ export class StructuresService {
if (!structure) {
throw new HttpException('Invalid structure id', HttpStatus.BAD_REQUEST);
}
const owners = await this.userService.getStructureOwnersDetails(structure._id, '');
const owners = await this.userService.getStructureOwnersDetails(structure._id);
const admins = await this.userService.getAdmins();
const emails = owners.map((owner) => owner.email).concat(admins.map((admin) => admin.email));
const uniqueEmails = [...new Set(emails)];
......
......@@ -10,6 +10,7 @@ import { UsersServiceMock } from '../../test/mock/services/user.mock.service';
import { CategoriesService } from '../categories/services/categories.service';
import { PersonalOffersService } from '../personal-offers/personal-offers.service';
import { TempUserService } from '../temp-user/temp-user.service';
import { UserRole } from '../users/enum/user-role.enum';
import { UsersService } from '../users/services/users.service';
import { PersonalOffersServiceMock } from './../../test/mock/services/personalOffers.mock.service';
import { PersonalOffer, PersonalOfferDocument } from './../personal-offers/schemas/personal-offer.schema';
......@@ -161,15 +162,32 @@ describe('AuthController', () => {
expect(res).toBeTruthy();
});
it('should find struct', async () => {
let res = controller.find('6093ba0e2ab5775cfc01ed3e');
expect(res).toBeTruthy();
res = controller.find('');
expect(res).toBeTruthy();
describe('find(structureId)', () => {
it('should find structure', async () => {
let res = controller.find({ user: {} }, '6093ba0e2ab5775cfc01ed3e');
expect(res).toBeTruthy();
res = controller.find({ user: {} }, '');
expect(res).toBeTruthy();
});
it('should find deleted structure as an admin', async () => {
const response = controller.find({ user: { role: UserRole.admin } }, '6093ba0e2ab5775cfc01ed33');
expect(response).toBeTruthy();
expect((await response).deletedAt).toBeTruthy();
});
it('should not find deleted structure as anonymous', async () => {
try {
await controller.find({ user: {} }, '6093ba0e2ab5775cfc01ed33');
expect(true).toBe(false);
} catch (error) {
expect(error.status).toEqual(HttpStatus.NOT_FOUND);
}
});
});
it('should find struct with owners', async () => {
const res = controller.findWithOwners('6093ba0e2ab5775cfc01ed3e', { emailUser: 'pauline.dupont@mii.com' });
const res = controller.findWithOwners('6093ba0e2ab5775cfc01ed3e');
expect(res).toBeTruthy();
});
......
......@@ -15,17 +15,18 @@ import {
Request,
UseGuards,
} from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { ApiOperation, ApiParam, ApiResponse, ApiTags } from '@nestjs/swagger';
import * as _ from 'lodash';
import { Types } from 'mongoose';
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
import { CategoriesService } from '../categories/services/categories.service';
import { hasAdminRole } from '../shared/utils';
import { CreateTempUserDto } from '../temp-user/dto/create-temp-user.dto';
import { ITempUser } from '../temp-user/temp-user.interface';
import { TempUser } from '../temp-user/temp-user.schema';
import { TempUserService } from '../temp-user/temp-user.service';
import { Roles } from '../users/decorators/roles.decorator';
import { UserRole } from '../users/enum/user-role.enum';
import { IsStructureOwnerGuard } from '../users/guards/isStructureOwner.guard';
import { RolesGuard } from '../users/guards/roles.guard';
import { pendingStructuresLink } from '../users/interfaces/pendingStructure';
......@@ -158,20 +159,20 @@ export class StructuresController {
}
@Get(':id')
public async find(@Param('id') id: string) {
@UseGuards(AuthGuard(['jwt', 'anonymous'])) // first success wins (allow anonymous call, req.user is an empty object if user is not authenticated)
public async find(@Request() req, @Param('id') id: string) {
this.logger.debug(`find with ${id}`);
const result = await this.structureService.findOne(id);
if (!result || result.deletedAt) {
const structure = await this.structureService.findOne(id);
if (!structure || (structure.deletedAt && !hasAdminRole(req.user))) {
this.logger.warn(`structure with ${id} does not exist`);
throw new HttpException('Structure does not exist', HttpStatus.NOT_FOUND);
} else {
return result;
}
return structure;
}
@Post(':id/withOwners')
public async findWithOwners(@Param('id') id: string, @Body() data: { emailUser: string }) {
return this.structureService.findWithOwners(id, data.emailUser);
@Get(':id/withOwners')
public async findWithOwners(@Param('id') id: string) {
return this.structureService.findWithOwners(id);
}
@Get(':id/tempUsers')
......@@ -206,7 +207,7 @@ export class StructuresController {
const otherOwners: IUser[] = (await this.userService.getStructureOwners(id)).filter((owner) => {
return !owner._id.equals(req.user._id);
});
if (otherOwners.length === 0 || req.user.role === UserRole.admin) {
if (otherOwners.length === 0 || hasAdminRole(req.user)) {
return this.structureService.deleteOne(structure);
} else {
return this.structureService.setToBeDeleted(req.user, structure);
......
......@@ -345,7 +345,7 @@ describe('UsersService', () => {
const userDetailsMock: IUser[] = userDetails;
it('should get Structure Owners Details', async () => {
jest.spyOn(service, 'getStructureOwnersDetails').mockResolvedValue(userDetails);
expect(await service.getStructureOwnersDetails('627b85aea0466f0f132e1599', null)).toEqual(userDetailsMock);
expect(await service.getStructureOwnersDetails('627b85aea0466f0f132e1599')).toEqual(userDetailsMock);
});
});
......
......@@ -745,9 +745,9 @@ export class UsersService {
return user.deleteOne();
}
public async getStructureOwnersDetails(structureId: string, emailUser: string): Promise<IUser[]> {
public async getStructureOwnersDetails(structureId: string): Promise<IUser[]> {
return this.userModel
.find({ structuresLink: new Types.ObjectId(structureId), email: { $ne: emailUser } })
.find({ structuresLink: new Types.ObjectId(structureId) })
.populate('job')
.populate('employer')
.select('name surname job employer email')
......
......@@ -262,6 +262,87 @@ export class StructuresServiceMock {
};
}
if (id.toString() === '6093ba0e2ab5775cfc01ed33') {
return {
_id: '6903ba0e2ab5775cfc01ed4d',
structureType: null,
categories: {
accessModality: ['accesLibre'],
labelsQualifications: [
'monEspaceSante',
'numRelay',
'pix',
'passNumerique',
'fabriqueDeTerritoire',
'aidantsConnect',
'espacePublicNumeriqueepn',
'maisonFranceService',
'conseillerNumFranceServices',
],
onlineProcedures: [
'idDoc',
'training',
'health',
'work',
'caf',
'cpam',
'foreigners',
'needs',
'franceConnect',
'taxes',
'housing',
'retirement',
'scolarity',
'transport',
'autres',
],
selfServiceMaterial: ['wifiEnAccesLibre', 'computer', 'printer', 'scanner'],
solidarityMaterial: [],
baseSkills: ['computer', 'internet'],
advancedSkills: [],
age: ['young', 'family'],
languageAndIlliteracy: ['illettrisme', 'english'],
handicaps: ['physicalDisability'],
publicOthers: ['uniquementFemmes'],
},
freeWorkShop: 'Non',
structureName: "L'Atelier Numérique",
description:
"L'Atelier Numérique est l'Espace Public Numérique des Centres Sociaux de Rillieux-la-Pape, ayant pour mission la médiation numérique pour toutes et tous.",
lockdownActivity:
'accesLibres, permanences numériques téléphoniques, cours et ateliers à distance, formations professionnelles.',
contactPhone: '',
contactMail: '',
website: '',
facebook: null,
twitter: null,
instagram: null,
pmrAccess: true,
exceptionalClosures: '',
publicsAccompaniment: [],
autresAccompagnements: '',
nbComputers: 16,
nbPrinters: 1,
__v: 0,
address: {
numero: '30 bis',
street: 'Avenue Leclerc',
commune: 'Rillieux-la-Pape',
postcode: '69140',
inseeCode: '69286',
},
coord: [4.9036773, 45.8142196],
accountVerified: true,
linkedin: null,
nbScanners: 1,
otherDescription: null,
personalOffers: [],
createdAt: new Date('2020-11-16T09:30:00.000Z'),
updatedAt: new Date('2020-11-16T09:30:00.000Z'),
deletedAt: new Date('2020-11-16T09:30:00.000Z'),
};
}
return null;
}
......