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.newsletter.daos.SubscribersDAO.UnsubscribeOrigin; 038import org.ametys.plugins.repository.AmetysObjectIterable; 039import org.ametys.runtime.config.Config; 040import org.ametys.web.repository.site.Site; 041import org.ametys.web.repository.site.SiteManager; 042 043/** 044 * Personal data policy removing subscription of missing user 045 */ 046public class SubscriberDataPolicy implements PersonalDataPolicy, Serviceable 047{ 048 /** The population context helper */ 049 protected PopulationContextHelper _populationContextHelper; 050 /** The site DAO */ 051 protected SiteManager _siteManager; 052 /** The subscriber DAO */ 053 protected SubscribersDAO _subscribersDao; 054 /** The user manager */ 055 protected UserManager _userManager; 056 private Period _retentionPeriod; 057 058 public void service(ServiceManager manager) throws ServiceException 059 { 060 _populationContextHelper = (PopulationContextHelper) manager.lookup(PopulationContextHelper.ROLE); 061 _siteManager = (SiteManager) manager.lookup(SiteManager.ROLE); 062 _subscribersDao = (SubscribersDAO) manager.lookup(SubscribersDAO.ROLE); 063 _userManager = (UserManager) manager.lookup(UserManager.ROLE); 064 Long config = Config.getInstance().<Long>getValue("runtime.data.policy.userpref.retention", false, null); 065 // disable the processing if config is negative 066 _retentionPeriod = config != null && config >= 0 ? Period.ofMonths(config.intValue()) : null; 067 } 068 public AnonymizationResult process(UserStatusInfo userStatusInfo) 069 { 070 if (_retentionPeriod != null && userStatusInfo.getMissingSinceDate().isBefore(ZonedDateTime.now().minus(_retentionPeriod))) 071 { 072 // remove the subscription from every site where the unknown user does not belong to a user. 073 // the subscription from the remaining site will be processed once the matching user will be deleted 074 075 long deletedSubscriptions = 0; 076 String email = userStatusInfo.getEmail(); 077 if (StringUtils.isNotBlank(email)) 078 { 079 try (AmetysObjectIterable<Site> sites = _siteManager.getSites()) 080 { 081 for (Site site: sites) 082 { 083 String siteName = site.getName(); 084 Set<String> userPopulationsOnSite = _populationContextHelper.getUserPopulationsOnContexts(Arrays.asList("/sites/" + siteName, "/sites-fo/" + siteName), false, false); 085 try 086 { 087 if (_userManager.getUserByEmail(userPopulationsOnSite, email) == null) 088 { 089 deletedSubscriptions += _subscribersDao.unsubscribe(email, siteName, UnsubscribeOrigin.DATAPOLICY); 090 } 091 } 092 catch (NotUniqueUserException e) 093 { 094 // Do nothing, this email is still matching existing users 095 } 096 } 097 } 098 } 099 100 if (deletedSubscriptions > 0) 101 { 102 ForensicLogger.info("data.policy.gdpr.remove.newsletter.subscriptions", Map.of("handled", Long.toString(deletedSubscriptions), "identity", userStatusInfo.getUserIdentity()), UserPopulationDAO.SYSTEM_USER_IDENTITY); 103 return AnonymizationResult.PROCESSED; 104 } 105 else 106 { 107 return AnonymizationResult.NO_DATA; 108 } 109 } 110 else 111 { 112 return AnonymizationResult.TOO_EARLY; 113 } 114 } 115 116}