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.service.attribute;
35  
36  import fr.paris.lutece.plugins.identitystore.business.attribute.AttributeCertificate;
37  import fr.paris.lutece.plugins.identitystore.business.attribute.AttributeCertificateHome;
38  import fr.paris.lutece.plugins.identitystore.business.attribute.AttributeKey;
39  import fr.paris.lutece.plugins.identitystore.business.attribute.AttributeKeyHome;
40  import fr.paris.lutece.plugins.identitystore.business.contract.AttributeRight;
41  import fr.paris.lutece.plugins.identitystore.business.identity.Identity;
42  import fr.paris.lutece.plugins.identitystore.business.identity.IdentityAttribute;
43  import fr.paris.lutece.plugins.identitystore.business.identity.IdentityAttributeHome;
44  import fr.paris.lutece.plugins.identitystore.business.referentiel.RefAttributeCertificationLevel;
45  import fr.paris.lutece.plugins.identitystore.cache.AttributeKeyCache;
46  import fr.paris.lutece.plugins.identitystore.service.contract.AttributeCertificationDefinitionService;
47  import fr.paris.lutece.plugins.identitystore.service.contract.ServiceContractService;
48  import fr.paris.lutece.plugins.identitystore.v3.web.rs.dto.common.AttributeChangeStatus;
49  import fr.paris.lutece.plugins.identitystore.v3.web.rs.dto.common.AttributeDto;
50  import fr.paris.lutece.plugins.identitystore.v3.web.rs.dto.common.AttributeStatus;
51  import fr.paris.lutece.plugins.identitystore.v3.web.rs.util.Constants;
52  import fr.paris.lutece.plugins.identitystore.web.exception.IdentityStoreException;
53  import fr.paris.lutece.plugins.identitystore.web.exception.ResourceNotFoundException;
54  import fr.paris.lutece.portal.service.spring.SpringContextService;
55  import fr.paris.lutece.portal.service.util.AppPropertiesService;
56  import fr.paris.lutece.util.sql.TransactionManager;
57  import org.apache.commons.lang3.StringUtils;
58  
59  import java.sql.Timestamp;
60  import java.time.ZoneId;
61  import java.time.ZonedDateTime;
62  import java.util.List;
63  import java.util.Objects;
64  import java.util.Optional;
65  import java.util.stream.Collectors;
66  
67  public class IdentityAttributeService
68  {
69      private static final String UNCERTIFY_PROCESSUS = "identitystore.identity.uncertify.processus";
70  
71      private final AttributeKeyCache _attributeKeyCache = SpringContextService.getBean( "identitystore.attributeKeyCache" );
72      private final AttributeCertificationDefinitionService _attributeCertificationDefinitionService = AttributeCertificationDefinitionService.instance( );
73      private final ServiceContractService _serviceContractService = ServiceContractService.instance( );
74  
75      private static IdentityAttributeService _instance;
76  
77      public static IdentityAttributeService instance( )
78      {
79          if ( _instance == null )
80          {
81              _instance = new IdentityAttributeService( );
82              _instance._attributeKeyCache.refresh( );
83          }
84          return _instance;
85      }
86  
87      /**
88       * Get all attributes
89       */
90      public List<AttributeKey> getAllAtributeKeys( )
91      {
92          if(_attributeKeyCache.isCacheEnable())
93          {
94              List<AttributeKey> attributeKeys = _attributeKeyCache.getAll();
95              if( attributeKeys != null )
96              {
97                  return attributeKeys;
98              }
99          }
100 
101         List<AttributeKey> attributeKeys =  AttributeKeyHome.getAttributeKeysList(true);
102         if( attributeKeys != null && _attributeKeyCache.isCacheEnable() )
103         {
104             attributeKeys.forEach(
105                     attributeKey -> _attributeKeyCache.put( attributeKey.getKeyName(), attributeKey ));
106 
107         }
108 
109         return attributeKeys;
110     }
111 
112     /**
113      * Get {@link AttributeKey} from its key name.
114      * 
115      * @param keyName
116      *            the key name
117      * @return {@link AttributeKey}
118      * @throws ResourceNotFoundException
119      *             if no {@link AttributeKey} exists for the provided key name.
120      */
121     public AttributeKey getAttributeKey( final String keyName ) throws ResourceNotFoundException
122     {
123         if(_attributeKeyCache.isCacheEnable())
124         {
125             AttributeKey attribute = _attributeKeyCache.get( keyName );
126             if( attribute != null )
127             {
128                 return attribute;
129             }
130         }
131 
132         AttributeKey attribute =  AttributeKeyHome.findByKey( keyName, true);
133         if( attribute != null && _attributeKeyCache.isCacheEnable() )
134         {
135             _attributeKeyCache.put( attribute.getKeyName(), attribute );
136         }
137 
138         return attribute;
139     }
140 
141     /**
142      * Get {@link AttributeKey} from its key name.
143      * 
144      * @param keyName
145      *            the key name
146      * @return {@link AttributeKey}, or <code>null</code> if no {@link AttributeKey} exists for the provided key name.
147      */
148     public AttributeKey getAttributeKeySafe( final String keyName )
149     {
150         if(_attributeKeyCache.isCacheEnable())
151         {
152             try
153             {
154                 return _attributeKeyCache.get(keyName);
155             } catch (final Exception e)
156             {
157                 return null;
158             }
159         }
160         return AttributeKeyHome.findByKey( keyName, true);
161     }
162 
163     /**
164      * Get all {@link AttributeKey} that are flaged as PIVOT.
165      * 
166      * @return {@link List<AttributeKey>}
167      */
168     public List<AttributeKey> getPivotAttributeKeys( )
169     {
170         if(_attributeKeyCache.isCacheEnable())
171         {
172             List<AttributeKey> attributeKeys = _attributeKeyCache.getAll().stream( ).filter( AttributeKey::getPivot ).collect( Collectors.toList( ) );
173             if( attributeKeys != null )
174             {
175                 return attributeKeys;
176             }
177         }
178 
179         List<AttributeKey> attributeKeys =  AttributeKeyHome.getMandatoryForCreationAttributeKeyList();
180         if( attributeKeys != null && _attributeKeyCache.isCacheEnable() )
181         {
182             attributeKeys.forEach(
183                     attributeKey -> _attributeKeyCache.put( attributeKey.getKeyName(), attributeKey ));
184 
185         }
186 
187         return attributeKeys;
188     }
189 
190     public List<AttributeKey> getCommonAttributeKeys( final String keyName )
191     {
192         if(_attributeKeyCache.isCacheEnable())
193         {
194             if (_attributeKeyCache.getKeys().isEmpty())
195             {
196                 _attributeKeyCache.refresh();
197             }
198             return _attributeKeyCache.getKeys().stream().map(this::getAttributeKeySafe).filter(Objects::nonNull)
199                     .filter(attributeKey -> attributeKey.getCommonSearchKeyName() != null && Objects.equals(attributeKey.getCommonSearchKeyName(), keyName))
200                     .collect(Collectors.toList());
201         }
202 
203         List<String> attributeKeysnames =  AttributeKeyHome.getAttributeKeysNamesList();
204         return attributeKeysnames.stream().map(this::getAttributeKeySafe).filter(Objects::nonNull)
205                 .filter(attributeKey -> attributeKey.getCommonSearchKeyName() != null && Objects.equals(attributeKey.getCommonSearchKeyName(), keyName))
206                 .collect(Collectors.toList());
207 
208     }
209 
210     public void createAttributeKey( final AttributeKey attributeKey ) throws IdentityStoreException
211     {
212         TransactionManager.beginTransaction( null );
213         try
214         {
215             AttributeKeyHome.create( attributeKey );
216             if(_attributeKeyCache.isCacheEnable())
217             {
218                 _attributeKeyCache.put(attributeKey.getKeyName(), attributeKey);
219             }
220             TransactionManager.commitTransaction( null );
221         }
222         catch( Exception e )
223         {
224             TransactionManager.rollBack( null );
225             throw new IdentityStoreException( e.getMessage( ), e );
226         }
227     }
228 
229     public void updateAttributeKey( final AttributeKey attributeKey ) throws IdentityStoreException
230     {
231         TransactionManager.beginTransaction( null );
232         try
233         {
234             AttributeKeyHome.update( attributeKey );
235             if(_attributeKeyCache.isCacheEnable())
236             {
237                 _attributeKeyCache.put(attributeKey.getKeyName(), attributeKey);
238             }
239             TransactionManager.commitTransaction( null );
240         }
241         catch( Exception e )
242         {
243             TransactionManager.rollBack( null );
244             throw new IdentityStoreException( e.getMessage( ), e );
245         }
246     }
247 
248     public void deleteAttributeKey( final AttributeKey attributeKey ) throws IdentityStoreException
249     {
250         TransactionManager.beginTransaction( null );
251         try
252         {
253             AttributeKeyHome.remove( attributeKey.getId( ) );
254             if(_attributeKeyCache.isCacheEnable())
255             {
256                 _attributeKeyCache.removeKey(attributeKey.getKeyName());
257             }
258             TransactionManager.commitTransaction( null );
259         }
260         catch( Exception e )
261         {
262             TransactionManager.rollBack( null );
263             throw new IdentityStoreException( e.getMessage( ), e );
264         }
265     }
266 
267     /**
268      * Private method used to create an attribute for an identity.
269      *
270      * @param attributeToCreate
271      * @param identity
272      * @param clientCode
273      * @return AttributeStatus
274      * @throws IdentityStoreException
275      */
276     public AttributeStatus createAttribute( final AttributeDto attributeToCreate, final Identity identity, final String clientCode )
277             throws IdentityStoreException
278     {
279         if ( StringUtils.isBlank( attributeToCreate.getValue( ) ) )
280         {
281             final AttributeStatus attributeStatus = new AttributeStatus( );
282             attributeStatus.setKey( attributeToCreate.getKey( ) );
283             attributeStatus.setStatus( AttributeChangeStatus.NOT_CREATED );
284             attributeStatus.setMessageKey( Constants.PROPERTY_ATTRIBUTE_STATUS_NOT_CREATED );
285             return attributeStatus;
286         }
287 
288         final IdentityAttributetore/business/identity/IdentityAttribute.html#IdentityAttribute">IdentityAttribute attribute = new IdentityAttribute( );
289         attribute.setIdIdentity( identity.getId( ) );
290         attribute.setAttributeKey( getAttributeKey( attributeToCreate.getKey( ) ) );
291         attribute.setValue( attributeToCreate.getValue( ) );
292         attribute.setLastUpdateClientCode( clientCode );
293 
294         if ( attributeToCreate.getCertifier( ) != null )
295         {
296             final AttributeCertificatebusiness/attribute/AttributeCertificate.html#AttributeCertificate">AttributeCertificate certificate = new AttributeCertificate( );
297             certificate.setCertificateDate( new Timestamp( attributeToCreate.getCertificationDate( ).getTime( ) ) );
298             certificate.setCertifierCode( attributeToCreate.getCertifier( ) );
299             certificate.setCertifierName( attributeToCreate.getCertifier( ) ); // ?
300             attribute.setCertificate( AttributeCertificateHome.create( certificate ) );
301             attribute.setIdCertificate( attribute.getCertificate( ).getId( ) );
302         }
303 
304         IdentityAttributeHome.create( attribute );
305         identity.getAttributes( ).put( attribute.getAttributeKey( ).getKeyName( ), attribute );
306 
307         final AttributeStatus attributeStatus = new AttributeStatus( );
308         attributeStatus.setKey( attribute.getAttributeKey( ).getKeyName( ) );
309         attributeStatus.setStatus( AttributeChangeStatus.CREATED );
310         attributeStatus.setMessageKey( Constants.PROPERTY_ATTRIBUTE_STATUS_CREATED );
311 
312         return attributeStatus;
313     }
314 
315     /**
316      * private method used to update an attribute of an identity
317      *
318      * @param attributeToUpdate
319      * @param identity
320      * @param clientCode
321      * @return AttributeStatus
322      * @throws IdentityStoreException
323      */
324     public AttributeStatus updateAttribute( final AttributeDto attributeToUpdate, final Identity identity, final String clientCode )
325             throws IdentityStoreException
326     {
327         final IdentityAttribute existingAttribute = identity.getAttributes( ).get( attributeToUpdate.getKey( ) );
328 
329         int attributeToUpdateLevelInt = _attributeCertificationDefinitionService.getLevelAsInteger( attributeToUpdate.getCertifier( ),
330                 attributeToUpdate.getKey( ) );
331         int existingAttributeLevelInt = _attributeCertificationDefinitionService.getLevelAsInteger( existingAttribute.getCertificate( ).getCertifierCode( ),
332                 existingAttribute.getAttributeKey( ).getKeyName( ) );
333         
334         // si l'attribut existe déjà avec la même valeur et le même niveau de certif : pas de mise à jour
335         if ( attributeToUpdateLevelInt == existingAttributeLevelInt && StringUtils.equals( attributeToUpdate.getValue( ), existingAttribute.getValue( ) )
336                 && ( attributeToUpdate.getCertificationDate( ).equals( existingAttribute.getCertificate( ).getCertificateDate( ) )
337                         || attributeToUpdate.getCertificationDate( ).before( existingAttribute.getCertificate( ).getCertificateDate( ) ) ) )
338         {
339             final AttributeStatus attributeStatus = new AttributeStatus( );
340             attributeStatus.setKey( attributeToUpdate.getKey( ) );
341             attributeStatus.setStatus( AttributeChangeStatus.NOT_UPDATED );
342             attributeStatus.setMessageKey( Constants.PROPERTY_ATTRIBUTE_STATUS_NOT_UPDATED );
343             return attributeStatus;
344         }
345         
346         // le niveau de certification est supérieur ou égal au niveau existant : on met à jour
347         if ( attributeToUpdateLevelInt >= existingAttributeLevelInt )
348         {
349             // la valeur envoyée est vide = demande de suppression d'attribut
350             if ( StringUtils.isBlank( attributeToUpdate.getValue( ) ) )
351             {
352                 // chesk if attribute is mandatory 
353                 final Optional<AttributeRight> right = _serviceContractService.getActiveServiceContract( clientCode ).getAttributeRights( ).stream( )
354                         .filter( ar -> ar.getAttributeKey( ).getKeyName( ).equals( attributeToUpdate.getKey( ) ) ).findAny( );
355                 if ( right.isPresent( ) && right.get( ).isMandatory( ) )
356                 {
357                     final AttributeStatus attributeStatus = new AttributeStatus( );
358                     attributeStatus.setKey( attributeToUpdate.getKey( ) );
359                     attributeStatus.setStatus( AttributeChangeStatus.NOT_REMOVED );
360                     attributeStatus.setMessageKey( Constants.PROPERTY_ATTRIBUTE_STATUS_NOT_REMOVED );
361                     return attributeStatus;
362                 }
363                 
364                 // suppression attribut
365                 IdentityAttributeHome.remove( identity.getId( ), existingAttribute.getAttributeKey( ).getId( ) );
366                 identity.getAttributes( ).remove( existingAttribute.getAttributeKey( ).getKeyName( ) );
367 
368                 final AttributeStatus attributeStatus = new AttributeStatus( );
369                 attributeStatus.setKey( attributeToUpdate.getKey( ) );
370                 attributeStatus.setStatus( AttributeChangeStatus.REMOVED );
371                 attributeStatus.setMessageKey( Constants.PROPERTY_ATTRIBUTE_STATUS_REMOVED );
372                 return attributeStatus;
373             }
374 
375             // Update
376             existingAttribute.setValue( attributeToUpdate.getValue( ) );
377             existingAttribute.setLastUpdateClientCode( clientCode );
378             if ( existingAttribute.getIdIdentity ( ) <= 0 )
379             {
380         	existingAttribute.setIdIdentity ( identity.getId ( ) );
381             }
382             if ( existingAttribute.getAttributeKey ( ).getId ( ) <= 0 )
383             {
384         	existingAttribute.getAttributeKey( ).setId ( getAttributeKey ( existingAttribute.getAttributeKey( ).getKeyName ( ) ).getId ( ) );
385             }
386 
387             if ( attributeToUpdate.getCertifier( ) != null )
388             {
389                 final AttributeCertificatebusiness/attribute/AttributeCertificate.html#AttributeCertificate">AttributeCertificate certificate = new AttributeCertificate( );
390                 certificate.setCertificateDate( new Timestamp( attributeToUpdate.getCertificationDate( ).getTime( ) ) );
391                 certificate.setCertifierCode( attributeToUpdate.getCertifier( ) );
392                 certificate.setCertifierName( attributeToUpdate.getCertifier( ) );
393 
394                 existingAttribute.setCertificate( AttributeCertificateHome.create( certificate ) ); // TODO supprime-t-on l'ancien certificat ?
395                 existingAttribute.setIdCertificate( existingAttribute.getCertificate( ).getId( ) );
396             }
397 
398             IdentityAttributeHome.update( existingAttribute );
399             identity.getAttributes( ).put( existingAttribute.getAttributeKey( ).getKeyName( ), existingAttribute );
400 
401             final AttributeStatus attributeStatus = new AttributeStatus( );
402             attributeStatus.setKey( attributeToUpdate.getKey( ) );
403             attributeStatus.setStatus( AttributeChangeStatus.UPDATED );
404             attributeStatus.setMessageKey( Constants.PROPERTY_ATTRIBUTE_STATUS_UPDATED );
405             return attributeStatus;
406         }
407 
408         final AttributeStatus attributeStatus = new AttributeStatus( );
409         attributeStatus.setKey( attributeToUpdate.getKey( ) );
410         attributeStatus.setStatus( AttributeChangeStatus.INSUFFICIENT_CERTIFICATION_LEVEL );
411         attributeStatus.setMessageKey( Constants.PROPERTY_ATTRIBUTE_STATUS_INSUFFICIENT_CERTIFICATION_LEVEL );
412         return attributeStatus;
413     }
414 
415     /**
416      * Dé-certification d'un attribut.<br/>
417      * Une identité ne pouvant pas posséder d'attributs non-certifiés, une dé-certification implique la certification de l'attribut avec le processus défini par
418      * la property : <code>identitystore.identity.uncertify.processus</code> (par défaut : "dec", qui correspond au niveau le plus faible de certification
419      * (auto-déclaratif))
420      *
421      * @param attribute
422      *            l'attribut à dé-certifier
423      * @return le status
424      */
425     public AttributeStatus uncertifyAttribute( final IdentityAttribute attribute ) throws IdentityStoreException
426     {
427         final AttributeStatus status = new AttributeStatus( );
428         status.setKey( attribute.getAttributeKey( ).getKeyName( ) );
429 
430         final String processus = AppPropertiesService.getProperty( UNCERTIFY_PROCESSUS, "dec" );
431         if ( StringUtils.isBlank( processus ) )
432         {
433             throw new IdentityStoreException( "No uncertification processus specified in property : " + UNCERTIFY_PROCESSUS );
434         }
435 
436         final RefAttributeCertificationLevel certif = _attributeCertificationDefinitionService.get( processus, attribute.getAttributeKey( ).getKeyName( ) );
437         if ( certif == null )
438         {
439             throw new IdentityStoreException( "No uncertification processus found in database for [code=" + processus + "][attributeKey="
440                     + attribute.getAttributeKey( ).getKeyName( ) + "]" );
441         }
442 
443         final AttributeCertificatebusiness/attribute/AttributeCertificate.html#AttributeCertificate">AttributeCertificate certificate = new AttributeCertificate( );
444         certificate.setCertificateDate( Timestamp.from( ZonedDateTime.now( ZoneId.systemDefault( ) ).toInstant( ) ) );
445         certificate.setCertifierCode( certif.getRefAttributeCertificationProcessus( ).getCode( ) );
446 
447         attribute.setCertificate( AttributeCertificateHome.create( certificate ) ); // TODO supprime-t-on l'ancien certificat ?
448         attribute.setIdCertificate( attribute.getCertificate( ).getId( ) );
449 
450         IdentityAttributeHome.update( attribute );
451 
452         status.setStatus( AttributeChangeStatus.UNCERTIFIED );
453         status.setMessageKey( Constants.PROPERTY_ATTRIBUTE_STATUS_UNCERTIFIED );
454         return status;
455     }
456 }