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.linkdirectory; 017 018import java.time.Period; 019import java.time.ZonedDateTime; 020import java.util.Map; 021 022import org.apache.avalon.framework.service.ServiceException; 023import org.apache.avalon.framework.service.ServiceManager; 024import org.apache.avalon.framework.service.Serviceable; 025 026import org.ametys.core.trace.ForensicLogger; 027import org.ametys.core.user.UserIdentity; 028import org.ametys.core.user.population.UserPopulationDAO; 029import org.ametys.core.user.status.PersonalDataPolicy; 030import org.ametys.core.user.status.PersonalDataProcessingException; 031import org.ametys.core.user.status.UserStatusInfo; 032import org.ametys.plugins.linkdirectory.link.LinkDAO; 033import org.ametys.plugins.linkdirectory.repository.DefaultLink; 034import org.ametys.plugins.repository.AmetysObjectIterable; 035import org.ametys.plugins.repository.AmetysRepositoryException; 036import org.ametys.runtime.config.Config; 037import org.ametys.web.repository.site.Site; 038import org.ametys.web.repository.site.SiteManager; 039import org.ametys.web.repository.sitemap.Sitemap; 040 041/** 042 * Delete user links after a period 043 */ 044public class UserLinksDataPolicy implements PersonalDataPolicy, Serviceable 045{ 046 /** The link directory helper */ 047 protected DirectoryHelper _directoryHelper; 048 /** The link DAO */ 049 protected LinkDAO _linkDAO; 050 /** The site manager */ 051 protected SiteManager _siteManager; 052 053 private Period _retentionPeriod; 054 055 public void service(ServiceManager manager) throws ServiceException 056 { 057 _directoryHelper = (DirectoryHelper) manager.lookup(DirectoryHelper.ROLE); 058 _linkDAO = (LinkDAO) manager.lookup(LinkDAO.ROLE); 059 _siteManager = (SiteManager) manager.lookup(SiteManager.ROLE); 060 061 Long config = Config.getInstance().<Long>getValue("link-directory.user-links.data-policy.retention.period", false, null); 062 _retentionPeriod = config != null && config >= 0 ? Period.ofMonths(config.intValue()) : null; 063 } 064 065 public AnonymizationResult process(UserStatusInfo userStatusInfo) throws PersonalDataProcessingException 066 { 067 if (_retentionPeriod == null) 068 { 069 return AnonymizationResult.TOO_EARLY; 070 } 071 else if (userStatusInfo.getMissingSinceDate().isBefore(ZonedDateTime.now().minus(_retentionPeriod))) 072 { 073 int handled = 0; 074 UserIdentity userIdentity = userStatusInfo.getUserIdentity(); 075 try (AmetysObjectIterable<Site> sites = _siteManager.getSites()) 076 { 077 for (Site site: sites) 078 { 079 try (AmetysObjectIterable<Sitemap> sitemaps = site.getSitemaps()) 080 { 081 for (Sitemap sitemap : sitemaps) 082 { 083 try (AmetysObjectIterable<DefaultLink> userLinks = _directoryHelper.getUserLinks(site.getName(), sitemap.getName(), userIdentity)) 084 { 085 for (DefaultLink link : userLinks) 086 { 087 _linkDAO.deleteLink(link); 088 handled++; 089 } 090 } 091 } 092 } 093 } 094 095 if (handled > 0) 096 { 097 ForensicLogger.info("data.policy.gdpr.remove.user.links", Map.of("handled", Integer.toString(handled), "identity", userIdentity), UserPopulationDAO.SYSTEM_USER_IDENTITY); 098 return AnonymizationResult.PROCESSED; 099 } 100 else 101 { 102 return AnonymizationResult.NO_DATA; 103 } 104 } 105 catch (AmetysRepositoryException e) 106 { 107 throw new PersonalDataProcessingException("An error prevented the processing of the links for user '" + userIdentity + "'.", e); 108 } 109 } 110 else 111 { 112 return AnonymizationResult.TOO_EARLY; 113 } 114 } 115}