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.service.identity.IdentityAttributeNotFoundException;
49  import fr.paris.lutece.plugins.identitystore.v3.web.rs.dto.common.AttributeChangeStatus;
50  import fr.paris.lutece.plugins.identitystore.v3.web.rs.dto.common.AttributeDto;
51  import fr.paris.lutece.plugins.identitystore.v3.web.rs.dto.common.AttributeStatus;
52  import fr.paris.lutece.plugins.identitystore.v3.web.rs.util.Constants;
53  import fr.paris.lutece.plugins.identitystore.web.exception.IdentityStoreException;
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 _cache = 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._cache.refresh( );
83          }
84          return _instance;
85      }
86  
87      /**
88       * Get all attributes
89       * 
90       * @return
91       * @throws IdentityAttributeNotFoundException
92       */
93      public List<AttributeKey> getAllAtributeKeys( )
94      {
95          return _cache.getAll( );
96      }
97  
98      /**
99       * Get {@link AttributeKey} from its key name.
100      * 
101      * @param keyName
102      *            the key name
103      * @return {@link AttributeKey}
104      * @throws IdentityAttributeNotFoundException
105      *             if no {@link AttributeKey} exists for the provided key name.
106      */
107     public AttributeKey getAttributeKey( final String keyName ) throws IdentityAttributeNotFoundException
108     {
109         return _cache.get( keyName );
110     }
111 
112     /**
113      * Get {@link AttributeKey} from its key name.
114      * 
115      * @param keyName
116      *            the key name
117      * @return {@link AttributeKey}, or <code>null</code> if no {@link AttributeKey} exists for the provided key name.
118      */
119     public AttributeKey getAttributeKeySafe( final String keyName )
120     {
121         try
122         {
123             return _cache.get( keyName );
124         }
125         catch( final Exception e )
126         {
127             return null;
128         }
129     }
130 
131     /**
132      * Get all {@link AttributeKey} that are flaged as PIVOT.
133      * 
134      * @return {@link List<AttributeKey>}
135      */
136     public List<AttributeKey> getPivotAttributeKeys( )
137     {
138         return _cache.getAll( ).stream( ).filter( AttributeKey::getPivot ).collect( Collectors.toList( ) );
139     }
140 
141     public List<AttributeKey> getCommonAttributeKeys( final String keyName )
142     {
143         return _cache.getAll( ).stream( )
144                 .filter( attributeKey -> attributeKey.getCommonSearchKeyName( ) != null && Objects.equals( attributeKey.getCommonSearchKeyName( ), keyName ) )
145                 .collect( Collectors.toList( ) );
146     }
147 
148     public void createAttributeKey( final AttributeKey attributeKey ) throws IdentityStoreException
149     {
150         TransactionManager.beginTransaction( null );
151         try
152         {
153             AttributeKeyHome.create( attributeKey );
154             _cache.put( attributeKey.getKeyName( ), attributeKey );
155             TransactionManager.commitTransaction( null );
156         }
157         catch( Exception e )
158         {
159             TransactionManager.rollBack( null );
160             throw new IdentityStoreException( e.getMessage( ), e );
161         }
162     }
163 
164     public void updateAttributeKey( final AttributeKey attributeKey ) throws IdentityStoreException
165     {
166         TransactionManager.beginTransaction( null );
167         try
168         {
169             AttributeKeyHome.update( attributeKey );
170             _cache.put( attributeKey.getKeyName( ), attributeKey );
171             TransactionManager.commitTransaction( null );
172         }
173         catch( Exception e )
174         {
175             TransactionManager.rollBack( null );
176             throw new IdentityStoreException( e.getMessage( ), e );
177         }
178     }
179 
180     public void deleteAttributeKey( final AttributeKey attributeKey ) throws IdentityStoreException
181     {
182         TransactionManager.beginTransaction( null );
183         try
184         {
185             AttributeKeyHome.remove( attributeKey.getId( ) );
186             _cache.removeKey( attributeKey.getKeyName( ) );
187             TransactionManager.commitTransaction( null );
188         }
189         catch( Exception e )
190         {
191             TransactionManager.rollBack( null );
192             throw new IdentityStoreException( e.getMessage( ), e );
193         }
194     }
195 
196     /**
197      * Private method used to create an attribute for an identity.
198      *
199      * @param attributeToCreate
200      * @param identity
201      * @param clientCode
202      * @return AttributeStatus
203      * @throws IdentityAttributeNotFoundException
204      */
205     public AttributeStatus createAttribute( final AttributeDto attributeToCreate, final Identity identity, final String clientCode )
206             throws IdentityStoreException
207     {
208         if ( StringUtils.isBlank( attributeToCreate.getValue( ) ) )
209         {
210             final AttributeStatus attributeStatus = new AttributeStatus( );
211             attributeStatus.setKey( attributeToCreate.getKey( ) );
212             attributeStatus.setStatus( AttributeChangeStatus.NOT_CREATED );
213             attributeStatus.setMessageKey( Constants.PROPERTY_ATTRIBUTE_STATUS_NOT_CREATED );
214             return attributeStatus;
215         }
216 
217         final IdentityAttributetore/business/identity/IdentityAttribute.html#IdentityAttribute">IdentityAttribute attribute = new IdentityAttribute( );
218         attribute.setIdIdentity( identity.getId( ) );
219         attribute.setAttributeKey( getAttributeKey( attributeToCreate.getKey( ) ) ); // ?
220         attribute.setValue( attributeToCreate.getValue( ) );
221         attribute.setLastUpdateClientCode( clientCode );
222 
223         if ( attributeToCreate.getCertifier( ) != null )
224         {
225             final AttributeCertificatebusiness/attribute/AttributeCertificate.html#AttributeCertificate">AttributeCertificate certificate = new AttributeCertificate( );
226             certificate.setCertificateDate( new Timestamp( attributeToCreate.getCertificationDate( ).getTime( ) ) );
227             certificate.setCertifierCode( attributeToCreate.getCertifier( ) );
228             certificate.setCertifierName( attributeToCreate.getCertifier( ) ); // ?
229             attribute.setCertificate( AttributeCertificateHome.create( certificate ) );
230             attribute.setIdCertificate( attribute.getCertificate( ).getId( ) );
231         }
232 
233         IdentityAttributeHome.create( attribute );
234         identity.getAttributes( ).put( attribute.getAttributeKey( ).getKeyName( ), attribute );
235 
236         final AttributeStatus attributeStatus = new AttributeStatus( );
237         attributeStatus.setKey( attribute.getAttributeKey( ).getKeyName( ) );
238         attributeStatus.setStatus( AttributeChangeStatus.CREATED );
239         attributeStatus.setMessageKey( Constants.PROPERTY_ATTRIBUTE_STATUS_CREATED );
240 
241         return attributeStatus;
242     }
243 
244     /**
245      * private method used to update an attribute of an identity
246      *
247      * @param attributeToUpdate
248      * @param identity
249      * @param clientCode
250      * @return AttributeStatus
251      * @throws IdentityStoreException
252      */
253     public AttributeStatus updateAttribute( final AttributeDto attributeToUpdate, final Identity identity, final String clientCode )
254             throws IdentityStoreException
255     {
256         final IdentityAttribute existingAttribute = identity.getAttributes( ).get( attributeToUpdate.getKey( ) );
257 
258         int attributeToUpdateLevelInt = _attributeCertificationDefinitionService.getLevelAsInteger( attributeToUpdate.getCertifier( ),
259                 attributeToUpdate.getKey( ) );
260         int existingAttributeLevelInt = _attributeCertificationDefinitionService.getLevelAsInteger( existingAttribute.getCertificate( ).getCertifierCode( ),
261                 existingAttribute.getAttributeKey( ).getKeyName( ) );
262         if ( attributeToUpdateLevelInt == existingAttributeLevelInt && StringUtils.equals( attributeToUpdate.getValue( ), existingAttribute.getValue( ) )
263                 && ( attributeToUpdate.getCertificationDate( ).equals( existingAttribute.getCertificate( ).getCertificateDate( ) )
264                         || attributeToUpdate.getCertificationDate( ).before( existingAttribute.getCertificate( ).getCertificateDate( ) ) ) )
265         {
266             final AttributeStatus attributeStatus = new AttributeStatus( );
267             attributeStatus.setKey( attributeToUpdate.getKey( ) );
268             attributeStatus.setStatus( AttributeChangeStatus.NOT_UPDATED );
269             attributeStatus.setMessageKey( Constants.PROPERTY_ATTRIBUTE_STATUS_NOT_UPDATED );
270             return attributeStatus;
271         }
272         if ( attributeToUpdateLevelInt >= existingAttributeLevelInt )
273         {
274             if ( StringUtils.isBlank( attributeToUpdate.getValue( ) ) )
275             {
276                 // #232 : remove attribute if :
277                 // - attribute is not mandatory
278                 // - new value is null or blank
279                 // - sent certification level is >= to existing one
280                 final Optional<AttributeRight> right = _serviceContractService.getActiveServiceContract( clientCode ).getAttributeRights( ).stream( )
281                         .filter( ar -> ar.getAttributeKey( ).getKeyName( ).equals( attributeToUpdate.getKey( ) ) ).findAny( );
282                 if ( right.isPresent( ) && right.get( ).isMandatory( ) )
283                 {
284                     final AttributeStatus attributeStatus = new AttributeStatus( );
285                     attributeStatus.setKey( attributeToUpdate.getKey( ) );
286                     attributeStatus.setStatus( AttributeChangeStatus.NOT_REMOVED );
287                     attributeStatus.setMessageKey( Constants.PROPERTY_ATTRIBUTE_STATUS_NOT_REMOVED );
288                     return attributeStatus;
289                 }
290                 IdentityAttributeHome.remove( identity.getId( ), existingAttribute.getAttributeKey( ).getId( ) );
291                 identity.getAttributes( ).remove( existingAttribute.getAttributeKey( ).getKeyName( ) );
292 
293                 final AttributeStatus attributeStatus = new AttributeStatus( );
294                 attributeStatus.setKey( attributeToUpdate.getKey( ) );
295                 attributeStatus.setStatus( AttributeChangeStatus.REMOVED );
296                 attributeStatus.setMessageKey( Constants.PROPERTY_ATTRIBUTE_STATUS_REMOVED );
297                 return attributeStatus;
298             }
299 
300             existingAttribute.setValue( attributeToUpdate.getValue( ) );
301             existingAttribute.setLastUpdateClientCode( clientCode );
302 
303             if ( attributeToUpdate.getCertifier( ) != null )
304             {
305                 final AttributeCertificatebusiness/attribute/AttributeCertificate.html#AttributeCertificate">AttributeCertificate certificate = new AttributeCertificate( );
306                 certificate.setCertificateDate( new Timestamp( attributeToUpdate.getCertificationDate( ).getTime( ) ) );
307                 certificate.setCertifierCode( attributeToUpdate.getCertifier( ) );
308                 certificate.setCertifierName( attributeToUpdate.getCertifier( ) );
309 
310                 existingAttribute.setCertificate( AttributeCertificateHome.create( certificate ) ); // TODO supprime-t-on l'ancien certificat ?
311                 existingAttribute.setIdCertificate( existingAttribute.getCertificate( ).getId( ) );
312             }
313 
314             IdentityAttributeHome.update( existingAttribute );
315             identity.getAttributes( ).put( existingAttribute.getAttributeKey( ).getKeyName( ), existingAttribute );
316 
317             final AttributeStatus attributeStatus = new AttributeStatus( );
318             attributeStatus.setKey( attributeToUpdate.getKey( ) );
319             attributeStatus.setStatus( AttributeChangeStatus.UPDATED );
320             attributeStatus.setMessageKey( Constants.PROPERTY_ATTRIBUTE_STATUS_UPDATED );
321             return attributeStatus;
322         }
323 
324         final AttributeStatus attributeStatus = new AttributeStatus( );
325         attributeStatus.setKey( attributeToUpdate.getKey( ) );
326         attributeStatus.setStatus( AttributeChangeStatus.INSUFFICIENT_CERTIFICATION_LEVEL );
327         attributeStatus.setMessageKey( Constants.PROPERTY_ATTRIBUTE_STATUS_INSUFFICIENT_CERTIFICATION_LEVEL );
328         return attributeStatus;
329     }
330 
331     /**
332      * Dé-certification d'un attribut.<br/>
333      * 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
334      * la property : <code>identitystore.identity.uncertify.processus</code> (par défaut : "dec", qui correspond au niveau le plus faible de certification
335      * (auto-déclaratif))
336      *
337      * @param attribute
338      *            l'attribut à dé-certifier
339      * @return le status
340      */
341     public AttributeStatus uncertifyAttribute( final IdentityAttribute attribute ) throws IdentityStoreException
342     {
343         final AttributeStatus status = new AttributeStatus( );
344         status.setKey( attribute.getAttributeKey( ).getKeyName( ) );
345 
346         final String processus = AppPropertiesService.getProperty( UNCERTIFY_PROCESSUS, "dec" );
347         if ( StringUtils.isBlank( processus ) )
348         {
349             throw new IdentityStoreException( "No uncertification processus specified in property : " + UNCERTIFY_PROCESSUS );
350         }
351 
352         final RefAttributeCertificationLevel certif = _attributeCertificationDefinitionService.get( processus, attribute.getAttributeKey( ).getKeyName( ) );
353         if ( certif == null )
354         {
355             throw new IdentityStoreException( "No uncertification processus found in database for [code=" + processus + "][attributeKey="
356                     + attribute.getAttributeKey( ).getKeyName( ) + "]" );
357         }
358 
359         final AttributeCertificatebusiness/attribute/AttributeCertificate.html#AttributeCertificate">AttributeCertificate certificate = new AttributeCertificate( );
360         certificate.setCertificateDate( Timestamp.from( ZonedDateTime.now( ZoneId.systemDefault( ) ).toInstant( ) ) );
361         certificate.setCertifierCode( certif.getRefAttributeCertificationProcessus( ).getCode( ) );
362 
363         attribute.setCertificate( AttributeCertificateHome.create( certificate ) ); // TODO supprime-t-on l'ancien certificat ?
364         attribute.setIdCertificate( attribute.getCertificate( ).getId( ) );
365 
366         IdentityAttributeHome.update( attribute );
367 
368         status.setStatus( AttributeChangeStatus.UNCERTIFIED );
369         status.setMessageKey( Constants.PROPERTY_ATTRIBUTE_STATUS_UNCERTIFIED );
370         return status;
371     }
372 }