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( "]" ).append( "\n" );
187
188 if ( demandExpirationDateMAX.before( olderCguRetentionDate ) )
189 {
190 msg.append( " ( demand Expiration Date MAX = " ).append( demandExpirationDateMAX )
191 .append( ") < MAX Cgu Retention Date = ").append( olderCguRetentionDate )
192 .append( " )" );
193 }
194 }
195 else
196 {
197
198
199 IdentityService.instance( ).delete( expiredIdentity.getCustomerId( ) );
200 msg.append( "Detete Identity [" ).append( expiredIdentity.getCustomerId( ) ).append( "]" ).append( "\n" );
201
202 if ( demandExpirationDateMAX.before( olderCguRetentionDate ) )
203 {
204 msg.append( " ( demand Expiration Date MAX = " ).append( demandExpirationDateMAX )
205 .append( ") < MAX Cgu Retention Date = ").append( olderCguRetentionDate )
206 .append( " )" );
207 }
208
209
210 _notificationStoreService.deleteNotificationByCuid( expiredIdentity.getCustomerId( ) );
211 msg.append( "Notifications deleted for main identity [" ).append( expiredIdentity.getCustomerId( ) ).append( "]" ).append( "\n" );
212 for ( final Identity mergedIdentity : mergedIdentities )
213 {
214 _notificationStoreService.deleteNotificationByCuid( mergedIdentity.getCustomerId( ) );
215 msg.append( "Notifications deleted for merged identity [" ).append( mergedIdentity.getCustomerId( ) ).append( "]" ).append( "\n" );
216 }
217 }
218 }
219 catch( final Exception e )
220 {
221 msg.append( "Daemon execution error for identity : " ).append( expiredIdentity.getCustomerId( ) ).append( " :: " ).append( e.getMessage( ) )
222 .append( "\n" );
223 return msg.toString( );
224 }
225 }
226
227
228 return msg.toString( );
229 }
230
231
232
233
234
235
236
237
238 public static String getAppCodeFromDemandTypeId( final String strTypeId, NotificationStoreService notificationStoreService )
239 {
240 DemandType demandType = notificationStoreService.getDemandType( strTypeId );
241
242 if ( demandType != null )
243 {
244 return demandType.getAppCode( );
245 }
246
247 return null;
248 }
249
250
251
252
253
254
255
256
257
258
259
260
261 public static Timestamp checkExpirationDateByDemand( DemandDisplay demand,
262 Map<String,Boolean> indicators, List<String> excludedAppCodes, StringBuilder msg,
263 NotificationStoreService notificationStoreService) throws ClientAuthorizationException
264 {
265
266 if (demand.getDemand().getStatusId() == EnumGenericStatus.CANCELED.getStatusId()) {
267 return null;
268 }
269
270 final String appCode = getAppCodeFromDemandTypeId( demand.getDemand( ).getTypeId( ), notificationStoreService );
271
272
273 if ( appCode == null )
274 {
275 msg.append( "Unknown AppCode for demand_type_id : ")
276 .append ( (demand.getDemand( ).getTypeId( )!=null?demand.getDemand( ).getTypeId( ):"") )
277 .append ( "\n" );
278 AppLogService.info( "CheckExpirationDate / Unknown AppCode for demand_type_id : "
279 + (demand.getDemand( ).getTypeId( )!=null?demand.getDemand( ).getTypeId( ):"") );
280
281 indicators.put( KEY_INFOS_ARE_MISSING, true);
282 return null;
283 }
284
285
286 if ( excludedAppCodes.contains( appCode.toUpperCase( ) ) )
287 {
288
289 return null;
290 }
291
292 final List<String> clientCodeList = ServiceContractService.instance( ).getClientCodesFromAppCode( appCode.toUpperCase( ) );
293
294 int nbMonthsCGUsMAX = 0;
295 for ( final String clientCode : clientCodeList )
296 {
297
298 Timestamp tscreate = new Timestamp( demand.getDemand( ).getCreationDate( ) );
299 Date demandCreationDate = Date.valueOf( tscreate.toLocalDateTime( ).toLocalDate( ) );
300
301
302 ServiceContract sc = ServiceContractService.instance( ).getActiveServiceContractAtSpecificDate( clientCode, demandCreationDate );
303 if ( sc == null )
304 {
305 try
306 {
307
308 sc = ServiceContractService.instance( ).getActiveServiceContract( clientCode );
309 }
310 catch (ClientAuthorizationException e )
311 {
312
313 }
314 }
315
316 if ( sc != null )
317 {
318
319 indicators.put( KEY_AT_LEAST_ONE_CS_FOUND, true);
320
321 if ( sc.getDataRetentionPeriodInMonths( ) > nbMonthsCGUsMAX )
322 {
323 nbMonthsCGUsMAX = sc.getDataRetentionPeriodInMonths( );
324 }
325 }
326 else
327 {
328
329 indicators.put( KEY_INFOS_ARE_MISSING, true);
330 }
331 }
332
333 final ZonedDateTime demandDate = ZonedDateTime.ofInstant( Instant.ofEpochMilli( demand.getDemand( ).getModifyDate( ) ),
334 ZoneId.systemDefault( ) );
335 final Timestamp expirationDateFromDemand = Timestamp.from( demandDate.plusMonths( nbMonthsCGUsMAX ).toInstant( ) );
336
337 return expirationDateFromDemand;
338 }
339 }