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