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