001/*
002 *  Copyright 2024 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.odfweb.rights;
017
018import org.apache.avalon.framework.context.Context;
019import org.apache.avalon.framework.context.ContextException;
020import org.apache.avalon.framework.context.Contextualizable;
021import org.apache.avalon.framework.service.ServiceException;
022import org.apache.avalon.framework.service.ServiceManager;
023import org.apache.cocoon.components.ContextHelper;
024import org.apache.cocoon.environment.Request;
025import org.apache.commons.lang3.StringUtils;
026
027import org.ametys.core.cache.Cache;
028import org.ametys.core.user.UserIdentity;
029import org.ametys.odf.ODFHelper;
030import org.ametys.odf.ProgramItem;
031import org.ametys.odf.program.AbstractProgram;
032import org.ametys.odf.program.Container;
033import org.ametys.plugins.core.impl.cache.AbstractCacheKey;
034import org.ametys.plugins.odfweb.repository.OdfPageHandler;
035import org.ametys.plugins.odfweb.repository.OdfPageResolver;
036import org.ametys.plugins.repository.AmetysObjectIterable;
037import org.ametys.runtime.i18n.I18nizableText;
038import org.ametys.web.WebHelper;
039import org.ametys.web.repository.site.Site;
040import org.ametys.web.repository.site.SiteManager;
041
042/**
043 * Implementation of {@link ODFRoleAccessControllerHelper} that consider current site to get program items with user as role.
044 *
045 */
046public class ODFRoleAccessControllerHelper extends org.ametys.odf.rights.ODFRoleAccessControllerHelper implements Contextualizable
047{
048    private static final String __CACHE_BY_SITE_ID = ODFRoleAccessControllerHelper.class.getName() + "$cacheBySite";
049
050    private SiteManager _siteManager;
051    private OdfPageHandler _odfPageHandler;
052    private OdfPageResolver _odfPageResolver;
053    private ODFHelper _odfHelper;
054
055    private Context _context;
056
057    public void contextualize(Context context) throws ContextException
058    {
059        _context = context;
060    }
061    
062    @Override
063    public void service(ServiceManager smanager) throws ServiceException
064    {
065        super.service(smanager);
066        _siteManager = (SiteManager) smanager.lookup(SiteManager.ROLE);
067        _odfPageHandler = (OdfPageHandler) smanager.lookup(OdfPageHandler.ROLE);
068        _odfPageResolver = (OdfPageResolver) smanager.lookup(OdfPageResolver.ROLE);
069        _odfHelper = (ODFHelper) smanager.lookup(ODFHelper.ROLE);
070    }
071    
072    @Override
073    public void initialize() throws Exception
074    {
075        if (!_cacheManager.hasCache(__CACHE_BY_SITE_ID))
076        {
077            _cacheManager.createRequestCache(__CACHE_BY_SITE_ID,
078                new I18nizableText("plugin.odf-web", "PLUGINS_ODF_WEB_CACHE_ROLE_ACCESS_CONTROLLER_LABEL"),
079                new I18nizableText("plugin.odf-web", "PLUGINS_ODF_WEB_CACHE_ROLE_ACCESS_CONTROLLER_DESCRIPTION"),
080                false
081            );
082        }
083    }
084    
085    static class CacheKey extends AbstractCacheKey
086    {
087        CacheKey(UserIdentity userIdentity, String siteName, String roleAttributePath)
088        {
089            super(userIdentity, siteName, roleAttributePath);
090        }
091
092        static CacheKey of(UserIdentity userIdentity, String siteName, String roleAttributePath)
093        {
094            return new CacheKey(userIdentity, siteName, roleAttributePath);
095        } 
096        
097    }
098    
099    @Override
100    public boolean hasODFRoleOnAnyProgramItem(UserIdentity user, String attributePath)
101    {
102        Cache<CacheKey, Boolean> cache = _getCacheBySite();
103        
104        Request request = ContextHelper.getRequest(_context);
105        String siteName = WebHelper.getSiteName(request);
106        
107        CacheKey key = CacheKey.of(user, siteName, attributePath);
108        return cache.get(key, __ -> _hasODFRoleOnAnyProgramItem(user, siteName, attributePath));
109    }
110    
111    private boolean _hasODFRoleOnAnyProgramItem(UserIdentity user, String siteName, String attributePath)
112    {
113        AmetysObjectIterable<ProgramItem> programItems = _odfRightHelper.getProgramItemsWithUserAsRole(user, attributePath);
114        if (programItems.getSize() == 0)
115        {
116            // user has no ODF role on program items
117            return false;
118        }
119        
120        if (StringUtils.isNotBlank(siteName))
121        {
122            Site site = _siteManager.getSite(siteName);
123            if (_odfPageHandler.hasOdfRootPage(site))
124            {
125                // Check if at least one program item is part of ODF sitemap tree of current site (taking into account ODF restrictions)
126                for (ProgramItem programItem : programItems)
127                {
128                    if (programItem instanceof Container)
129                    {
130                        for (AbstractProgram abstractProgram : _odfHelper.getParentAbstractPrograms(programItem))
131                        {
132                            if (!_odfPageResolver.getReferencingPages(abstractProgram, siteName, null).isEmpty())
133                            {
134                                return true;
135                            }
136                        }
137                    }
138                    else if (!_odfPageResolver.getReferencingPages(programItem, siteName, null).isEmpty())
139                    {
140                        return true;
141                    }
142                }
143                
144                // No find program item which is part of ODF tree on current site (there is no ODF root page, or ODF restrictions do not match program items)
145                return false;
146            }
147            else
148            {
149                // Consider user has ODF role on program items on current site, ONLY IF there is no other site with ODF root page
150                return !_hasOdfRootPage();
151            }
152        }
153        else
154        {
155            // user has ODF role on program items on no site context
156            return true;
157        }
158    }
159    
160    private boolean _hasOdfRootPage()
161    {
162        try (AmetysObjectIterable<Site> sites = _siteManager.getSites())
163        {
164            for (Site site : sites)
165            {
166                if (_odfPageHandler.hasOdfRootPage(site))
167                {
168                    return true;
169                }
170            }
171        }
172        return false;
173    }
174    
175    private Cache<CacheKey, Boolean> _getCacheBySite()
176    {
177        return _cacheManager.get(__CACHE_BY_SITE_ID);
178    }
179}