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