1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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.IdentityAttributeHome;
42 import fr.paris.lutece.plugins.identitystore.business.identity.IdentityHome;
43 import fr.paris.lutece.plugins.identitystore.service.IdentityManagementResourceIdService;
44 import fr.paris.lutece.plugins.identitystore.service.identity.IdentityService;
45 import fr.paris.lutece.plugins.identitystore.service.search.ISearchIdentityService;
46 import fr.paris.lutece.plugins.identitystore.utils.Batch;
47 import fr.paris.lutece.plugins.identitystore.v3.csv.CsvIdentityService;
48 import fr.paris.lutece.plugins.identitystore.v3.web.rs.DtoConverter;
49 import fr.paris.lutece.plugins.identitystore.v3.web.rs.dto.common.AttributeTreatmentType;
50 import fr.paris.lutece.plugins.identitystore.v3.web.rs.dto.common.BatchDto;
51 import fr.paris.lutece.plugins.identitystore.v3.web.rs.dto.common.IdentityDto;
52 import fr.paris.lutece.plugins.identitystore.v3.web.rs.dto.history.AttributeChange;
53 import fr.paris.lutece.plugins.identitystore.v3.web.rs.dto.history.IdentityChange;
54 import fr.paris.lutece.plugins.identitystore.v3.web.rs.dto.importing.BatchImportRequest;
55 import fr.paris.lutece.plugins.identitystore.v3.web.rs.dto.search.SearchAttribute;
56 import fr.paris.lutece.plugins.identitystore.v3.web.rs.util.Constants;
57 import fr.paris.lutece.plugins.identitystore.web.exception.IdentityStoreException;
58 import fr.paris.lutece.portal.service.admin.AccessDeniedException;
59 import fr.paris.lutece.portal.service.security.AccessLogService;
60 import fr.paris.lutece.portal.service.security.AccessLoggerConstants;
61 import fr.paris.lutece.portal.service.spring.SpringContextService;
62 import fr.paris.lutece.portal.service.util.AppPropertiesService;
63 import fr.paris.lutece.portal.util.mvc.admin.annotations.Controller;
64 import fr.paris.lutece.portal.util.mvc.commons.annotations.Action;
65 import fr.paris.lutece.portal.util.mvc.commons.annotations.View;
66 import fr.paris.lutece.util.http.SecurityUtil;
67 import org.apache.commons.collections.CollectionUtils;
68 import org.apache.commons.collections.MapUtils;
69 import org.apache.commons.lang3.StringUtils;
70
71 import javax.servlet.http.HttpServletRequest;
72 import java.io.ByteArrayOutputStream;
73 import java.sql.Date;
74 import java.time.LocalDate;
75 import java.util.ArrayList;
76 import java.util.Collections;
77 import java.util.List;
78 import java.util.Map;
79 import java.util.Objects;
80 import java.util.Optional;
81 import java.util.UUID;
82 import java.util.stream.Collectors;
83 import java.util.zip.ZipEntry;
84 import java.util.zip.ZipOutputStream;
85
86
87
88
89 @Controller( controllerJsp = "ManageIdentities.jsp", controllerPath = "jsp/admin/plugins/identitystore/", right = "IDENTITYSTORE_MANAGEMENT" )
90 public class IdentityJspBean extends ManageIdentitiesJspBean
91 {
92
93
94
95 private static final long serialVersionUID = 6053504380426222888L;
96
97 private static final String TEMPLATE_SEARCH_IDENTITIES = "/admin/plugins/identitystore/search_identities.html";
98 private static final String TEMPLATE_VIEW_IDENTITY = "/admin/plugins/identitystore/view_identity.html";
99 private static final String TEMPLATE_VIEW_IDENTITY_HISTORY = "/admin/plugins/identitystore/view_identity_change_history.html";
100
101
102 private static final String PARAMETER_ID_IDENTITY = "id";
103
104
105 private static final String PROPERTY_PAGE_TITLE_MANAGE_IDENTITIES = "identitystore.manage_identities.pageTitle";
106 private static final String PROPERTY_PAGE_TITLE_VIEW_IDENTITY = "identitystore.create_identity.pageTitle";
107 private static final String PROPERTY_PAGE_TITLE_VIEW_CHANGE_HISTORY = "identitystore.view_change_history.pageTitle";
108
109
110 private static final String MARK_IDENTITY_LIST = "identity_list";
111 private static final String MARK_IDENTITY = "identity";
112 private static final String MARK_IDENTITY_IS_SUSPICIOUS = "identity_is_suspicious";
113 private static final String MARK_IDENTITY_CHANGE_LIST = "identity_change_list";
114 private static final String MARK_ATTRIBUTES_CHANGE_LIST = "attributes_change_list";
115 private static final String MARK_HAS_CREATE_ROLE = "createIdentityRole";
116 private static final String MARK_HAS_MODIFY_ROLE = "modifyIdentityRole";
117 private static final String MARK_HAS_DELETE_ROLE = "deleteIdentityRole";
118 private static final String MARK_HAS_VIEW_ROLE = "viewIdentityRole";
119 private static final String MARK_HAS_ATTRIBUTS_HISTO_ROLE = "histoAttributsRole";
120 private static final String JSP_MANAGE_IDENTITIES = "jsp/admin/plugins/identitystore/ManageIdentities.jsp";
121
122
123 private static final String VIEW_MANAGE_IDENTITIES = "manageIdentitys";
124 private static final String VIEW_IDENTITY = "viewIdentity";
125 private static final String VIEW_IDENTITY_HISTORY = "viewIdentityHistory";
126
127
128 private static final String ACTION_EXPORT_IDENTITIES = "exportIdentities";
129 private static final String ACTION_BATCH_GENERATE_REQUESTS = "exportRequestIdentities";
130
131
132 private static final String DISPLAY_IDENTITY_EVENT_CODE = "DISPLAY_IDENTITY";
133 private static final String DISPLAY_IDENTITY_HISTORY_EVENT_CODE = "DISPLAY_HISTORY_IDENTITY";
134
135
136 private static final String DATASOURCE_DB = "db";
137 private static final String DATASOURCE_ES = "es";
138 private static final int BATCH_PARTITION_SIZE = AppPropertiesService.getPropertyInt( "identitystore.export.batch.size", 100 );
139
140
141 private Identity _identity;
142
143 private final List<IdentityDto> _identities = new ArrayList<>( );
144
145 private final ISearchIdentityService _searchIdentityServiceDB = SpringContextService.getBean( "identitystore.searchIdentityService.database" );
146 private final ISearchIdentityService _searchIdentityServiceES = SpringContextService.getBean( "identitystore.searchIdentityService.elasticsearch" );
147
148 @View( value = VIEW_MANAGE_IDENTITIES, defaultView = true )
149 public String getManageIdentitys( HttpServletRequest request )
150 {
151 _identity = null;
152 _identities.clear( );
153 final Map<String, String> queryParameters = this.getQueryParameters( request );
154
155 final List<SearchAttribute> atttributes = new ArrayList<>( );
156 final String cuid = queryParameters.get( QUERY_PARAM_CUID );
157 final String guid = queryParameters.get( QUERY_PARAM_GUID );
158 final String email = queryParameters.get( QUERY_PARAM_EMAIL );
159 final String gender = queryParameters.get( QUERY_PARAM_GENDER );
160 final String family_name = queryParameters.get( QUERY_PARAM_FAMILY_NAME );
161 final String preferred_username = queryParameters.get( QUERY_PARAM_PREFERRED_USERNAME );
162 final String first_name = queryParameters.get( QUERY_PARAM_FIRST_NAME );
163 final String birthdate = queryParameters.get( QUERY_PARAM_BIRTHDATE );
164 final String birthplace = queryParameters.get( QUERY_PARAM_INSEE_BIRTHPLACE_LABEL );
165 final String birthcountry = queryParameters.get( QUERY_PARAM_INSEE_BIRTHCOUNTRY_LABEL );
166 final String phone = queryParameters.get( QUERY_PARAM_PHONE );
167 final String datasource = Optional.ofNullable( queryParameters.get( QUERY_PARAM_DATASOURCE ) ).orElse( DATASOURCE_DB );
168
169 try
170 {
171 if ( StringUtils.isNotEmpty( cuid ) )
172 {
173 final Identity identity = IdentityHome.findMasterIdentityByCustomerId( cuid );
174 if ( identity != null )
175 {
176 final IdentityDto qualifiedIdentity = DtoConverter.convertIdentityToDto( identity );
177 _identities.add( qualifiedIdentity );
178 }
179 }
180 else
181 {
182 if ( StringUtils.isNotEmpty( guid ) )
183 {
184 final Identity identity = IdentityHome.findMasterIdentityByConnectionId( guid );
185 if ( identity != null )
186 {
187 final IdentityDto qualifiedIdentity = DtoConverter.convertIdentityToDto( identity );
188 _identities.add( qualifiedIdentity );
189 }
190 }
191 else
192 {
193 if ( StringUtils.isNotEmpty( email ) )
194 {
195 atttributes.add( new SearchAttribute( Constants.PARAM_LOGIN, email, AttributeTreatmentType.STRICT ) );
196 }
197 if ( StringUtils.isNotEmpty( gender ) )
198 {
199 atttributes.add( new SearchAttribute( Constants.PARAM_GENDER, gender, AttributeTreatmentType.STRICT ) );
200 }
201 if ( StringUtils.isNotEmpty( family_name ) )
202 {
203 atttributes.add( new SearchAttribute( Constants.PARAM_FAMILY_NAME, family_name, AttributeTreatmentType.APPROXIMATED ) );
204 }
205 if ( StringUtils.isNotEmpty( preferred_username ) )
206 {
207 atttributes.add( new SearchAttribute( Constants.PARAM_PREFERRED_USERNAME, preferred_username, AttributeTreatmentType.APPROXIMATED ) );
208 }
209 if ( StringUtils.isNotEmpty( first_name ) )
210 {
211 atttributes.add( new SearchAttribute( Constants.PARAM_FIRST_NAME, first_name, AttributeTreatmentType.APPROXIMATED ) );
212 }
213 if ( StringUtils.isNotEmpty( birthdate ) )
214 {
215 atttributes.add( new SearchAttribute( Constants.PARAM_BIRTH_DATE, birthdate, AttributeTreatmentType.STRICT ) );
216 }
217 if ( StringUtils.isNotEmpty( birthplace ) )
218 {
219 atttributes.add( new SearchAttribute( Constants.PARAM_BIRTH_PLACE, birthplace, AttributeTreatmentType.STRICT ) );
220 }
221 if ( StringUtils.isNotEmpty( birthcountry ) )
222 {
223 atttributes.add( new SearchAttribute( Constants.PARAM_BIRTH_COUNTRY, birthcountry, AttributeTreatmentType.STRICT ) );
224 }
225 if ( StringUtils.isNotEmpty( phone ) )
226 {
227 atttributes.add( new SearchAttribute( Constants.PARAM_MOBILE_PHONE, phone, AttributeTreatmentType.STRICT ) );
228 }
229 if ( CollectionUtils.isNotEmpty( atttributes ) )
230 {
231 if ( datasource.equals( DATASOURCE_DB ) )
232 {
233 _identities.addAll( _searchIdentityServiceDB.getQualifiedIdentities( atttributes, 0, false, Collections.emptyList( ) )
234 .getQualifiedIdentities( ) );
235 }
236 else
237 if ( datasource.equals( DATASOURCE_ES ) )
238 {
239 _identities.addAll( _searchIdentityServiceES.getQualifiedIdentities( atttributes, 0, false, Collections.emptyList( ) )
240 .getQualifiedIdentities( ) );
241 }
242 }
243 }
244 }
245 }
246 catch( Exception e )
247 {
248 addError( e.getMessage( ) );
249 this.clearParameters( request );
250 return redirectView( request, VIEW_MANAGE_IDENTITIES );
251 }
252
253 _identities.forEach(
254 qualifiedIdentity -> AccessLogService.getInstance( ).info( AccessLoggerConstants.EVENT_TYPE_READ, IdentityService.SEARCH_IDENTITY_EVENT_CODE,
255 getUser( ), SecurityUtil.logForgingProtect( qualifiedIdentity.getCustomerId( ) ), IdentityService.SPECIFIC_ORIGIN ) );
256
257 final Map<String, Object> model = getPaginatedListModel( request, MARK_IDENTITY_LIST, _identities, JSP_MANAGE_IDENTITIES );
258 model.put( MARK_HAS_CREATE_ROLE,
259 IdentityManagementResourceIdService.isAuthorized( IdentityManagementResourceIdService.PERMISSION_CREATE_IDENTITY, getUser( ) ) );
260 model.put( MARK_HAS_MODIFY_ROLE,
261 IdentityManagementResourceIdService.isAuthorized( IdentityManagementResourceIdService.PERMISSION_MODIFY_IDENTITY, getUser( ) ) );
262 model.put( MARK_HAS_DELETE_ROLE,
263 IdentityManagementResourceIdService.isAuthorized( IdentityManagementResourceIdService.PERMISSION_DELETE_IDENTITY, getUser( ) ) );
264 model.put( MARK_HAS_VIEW_ROLE,
265 IdentityManagementResourceIdService.isAuthorized( IdentityManagementResourceIdService.PERMISSION_VIEW_IDENTITY, getUser( ) ) );
266
267 model.put( QUERY_PARAM_CUID, cuid );
268 model.put( QUERY_PARAM_GUID, guid );
269 model.put( QUERY_PARAM_FAMILY_NAME, family_name );
270 model.put( QUERY_PARAM_PREFERRED_USERNAME, preferred_username );
271 model.put( QUERY_PARAM_FIRST_NAME, first_name );
272 model.put( QUERY_PARAM_EMAIL, email );
273 model.put( QUERY_PARAM_INSEE_BIRTHPLACE_LABEL, birthplace );
274 model.put( QUERY_PARAM_INSEE_BIRTHCOUNTRY_LABEL, birthcountry );
275 model.put( QUERY_PARAM_PHONE, phone );
276 model.put( QUERY_PARAM_BIRTHDATE, birthdate );
277 model.put( QUERY_PARAM_GENDER, gender );
278 model.put( QUERY_PARAM_DATASOURCE, datasource );
279
280 return getPage( PROPERTY_PAGE_TITLE_MANAGE_IDENTITIES, TEMPLATE_SEARCH_IDENTITIES, model );
281 }
282
283
284
285
286
287
288
289
290 @View( VIEW_IDENTITY )
291 public String getViewIdentity( HttpServletRequest request )
292 {
293 final String nId = request.getParameter( PARAMETER_ID_IDENTITY );
294
295 _identity = IdentityHome.findByCustomerId( nId );
296 final String filteredCustomerId = SecurityUtil.logForgingProtect( _identity.getCustomerId( ) );
297 AccessLogService.getInstance( ).info( AccessLoggerConstants.EVENT_TYPE_READ, DISPLAY_IDENTITY_EVENT_CODE, getUser( ), filteredCustomerId,
298 IdentityService.SPECIFIC_ORIGIN );
299
300 final Map<String, Object> model = getModel( );
301 model.put( MARK_IDENTITY, _identity );
302 model.put( MARK_IDENTITY_IS_SUSPICIOUS, SuspiciousIdentityHome.hasSuspicious( Collections.singletonList( _identity.getCustomerId( ) ) ) );
303 model.put( MARK_HAS_ATTRIBUTS_HISTO_ROLE,
304 IdentityManagementResourceIdService.isAuthorized( IdentityManagementResourceIdService.PERMISSION_ATTRIBUTS_HISTO, getUser( ) ) );
305
306 return getPage( PROPERTY_PAGE_TITLE_VIEW_IDENTITY, TEMPLATE_VIEW_IDENTITY, model );
307 }
308
309
310
311
312
313
314
315
316 @View( value = VIEW_IDENTITY_HISTORY )
317 public String getIdentityHistoryView( HttpServletRequest request )
318 {
319
320 final List<AttributeChange> attributeChangeList = new ArrayList<>( );
321 final List<IdentityChange> identityChangeList = new ArrayList<>( );
322
323 if ( _identity != null && MapUtils.isNotEmpty( _identity.getAttributes( ) ) )
324 {
325 try
326 {
327 attributeChangeList.addAll( IdentityAttributeHome.getAttributeChangeHistory( _identity.getId( ) ) );
328 identityChangeList.addAll( IdentityHome.findHistoryByCustomerId( _identity.getCustomerId( ) ) );
329 }
330 catch( IdentityStoreException e )
331 {
332 addError( e.getMessage( ) );
333 return getViewIdentity( request );
334 }
335 }
336 if ( _identity != null )
337 {
338 final String filteredCustomerId = SecurityUtil.logForgingProtect( _identity.getCustomerId( ) );
339 AccessLogService.getInstance( ).info( AccessLoggerConstants.EVENT_TYPE_READ, DISPLAY_IDENTITY_HISTORY_EVENT_CODE, getUser( ), filteredCustomerId,
340 IdentityService.SPECIFIC_ORIGIN );
341 }
342
343 final Map<String, Object> model = getModel( );
344 model.put( MARK_IDENTITY_CHANGE_LIST, identityChangeList );
345 model.put( MARK_ATTRIBUTES_CHANGE_LIST, attributeChangeList );
346
347 return getPage( PROPERTY_PAGE_TITLE_VIEW_CHANGE_HISTORY, TEMPLATE_VIEW_IDENTITY_HISTORY, model );
348 }
349
350
351
352
353
354
355
356
357
358 @Action( ACTION_EXPORT_IDENTITIES )
359 public void doExportIdentities( HttpServletRequest request )
360 {
361 try
362 {
363 final List<IdentityDto> identitiesToProcess = _identities.stream( ).filter( this::validateMinimumAttributes )
364 .peek( identityDto -> identityDto.setExternalCustomerId( UUID.randomUUID( ).toString( ) ) ).collect( Collectors.toList( ) );
365 final Batch<IdentityDto> batches = Batch.ofSize( identitiesToProcess, BATCH_PARTITION_SIZE );
366
367 final ByteArrayOutputStream outputStream = new ByteArrayOutputStream( );
368 final ZipOutputStream zipOut = new ZipOutputStream( outputStream );
369
370 int i = 0;
371 for ( final List<IdentityDto> batch : batches )
372 {
373 final byte [ ] bytes = CsvIdentityService.instance( ).write( batch );
374 final ZipEntry zipEntry = new ZipEntry( "identities-" + ++i + ".csv" );
375 zipEntry.setSize( bytes.length );
376 zipOut.putNextEntry( zipEntry );
377 zipOut.write( bytes );
378 }
379 zipOut.closeEntry( );
380 zipOut.close( );
381 this.download( outputStream.toByteArray( ), "identities.zip", "application/zip" );
382 }
383 catch( Exception e )
384 {
385 addError( e.getMessage( ) );
386 redirectView( request, VIEW_MANAGE_IDENTITIES );
387 }
388 }
389
390 private boolean validateMinimumAttributes( final IdentityDto identity )
391 {
392 return this.checkAttributeExists( identity, Constants.PARAM_FAMILY_NAME ) && this.checkAttributeExists( identity, Constants.PARAM_FIRST_NAME )
393 && this.checkAttributeExists( identity, Constants.PARAM_BIRTH_DATE );
394 }
395
396 private boolean checkAttributeExists( final IdentityDto identity, final String attributeKey )
397 {
398 return identity.getAttributes( ).stream( )
399 .anyMatch( attributeDto -> Objects.equals( attributeDto.getKey( ), attributeKey ) && StringUtils.isNotBlank( attributeDto.getValue( ) ) );
400 }
401
402
403
404
405
406
407
408
409
410 @Action( ACTION_BATCH_GENERATE_REQUESTS )
411 public void doGenerateBatchIdentities( HttpServletRequest request )
412 {
413 try
414 {
415
416 _identities.forEach( identityDto -> {
417 identityDto.setExternalCustomerId( UUID.randomUUID( ).toString( ) );
418 identityDto.setCustomerId( null );
419 identityDto.setQuality( null );
420 identityDto.setExpiration( null );
421 identityDto.setMerge( null );
422 identityDto.setLastUpdateDate( null );
423 identityDto.setConnectionId( null );
424 identityDto.setMonParisActive( null );
425 identityDto.setCreationDate( null );
426 identityDto.setDuplicateDefinition( null );
427 identityDto.setSuspicious( null );
428 } );
429 final Batch<IdentityDto> batches = Batch.ofSize( _identities, BATCH_PARTITION_SIZE );
430
431 final ByteArrayOutputStream outputStream = new ByteArrayOutputStream( );
432 final ZipOutputStream zipOut = new ZipOutputStream( outputStream );
433
434 int i = 0;
435 final String reference = UUID.randomUUID( ).toString( );
436 final Date today = new Date( LocalDate.now( ).toEpochDay( ) );
437
438 final ObjectMapper mapper = new ObjectMapper( );
439 mapper.enable( SerializationFeature.INDENT_OUTPUT );
440 mapper.disable( DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES );
441
442 for ( final List<IdentityDto> batch : batches )
443 {
444 final BatchImportRequest batchImportRequest = new BatchImportRequest( );
445 batchImportRequest.setBatch( new BatchDto( ) );
446 batchImportRequest.getBatch( ).setReference( reference );
447 batchImportRequest.getBatch( ).setComment( "Batch exporté depuis identity store" );
448 batchImportRequest.getBatch( ).setDate( today );
449 batchImportRequest.getBatch( ).setUser( getUser( ).getEmail( ) );
450 batchImportRequest.getBatch( ).setAppCode( "TEST" );
451 batchImportRequest.getBatch( ).setIdentities( batch );
452 final ZipEntry zipEntry = new ZipEntry( "identities-" + ++i + ".json" );
453 final byte [ ] bytes = mapper.writeValueAsBytes( batchImportRequest );
454 zipEntry.setSize( bytes.length );
455 zipOut.putNextEntry( zipEntry );
456 zipOut.write( bytes );
457 }
458 zipOut.closeEntry( );
459 zipOut.close( );
460 this.download( outputStream.toByteArray( ), "identity_requests.zip", "application/zip" );
461 }
462 catch( Exception e )
463 {
464 addError( e.getMessage( ) );
465 redirectView( request, VIEW_MANAGE_IDENTITIES );
466 }
467 }
468 }