IdentityAttributeService.java
/*
* Copyright (c) 2002-2024, City of Paris
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright notice
* and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice
* and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* 3. Neither the name of 'Mairie de Paris' nor 'Lutece' nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* License 1.0
*/
package fr.paris.lutece.plugins.identitystore.service.attribute;
import fr.paris.lutece.plugins.identitystore.business.attribute.AttributeCertificate;
import fr.paris.lutece.plugins.identitystore.business.attribute.AttributeCertificateHome;
import fr.paris.lutece.plugins.identitystore.business.attribute.AttributeKey;
import fr.paris.lutece.plugins.identitystore.business.attribute.AttributeKeyHome;
import fr.paris.lutece.plugins.identitystore.business.contract.AttributeRight;
import fr.paris.lutece.plugins.identitystore.business.identity.Identity;
import fr.paris.lutece.plugins.identitystore.business.identity.IdentityAttribute;
import fr.paris.lutece.plugins.identitystore.business.identity.IdentityAttributeHome;
import fr.paris.lutece.plugins.identitystore.business.referentiel.RefAttributeCertificationLevel;
import fr.paris.lutece.plugins.identitystore.cache.AttributeKeyCache;
import fr.paris.lutece.plugins.identitystore.service.contract.AttributeCertificationDefinitionService;
import fr.paris.lutece.plugins.identitystore.service.contract.ServiceContractService;
import fr.paris.lutece.plugins.identitystore.service.identity.IdentityAttributeNotFoundException;
import fr.paris.lutece.plugins.identitystore.v3.web.rs.dto.common.AttributeChangeStatus;
import fr.paris.lutece.plugins.identitystore.v3.web.rs.dto.common.AttributeDto;
import fr.paris.lutece.plugins.identitystore.v3.web.rs.dto.common.AttributeStatus;
import fr.paris.lutece.plugins.identitystore.v3.web.rs.util.Constants;
import fr.paris.lutece.plugins.identitystore.web.exception.IdentityStoreException;
import fr.paris.lutece.portal.service.spring.SpringContextService;
import fr.paris.lutece.portal.service.util.AppPropertiesService;
import fr.paris.lutece.util.sql.TransactionManager;
import org.apache.commons.lang3.StringUtils;
import java.sql.Timestamp;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
public class IdentityAttributeService
{
private static final String UNCERTIFY_PROCESSUS = "identitystore.identity.uncertify.processus";
private final AttributeKeyCache _cache = SpringContextService.getBean( "identitystore.attributeKeyCache" );
private final AttributeCertificationDefinitionService _attributeCertificationDefinitionService = AttributeCertificationDefinitionService.instance( );
private final ServiceContractService _serviceContractService = ServiceContractService.instance( );
private static IdentityAttributeService _instance;
public static IdentityAttributeService instance( )
{
if ( _instance == null )
{
_instance = new IdentityAttributeService( );
_instance._cache.refresh( );
}
return _instance;
}
/**
* Get all attributes
*
* @return
* @throws IdentityAttributeNotFoundException
*/
public List<AttributeKey> getAllAtributeKeys( )
{
return _cache.getAll( );
}
/**
* Get {@link AttributeKey} from its key name.
*
* @param keyName
* the key name
* @return {@link AttributeKey}
* @throws IdentityAttributeNotFoundException
* if no {@link AttributeKey} exists for the provided key name.
*/
public AttributeKey getAttributeKey( final String keyName ) throws IdentityAttributeNotFoundException
{
return _cache.get( keyName );
}
/**
* Get {@link AttributeKey} from its key name.
*
* @param keyName
* the key name
* @return {@link AttributeKey}, or <code>null</code> if no {@link AttributeKey} exists for the provided key name.
*/
public AttributeKey getAttributeKeySafe( final String keyName )
{
try
{
return _cache.get( keyName );
}
catch( final Exception e )
{
return null;
}
}
/**
* Get all {@link AttributeKey} that are flaged as PIVOT.
*
* @return {@link List<AttributeKey>}
*/
public List<AttributeKey> getPivotAttributeKeys( )
{
return _cache.getAll( ).stream( ).filter( AttributeKey::getPivot ).collect( Collectors.toList( ) );
}
public List<AttributeKey> getCommonAttributeKeys( final String keyName )
{
return _cache.getAll( ).stream( )
.filter( attributeKey -> attributeKey.getCommonSearchKeyName( ) != null && Objects.equals( attributeKey.getCommonSearchKeyName( ), keyName ) )
.collect( Collectors.toList( ) );
}
public void createAttributeKey( final AttributeKey attributeKey ) throws IdentityStoreException
{
TransactionManager.beginTransaction( null );
try
{
AttributeKeyHome.create( attributeKey );
_cache.put( attributeKey.getKeyName( ), attributeKey );
TransactionManager.commitTransaction( null );
}
catch( Exception e )
{
TransactionManager.rollBack( null );
throw new IdentityStoreException( e.getMessage( ), e );
}
}
public void updateAttributeKey( final AttributeKey attributeKey ) throws IdentityStoreException
{
TransactionManager.beginTransaction( null );
try
{
AttributeKeyHome.update( attributeKey );
_cache.put( attributeKey.getKeyName( ), attributeKey );
TransactionManager.commitTransaction( null );
}
catch( Exception e )
{
TransactionManager.rollBack( null );
throw new IdentityStoreException( e.getMessage( ), e );
}
}
public void deleteAttributeKey( final AttributeKey attributeKey ) throws IdentityStoreException
{
TransactionManager.beginTransaction( null );
try
{
AttributeKeyHome.remove( attributeKey.getId( ) );
_cache.removeKey( attributeKey.getKeyName( ) );
TransactionManager.commitTransaction( null );
}
catch( Exception e )
{
TransactionManager.rollBack( null );
throw new IdentityStoreException( e.getMessage( ), e );
}
}
/**
* Private method used to create an attribute for an identity.
*
* @param attributeToCreate
* @param identity
* @param clientCode
* @return AttributeStatus
* @throws IdentityAttributeNotFoundException
*/
public AttributeStatus createAttribute( final AttributeDto attributeToCreate, final Identity identity, final String clientCode )
throws IdentityStoreException
{
if ( StringUtils.isBlank( attributeToCreate.getValue( ) ) )
{
final AttributeStatus attributeStatus = new AttributeStatus( );
attributeStatus.setKey( attributeToCreate.getKey( ) );
attributeStatus.setStatus( AttributeChangeStatus.NOT_CREATED );
attributeStatus.setMessageKey( Constants.PROPERTY_ATTRIBUTE_STATUS_NOT_CREATED );
return attributeStatus;
}
final IdentityAttribute attribute = new IdentityAttribute( );
attribute.setIdIdentity( identity.getId( ) );
attribute.setAttributeKey( getAttributeKey( attributeToCreate.getKey( ) ) ); // ?
attribute.setValue( attributeToCreate.getValue( ) );
attribute.setLastUpdateClientCode( clientCode );
if ( attributeToCreate.getCertifier( ) != null )
{
final AttributeCertificate certificate = new AttributeCertificate( );
certificate.setCertificateDate( new Timestamp( attributeToCreate.getCertificationDate( ).getTime( ) ) );
certificate.setCertifierCode( attributeToCreate.getCertifier( ) );
certificate.setCertifierName( attributeToCreate.getCertifier( ) ); // ?
attribute.setCertificate( AttributeCertificateHome.create( certificate ) );
attribute.setIdCertificate( attribute.getCertificate( ).getId( ) );
}
IdentityAttributeHome.create( attribute );
identity.getAttributes( ).put( attribute.getAttributeKey( ).getKeyName( ), attribute );
final AttributeStatus attributeStatus = new AttributeStatus( );
attributeStatus.setKey( attribute.getAttributeKey( ).getKeyName( ) );
attributeStatus.setStatus( AttributeChangeStatus.CREATED );
attributeStatus.setMessageKey( Constants.PROPERTY_ATTRIBUTE_STATUS_CREATED );
return attributeStatus;
}
/**
* private method used to update an attribute of an identity
*
* @param attributeToUpdate
* @param identity
* @param clientCode
* @return AttributeStatus
* @throws IdentityStoreException
*/
public AttributeStatus updateAttribute( final AttributeDto attributeToUpdate, final Identity identity, final String clientCode )
throws IdentityStoreException
{
final IdentityAttribute existingAttribute = identity.getAttributes( ).get( attributeToUpdate.getKey( ) );
int attributeToUpdateLevelInt = _attributeCertificationDefinitionService.getLevelAsInteger( attributeToUpdate.getCertifier( ),
attributeToUpdate.getKey( ) );
int existingAttributeLevelInt = _attributeCertificationDefinitionService.getLevelAsInteger( existingAttribute.getCertificate( ).getCertifierCode( ),
existingAttribute.getAttributeKey( ).getKeyName( ) );
if ( attributeToUpdateLevelInt == existingAttributeLevelInt && StringUtils.equals( attributeToUpdate.getValue( ), existingAttribute.getValue( ) )
&& ( attributeToUpdate.getCertificationDate( ).equals( existingAttribute.getCertificate( ).getCertificateDate( ) )
|| attributeToUpdate.getCertificationDate( ).before( existingAttribute.getCertificate( ).getCertificateDate( ) ) ) )
{
final AttributeStatus attributeStatus = new AttributeStatus( );
attributeStatus.setKey( attributeToUpdate.getKey( ) );
attributeStatus.setStatus( AttributeChangeStatus.NOT_UPDATED );
attributeStatus.setMessageKey( Constants.PROPERTY_ATTRIBUTE_STATUS_NOT_UPDATED );
return attributeStatus;
}
if ( attributeToUpdateLevelInt >= existingAttributeLevelInt )
{
if ( StringUtils.isBlank( attributeToUpdate.getValue( ) ) )
{
// #232 : remove attribute if :
// - attribute is not mandatory
// - new value is null or blank
// - sent certification level is >= to existing one
final Optional<AttributeRight> right = _serviceContractService.getActiveServiceContract( clientCode ).getAttributeRights( ).stream( )
.filter( ar -> ar.getAttributeKey( ).getKeyName( ).equals( attributeToUpdate.getKey( ) ) ).findAny( );
if ( right.isPresent( ) && right.get( ).isMandatory( ) )
{
final AttributeStatus attributeStatus = new AttributeStatus( );
attributeStatus.setKey( attributeToUpdate.getKey( ) );
attributeStatus.setStatus( AttributeChangeStatus.NOT_REMOVED );
attributeStatus.setMessageKey( Constants.PROPERTY_ATTRIBUTE_STATUS_NOT_REMOVED );
return attributeStatus;
}
IdentityAttributeHome.remove( identity.getId( ), existingAttribute.getAttributeKey( ).getId( ) );
identity.getAttributes( ).remove( existingAttribute.getAttributeKey( ).getKeyName( ) );
final AttributeStatus attributeStatus = new AttributeStatus( );
attributeStatus.setKey( attributeToUpdate.getKey( ) );
attributeStatus.setStatus( AttributeChangeStatus.REMOVED );
attributeStatus.setMessageKey( Constants.PROPERTY_ATTRIBUTE_STATUS_REMOVED );
return attributeStatus;
}
existingAttribute.setValue( attributeToUpdate.getValue( ) );
existingAttribute.setLastUpdateClientCode( clientCode );
if ( attributeToUpdate.getCertifier( ) != null )
{
final AttributeCertificate certificate = new AttributeCertificate( );
certificate.setCertificateDate( new Timestamp( attributeToUpdate.getCertificationDate( ).getTime( ) ) );
certificate.setCertifierCode( attributeToUpdate.getCertifier( ) );
certificate.setCertifierName( attributeToUpdate.getCertifier( ) );
existingAttribute.setCertificate( AttributeCertificateHome.create( certificate ) ); // TODO supprime-t-on l'ancien certificat ?
existingAttribute.setIdCertificate( existingAttribute.getCertificate( ).getId( ) );
}
IdentityAttributeHome.update( existingAttribute );
identity.getAttributes( ).put( existingAttribute.getAttributeKey( ).getKeyName( ), existingAttribute );
final AttributeStatus attributeStatus = new AttributeStatus( );
attributeStatus.setKey( attributeToUpdate.getKey( ) );
attributeStatus.setStatus( AttributeChangeStatus.UPDATED );
attributeStatus.setMessageKey( Constants.PROPERTY_ATTRIBUTE_STATUS_UPDATED );
return attributeStatus;
}
final AttributeStatus attributeStatus = new AttributeStatus( );
attributeStatus.setKey( attributeToUpdate.getKey( ) );
attributeStatus.setStatus( AttributeChangeStatus.INSUFFICIENT_CERTIFICATION_LEVEL );
attributeStatus.setMessageKey( Constants.PROPERTY_ATTRIBUTE_STATUS_INSUFFICIENT_CERTIFICATION_LEVEL );
return attributeStatus;
}
/**
* Dé-certification d'un attribut.<br/>
* 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
* la property : <code>identitystore.identity.uncertify.processus</code> (par défaut : "dec", qui correspond au niveau le plus faible de certification
* (auto-déclaratif))
*
* @param attribute
* l'attribut à dé-certifier
* @return le status
*/
public AttributeStatus uncertifyAttribute( final IdentityAttribute attribute ) throws IdentityStoreException
{
final AttributeStatus status = new AttributeStatus( );
status.setKey( attribute.getAttributeKey( ).getKeyName( ) );
final String processus = AppPropertiesService.getProperty( UNCERTIFY_PROCESSUS, "dec" );
if ( StringUtils.isBlank( processus ) )
{
throw new IdentityStoreException( "No uncertification processus specified in property : " + UNCERTIFY_PROCESSUS );
}
final RefAttributeCertificationLevel certif = _attributeCertificationDefinitionService.get( processus, attribute.getAttributeKey( ).getKeyName( ) );
if ( certif == null )
{
throw new IdentityStoreException( "No uncertification processus found in database for [code=" + processus + "][attributeKey="
+ attribute.getAttributeKey( ).getKeyName( ) + "]" );
}
final AttributeCertificate certificate = new AttributeCertificate( );
certificate.setCertificateDate( Timestamp.from( ZonedDateTime.now( ZoneId.systemDefault( ) ).toInstant( ) ) );
certificate.setCertifierCode( certif.getRefAttributeCertificationProcessus( ).getCode( ) );
attribute.setCertificate( AttributeCertificateHome.create( certificate ) ); // TODO supprime-t-on l'ancien certificat ?
attribute.setIdCertificate( attribute.getCertificate( ).getId( ) );
IdentityAttributeHome.update( attribute );
status.setStatus( AttributeChangeStatus.UNCERTIFIED );
status.setMessageKey( Constants.PROPERTY_ATTRIBUTE_STATUS_UNCERTIFIED );
return status;
}
}