001/* 002 * Copyright 2018 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.cms.model.restrictions; 017 018import java.util.HashMap; 019import java.util.Map; 020import java.util.UUID; 021 022import org.apache.avalon.framework.component.Component; 023import org.apache.avalon.framework.component.ComponentException; 024import org.apache.avalon.framework.configuration.Configuration; 025import org.apache.avalon.framework.configuration.ConfigurationException; 026 027import org.ametys.cms.model.restrictions.Restriction.RestrictionResult; 028import org.ametys.cms.repository.Content; 029import org.ametys.plugins.repository.AmetysRepositoryException; 030import org.ametys.runtime.model.Model; 031import org.ametys.runtime.model.ModelItem; 032import org.ametys.runtime.plugin.component.ThreadSafeComponentManager; 033 034/** 035 * Helper for definitions with restrictions on contents 036 */ 037public class ContentRestrictedModelItemHelper implements Component 038{ 039 /** The Avalon role name */ 040 public static final String ROLE = ContentRestrictedModelItemHelper.class.getName(); 041 042 private final Map<String, Map<String, RestrictedModelItem>> _restrictionsToLookup = new HashMap<>(); 043 044 /** 045 * Determine whether a model item can be read at this time. 046 * @param item the restricted model item on which check the restrictions 047 * @param content The content where item is to be written on. Can be null, on content creation. 048 * @return <code>true</code> if the current user is allowed to write the model item of this content. 049 * @throws AmetysRepositoryException if an error occurs while accessing the content workflow. 050 */ 051 @SuppressWarnings("unchecked") 052 public boolean canRead(Content content, RestrictedModelItem item) throws AmetysRepositoryException 053 { 054 Restriction restrictions = item.getRestriction(); 055 if (restrictions == null) 056 { 057 return true; 058 } 059 060 RestrictionResult firstResult = restrictions.canRead(content, item); 061 if (!RestrictionResult.UNKNOWN.equals(firstResult)) 062 { 063 return RestrictionResult.TRUE.equals(firstResult); 064 } 065 066 ModelItem parent = item.getParent(); 067 if (parent != null && parent instanceof RestrictedModelItem) 068 { 069 // Check write access on parent model item 070 return ((RestrictedModelItem<Content>) parent).canRead(content); 071 } 072 073 return true; 074 } 075 076 /** 077 * Determine whether a model item can be written at this time. 078 * @param item the restricted model item on which check the restrictions 079 * @param content The content where item is to be written on. Can be null, on content creation. 080 * @return <code>true</code> if the current user is allowed to write the model item of this content. 081 * @throws AmetysRepositoryException if an error occurs while accessing the content workflow. 082 */ 083 @SuppressWarnings("unchecked") 084 public boolean canWrite(Content content, RestrictedModelItem item) throws AmetysRepositoryException 085 { 086 Restriction restrictions = item.getRestriction(); 087 if (restrictions == null) 088 { 089 return true; 090 } 091 092 RestrictionResult firstResult = restrictions.canWrite(content, item); 093 if (!RestrictionResult.UNKNOWN.equals(firstResult)) 094 { 095 return RestrictionResult.TRUE.equals(firstResult); 096 } 097 098 ModelItem parent = item.getParent(); 099 if (parent != null && parent instanceof RestrictedModelItem) 100 { 101 // Check write access on parent model item 102 return ((RestrictedModelItem<Content>) parent).canWrite(content); 103 } 104 105 return canRead(content, item); 106 } 107 108 /** 109 * Parses the attribute definition's restrictions. 110 * @param pluginName the plugin name 111 * @param attributeConfiguration the attribute configuration to use. 112 * @param restrictedModelItem the restricted model item 113 * @param restrictionManager the restrictions component manager. 114 * @throws ConfigurationException if the configuration is not valid. 115 */ 116 @SuppressWarnings("unchecked") 117 public void parseAndSetRestriction(String pluginName, RestrictedModelItem restrictedModelItem, Configuration attributeConfiguration, ThreadSafeComponentManager<Restriction> restrictionManager) throws ConfigurationException 118 { 119 Configuration restrictToConf = attributeConfiguration.getChild("restrict-to", false); 120 121 if (restrictToConf != null) 122 { 123 Configuration customRestriction = restrictToConf.getChild("custom-restriction", false); 124 String restrictionClassName; 125 126 if (customRestriction != null) 127 { 128 restrictionClassName = customRestriction.getAttribute("class"); 129 } 130 else 131 { 132 restrictionClassName = DefaultRestriction.class.getName(); 133 } 134 135 String modelId = restrictedModelItem.getModel().getId(); 136 final String restrictionRole = modelId + "$" + restrictedModelItem.getPath() + "$" + UUID.randomUUID().toString(); 137 138 try 139 { 140 Class restrictionClass = Class.forName(restrictionClassName); 141 restrictionManager.addComponent(pluginName, null, restrictionRole, restrictionClass, restrictToConf); 142 } 143 catch (Exception e) 144 { 145 throw new ConfigurationException("Unable to instantiate restrictions for class: " + restrictionClassName, e); 146 } 147 148 if (!_restrictionsToLookup.containsKey(modelId)) 149 { 150 _restrictionsToLookup.put(modelId, new HashMap<>()); 151 } 152 153 Map<String, RestrictedModelItem> restrictionsByModel = _restrictionsToLookup.get(modelId); 154 155 // Will be affected later when restrictionManager will be initialized 156 // in lookupRestrictions() call 157 restrictionsByModel.put(restrictionRole, restrictedModelItem); 158 159 } 160 } 161 162 /** 163 * Retrieves local restrictions components and set them into 164 * previously parsed element definition. 165 * @param model the model 166 * @param restrictionManager the restrictions component manager. 167 * @throws Exception if an error occurs. 168 */ 169 public void lookupRestrictions(Model model, ThreadSafeComponentManager<Restriction> restrictionManager) throws Exception 170 { 171 restrictionManager.initialize(); 172 173 if (_restrictionsToLookup.containsKey(model.getId())) 174 { 175 for (Map.Entry<String, RestrictedModelItem> entry : _restrictionsToLookup.get(model.getId()).entrySet()) 176 { 177 String restrictionRole = entry.getKey(); 178 RestrictedModelItem modelItem = entry.getValue(); 179 180 try 181 { 182 modelItem.setRestriction(restrictionManager.lookup(restrictionRole)); 183 } 184 catch (ComponentException e) 185 { 186 throw new Exception("Unable to lookup restriction role: '" + restrictionRole + "' for model item: " + modelItem, e); 187 } 188 } 189 } 190 191 } 192}