001/*
002 *  Copyright 2025 Anyware Services
003 *
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package org.ametys.plugins.newsletter.subscribe;
017
018import java.time.Period;
019import java.time.ZonedDateTime;
020import java.util.Arrays;
021import java.util.Map;
022import java.util.Set;
023
024import org.apache.avalon.framework.service.ServiceException;
025import org.apache.avalon.framework.service.ServiceManager;
026import org.apache.avalon.framework.service.Serviceable;
027import org.apache.commons.lang3.StringUtils;
028
029import org.ametys.core.trace.ForensicLogger;
030import org.ametys.core.user.UserManager;
031import org.ametys.core.user.directory.NotUniqueUserException;
032import org.ametys.core.user.population.PopulationContextHelper;
033import org.ametys.core.user.population.UserPopulationDAO;
034import org.ametys.core.user.status.PersonalDataPolicy;
035import org.ametys.core.user.status.UserStatusInfo;
036import org.ametys.plugins.newsletter.daos.SubscribersDAO;
037import org.ametys.plugins.repository.AmetysObjectIterable;
038import org.ametys.runtime.config.Config;
039import org.ametys.web.repository.site.Site;
040import org.ametys.web.repository.site.SiteManager;
041
042/**
043 * Personal data policy removing subscription of missing user
044 */
045public class SubscriberDataPolicy implements PersonalDataPolicy, Serviceable
046{
047    /** The population context helper */
048    protected PopulationContextHelper _populationContextHelper;
049    /** The site DAO */
050    protected SiteManager _siteManager;
051    /** The subscriber DAO */
052    protected SubscribersDAO _subscribersDao;
053    /** The user manager */
054    protected UserManager _userManager;
055    private Period _retentionPeriod;
056    
057    public void service(ServiceManager manager) throws ServiceException
058    {
059        _populationContextHelper = (PopulationContextHelper) manager.lookup(PopulationContextHelper.ROLE);
060        _siteManager = (SiteManager) manager.lookup(SiteManager.ROLE);
061        _subscribersDao = (SubscribersDAO) manager.lookup(SubscribersDAO.ROLE);
062        _userManager = (UserManager) manager.lookup(UserManager.ROLE);
063        Long config = Config.getInstance().<Long>getValue("runtime.data.policy.userpref.retention", false, null);
064        // disable the processing if config is negative
065        _retentionPeriod = config != null && config >= 0 ? Period.ofMonths(config.intValue()) : null;
066    }
067    public AnonymizationResult process(UserStatusInfo userStatusInfo)
068    {
069        if (_retentionPeriod != null && userStatusInfo.getMissingSinceDate().isBefore(ZonedDateTime.now().minus(_retentionPeriod)))
070        {
071            // remove the subscription from every site where the unknown user does not belong to a user.
072            // the subscription from the remaining site will be processed once the matching user will be deleted
073            
074            long deletedSubscriptions = 0;
075            String email = userStatusInfo.getEmail();
076            if (StringUtils.isNotBlank(email))
077            {
078                try (AmetysObjectIterable<Site> sites = _siteManager.getSites())
079                {
080                    for (Site site: sites)
081                    {
082                        String siteName = site.getName();
083                        Set<String> userPopulationsOnSite = _populationContextHelper.getUserPopulationsOnContexts(Arrays.asList("/sites/" + siteName, "/sites-fo/" + siteName), false, false);
084                        try
085                        {
086                            if (_userManager.getUserByEmail(userPopulationsOnSite, email) == null)
087                            {
088                                deletedSubscriptions += _subscribersDao.unsubscribe(email, siteName);
089                            }
090                        }
091                        catch (NotUniqueUserException e)
092                        {
093                            // Do nothing, this email is still matching existing users
094                        }
095                    }
096                }
097            }
098            
099            if (deletedSubscriptions > 0)
100            {
101                ForensicLogger.info("data.policy.gdpr.remove.newsletter.subscriptions", Map.of("handled", Long.toString(deletedSubscriptions), "identity", userStatusInfo.getUserIdentity()), UserPopulationDAO.SYSTEM_USER_IDENTITY);
102                return AnonymizationResult.PROCESSED;
103            }
104            else
105            {
106                return AnonymizationResult.NO_DATA;
107            }
108        }
109        else
110        {
111            return AnonymizationResult.TOO_EARLY;
112        }
113    }
114
115}