001/*
002 *  Copyright 2020 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.ugc.accesscontroller;
017
018import java.util.ArrayList;
019import java.util.Collection;
020import java.util.HashMap;
021import java.util.List;
022import java.util.Map;
023import java.util.Set;
024
025import org.apache.avalon.framework.service.ServiceException;
026import org.apache.avalon.framework.service.ServiceManager;
027import org.apache.avalon.framework.service.Serviceable;
028import org.apache.cocoon.components.ContextHelper;
029import org.apache.commons.collections.MapUtils;
030
031import org.ametys.cms.contenttype.ContentTypesHelper;
032import org.ametys.cms.repository.Content;
033import org.ametys.cms.repository.ContentQueryHelper;
034import org.ametys.cms.repository.DefaultContent;
035import org.ametys.cms.repository.MixinTypeExpression;
036import org.ametys.core.group.GroupIdentity;
037import org.ametys.core.right.AccessController;
038import org.ametys.core.right.AccessExplanation;
039import org.ametys.core.right.RightsException;
040import org.ametys.core.user.UserIdentity;
041import org.ametys.plugins.core.impl.right.AbstractRightBasedAccessController;
042import org.ametys.plugins.repository.AmetysObjectIterable;
043import org.ametys.plugins.repository.AmetysObjectResolver;
044import org.ametys.plugins.repository.query.expression.AndExpression;
045import org.ametys.plugins.repository.query.expression.Expression;
046import org.ametys.plugins.repository.query.expression.Expression.Operator;
047import org.ametys.plugins.repository.query.expression.ExpressionContext;
048import org.ametys.plugins.repository.query.expression.StringExpression;
049import org.ametys.plugins.repository.query.expression.UserExpression;
050import org.ametys.plugins.ugc.UGCConstants;
051import org.ametys.plugins.ugc.page.UGCPage;
052import org.ametys.plugins.ugc.page.UGCPageHandler;
053import org.ametys.runtime.i18n.I18nizableText;
054import org.ametys.web.WebHelper;
055import org.ametys.web.repository.SiteAwareAmetysObject;
056import org.ametys.web.repository.page.Page;
057import org.ametys.web.repository.site.Site;
058import org.ametys.web.repository.site.SiteManager;
059import org.ametys.web.repository.sitemap.Sitemap;
060import org.ametys.web.rights.PageAccessController;
061
062/**
063 * {@link AccessController} so creator of a UGC content types receive edit/delete rights on it
064 *
065 */
066public class UGCCreatorPageAccessController extends AbstractRightBasedAccessController implements Serviceable
067{
068    private static final List<String> __CREATOR_RIGHTS = List.of("Front_Edition_Access_Right");
069    
070    /** ContentTypes Helper */
071    protected ContentTypesHelper _cTypeHelper;
072    /** The ametys object resolver */
073    protected AmetysObjectResolver _resolver;
074    /** The site manager */
075    protected SiteManager _siteManager;
076    /** the UGC page handler */
077    protected UGCPageHandler _ugcPageHandler;
078
079    public void service(ServiceManager smanager) throws ServiceException
080    {
081        _cTypeHelper = (ContentTypesHelper) smanager.lookup(ContentTypesHelper.ROLE);
082        _resolver = (AmetysObjectResolver) smanager.lookup(AmetysObjectResolver.ROLE);
083        _siteManager = (SiteManager) smanager.lookup(SiteManager.ROLE);
084        _ugcPageHandler = (UGCPageHandler) smanager.lookup(UGCPageHandler.ROLE);
085    }
086    
087    public boolean supports(Object object)
088    {
089        return object instanceof UGCPage;
090    }
091    
092    public AccessResult getPermission(UserIdentity user, Set<GroupIdentity> userGroups, String rightId, Object object)
093    {
094        if (((UGCPage) object).getContent().getCreator().equals(user))
095        {
096            return __CREATOR_RIGHTS.contains(rightId) ? AccessResult.USER_ALLOWED : AccessResult.UNKNOWN;
097        }
098        
099        return AccessResult.UNKNOWN;
100    }
101
102    public AccessResult getReadAccessPermission(UserIdentity user, Set<GroupIdentity> userGroups, Object object)
103    {
104        return AccessResult.UNKNOWN;
105    }
106
107    /**
108     * If creator, access to a list of rights
109     */
110    public Map<String, AccessResult> getPermissionByRight(UserIdentity user, Set<GroupIdentity> userGroups, Object object)
111    {
112        Map<String, AccessResult> permissionByRight = new HashMap<>();
113        
114        if (((UGCPage) object).getContent().getCreator().equals(user))
115        {
116            for (String rightId : __CREATOR_RIGHTS)
117            {
118                permissionByRight.put(rightId, AccessResult.USER_ALLOWED);
119            }
120        }
121        
122        return permissionByRight;
123    }
124
125    public AccessResult getPermissionForAnonymous(String rightId, Object object)
126    {
127        return AccessResult.UNKNOWN;
128    }
129
130    public AccessResult getReadAccessPermissionForAnonymous(Object object)
131    {
132        return AccessResult.UNKNOWN;
133    }
134
135    public AccessResult getPermissionForAnyConnectedUser(String rightId, Object object)
136    {
137        return AccessResult.UNKNOWN;
138    }
139
140    public AccessResult getReadAccessPermissionForAnyConnectedUser(Object object)
141    {
142        return AccessResult.UNKNOWN;
143    }
144
145    /**
146     * If right requested is in the list, the creator is added the list of USER_ALLOWED
147     */
148    public Map<UserIdentity, AccessResult> getPermissionByUser(String rightId, Object object)
149    {
150        Map<UserIdentity, AccessResult> permissionByUser = new HashMap<>();
151        
152        if (__CREATOR_RIGHTS.contains(rightId))
153        {
154            permissionByUser.put(((UGCPage) object).getContent().getCreator(), AccessResult.USER_ALLOWED);
155        }
156        return permissionByUser;
157    }
158
159    public Map<UserIdentity, AccessResult> getReadAccessPermissionByUser(Object object)
160    {
161        return MapUtils.EMPTY_MAP;
162    }
163
164    public Map<GroupIdentity, AccessResult> getPermissionByGroup(String rightId, Object object)
165    {
166        return MapUtils.EMPTY_MAP;
167    }
168
169    public Map<GroupIdentity, AccessResult> getReadAccessPermissionByGroup(Object object)
170    {
171        return MapUtils.EMPTY_MAP;
172    }
173
174    public boolean hasUserAnyPermissionOnWorkspace(Set<Object> workspacesContexts, UserIdentity user, Set<GroupIdentity> userGroups, String rightId)
175    {
176        return false;
177    }
178
179    public boolean hasUserAnyReadAccessPermissionOnWorkspace(Set<Object> workspacesContexts, UserIdentity user, Set<GroupIdentity> userGroups)
180    {
181        return false;
182    }
183
184    public boolean hasAnonymousAnyPermissionOnWorkspace(Set<Object> workspacesContexts, String rightId)
185    {
186        return false;
187    }
188
189    public boolean hasAnonymousAnyReadAccessPermissionOnWorkspace(Set<Object> workspacesContexts)
190    {
191        return false;
192    }
193
194    public boolean hasAnyConnectedUserAnyPermissionOnWorkspace(Set<Object> workspacesContexts, String rightId)
195    {
196        return false;
197    }
198
199    public boolean hasAnyConnectedUserAnyReadAccessPermissionOnWorkspace(Set<Object> workspacesContexts)
200    {
201        return false;
202    }
203    
204    @Override
205    protected AccessExplanation _getAccessExplanation(AccessResult result, Object object, UserIdentity user, Set<GroupIdentity> groups, String rightId)
206    {
207        switch (result)
208        {
209            case USER_ALLOWED:
210            case UNKNOWN:
211                return new AccessExplanation(
212                        getId(),
213                        result,
214                        new I18nizableText("plugin.ugc", "PLUGINS_UGC_CREATOR_ACCESS_CONTROLLER_" + result.name() + "_EXPLANATION",
215                                Map.of("title", new I18nizableText(((UGCPage) object).getTitle()))
216                                )
217                        );
218            default:
219                return AccessController.getDefaultAccessExplanation(getId(), result);
220        }
221    }
222    
223    public I18nizableText getObjectLabel(Object object)
224    {
225        if (object instanceof Page page)
226        {
227            return new I18nizableText(PageAccessController.getPageObjectLabel(page));
228        }
229        throw new RightsException("Unsupported context: " + object.toString());
230    }
231
232    public I18nizableText getObjectCategory(Object object)
233    {
234        return PageAccessController.PAGE_CONTEXT_CATEGORY;
235    }
236
237    @Override
238    protected Iterable< ? extends Object> getHandledObjects(UserIdentity identity, Set<GroupIdentity> groups, Set<Object> workspacesContexts)
239    {
240        
241        String siteName = WebHelper.getSiteName(ContextHelper.getRequest(_context));
242        Site site = _siteManager.getSite(siteName);
243        
244        if (site != null)
245        {
246            List<Page> pages = new ArrayList<>();
247            
248            Expression typeExpr = new MixinTypeExpression(Operator.EQ, UGCConstants.UGC_MIXIN_TYPE);
249            
250            ExpressionContext expressionContextForInternal = ExpressionContext.newInstance().withInternal(true);
251            Expression userExpression = new UserExpression(DefaultContent.METADATA_CREATOR, Operator.EQ, identity, expressionContextForInternal);
252            Expression siteExpression = new StringExpression(SiteAwareAmetysObject.METADATA_SITE, Operator.EQ, siteName, expressionContextForInternal);
253            
254            String query = ContentQueryHelper.getContentXPathQuery(new AndExpression(siteExpression, typeExpr, userExpression));
255            try (AmetysObjectIterable<Content> contents = _resolver.query(query))
256            {
257                for (Content content : contents)
258                {
259                    try (AmetysObjectIterable<Sitemap> sitemaps = site.getSitemaps())
260                    {
261                        for (Sitemap sitemap: sitemaps)
262                        {
263                            for (String type : content.getTypes())
264                            {
265                                _ugcPageHandler.getUgcPage(content, siteName, sitemap.getSitemapName(), type)
266                                    .ifPresent(page -> pages.add(page));
267                            }
268                        }
269                    }
270                }
271            }
272            
273        }
274        return List.of();
275    }
276    
277    @Override
278    protected Collection<String> getHandledRights()
279    {
280        return __CREATOR_RIGHTS;
281    }
282}