001/* 002 * Copyright 2016 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.odf.rights; 017 018import java.util.Collections; 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; 027 028import org.ametys.cms.repository.Content; 029import org.ametys.cms.repository.ContentQueryHelper; 030import org.ametys.cms.repository.ContentTypeExpression; 031import org.ametys.cms.repository.DefaultContent; 032import org.ametys.core.group.GroupIdentity; 033import org.ametys.core.right.AccessController; 034import org.ametys.core.right.AccessController.Permission.PermissionType; 035import org.ametys.core.right.AccessExplanation; 036import org.ametys.core.right.RightProfilesDAO; 037import org.ametys.core.right.RightsException; 038import org.ametys.core.user.UserIdentity; 039import org.ametys.odf.ODFHelper; 040import org.ametys.odf.ProgramItem; 041import org.ametys.odf.course.CourseFactory; 042import org.ametys.odf.courselist.CourseListFactory; 043import org.ametys.odf.coursepart.CoursePart; 044import org.ametys.odf.coursepart.CoursePartFactory; 045import org.ametys.odf.orgunit.OrgUnit; 046import org.ametys.odf.orgunit.OrgUnitFactory; 047import org.ametys.odf.program.ContainerFactory; 048import org.ametys.odf.program.ProgramFactory; 049import org.ametys.odf.program.SubProgramFactory; 050import org.ametys.odf.tree.ODFContentsTreeHelper; 051import org.ametys.plugins.repository.AmetysObjectIterable; 052import org.ametys.plugins.repository.AmetysObjectResolver; 053import org.ametys.plugins.repository.query.expression.AndExpression; 054import org.ametys.plugins.repository.query.expression.Expression; 055import org.ametys.plugins.repository.query.expression.Expression.Operator; 056import org.ametys.plugins.repository.query.expression.UserExpression; 057import org.ametys.runtime.i18n.I18nizableText; 058import org.ametys.runtime.i18n.I18nizableTextParameter; 059import org.ametys.runtime.plugin.component.AbstractLogEnabled; 060import org.ametys.runtime.plugin.component.PluginAware; 061 062/** 063 * This access controller give access the content's creator, regardless of the required right, 064 * if and only if the ODF content is still orphan (during creation process for example) 065 */ 066public class ODFOrphanContentAccessController extends AbstractLogEnabled implements AccessController, Serviceable, PluginAware 067{ 068 /** The right profile DAO */ 069 protected RightProfilesDAO _profileDAO; 070 /** The ametys object resolver */ 071 protected AmetysObjectResolver _resolver; 072 /** The ODF contents tree helper */ 073 protected ODFContentsTreeHelper _odfContentsTreeHelper; 074 private ODFHelper _odfHelper; 075 private String _id; 076 077 public void service(ServiceManager manager) throws ServiceException 078 { 079 _odfContentsTreeHelper = (ODFContentsTreeHelper) manager.lookup(ODFContentsTreeHelper.ROLE); 080 _odfHelper = (ODFHelper) manager.lookup(ODFHelper.ROLE); 081 _profileDAO = (RightProfilesDAO) manager.lookup(RightProfilesDAO.ROLE); 082 _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE); 083 } 084 085 public void setPluginInfo(String pluginName, String featureName, String id) 086 { 087 _id = id; 088 } 089 090 public String getId() 091 { 092 return _id; 093 } 094 095 @Override 096 public boolean isSupported(Object object) 097 { 098 return object instanceof ProgramItem || object instanceof OrgUnit || object instanceof CoursePart; 099 } 100 101 /** 102 * Determines if the object is a orphan program item (without parent) 103 * @param object the object 104 * @return true if the object is a orphan program item 105 */ 106 protected boolean _isOrphan(Object object) 107 { 108 if (object instanceof ProgramItem) 109 { 110 List<ProgramItem> parentProgramItems = _odfHelper.getParentProgramItems((ProgramItem) object); 111 return parentProgramItems.isEmpty(); 112 } 113 else if (object instanceof OrgUnit) 114 { 115 return ((OrgUnit) object).getParentOrgUnit() == null; 116 } 117 else if (object instanceof CoursePart) 118 { 119 return ((CoursePart) object).getCourses().isEmpty(); 120 } 121 122 return false; 123 } 124 125 /** 126 * Get the user permission on object 127 * @param user the user 128 * @param object the object 129 * @return The access result 130 */ 131 protected AccessResult _getUserPermission(UserIdentity user, Object object) 132 { 133 if (_isOrphan(object) && !_hasOrgUnit(object)) 134 { 135 if (user.equals(((Content) object).getCreator())) 136 { 137 return AccessResult.USER_ALLOWED; 138 } 139 } 140 return AccessResult.UNKNOWN; 141 } 142 143 /** 144 * Determines if the object has a orgunit 145 * @param object the object 146 * @return true if the object is attach to a orgunit 147 */ 148 protected boolean _hasOrgUnit(Object object) 149 { 150 if (object instanceof ProgramItem programItem) 151 { 152 return !programItem.getOrgUnits().isEmpty(); 153 } 154 return false; 155 } 156 157 /** 158 * Get the permission by users 159 * @param object the object 160 * @return the permission by users 161 */ 162 protected Map<UserIdentity, AccessResult> _getPermissionByUser(Object object) 163 { 164 Map<UserIdentity, AccessResult> permissions = new HashMap<>(); 165 if (_isOrphan(object) && !_hasOrgUnit(object)) 166 { 167 permissions.put(((Content) object).getCreator(), AccessResult.USER_ALLOWED); 168 } 169 return permissions; 170 } 171 172 @Override 173 public AccessResult getPermission(UserIdentity user, Set<GroupIdentity> userGroups, String rightId, Object object) 174 { 175 return _getUserPermission(user, object); 176 } 177 178 @Override 179 public AccessResult getReadAccessPermission(UserIdentity user, Set<GroupIdentity> userGroups, Object object) 180 { 181 return _getUserPermission(user, object); 182 } 183 184 @Override 185 public Map<String, AccessResult> getPermissionByRight(UserIdentity user, Set<GroupIdentity> userGroups, Object object) 186 { 187 return Collections.EMPTY_MAP; 188 } 189 190 @Override 191 public AccessResult getPermissionForAnonymous(String rightId, Object object) 192 { 193 return AccessResult.UNKNOWN; 194 } 195 196 public AccessResult getReadAccessPermissionForAnonymous(Object object) 197 { 198 return AccessResult.UNKNOWN; 199 } 200 201 @Override 202 public AccessResult getPermissionForAnyConnectedUser(String rightId, Object object) 203 { 204 return AccessResult.UNKNOWN; 205 } 206 207 @Override 208 public AccessResult getReadAccessPermissionForAnyConnectedUser(Object object) 209 { 210 return AccessResult.UNKNOWN; 211 } 212 213 @Override 214 public Map<UserIdentity, AccessResult> getPermissionByUser(String rightId, Object object) 215 { 216 return _getPermissionByUser(object); 217 } 218 219 @Override 220 public Map<UserIdentity, AccessResult> getReadAccessPermissionByUser(Object object) 221 { 222 return _getPermissionByUser(object); 223 } 224 225 @Override 226 public Map<GroupIdentity, AccessResult> getPermissionByGroup(String rightId, Object object) 227 { 228 return Collections.EMPTY_MAP; 229 } 230 231 @Override 232 public Map<GroupIdentity, AccessResult> getReadAccessPermissionByGroup(Object object) 233 { 234 return Collections.EMPTY_MAP; 235 } 236 237 @Override 238 public boolean hasUserAnyPermissionOnWorkspace(Set<Object> workspacesContexts, UserIdentity user, Set<GroupIdentity> userGroups, String rightId) 239 { 240 return false; 241 } 242 243 @Override 244 public boolean hasUserAnyReadAccessPermissionOnWorkspace(Set<Object> workspacesContexts, UserIdentity user, Set<GroupIdentity> userGroups) 245 { 246 return false; 247 } 248 249 @Override 250 public boolean hasAnonymousAnyPermissionOnWorkspace(Set<Object> workspacesContexts, String rightId) 251 { 252 return false; 253 } 254 255 @Override 256 public boolean hasAnonymousAnyReadAccessPermissionOnWorkspace(Set<Object> workspacesContexts) 257 { 258 return false; 259 } 260 261 @Override 262 public boolean hasAnyConnectedUserAnyPermissionOnWorkspace(Set<Object> workspacesContexts, String rightId) 263 { 264 return false; 265 } 266 267 @Override 268 public boolean hasAnyConnectedUserAnyReadAccessPermissionOnWorkspace(Set<Object> workspacesContexts) 269 { 270 return false; 271 } 272 273 @Override 274 public AccessExplanation getStandardAccessExplanation(AccessResult permission, Object object) 275 { 276 if (permission == AccessResult.USER_ALLOWED) 277 { 278 return new AccessExplanation(getId(), permission, 279 new I18nizableText("plugin.odf", "PLUGINS_ODF_ORPHAN_ACCESS_CONTROLLER_EXPLANATION", _getI18nParameters(object)) 280 ); 281 } 282 return AccessController.getDefaultAccessExplanation(getId(), permission); 283 } 284 285 /** 286 * Get the context label 287 * @param object the context 288 * @return the label 289 */ 290 protected Map<String, I18nizableTextParameter> _getI18nParameters(Object object) 291 { 292 Map<String, I18nizableTextParameter> params = new HashMap<>(); 293 if (object instanceof Content content) 294 { 295 params.put("title", new I18nizableText(content.getTitle())); 296 } 297 298 if (object instanceof ProgramItem item) 299 { 300 params.put("code", new I18nizableText(item.getCode())); 301 } 302 else if (object instanceof OrgUnit orgunit) 303 { 304 params.put("code", new I18nizableText(orgunit.getUAICode())); 305 } 306 else if (object instanceof CoursePart part) 307 { 308 params.put("code", new I18nizableText(part.getCode())); 309 } 310 return params; 311 } 312 313 @Override 314 public Map<ExplanationObject, Map<Permission, AccessExplanation>> explainAllPermissions(UserIdentity identity, Set<GroupIdentity> groups) 315 { 316 Map<ExplanationObject, Map<Permission, AccessExplanation>> result = new HashMap<>(); 317 318 UserExpression userExpression = new UserExpression(DefaultContent.METADATA_CREATOR, Operator.EQ, identity); 319 Expression contentTypeExpression = new ContentTypeExpression(Operator.EQ, ProgramFactory.PROGRAM_CONTENT_TYPE, SubProgramFactory.SUBPROGRAM_CONTENT_TYPE, CourseFactory.COURSE_CONTENT_TYPE, ContainerFactory.CONTAINER_CONTENT_TYPE, CoursePartFactory.COURSE_PART_CONTENT_TYPE, CourseListFactory.COURSE_LIST_CONTENT_TYPE, OrgUnitFactory.ORGUNIT_CONTENT_TYPE); 320 String query = ContentQueryHelper.getContentXPathQuery(new AndExpression(userExpression, contentTypeExpression)); 321 AmetysObjectIterable<Content> contents = _resolver.query(query); 322 323 for (Content content : contents) 324 { 325 Map<Permission, AccessExplanation> contextResult = new HashMap<>(); 326 AccessExplanation explanation = explainPermission(identity, groups, null, content); 327 if (explanation.accessResult() != AccessResult.UNKNOWN) 328 { 329 contextResult.put(new Permission(PermissionType.ALL_RIGHTS, null), explanation); 330 } 331 332 explanation = explainReadAccessPermission(identity, groups, content); 333 if (explanation.accessResult() != AccessResult.UNKNOWN) 334 { 335 contextResult.put(new Permission(PermissionType.READ, null), explanation); 336 } 337 338 if (!contextResult.isEmpty()) 339 { 340 ExplanationObject explanationContext = getExplanationObject(content); 341 result.put(explanationContext, contextResult); 342 } 343 } 344 345 return result; 346 } 347 348 public I18nizableText getObjectLabel(Object object) 349 { 350 if (object instanceof Content content) 351 { 352 return ODFContentHierarchicalAccessController.getContentObjectLabel(content, _odfContentsTreeHelper); 353 } 354 throw new RightsException("Unsupported context: " + object.toString()); 355 } 356 357 public I18nizableText getObjectCategory(Object object) 358 { 359 return ODFContentHierarchicalAccessController.ODF_CONTEXT_CATEGORY; 360 } 361 362}