View Javadoc
1   /*
2    * Copyright (c) 2002-2024, City of Paris
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without
6    * modification, are permitted provided that the following conditions
7    * are met:
8    *
9    *  1. Redistributions of source code must retain the above copyright notice
10   *     and the following disclaimer.
11   *
12   *  2. Redistributions in binary form must reproduce the above copyright notice
13   *     and the following disclaimer in the documentation and/or other materials
14   *     provided with the distribution.
15   *
16   *  3. Neither the name of 'Mairie de Paris' nor 'Lutece' nor the names of its
17   *     contributors may be used to endorse or promote products derived from
18   *     this software without specific prior written permission.
19   *
20   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
24   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30   * POSSIBILITY OF SUCH DAMAGE.
31   *
32   * License 1.0
33   */
34  package fr.paris.lutece.plugins.identitystore.web;
35  
36  import com.fasterxml.jackson.databind.DeserializationFeature;
37  import com.fasterxml.jackson.databind.ObjectMapper;
38  import com.fasterxml.jackson.databind.SerializationFeature;
39  import fr.paris.lutece.plugins.identitystore.business.duplicates.suspicions.SuspiciousIdentityHome;
40  import fr.paris.lutece.plugins.identitystore.business.identity.Identity;
41  import fr.paris.lutece.plugins.identitystore.business.identity.IdentityAttribute;
42  import fr.paris.lutece.plugins.identitystore.business.identity.IdentityAttributeHome;
43  import fr.paris.lutece.plugins.identitystore.business.identity.IdentityConstants;
44  import fr.paris.lutece.plugins.identitystore.business.identity.IdentityHome;
45  import fr.paris.lutece.plugins.identitystore.service.IdentityManagementResourceIdService;
46  import fr.paris.lutece.plugins.identitystore.service.identity.IdentityService;
47  import fr.paris.lutece.plugins.identitystore.service.search.ISearchIdentityService;
48  import fr.paris.lutece.plugins.identitystore.utils.Batch;
49  import fr.paris.lutece.plugins.identitystore.v3.csv.CsvIdentityService;
50  import fr.paris.lutece.plugins.identitystore.v3.web.rs.DtoConverter;
51  import fr.paris.lutece.plugins.identitystore.v3.web.rs.dto.common.AttributeTreatmentType;
52  import fr.paris.lutece.plugins.identitystore.v3.web.rs.dto.common.BatchDto;
53  import fr.paris.lutece.plugins.identitystore.v3.web.rs.dto.common.IdentityDto;
54  import fr.paris.lutece.plugins.identitystore.v3.web.rs.dto.history.AttributeChange;
55  import fr.paris.lutece.plugins.identitystore.v3.web.rs.dto.history.IdentityChange;
56  import fr.paris.lutece.plugins.identitystore.v3.web.rs.dto.importing.BatchImportRequest;
57  import fr.paris.lutece.plugins.identitystore.v3.web.rs.dto.search.SearchAttribute;
58  import fr.paris.lutece.plugins.identitystore.v3.web.rs.util.Constants;
59  import fr.paris.lutece.plugins.identitystore.web.exception.IdentityStoreException;
60  import fr.paris.lutece.portal.service.admin.AccessDeniedException;
61  import fr.paris.lutece.portal.service.security.AccessLogService;
62  import fr.paris.lutece.portal.service.security.AccessLoggerConstants;
63  import fr.paris.lutece.portal.service.spring.SpringContextService;
64  import fr.paris.lutece.portal.service.util.AppPropertiesService;
65  import fr.paris.lutece.portal.util.mvc.admin.annotations.Controller;
66  import fr.paris.lutece.portal.util.mvc.commons.annotations.Action;
67  import fr.paris.lutece.portal.util.mvc.commons.annotations.View;
68  import fr.paris.lutece.util.http.SecurityUtil;
69  import org.apache.commons.collections.CollectionUtils;
70  import org.apache.commons.collections.MapUtils;
71  import org.apache.commons.lang3.StringUtils;
72  
73  import javax.servlet.http.HttpServletRequest;
74  import java.io.ByteArrayOutputStream;
75  import java.sql.Date;
76  import java.sql.Timestamp;
77  import java.time.Instant;
78  import java.time.LocalDate;
79  import java.util.ArrayList;
80  import java.util.Arrays;
81  import java.util.Collections;
82  import java.util.List;
83  import java.util.Map;
84  import java.util.Objects;
85  import java.util.Optional;
86  import java.util.UUID;
87  import java.util.stream.Collectors;
88  import java.util.zip.ZipEntry;
89  import java.util.zip.ZipOutputStream;
90  
91  /**
92   * This class provides the user interface to manage Identity features ( manage, create, modify, remove )
93   */
94  @Controller( controllerJsp = "ManageIdentities.jsp", controllerPath = "jsp/admin/plugins/identitystore/", right = "IDENTITYSTORE_MANAGEMENT" )
95  public class IdentityJspBean extends ManageIdentitiesJspBean
96  {
97      /**
98       * 
99       */
100     private static final long serialVersionUID = 6053504380426222888L;
101     // Templates
102     private static final String TEMPLATE_SEARCH_IDENTITIES = "/admin/plugins/identitystore/search_identities.html";
103     private static final String TEMPLATE_VIEW_IDENTITY = "/admin/plugins/identitystore/view_identity.html";
104     private static final String TEMPLATE_VIEW_IDENTITY_HISTORY = "/admin/plugins/identitystore/view_identity_change_history.html";
105 
106     // Parameters
107     private static final String PARAMETER_ID_IDENTITY = "id";
108 
109     // Properties for page titles
110     private static final String PROPERTY_PAGE_TITLE_MANAGE_IDENTITIES = "identitystore.manage_identities.pageTitle";
111     private static final String PROPERTY_PAGE_TITLE_VIEW_IDENTITY = "identitystore.create_identity.pageTitle";
112     private static final String PROPERTY_PAGE_TITLE_VIEW_CHANGE_HISTORY = "identitystore.view_change_history.pageTitle";
113 
114     // Markers
115     private static final String MARK_IDENTITY_LIST = "identity_list";
116     private static final String MARK_IDENTITY = "identity";
117     private static final String MARK_ATTRIBUTES = "attributes";
118     private static final String MARK_MERGED_IDENTITIES = "merged_identities";
119     private static final String MARK_IDENTITY_IS_SUSPICIOUS = "identity_is_suspicious";
120     private static final String MARK_IDENTITY_CHANGE_LIST = "identity_change_list";
121     private static final String MARK_ATTRIBUTES_CHANGE_LIST = "attributes_change_list";
122     private static final String MARK_HAS_CREATE_ROLE = "createIdentityRole";
123     private static final String MARK_HAS_MODIFY_ROLE = "modifyIdentityRole";
124     private static final String MARK_HAS_DELETE_ROLE = "deleteIdentityRole";
125     private static final String MARK_HAS_VIEW_ROLE = "viewIdentityRole";
126     private static final String MARK_HAS_ATTRIBUTS_HISTO_ROLE = "histoAttributsRole";
127     private static final String JSP_MANAGE_IDENTITIES = "jsp/admin/plugins/identitystore/ManageIdentities.jsp";
128 
129     // Views
130     private static final String VIEW_MANAGE_IDENTITIES = "manageIdentitys";
131     private static final String VIEW_IDENTITY = "viewIdentity";
132     private static final String VIEW_IDENTITY_HISTORY = "viewIdentityHistory";
133 
134     // Actions
135     private static final String ACTION_EXPORT_IDENTITIES = "exportIdentities";
136     private static final String ACTION_BATCH_GENERATE_REQUESTS = "exportRequestIdentities";
137 
138     // Events
139     private static final String DISPLAY_IDENTITY_EVENT_CODE = "DISPLAY_IDENTITY";
140     private static final String DISPLAY_IDENTITY_HISTORY_EVENT_CODE = "DISPLAY_HISTORY_IDENTITY";
141 
142     // Datasource
143     private static final String DATASOURCE_DB = "db";
144     private static final String DATASOURCE_ES = "es";
145     private static final int BATCH_PARTITION_SIZE = AppPropertiesService.getPropertyInt( "identitystore.export.batch.size", 100 );
146     private static final int PROPERTY_MAX_NB_IDENTITY_RETURNED = AppPropertiesService.getPropertyInt("identitystore.search.maxNbIdentityReturned", 0);
147     private static final String RESOURCE_SEARCH_LINK = AppPropertiesService.getProperty("identitystore.search.resource.link", "");
148 
149     // Session variable to store working values
150     private Identity _identity;
151 
152     private final List<IdentityDto> _identities = new ArrayList<>( );
153 
154     private final ISearchIdentityService _searchIdentityServiceDB = SpringContextService.getBean( "identitystore.searchIdentityService.database" );
155     private final ISearchIdentityService _searchIdentityServiceES = SpringContextService.getBean( "identitystore.searchIdentityService.elasticsearch" );
156 
157     @View( value = VIEW_MANAGE_IDENTITIES, defaultView = true )
158     public String getManageIdentitys( HttpServletRequest request )
159     {
160         _identity = null;
161         _identities.clear( );
162         final Map<String, String> queryParameters = this.getQueryParameters( request );
163 
164         final List<SearchAttribute> atttributes = new ArrayList<>( );
165         final String cuid = queryParameters.get( QUERY_PARAM_CUID );
166         final String guid = queryParameters.get( QUERY_PARAM_GUID );
167         final String insee_city = queryParameters.get( QUERY_PARAM_INSEE_CITY );
168         final String insee_country = queryParameters.get( QUERY_PARAM_INSEE_COUNTRY );
169         final String email = queryParameters.get( QUERY_PARAM_EMAIL );
170         final String gender = queryParameters.get( QUERY_PARAM_GENDER );
171         final String common_name = queryParameters.get(QUERY_PARAM_COMMON_LASTNAME);
172         final String first_name = queryParameters.get( QUERY_PARAM_FIRST_NAME );
173         final String birthdate = queryParameters.get( QUERY_PARAM_BIRTHDATE );
174         final String birthplace = queryParameters.get( QUERY_PARAM_INSEE_BIRTHPLACE_LABEL );
175         final String birthcountry = queryParameters.get( QUERY_PARAM_INSEE_BIRTHCOUNTRY_LABEL );
176         final String phone = queryParameters.get( QUERY_PARAM_PHONE );
177         final String datasource = Optional.ofNullable( queryParameters.get( QUERY_PARAM_DATASOURCE ) ).orElse( DATASOURCE_DB );
178 
179         try
180         {
181             if ( StringUtils.isNotEmpty( cuid ) )
182             {
183                 if ( datasource.equals( DATASOURCE_DB ) )
184                 {
185                     final Identity identity = IdentityHome.findMasterIdentityByCustomerId( cuid );
186                     if ( identity != null )
187                     {
188                         final IdentityDto qualifiedIdentity = DtoConverter.convertIdentityToDto( identity );
189                         _identities.add( qualifiedIdentity );
190                     }
191                 }
192                 else
193                 if ( datasource.equals( DATASOURCE_ES ) )
194                 {
195                     _identities.addAll( _searchIdentityServiceES.getQualifiedIdentities( cuid, Collections.emptyList() )
196                             .getQualifiedIdentities( ) );
197                 }
198             }
199             else
200             {
201                 if ( StringUtils.isNotEmpty( guid ) )
202                 {
203                     if ( datasource.equals( DATASOURCE_DB ) )
204                     {
205                         final Identity identity = IdentityHome.findMasterIdentityByConnectionId( guid );
206                         if ( identity != null )
207                         {
208                             final IdentityDto qualifiedIdentity = DtoConverter.convertIdentityToDto( identity );
209                             _identities.add( qualifiedIdentity );
210                         }
211                     }
212                     else
213                     if ( datasource.equals( DATASOURCE_ES ) )
214                     {
215                         _identities.addAll( _searchIdentityServiceES.getQualifiedIdentitiesByConnectionId( guid, Collections.emptyList() )
216                                 .getQualifiedIdentities( ) );
217                     }
218                 }
219                 else
220                 {
221                     if ( StringUtils.isNotEmpty( insee_city ) )
222                     {
223                         atttributes.add( new SearchAttribute( Constants.PARAM_BIRTH_PLACE_CODE, insee_city, AttributeTreatmentType.STRICT ) );
224                     }
225                     if ( StringUtils.isNotEmpty( insee_country ) )
226                     {
227                         atttributes.add( new SearchAttribute( Constants.PARAM_BIRTH_COUNTRY_CODE, insee_country, AttributeTreatmentType.STRICT ) );
228                     }
229                     if ( StringUtils.isNotEmpty( email ) )
230                     {
231                         atttributes.add( new SearchAttribute( Constants.PARAM_COMMON_EMAIL, email, AttributeTreatmentType.STRICT ) );
232                     }
233                     if ( StringUtils.isNotEmpty( gender ) )
234                     {
235                         atttributes.add( new SearchAttribute( Constants.PARAM_GENDER, gender, AttributeTreatmentType.STRICT ) );
236                     }
237                     if ( StringUtils.isNotEmpty( common_name ) )
238                     {
239                         atttributes.add( new SearchAttribute( Constants.PARAM_COMMON_LASTNAME, common_name, AttributeTreatmentType.APPROXIMATED ) );
240                     }
241                     if ( StringUtils.isNotEmpty( first_name ) )
242                     {
243                         atttributes.add( new SearchAttribute( Constants.PARAM_FIRST_NAME, first_name, AttributeTreatmentType.APPROXIMATED ) );
244                     }
245                     if ( StringUtils.isNotEmpty( birthdate ) )
246                     {
247                         atttributes.add( new SearchAttribute( Constants.PARAM_BIRTH_DATE, birthdate, AttributeTreatmentType.STRICT ) );
248                     }
249                     if ( StringUtils.isNotEmpty( birthplace ) )
250                     {
251                         atttributes.add( new SearchAttribute( Constants.PARAM_BIRTH_PLACE, birthplace, AttributeTreatmentType.APPROXIMATED ) );
252                     }
253                     if ( StringUtils.isNotEmpty( birthcountry ) )
254                     {
255                         atttributes.add( new SearchAttribute( Constants.PARAM_BIRTH_COUNTRY, birthcountry, AttributeTreatmentType.APPROXIMATED ) );
256                     }
257                     if ( StringUtils.isNotEmpty( phone ) )
258                     {
259                         atttributes.add( new SearchAttribute( Constants.PARAM_COMMON_PHONE, phone, AttributeTreatmentType.STRICT ) );
260                     }
261                     if ( CollectionUtils.isNotEmpty( atttributes ) )
262                     {
263                         if ( datasource.equals( DATASOURCE_DB ) )
264                         {
265                             _identities.addAll( _searchIdentityServiceDB.getQualifiedIdentities( atttributes, PROPERTY_MAX_NB_IDENTITY_RETURNED, false, Collections.emptyList( ) )
266                                     .getQualifiedIdentities( ) );
267                         }
268                         else
269                             if ( datasource.equals( DATASOURCE_ES ) )
270                             {
271                                 _identities.addAll( _searchIdentityServiceES.getQualifiedIdentities( atttributes, PROPERTY_MAX_NB_IDENTITY_RETURNED, false, Collections.emptyList( ) )
272                                         .getQualifiedIdentities( ) );
273                             }
274                     }
275                 }
276             }
277         }
278         catch( Exception e )
279         {
280             addError( e.getMessage( ) );
281             this.clearParameters( request );
282             return redirectView( request, VIEW_MANAGE_IDENTITIES );
283         }
284 
285         _identities.forEach(
286                 qualifiedIdentity -> AccessLogService.getInstance( ).info( AccessLoggerConstants.EVENT_TYPE_READ, IdentityService.SEARCH_IDENTITY_EVENT_CODE,
287                         getUser( ), SecurityUtil.logForgingProtect( qualifiedIdentity.getCustomerId( ) ), IdentityService.SPECIFIC_ORIGIN ) );
288 
289         final Map<String, Object> model = getPaginatedListModel( request, MARK_IDENTITY_LIST, _identities, JSP_MANAGE_IDENTITIES );
290         model.put( MARK_HAS_CREATE_ROLE,
291                 IdentityManagementResourceIdService.isAuthorized( IdentityManagementResourceIdService.PERMISSION_CREATE_IDENTITY, getUser( ) ) );
292         model.put( MARK_HAS_MODIFY_ROLE,
293                 IdentityManagementResourceIdService.isAuthorized( IdentityManagementResourceIdService.PERMISSION_MODIFY_IDENTITY, getUser( ) ) );
294         model.put( MARK_HAS_DELETE_ROLE,
295                 IdentityManagementResourceIdService.isAuthorized( IdentityManagementResourceIdService.PERMISSION_DELETE_IDENTITY, getUser( ) ) );
296         model.put( MARK_HAS_VIEW_ROLE,
297                 IdentityManagementResourceIdService.isAuthorized( IdentityManagementResourceIdService.PERMISSION_VIEW_IDENTITY, getUser( ) ) );
298 
299         model.put( QUERY_PARAM_CUID, cuid );
300         model.put( QUERY_PARAM_GUID, guid );
301         model.put( QUERY_PARAM_INSEE_CITY, insee_city );
302         model.put( QUERY_PARAM_INSEE_COUNTRY, insee_country );
303         model.put( QUERY_PARAM_COMMON_LASTNAME, common_name );
304         model.put( QUERY_PARAM_FIRST_NAME, first_name );
305         model.put( QUERY_PARAM_EMAIL, email );
306         model.put( QUERY_PARAM_INSEE_BIRTHPLACE_LABEL, birthplace );
307         model.put( QUERY_PARAM_INSEE_BIRTHCOUNTRY_LABEL, birthcountry );
308         model.put( QUERY_PARAM_PHONE, phone );
309         model.put( QUERY_PARAM_BIRTHDATE, birthdate );
310         model.put( QUERY_PARAM_GENDER, gender );
311         model.put( QUERY_PARAM_DATASOURCE, datasource );
312 
313         return getPage( PROPERTY_PAGE_TITLE_MANAGE_IDENTITIES, TEMPLATE_SEARCH_IDENTITIES, model );
314     }
315 
316     /**
317      * view identity
318      *
319      * @param request
320      *            http request
321      * @return The HTML form to view info
322      */
323     @View( VIEW_IDENTITY )
324     public String getViewIdentity( HttpServletRequest request )
325     {
326         final String nId = request.getParameter( PARAMETER_ID_IDENTITY );
327 
328         _identity = IdentityHome.findByCustomerId( nId );
329 
330         List<Identity> mergedIdentities = IdentityHome.findMergedIdentities(_identity.getId());
331         _identity.setMerged(mergedIdentities != null && !mergedIdentities.isEmpty());
332 
333         final String filteredCustomerId = SecurityUtil.logForgingProtect( _identity.getCustomerId( ) );
334         AccessLogService.getInstance( ).info( AccessLoggerConstants.EVENT_TYPE_READ, DISPLAY_IDENTITY_EVENT_CODE, getUser( ), filteredCustomerId,
335                 IdentityService.SPECIFIC_ORIGIN );
336 
337         List<IdentityAttribute> attributes = sortIdentityttributes( );
338 
339         final Map<String, Object> model = getModel( );
340         model.put( MARK_IDENTITY, _identity );
341         model.put( MARK_ATTRIBUTES, attributes );
342         model.put( MARK_MERGED_IDENTITIES, mergedIdentities );
343         model.put( MARK_IDENTITY_IS_SUSPICIOUS, SuspiciousIdentityHome.hasSuspicious( Collections.singletonList( _identity.getCustomerId( ) ) ) );
344         model.put( MARK_HAS_ATTRIBUTS_HISTO_ROLE,
345                 IdentityManagementResourceIdService.isAuthorized( IdentityManagementResourceIdService.PERMISSION_ATTRIBUTS_HISTO, getUser( ) ) );
346         model.put( QUERY_PARAM_CUID_LINK, RESOURCE_SEARCH_LINK );
347 
348         return getPage( PROPERTY_PAGE_TITLE_VIEW_IDENTITY, TEMPLATE_VIEW_IDENTITY, model );
349     }
350 
351     /**
352      * Build the attribute history View
353      *
354      * @param request
355      *            The HTTP request
356      * @return The page
357      */
358     @View( value = VIEW_IDENTITY_HISTORY )
359     public String getIdentityHistoryView( HttpServletRequest request )
360     {
361         // here we use a LinkedHashMap to have same attributs order as in viewIdentity
362         final List<AttributeChange> attributeChangeList = new ArrayList<>( );
363         final List<IdentityChange> identityChangeList = new ArrayList<>( );
364 
365         if ( _identity != null && MapUtils.isNotEmpty( _identity.getAttributes( ) ) )
366         {
367             try
368             {
369                 attributeChangeList.addAll( IdentityAttributeHome.getAttributeChangeHistory( _identity.getId( ) ) );
370                 identityChangeList.addAll( IdentityHome.findHistoryByCustomerId( _identity.getCustomerId( ) ) );
371             }
372             catch( IdentityStoreException e )
373             {
374                 addError( e.getMessage( ) );
375                 return getViewIdentity( request );
376             }
377         }
378         if ( _identity != null )
379         {
380             final String filteredCustomerId = SecurityUtil.logForgingProtect( _identity.getCustomerId( ) );
381             AccessLogService.getInstance( ).info( AccessLoggerConstants.EVENT_TYPE_READ, DISPLAY_IDENTITY_HISTORY_EVENT_CODE, getUser( ), filteredCustomerId,
382                     IdentityService.SPECIFIC_ORIGIN );
383         }
384 
385         final Map<String, Object> model = getModel( );
386         model.put( MARK_IDENTITY_CHANGE_LIST, identityChangeList );
387         model.put( MARK_ATTRIBUTES_CHANGE_LIST, attributeChangeList );
388 
389         return getPage( PROPERTY_PAGE_TITLE_VIEW_CHANGE_HISTORY, TEMPLATE_VIEW_IDENTITY_HISTORY, model );
390     }
391 
392     /**
393      * Process the data capture form of a new suspiciousidentity
394      *
395      * @param request
396      *            The Http Request
397      * @return The Jsp URL of the process result
398      * @throws AccessDeniedException
399      */
400     @Action( ACTION_EXPORT_IDENTITIES )
401     public void doExportIdentities( HttpServletRequest request )
402     {
403         try
404         {
405             final List<IdentityDto> identitiesToProcess = _identities.stream( ).filter( this::validateMinimumAttributes )
406                     .peek( identityDto -> identityDto.setExternalCustomerId( UUID.randomUUID( ).toString( ) ) ).collect( Collectors.toList( ) );
407             final Batch<IdentityDto> batches = Batch.ofSize( identitiesToProcess, BATCH_PARTITION_SIZE );
408 
409             final ByteArrayOutputStream outputStream = new ByteArrayOutputStream( );
410             final ZipOutputStream zipOut = new ZipOutputStream( outputStream );
411 
412             int i = 0;
413             for ( final List<IdentityDto> batch : batches )
414             {
415                 final byte [ ] bytes = CsvIdentityService.instance( ).write( batch );
416                 final ZipEntry zipEntry = new ZipEntry( "identities-" + ++i + ".csv" );
417                 zipEntry.setSize( bytes.length );
418                 zipOut.putNextEntry( zipEntry );
419                 zipOut.write( bytes );
420             }
421             zipOut.closeEntry( );
422             zipOut.close( );
423             this.download( outputStream.toByteArray( ), "identities.zip", "application/zip" );
424         }
425         catch( Exception e )
426         {
427             addError( e.getMessage( ) );
428             redirectView( request, VIEW_MANAGE_IDENTITIES );
429         }
430     }
431 
432     private boolean validateMinimumAttributes( final IdentityDto identity )
433     {
434         return this.checkAttributeExists( identity, Constants.PARAM_FAMILY_NAME ) && this.checkAttributeExists( identity, Constants.PARAM_FIRST_NAME )
435                 && this.checkAttributeExists( identity, Constants.PARAM_BIRTH_DATE );
436     }
437 
438     private boolean checkAttributeExists( final IdentityDto identity, final String attributeKey )
439     {
440         return identity.getAttributes( ).stream( )
441                 .anyMatch( attributeDto -> Objects.equals( attributeDto.getKey( ), attributeKey ) && StringUtils.isNotBlank( attributeDto.getValue( ) ) );
442     }
443 
444     /**
445      * Process the data capture form of a new suspiciousidentity
446      *
447      * @param request
448      *            The Http Request
449      * @return The Jsp URL of the process result
450      * @throws AccessDeniedException
451      */
452     @Action( ACTION_BATCH_GENERATE_REQUESTS )
453     public void doGenerateBatchIdentities( HttpServletRequest request )
454     {
455         try
456         {
457             // Prepare identities for import (clear not used params)
458             _identities.forEach( identityDto -> {
459                 identityDto.setExternalCustomerId( UUID.randomUUID( ).toString( ) );
460                 identityDto.setCustomerId( null );
461                 identityDto.setQuality( null );
462                 identityDto.setExpiration( null );
463                 identityDto.setMerge( null );
464                 identityDto.setLastUpdateDate( null );
465                 identityDto.setConnectionId( null );
466                 identityDto.setMonParisActive( null );
467                 identityDto.setCreationDate( null );
468                 identityDto.setDuplicateDefinition( null );
469                 identityDto.setSuspicious( null );
470             } );
471             final Batch<IdentityDto> batches = Batch.ofSize( _identities, BATCH_PARTITION_SIZE );
472 
473             final ByteArrayOutputStream outputStream = new ByteArrayOutputStream( );
474             final ZipOutputStream zipOut = new ZipOutputStream( outputStream );
475 
476             int i = 0;
477             final String reference = UUID.randomUUID( ).toString( );
478 
479             final ObjectMapper mapper = new ObjectMapper( );
480             mapper.enable( SerializationFeature.INDENT_OUTPUT );
481             mapper.disable( DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES );
482 
483             for ( final List<IdentityDto> batch : batches )
484             {
485                 final BatchImportRequest batchImportRequest = new BatchImportRequest( );
486                 batchImportRequest.setBatch( new BatchDto( ) );
487                 batchImportRequest.getBatch( ).setReference( reference );
488                 batchImportRequest.getBatch( ).setComment( "Batch exporté depuis identity store" );
489                 batchImportRequest.getBatch( ).setCreationDate( Timestamp.from( Instant.now( ) ) );
490                 batchImportRequest.getBatch( ).setUser( getUser( ).getEmail( ) );
491                 batchImportRequest.getBatch( ).setAppCode( "TEST" );
492                 batchImportRequest.getBatch( ).setIdentities( batch );
493                 final ZipEntry zipEntry = new ZipEntry( "identities-" + ++i + ".json" );
494                 final byte [ ] bytes = mapper.writeValueAsBytes( batchImportRequest );
495                 zipEntry.setSize( bytes.length );
496                 zipOut.putNextEntry( zipEntry );
497                 zipOut.write( bytes );
498             }
499             zipOut.closeEntry( );
500             zipOut.close( );
501             this.download( outputStream.toByteArray( ), "identity_requests.zip", "application/zip" );
502         }
503         catch( Exception e )
504         {
505             addError( e.getMessage( ) );
506             redirectView( request, VIEW_MANAGE_IDENTITIES );
507         }
508     }
509 
510     private List<IdentityAttribute> sortIdentityttributes( )
511     {
512         if ( _identity != null )
513         {
514             final List<String> _sortedAttributeKeyList = Arrays.asList( AppPropertiesService.getProperty(IdentityConstants.PROPERTY_IDENTITY_ATTRIBUTE_ORDER, "" ).split( "," ) );
515             List<IdentityAttribute> valueList = new ArrayList<>(_identity.getAttributes( ).values());
516             valueList.sort( ( a1, a2 ) -> {
517                 final int index1 = _sortedAttributeKeyList.indexOf( a1.getAttributeKey( ).getKeyName() );
518                 final int index2 = _sortedAttributeKeyList.indexOf( a2.getAttributeKey( ).getKeyName() );
519                 final int i1 = index1 == -1 ? 999 : index1;
520                 final int i2 = index2 == -1 ? 999 : index2;
521                 if ( i1 == i2 )
522                 {
523                     return a1.getAttributeKey( ).getKeyName().compareTo( a2.getAttributeKey( ).getKeyName() );
524                 }
525                 return Integer.compare( i1, i2 );
526             } );
527             return valueList;
528         }
529         return null;
530     }
531 }