001/* 002 * Copyright 2017 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.restrictions; 017 018import java.io.File; 019import java.io.FileInputStream; 020import java.io.InputStream; 021import java.util.ArrayList; 022import java.util.Collections; 023import java.util.HashMap; 024import java.util.List; 025import java.util.Map; 026import java.util.Set; 027 028import org.apache.avalon.framework.activity.Disposable; 029import org.apache.avalon.framework.component.Component; 030import org.apache.avalon.framework.configuration.Configuration; 031import org.apache.avalon.framework.configuration.ConfigurationException; 032import org.apache.avalon.framework.configuration.DefaultConfigurationBuilder; 033import org.apache.avalon.framework.context.Context; 034import org.apache.avalon.framework.context.ContextException; 035import org.apache.avalon.framework.context.Contextualizable; 036import org.apache.avalon.framework.service.ServiceException; 037import org.apache.avalon.framework.service.ServiceManager; 038import org.apache.avalon.framework.service.Serviceable; 039import org.apache.cocoon.Constants; 040import org.apache.cocoon.components.ContextHelper; 041import org.apache.cocoon.environment.Request; 042import org.apache.commons.lang.StringUtils; 043 044import org.ametys.cms.contenttype.ContentType; 045import org.ametys.cms.contenttype.ContentTypeExtensionPoint; 046import org.ametys.cms.model.ContentElementDefinition; 047import org.ametys.core.ui.Callable; 048import org.ametys.odf.enumeration.OdfReferenceTableEntry; 049import org.ametys.odf.enumeration.OdfReferenceTableHelper; 050import org.ametys.odf.orgunit.OrgUnit; 051import org.ametys.odf.orgunit.RootOrgUnitProvider; 052import org.ametys.odf.program.ProgramFactory; 053import org.ametys.plugins.odfweb.restrictions.rules.OdfAndRestrictionRule; 054import org.ametys.plugins.odfweb.restrictions.rules.OdfAttributeRestrictionRule; 055import org.ametys.plugins.odfweb.restrictions.rules.OdfNotRestrictionRule; 056import org.ametys.plugins.odfweb.restrictions.rules.OdfOrRestrictionRule; 057import org.ametys.plugins.odfweb.restrictions.rules.OdfOrgunitRestrictionRule; 058import org.ametys.plugins.odfweb.restrictions.rules.OdfRestrictionRule; 059import org.ametys.plugins.repository.AmetysObjectResolver; 060import org.ametys.plugins.repository.provider.RequestAttributeWorkspaceSelector; 061import org.ametys.runtime.i18n.I18nizableText; 062import org.ametys.runtime.model.ModelItem; 063import org.ametys.runtime.plugin.component.AbstractLogEnabled; 064import org.ametys.web.repository.page.Page; 065import org.ametys.web.repository.site.Site; 066import org.ametys.web.repository.site.SiteManager; 067 068/** 069 * Component able to handle program restrictions related to the "odf-restrictions" site parameter. 070 */ 071public class OdfProgramRestrictionManager extends AbstractLogEnabled implements Component, Serviceable, Contextualizable, Disposable 072{ 073 /** The avalon role. */ 074 public static final String ROLE = OdfProgramRestrictionManager.class.getName(); 075 076 /** Site Manager */ 077 protected SiteManager _siteManager; 078 079 /** The content type extension point. */ 080 protected ContentTypeExtensionPoint _cTypeEP; 081 082 /** Ametys object resolver */ 083 protected AmetysObjectResolver _resolver; 084 085 /** The ODF reference table helper */ 086 protected OdfReferenceTableHelper _odfReferenceTableHelper; 087 088 /** Root orgunit provider */ 089 protected RootOrgUnitProvider _rootOrgUnitProvider; 090 091 /** Cocoon context */ 092 protected org.apache.cocoon.environment.Context _cocoonContext; 093 094 /** The available odf restrictions */ 095 protected Map<String, OdfProgramRestriction> _restrictions; 096 097 private Context _context; 098 099 @Override 100 public void service(ServiceManager serviceManager) throws ServiceException 101 { 102 _siteManager = (SiteManager) serviceManager.lookup(SiteManager.ROLE); 103 _cTypeEP = (ContentTypeExtensionPoint) serviceManager.lookup(ContentTypeExtensionPoint.ROLE); 104 _resolver = (AmetysObjectResolver) serviceManager.lookup(AmetysObjectResolver.ROLE); 105 _rootOrgUnitProvider = (RootOrgUnitProvider) serviceManager.lookup(RootOrgUnitProvider.ROLE); 106 _odfReferenceTableHelper = (OdfReferenceTableHelper) serviceManager.lookup(OdfReferenceTableHelper.ROLE); 107 } 108 109 @Override 110 public void contextualize(Context context) throws ContextException 111 { 112 _context = context; 113 _cocoonContext = (org.apache.cocoon.environment.Context) context.get(Constants.CONTEXT_ENVIRONMENT_CONTEXT); 114 } 115 116 @Override 117 public void dispose() 118 { 119 _restrictions = null; 120 } 121 122 /** 123 * Retrieves the available restrictions 124 * @return The map of restriction, where keys are restriction ids. 125 */ 126 public Map<String, OdfProgramRestriction> getRestrictions() 127 { 128 // Lazy initialize restrictions because orgunit restrictions cannot be 129 // resolved during the initialization of the component 130 if (_restrictions == null) 131 { 132 _readRestrictionConfigurationFile(); 133 } 134 135 return _restrictions; 136 } 137 138 /** 139 * Get the ODF restriction for the given ODF root page 140 * @param odfRootPage The ODF root page 141 * @return The restriction or <code>null</code> if there is no restriction. 142 */ 143 public OdfProgramRestriction getRestriction(Page odfRootPage) 144 { 145 Site site = odfRootPage.getSite(); 146 return getRestriction(site); 147 } 148 149 /** 150 * Get the ODF restriction for the given site 151 * @param site the site 152 * @return The restriction or <code>null</code> if there is no restriction. 153 */ 154 public OdfProgramRestriction getRestriction(Site site) 155 { 156 String restrictionId = site.getValue("odf-restrictions"); 157 if (StringUtils.isNotEmpty(restrictionId)) 158 { 159 return getRestrictions().get(restrictionId); 160 } 161 162 return null; 163 } 164 165 /** 166 * Get the ODF restriction for the given site name 167 * @param siteName the site name 168 * @return The restriction or <code>null</code> if there is no restriction. 169 */ 170 public OdfProgramRestriction getRestriction(String siteName) 171 { 172 Site site = _siteManager.getSite(siteName); 173 return getRestriction(site); 174 } 175 176 /** 177 * Indicate is there is any restriction for this root page 178 * @param rootPage odf root page 179 * @return true if it is the case 180 */ 181 public boolean hasRestrictions(Page rootPage) 182 { 183 Site site = rootPage.getSite(); 184 String restrictionId = site.getValue("odf-restrictions"); 185 return StringUtils.isNotEmpty(restrictionId) ? getRestrictions().containsKey(restrictionId) : false; 186 } 187 188 /** 189 * Indicate is there is any restriction related to orgunit for this root page 190 * @param rootPage odf root page 191 * @return true if it is the case 192 */ 193 public boolean hasOrgunitRestrictions(Page rootPage) 194 { 195 Site site = rootPage.getSite(); 196 String restrictionId = site.getValue("odf-restrictions"); 197 if (StringUtils.isNotEmpty(restrictionId)) 198 { 199 OdfProgramRestriction odfProgramRestriction = getRestrictions().get(restrictionId); 200 return odfProgramRestriction.hasOrgunitRestrictions(); 201 } 202 203 return false; 204 } 205 206 /** 207 * Get the id of the restriction root orgunit 208 * @param siteName the site name 209 * @return the id of the restriction root orgunit 210 */ 211 @Callable(rights = Callable.NO_CHECK_REQUIRED) // only return content id without more info 212 public String getRestrictionRootOrgUnitId(String siteName) 213 { 214 String odfRootId = null; 215 216 Site site = _siteManager.getSite(siteName); 217 String restrictionId = site.getValue("odf-restrictions"); 218 if (StringUtils.isNotEmpty(restrictionId)) 219 { 220 OdfProgramRestriction odfProgramRestriction = getRestrictions().get(restrictionId); 221 if (odfProgramRestriction != null && odfProgramRestriction.hasOrgunitRestrictions()) 222 { 223 odfRootId = odfProgramRestriction.getId(); 224 } 225 } 226 227 if (StringUtils.isBlank(odfRootId)) 228 { 229 odfRootId = _rootOrgUnitProvider.getRootId(); 230 } 231 232 return odfRootId; 233 } 234 235 /** 236 * Configured the restrictions from the XML configuration file 237 */ 238 protected void _readRestrictionConfigurationFile() 239 { 240 _restrictions = new HashMap<>(); 241 242 File restrictionFile = new File (_cocoonContext.getRealPath("/WEB-INF/param/odf-restrictions.xml")); 243 244 if (restrictionFile.exists()) 245 { 246 try (InputStream is = new FileInputStream(restrictionFile);) 247 { 248 Configuration cfg = new DefaultConfigurationBuilder().build(is); 249 250 Configuration[] restrictionConfs = cfg.getChildren("restriction"); 251 for (Configuration restrictionConf : restrictionConfs) 252 { 253 String id = restrictionConf.getAttribute("id"); 254 I18nizableText label = _configureI18nizableText(restrictionConf, "label", "", "application"); 255 List<OdfRestrictionRule> rules = _configureRestrictionRules(restrictionConf.getChild("rules")); 256 257 _restrictions.put(id, new OdfProgramRestriction(id, label, rules)); 258 } 259 260 Configuration orgunitConf = cfg.getChild("orgunits", false); 261 if (orgunitConf != null) 262 { 263 _addDefaultOrgunitRestrictions(); 264 } 265 } 266 catch (Exception e) 267 { 268 getLogger().error("Cannot read the configuration file located at /WEB-INF/param/odf-restrictions.xml. Reverting to default.", e); 269 _restrictions.clear(); 270 _addDefaultOrgunitRestrictions(); 271 } 272 } 273 else 274 { 275 _addDefaultOrgunitRestrictions(); 276 } 277 } 278 279 private I18nizableText _configureI18nizableText(Configuration config, String name, String defaultValue, String defaultCatalog) 280 { 281 Configuration textConfig = config.getChild(name); 282 boolean i18nSupported = textConfig.getAttributeAsBoolean("i18n", false); 283 String text = textConfig.getValue(defaultValue); 284 285 if (i18nSupported) 286 { 287 String catalogue = textConfig.getAttribute("catalogue", defaultCatalog); 288 return new I18nizableText(catalogue, text); 289 } 290 else 291 { 292 return new I18nizableText(text); 293 } 294 } 295 296 /** 297 * Create a list of rules from configuration nodes 298 * @param rulesConf Array of configuration node that represents the set of rules 299 * @return list of rules 300 * @throws ConfigurationException if a configuration error is encountered 301 */ 302 protected List<OdfRestrictionRule> _configureRestrictionRules(Configuration rulesConf) throws ConfigurationException 303 { 304 List<OdfRestrictionRule> rules = new ArrayList<>(); 305 306 for (Configuration ruleConf : rulesConf.getChildren()) 307 { 308 rules.add(_configureRestrictionRule(ruleConf)); 309 } 310 311 return rules; 312 } 313 314 /** 315 * Configure a restriction rule from a configuration node 316 * @param ruleConf The configuration node representing the rule 317 * @return The odf restriction rule 318 * @throws ConfigurationException if a configuration error is encountered 319 */ 320 protected OdfRestrictionRule _configureRestrictionRule(Configuration ruleConf) throws ConfigurationException 321 { 322 String name = ruleConf.getName(); 323 324 if ("item".equals(name)) 325 { 326 String attributePath = ruleConf.getAttribute("ref"); 327 String attributeValue = ruleConf.getAttribute("value"); 328 329 ContentType programContentType = _cTypeEP.getExtension(ProgramFactory.PROGRAM_CONTENT_TYPE); 330 if (programContentType.hasModelItem(attributePath)) 331 { 332 ModelItem modelItem = programContentType.getModelItem(attributePath); 333 if (modelItem instanceof ContentElementDefinition def && _odfReferenceTableHelper.isTableReference(def.getContentTypeId())) 334 { 335 // If the attribute value is the table reference code, convert it and test the JCR id 336 OdfReferenceTableEntry itemFromCode = _odfReferenceTableHelper.getItemFromCode(def.getContentTypeId(), attributeValue); 337 if (itemFromCode != null) 338 { 339 return new OdfAttributeRestrictionRule(attributePath, itemFromCode.getId()); 340 } 341 } 342 343 return new OdfAttributeRestrictionRule(attributePath, attributeValue); 344 } 345 346 throw new ConfigurationException("Attribute path '" + attributePath + "' is unknown for program content type."); 347 } 348 else if ("orgunit".equals(name)) 349 { 350 String orgunitId = ruleConf.getAttribute("id", null); 351 352 if (StringUtils.isEmpty(orgunitId)) 353 { 354 throw new ConfigurationException("Expecting 'id' attribute for orgunit restriction rule."); 355 } 356 357 return new OdfOrgunitRestrictionRule(_rootOrgUnitProvider, orgunitId); 358 } 359 else if ("and".equals(name)) 360 { 361 List<OdfRestrictionRule> childRules = _configureRestrictionRules(ruleConf); 362 return new OdfAndRestrictionRule(childRules); 363 } 364 else if ("or".equals(name)) 365 { 366 List<OdfRestrictionRule> childRules = _configureRestrictionRules(ruleConf); 367 return new OdfOrRestrictionRule(childRules); 368 } 369 else if ("not".equals(name)) 370 { 371 List<OdfRestrictionRule> childRules = _configureRestrictionRules(ruleConf); 372 return new OdfNotRestrictionRule(childRules); 373 } 374 375 throw new ConfigurationException("Unknow node name in restriction configuration : " + name); 376 } 377 378 private void _addDefaultOrgunitRestrictions() 379 { 380 Request request = ContextHelper.getRequest(_context); 381 String currentWsp = RequestAttributeWorkspaceSelector.getForcedWorkspace(request); 382 383 try 384 { 385 // Force default workspace 386 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, null); 387 388 String rootOrgunitId = _rootOrgUnitProvider.getRootId(); 389 Set<String> orgunitIds = _rootOrgUnitProvider.getChildOrgUnitIds(rootOrgunitId, true); 390 orgunitIds.add(rootOrgunitId); 391 392 for (String id : orgunitIds) 393 { 394 if (!StringUtils.equals(id, rootOrgunitId)) 395 { 396 OrgUnit orgunit = _resolver.resolveById(id); 397 398 OdfRestrictionRule rule = new OdfOrgunitRestrictionRule(_rootOrgUnitProvider, id); 399 OdfProgramRestriction restriction = new OdfProgramRestriction(id, new I18nizableText(orgunit.getTitle()), Collections.singletonList(rule)); 400 401 _restrictions.put(id, restriction); 402 } 403 } 404 } 405 finally 406 { 407 // Restore workspace 408 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, currentWsp); 409 } 410 } 411}