1- import {
2- BadRequestException ,
3- Body ,
4- Controller ,
5- Delete ,
6- Get ,
7- HttpStatus ,
8- Param ,
9- Patch ,
10- Post ,
11- Query ,
12- Res ,
13- } from '@nestjs/common' ;
1+ import { BadRequestException , Body , Controller , Get , HttpStatus , Param , Patch , Post , Query , Res } from '@nestjs/common' ;
142import { ApiOperation , ApiParam , ApiTags } from '@nestjs/swagger' ;
15- import {
16- FilterOptions ,
17- filterSchema ,
18- FilterSchema ,
19- SearchFilterOptions ,
20- SearchFilterSchema ,
21- } from '~/_common/restools' ;
3+ import { FilterOptions , filterSchema , FilterSchema , SearchFilterOptions , SearchFilterSchema } from '~/_common/restools' ;
224import { Response } from 'express' ;
235import { Document , Types } from 'mongoose' ;
246import { AbstractController } from '~/_common/abstracts/abstract.controller' ;
@@ -39,6 +21,13 @@ import { TransformersFilestorageService } from '~/core/filestorage/_services/tra
3921import { IdentitiesCrudService } from '~/management/identities/identities-crud.service' ;
4022import { UseRoles } from '~/_common/decorators/use-roles.decorator' ;
4123import { AC_ACTIONS , AC_DEFAULT_POSSESSION } from '~/_common/types/ac-types' ;
24+ import { PasswdadmService } from '~/settings/passwdadm.service' ;
25+ import {
26+ buildExpiredInitInvitationFilter ,
27+ buildNonExpiredInitInvitationFilter ,
28+ INIT_INVITATION_EXPIRED_QUERY_PARAM ,
29+ parseInitInvitationExpiredQuery ,
30+ } from '~/management/passwd/init-invitation-expiration.helper' ;
4231
4332@ApiTags ( 'management/identities' )
4433@Controller ( 'identities' )
@@ -48,6 +37,7 @@ export class IdentitiesCrudController extends AbstractController {
4837 protected readonly _validation : IdentitiesValidationService ,
4938 protected readonly filestorage : FilestorageService ,
5039 private readonly transformerService : TransformersFilestorageService ,
40+ private readonly passwdadmService : PasswdadmService ,
5141 ) {
5242 super ( ) ;
5343 }
@@ -96,16 +86,12 @@ export class IdentitiesCrudController extends AbstractController {
9686 body . inetOrgPerson . employeeType = 'LOCAL' ;
9787 }
9888 if ( ! body . inetOrgPerson . cn ) {
99- body . inetOrgPerson . cn = [
100- body . inetOrgPerson . sn ?. toUpperCase ( ) ,
101- body . inetOrgPerson . givenName ,
102- ] . join ( ' ' ) . trim ( ) ;
89+ body . inetOrgPerson . cn = [ body . inetOrgPerson . sn ?. toUpperCase ( ) , body . inetOrgPerson . givenName ] . join ( ' ' ) . trim ( ) ;
10390 }
10491 if ( ! body . inetOrgPerson . displayName ) {
105- body . inetOrgPerson . displayName = [
106- body . inetOrgPerson . givenName ,
107- body . inetOrgPerson . sn ?. toUpperCase ( ) ,
108- ] . join ( ' ' ) . trim ( ) ;
92+ body . inetOrgPerson . displayName = [ body . inetOrgPerson . givenName , body . inetOrgPerson . sn ?. toUpperCase ( ) ]
93+ . join ( ' ' )
94+ . trim ( ) ;
10995 }
11096 const data = await this . _service . create < Identities > ( body ) ;
11197 // If the state is TO_COMPLETE, the identity is created but additional fields are missing or invalid
@@ -165,45 +151,62 @@ export class IdentitiesCrudController extends AbstractController {
165151 @Query ( 'search' ) search : string ,
166152 @SearchFilterSchema ( ) searchFilterSchema : FilterSchema ,
167153 @SearchFilterOptions ( { allowUnlimited : true } ) searchFilterOptions : FilterOptions ,
168- ) : Promise < Response < {
169- statusCode : number ;
170- data ?: Document < Identities , any , Identities > ;
171- total ?: number ;
172- message ?: string ;
173- validations ?: MixedValue ;
174- } > > {
175- const searchFilter = { }
154+ @Query ( INIT_INVITATION_EXPIRED_QUERY_PARAM ) initInvitationExpired : string ,
155+ ) : Promise <
156+ Response < {
157+ statusCode : number ;
158+ data ?: Document < Identities , any , Identities > ;
159+ total ?: number ;
160+ message ?: string ;
161+ validations ?: MixedValue ;
162+ } >
163+ > {
164+ const searchFilters = [ ] ;
176165 // Par défaut, on cache les identités "ne pas synchroniser" dans la recherche.
177166 // Si le client fournit déjà un filtre `state`, on ne l'écrase pas.
178167 // Le type `FilterSchema` est récursif (valeurs attendues), alors que pour Mongo on injecte parfois
179168 // des opérateurs comme `{ $ne: ... }`. On garde un cast `any` ici côté controller.
180- const effectiveSearchFilterSchema : any = { ...searchFilterSchema }
169+ const effectiveSearchFilterSchema : any = { ...searchFilterSchema } ;
181170 if ( ! Object . prototype . hasOwnProperty . call ( effectiveSearchFilterSchema , 'state' ) ) {
182- effectiveSearchFilterSchema . state = { $ne : IdentityState . DONT_SYNC }
171+ effectiveSearchFilterSchema . state = { $ne : IdentityState . DONT_SYNC } ;
183172 }
184173
185174 if ( search && search . trim ( ) . length > 0 ) {
186- const searchRequest = { }
187- searchRequest [ '$or' ] = Object . keys ( IdentitiesCrudController . searchFields ) . map ( ( key ) => {
188- return { [ key ] : { $regex : `^${ search } ` , $options : 'i' } }
189- } ) . filter ( item => item !== undefined )
190- searchFilter [ '$and' ] = [ searchRequest ]
191- searchFilter [ '$and' ] . push ( effectiveSearchFilterSchema )
175+ const searchRequest = { } ;
176+ searchRequest [ '$or' ] = Object . keys ( IdentitiesCrudController . searchFields )
177+ . map ( ( key ) => {
178+ return { [ key ] : { $regex : `^${ search } ` , $options : 'i' } } ;
179+ } )
180+ . filter ( ( item ) => item !== undefined ) ;
181+ searchFilters . push ( searchRequest ) ;
182+ searchFilters . push ( effectiveSearchFilterSchema ) ;
192183 } else {
193- Object . assign ( searchFilter , effectiveSearchFilterSchema )
184+ searchFilters . push ( effectiveSearchFilterSchema ) ;
185+ }
186+
187+ const expiredQuery = parseInitInvitationExpiredQuery ( initInvitationExpired ) ;
188+ if ( expiredQuery !== null ) {
189+ const policies = await this . passwdadmService . getPolicies ( ) ;
190+ searchFilters . push (
191+ expiredQuery
192+ ? buildExpiredInitInvitationFilter ( policies ?. initTokenTTL )
193+ : buildNonExpiredInitInvitationFilter ( policies ?. initTokenTTL ) ,
194+ ) ;
194195 }
195196
197+ const searchFilter = searchFilters . length === 1 ? searchFilters [ 0 ] : { $and : searchFilters } ;
198+
196199 const [ data , total ] = await this . _service . findAndCount (
197200 searchFilter ,
198201 IdentitiesCrudController . projection ,
199202 searchFilterOptions ,
200- )
203+ ) ;
201204
202205 return res . status ( HttpStatus . OK ) . json ( {
203206 statusCode : HttpStatus . OK ,
204207 total,
205208 data,
206- } )
209+ } ) ;
207210 }
208211
209212 @Get ( ':_id([0-9a-fA-F]{24})' )
@@ -255,24 +258,30 @@ export class IdentitiesCrudController extends AbstractController {
255258 @ApiOperation ( { summary : "Compte le nombre d'identitées en fonctions des filtres fournis via un body de counts" } )
256259 public async countAll (
257260 @Res ( ) res : Response ,
258- @Body ( ) body : {
261+ @Body ( )
262+ body : {
259263 [ key : string ] : FilterSchema ;
260264 } ,
261265 @SearchFilterOptions ( ) searchFilterOptions : FilterOptions ,
262- ) : Promise < Response < {
263- statusCode : number ;
264- data : {
265- [ key : string ] : number ;
266- } ;
267- } > > {
268- let filters : Record < string , FilterSchema >
266+ ) : Promise <
267+ Response < {
268+ statusCode : number ;
269+ data : {
270+ [ key : string ] : number ;
271+ } ;
272+ } >
273+ > {
274+ let filters : Record < string , FilterSchema > ;
269275 try {
270- filters = Object . entries ( body ) . reduce ( ( acc , [ key , value ] ) => {
271- acc [ key ] = filterSchema ( value )
272- return acc
273- } , { } as Record < string , FilterSchema > )
276+ filters = Object . entries ( body ) . reduce (
277+ ( acc , [ key , value ] ) => {
278+ acc [ key ] = filterSchema ( value ) ;
279+ return acc ;
280+ } ,
281+ { } as Record < string , FilterSchema > ,
282+ ) ;
274283 } catch ( error : any ) {
275- throw new BadRequestException ( error ?. message ?? 'Invalid filters' )
284+ throw new BadRequestException ( error ?. message ?? 'Invalid filters' ) ;
276285 }
277286
278287 const data = await this . _service . countAll ( filters , searchFilterOptions ) ;
0 commit comments