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.service;
35
36 import static fr.paris.lutece.plugins.identitystore.service.identity.IdentityService.UPDATE_IDENTITY_EVENT_CODE;
37
38 import java.sql.Date;
39 import java.sql.Timestamp;
40 import java.time.Instant;
41 import java.time.LocalDateTime;
42 import java.time.ZoneId;
43 import java.time.ZonedDateTime;
44 import java.util.ArrayList;
45 import java.util.Collections;
46 import java.util.HashMap;
47 import java.util.List;
48 import java.util.Map;
49
50 import org.apache.commons.lang3.StringUtils;
51
52 import fr.paris.lutece.plugins.grubusiness.business.demand.DemandType;
53 import fr.paris.lutece.plugins.grubusiness.business.web.rs.DemandDisplay;
54 import fr.paris.lutece.plugins.grubusiness.business.web.rs.DemandResult;
55 import fr.paris.lutece.plugins.grubusiness.business.web.rs.EnumGenericStatus;
56 import fr.paris.lutece.plugins.identitystore.business.contract.ServiceContract;
57 import fr.paris.lutece.plugins.identitystore.business.identity.Identity;
58 import fr.paris.lutece.plugins.identitystore.business.identity.IdentityHome;
59 import fr.paris.lutece.plugins.identitystore.service.contract.ServiceContractService;
60 import fr.paris.lutece.plugins.identitystore.service.identity.IdentityService;
61 import fr.paris.lutece.plugins.identitystore.service.listeners.IdentityStoreNotifyListenerService;
62 import fr.paris.lutece.plugins.identitystore.service.user.InternalUserService;
63 import fr.paris.lutece.plugins.identitystore.v3.web.rs.dto.common.AuthorType;
64 import fr.paris.lutece.plugins.identitystore.v3.web.rs.dto.common.RequestAuthor;
65 import fr.paris.lutece.plugins.identitystore.v3.web.rs.dto.common.ResponseStatusType;
66 import fr.paris.lutece.plugins.identitystore.v3.web.rs.dto.history.IdentityChangeType;
67 import fr.paris.lutece.plugins.identitystore.web.exception.ClientAuthorizationException;
68 import fr.paris.lutece.plugins.notificationstore.v1.web.service.NotificationStoreService;
69 import fr.paris.lutece.portal.service.security.AccessLogService;
70 import fr.paris.lutece.portal.service.security.AccessLoggerConstants;
71 import fr.paris.lutece.portal.service.spring.SpringContextService;
72 import fr.paris.lutece.portal.service.util.AppLogService;
73 import fr.paris.lutece.portal.service.util.AppPropertiesService;
74
75 public final class PurgeIdentityService
76 {
77 public static final String KEY_INFOS_ARE_MISSING = "isInfosAreMissing";
78 public static final String KEY_AT_LEAST_ONE_CS_FOUND = "isAtLeastOneServiceContractFound";
79
80 private static PurgeIdentityService _instance;
81
82 private final NotificationStoreService _notificationStoreService = SpringContextService.getBean( "notificationStore.notificationStoreService" );
83 private final IdentityStoreNotifyListenerService _identityStoreNotifyListenerService = IdentityStoreNotifyListenerService.instance( );
84
85 public static PurgeIdentityService getInstance( )
86 {
87 if ( _instance == null )
88 {
89 _instance = new PurgeIdentityService( );
90 }
91 return _instance;
92 }
93
94
95
96
97
98
99 public String purge( final RequestAuthor daemonAuthor, final String daemonClientCode, final List<String> excludedAppCodes, final int batchLimit )
100 {
101 final StringBuilder msg = new StringBuilder( );
102 final boolean isDryRun = AppPropertiesService.getPropertyBoolean ( "daemon.purgeIdentityDaemon.dryRun", false );
103 final int nMaxCguRetentionPeridodInMonths = AppPropertiesService.getPropertyInt( "daemon.purgeIdentityDaemon.maxCguRetentionPeridodInMonths", 120 );
104
105
106 final List<Identity> expiredIdentities = IdentityHome.findExpiredNotMergedAndNotConnectedIdentities(
107 batchLimit, AppPropertiesService.getPropertyBoolean ( "daemon.purgeIdentityDaemon.withGuidOnly", true) );
108 final Timestamp now = Timestamp.from( Instant.now( ) );
109 final Timestamp olderCguRetentionDate = Timestamp.valueOf ( now.toLocalDateTime ( ).minusMonths ( nMaxCguRetentionPeridodInMonths ) );
110
111 msg.append( expiredIdentities.size( ) ).append( " expired identities found" ).append( "\n" );
112
113 for ( final Identity expiredIdentity : expiredIdentities )
114 {
115 try
116 {
117 final List<Identity> mergedIdentities = IdentityHome.findMergedIdentities( expiredIdentity.getId( ) );
118
119
120 DemandResult demandResult = _notificationStoreService.getListDemand( expiredIdentity.getCustomerId( ), null, null, null, null );
121 final List<DemandDisplay> demandDisplayList = demandResult.getListDemandDisplay() == null ? new ArrayList<>() : new ArrayList<>(demandResult.getListDemandDisplay());
122 for ( final Identity mergedIdentity : mergedIdentities )
123 {
124 final DemandResult mergedDemands = _notificationStoreService.getListDemand( mergedIdentity.getCustomerId( ), null, null, null, null );
125 if ( mergedDemands.getListDemandDisplay( ) != null )
126 {
127 demandDisplayList.addAll( mergedDemands.getListDemandDisplay( ) );
128 }
129 }
130
131 Map<String,Boolean> indicators = new HashMap<>( );
132 indicators.put( KEY_AT_LEAST_ONE_CS_FOUND, false);
133 indicators.put( KEY_INFOS_ARE_MISSING, false);
134
135
136 Timestamp demandExpirationDateMAX = expiredIdentity.getExpirationDate( );
137 for ( final DemandDisplay demand : demandDisplayList )
138 {
139 final Timestamp expirationDateFromDemand = checkExpirationDateByDemand( demand,
140 indicators, excludedAppCodes, msg, _notificationStoreService );
141
142
143 if ( expirationDateFromDemand != null && demandExpirationDateMAX.before( expirationDateFromDemand ) )
144 {
145 demandExpirationDateMAX = expirationDateFromDemand;
146 }
147 }
148
149
150
151 if ( demandExpirationDateMAX.after( now ) )
152 {
153
154
155 expiredIdentity.setExpirationDate( demandExpirationDateMAX );
156 IdentityHome.update( expiredIdentity );
157
158
159 IdentityStoreNotifyListenerService.instance( ).notifyListenersIdentityChange( IdentityChangeType.UPDATE, expiredIdentity,
160 "EXPIRATION_POSTPONED", StringUtils.EMPTY, daemonAuthor, daemonClientCode, Collections.emptyMap( ) );
161 AccessLogService.getInstance( ).info( AccessLoggerConstants.EVENT_TYPE_MODIFY, UPDATE_IDENTITY_EVENT_CODE,
162 InternalUserService.getInstance( ).getApiUser( daemonAuthor, daemonClientCode ), null, "PURGE_DAEMON" );
163
164 msg.append( "Identity expiration date updated : [" ).append( expiredIdentity.getCustomerId( ) ).append( "]" ).append( "\n" );
165 }
166 else if ( indicators.get( KEY_INFOS_ARE_MISSING ) && demandExpirationDateMAX.after( olderCguRetentionDate ) )
167 {
168
169 if ( !indicators.get( KEY_AT_LEAST_ONE_CS_FOUND ) )
170 {
171 msg.append ( "No service contact found for identity [").append( expiredIdentity.getCustomerId( ) ).append ( "]\n");
172 }
173
174
175 msg.append( "Unsufficient infos to delete [" ).append( expiredIdentity.getCustomerId( ) ).append( "]" ).append( "\n" );
176
177
178 _identityStoreNotifyListenerService.notifyListenersIdentityChange(IdentityChangeType.DELETION_ATTEMPT_FAILED, expiredIdentity,
179 ResponseStatusType.UNAUTHORIZED.name(),ResponseStatusType.UNAUTHORIZED.name(),
180 new RequestAuthor ("DAEMON", AuthorType.application.name ( ) ), "DAEMON", null);
181
182 }
183 else if ( isDryRun )
184 {
185
186 msg.append( "(Dry run) should Detete Identity [" ).append( expiredIdentity.getCustomerId( ) ).append( "]" )
187 .append( " - Notified demands found : " ).append( demandDisplayList.size( ) ).append( "\n" );
188
189 if ( demandExpirationDateMAX.before( olderCguRetentionDate ) )
190 {
191 msg.append( " ( demand Expiration Date MAX = " ).append( demandExpirationDateMAX )
192 .append( ") < MAX Cgu Retention Date = ").append( olderCguRetentionDate )
193 .append( " )" );
194 }
195 }
196 else
197 {
198
199
200 IdentityService.instance( ).delete( expiredIdentity.getCustomerId( ) );
201 msg.append( "Detete Identity [" ).append( expiredIdentity.getCustomerId( ) ).append( "]" )
202 .append( " - Notified demands found : " ).append( demandDisplayList.size( ) ).append( "\n" );
203
204 if ( demandExpirationDateMAX.before( olderCguRetentionDate ) )
205 {
206 msg.append( " ( demand Expiration Date MAX = " ).append( demandExpirationDateMAX )
207 .append( ") < MAX Cgu Retention Date = ").append( olderCguRetentionDate )
208 .append( " )" );
209 }
210
211 if ( demandDisplayList.size( ) > 0 )
212 {
213
214 _notificationStoreService.deleteNotificationByCuid( expiredIdentity.getCustomerId( ) );
215 msg.append( "Notifications deleted for main identity [" ).append( expiredIdentity.getCustomerId( ) ).append( "]" ).append( "\n" );
216 for ( final Identity mergedIdentity : mergedIdentities )
217 {
218 _notificationStoreService.deleteNotificationByCuid( mergedIdentity.getCustomerId( ) );
219 msg.append( "Notifications deleted for merged identity [" ).append( mergedIdentity.getCustomerId( ) ).append( "]" ).append( "\n" );
220 }
221 }
222 }
223 }
224 catch( final Exception e )
225 {
226 msg.append( "Daemon execution error for identity : " ).append( expiredIdentity.getCustomerId( ) ).append( " :: " ).append( e.getMessage( ) )
227 .append( "\n" );
228 return msg.toString( );
229 }
230 }
231
232
233 return msg.toString( );
234 }
235
236
237
238
239
240
241
242
243 public static String getAppCodeFromDemandTypeId( final String strTypeId, NotificationStoreService notificationStoreService )
244 {
245 DemandType demandType = notificationStoreService.getDemandType( strTypeId );
246
247 if ( demandType != null )
248 {
249 return demandType.getAppCode( );
250 }
251
252 return null;
253 }
254
255
256
257
258
259
260
261
262
263
264
265
266 public static Timestamp checkExpirationDateByDemand( DemandDisplay demand,
267 Map<String,Boolean> indicators, List<String> excludedAppCodes, StringBuilder msg,
268 NotificationStoreService notificationStoreService) throws ClientAuthorizationException
269 {
270
271 if (demand.getDemand().getStatusId() == EnumGenericStatus.CANCELED.getStatusId()) {
272 return null;
273 }
274
275 final String appCode = getAppCodeFromDemandTypeId( demand.getDemand( ).getTypeId( ), notificationStoreService );
276
277
278 if ( appCode == null )
279 {
280 msg.append( "Unknown AppCode for demand_type_id : ")
281 .append ( (demand.getDemand( ).getTypeId( )!=null?demand.getDemand( ).getTypeId( ):"") )
282 .append ( "\n" );
283 AppLogService.info( "CheckExpirationDate / Unknown AppCode for demand_type_id : "
284 + (demand.getDemand( ).getTypeId( )!=null?demand.getDemand( ).getTypeId( ):"") );
285
286 indicators.put( KEY_INFOS_ARE_MISSING, true);
287 return null;
288 }
289
290
291 if ( excludedAppCodes.contains( appCode.toUpperCase( ) ) )
292 {
293
294 return null;
295 }
296
297 final List<String> clientCodeList = ServiceContractService.instance( ).getClientCodesFromAppCode( appCode.toUpperCase( ) );
298
299 int nbMonthsCGUsMAX = 0;
300 for ( final String clientCode : clientCodeList )
301 {
302
303 Timestamp tscreate = new Timestamp( demand.getDemand( ).getCreationDate( ) );
304 Date demandCreationDate = Date.valueOf( tscreate.toLocalDateTime( ).toLocalDate( ) );
305
306
307 ServiceContract sc = ServiceContractService.instance( ).getActiveServiceContractAtSpecificDate( clientCode, demandCreationDate );
308 if ( sc == null )
309 {
310 try
311 {
312
313 sc = ServiceContractService.instance( ).getActiveServiceContract( clientCode );
314 }
315 catch (ClientAuthorizationException e )
316 {
317
318 }
319 }
320
321 if ( sc != null )
322 {
323
324 indicators.put( KEY_AT_LEAST_ONE_CS_FOUND, true);
325
326 if ( sc.getDataRetentionPeriodInMonths( ) > nbMonthsCGUsMAX )
327 {
328 nbMonthsCGUsMAX = sc.getDataRetentionPeriodInMonths( );
329 }
330 }
331 else
332 {
333
334 indicators.put( KEY_INFOS_ARE_MISSING, true);
335 }
336 }
337
338 final ZonedDateTime demandDate = ZonedDateTime.ofInstant( Instant.ofEpochMilli( demand.getDemand( ).getModifyDate( ) ),
339 ZoneId.systemDefault( ) );
340 final Timestamp expirationDateFromDemand = Timestamp.from( demandDate.plusMonths( nbMonthsCGUsMAX ).toInstant( ) );
341
342 return expirationDateFromDemand;
343 }
344 }