001/* 002 * Copyright 2010 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.workflow.component; 017 018import java.util.List; 019import java.util.Map; 020 021import org.apache.avalon.framework.activity.Disposable; 022import org.apache.avalon.framework.activity.Initializable; 023import org.apache.commons.lang3.StringUtils; 024 025import org.ametys.core.right.Right; 026import org.ametys.core.right.RightManager; 027import org.ametys.core.right.RightManager.RightResult; 028import org.ametys.core.right.RightsExtensionPoint; 029import org.ametys.core.user.UserIdentity; 030import org.ametys.core.util.I18nUtils; 031import org.ametys.plugins.workflow.AbstractWorkflowComponent; 032import org.ametys.plugins.workflow.EnhancedCondition; 033import org.ametys.plugins.workflow.support.WorkflowElementDefinitionHelper; 034import org.ametys.runtime.i18n.I18nizableText; 035import org.ametys.runtime.i18n.I18nizableTextParameter; 036import org.ametys.runtime.model.StaticEnumerator; 037 038import com.opensymphony.module.propertyset.PropertySet; 039import com.opensymphony.workflow.WorkflowException; 040 041/** 042 * Condition for checking rights of an user for the current action.<p> 043 * The following configuration can be used for checking rights:<br> 044 * <pre> 045 * <condition type="avalon"> 046 * <arg name="role">org.ametys.plugins.workflow.component.CheckRightsCondition</arg> 047 * <arg name="right">Right_Edition</arg> 048 * [ <arg name="context">/cms</arg>] 049 * </condition> 050 * </pre> 051 */ 052public class CheckRightsCondition extends AbstractWorkflowComponent implements EnhancedCondition, Initializable, Disposable 053{ 054 /** 055 * Boolean to force the CheckRightsCondition to returns ok. 056 */ 057 public static final String FORCE = CheckRightsCondition.class.getName() + "$force"; 058 059 /** Default context to use. */ 060 protected static final String __DEFAULT_CONTEXT = "/cms"; 061 /** Key for getting the right to check. */ 062 protected static final String __RIGHT_KEY = "right"; 063 /** Key for getting the context to use. */ 064 protected static final String __CONTEXT_KEY = "context"; 065 /** Rights manager available to subclasses. */ 066 protected RightManager _rightManager; 067 /** The rights extension point */ 068 protected RightsExtensionPoint _rightsExtensionPoint; 069 /** I18nUtils */ 070 protected I18nUtils _i18nUtils; 071 072 @Override 073 public void initialize() throws Exception 074 { 075 _rightManager = (RightManager) _manager.lookup(RightManager.ROLE); 076 _rightsExtensionPoint = (RightsExtensionPoint) _manager.lookup(RightsExtensionPoint.ROLE); 077 _i18nUtils = (I18nUtils) _manager.lookup(I18nUtils.ROLE); 078 } 079 080 @Override 081 public boolean passesCondition(Map transientVars, Map args, PropertySet ps) throws WorkflowException 082 { 083 // Retrieve the right to check 084 String rightNeeded = (String) args.get(__RIGHT_KEY); 085 if (rightNeeded == null) 086 { 087 if (_logger.isWarnEnabled()) 088 { 089 _logger.warn(String.format("Missing 'right' argument for workflow action id: %d, failing condition", 090 transientVars.get("actionId"))); 091 } 092 093 return false; 094 } 095 096 Boolean force = (Boolean) transientVars.get(FORCE); 097 if (force != null && force) 098 { 099 // Pass condition 100 return true; 101 } 102 103 // Retrieve the user 104 UserIdentity user = getUser(transientVars); 105 boolean passesCondition = _checkRights(transientVars, args, user, rightNeeded); 106 107 if (!passesCondition) 108 { 109 List<ConditionFailure> conditionFailures = getConditionFailures(transientVars); 110 if (conditionFailures != null) 111 { 112 conditionFailures.add(new ConditionFailure( 113 String.format("Check right condition failed for workflow action id %d, user '%s' and right '%s'", transientVars.get("actionId"), user, rightNeeded), 114 CheckRightsCondition.class.getName() 115 )); 116 } 117 118 addWorkflowError(transientVars, new I18nizableText("plugin.workflow", "WORKFLOW_CHECK_RIGHTS_CONDITION_FAILED")); 119 } 120 return passesCondition; 121 } 122 123 /** 124 * Check if the current user has the needed right. 125 * @param transientVars variables that will not be persisted. 126 * @param args the properties for this function invocation. 127 * @param user the current user. 128 * @param rightNeeded the needed right. 129 * @return <code>true</code> if the user has the right, <code>false</code> 130 * otherwise. 131 * @throws WorkflowException if an error occurs. 132 */ 133 protected boolean _checkRights(Map transientVars, Map args, UserIdentity user, String rightNeeded) throws WorkflowException 134 { 135 // Compute the context 136 Object context = _computeContext(transientVars, args, user, rightNeeded); 137 138 // Check the context presence 139 if (context == null) 140 { 141 if (_logger.isWarnEnabled()) 142 { 143 _logger.warn(String.format("Missing context for checking rights for workflow action id: %d, failing condition", 144 transientVars.get("actionId"))); 145 } 146 147 return false; 148 } 149 150 if (rightNeeded.contains("|")) 151 { 152 return checkMultipleOrRights(user, StringUtils.split(rightNeeded, '|'), context); 153 } 154 else if (rightNeeded.contains("&")) 155 { 156 return checkMultipleAndRights(user, StringUtils.split(rightNeeded, '&'), context); 157 } 158 159 // Check if current user has the right 160 return hasRight(user, rightNeeded, context); 161 } 162 163 /** 164 * Check that a user has all the given rights on a context (AND condition). 165 * @param user the user. 166 * @param rights the rights to check. 167 * @param context the right context. 168 * @return true if the user has all the rights, false otherwise. 169 */ 170 protected boolean checkMultipleAndRights(UserIdentity user, String[] rights, Object context) 171 { 172 for (String right : rights) 173 { 174 if (!hasRight(user, right, context)) 175 { 176 return false; 177 } 178 } 179 return true; 180 } 181 182 /** 183 * Check that a user has at least one of the given rights on a context (OR condition). 184 * @param user the user. 185 * @param rights the rights to check. 186 * @param context the right context. 187 * @return true if the user has at least one right, false otherwise. 188 */ 189 protected boolean checkMultipleOrRights(UserIdentity user, String[] rights, Object context) 190 { 191 for (String right : rights) 192 { 193 if (hasRight(user, right, context)) 194 { 195 return true; 196 } 197 } 198 return false; 199 } 200 201 /** 202 * Test if a user has a right on a context. 203 * @param user the user 204 * @param right the right to check. 205 * @param context the right context. 206 * @return true if the user has the right, false otherwise. 207 */ 208 protected boolean hasRight(UserIdentity user, String right, Object context) 209 { 210 return _rightManager.hasRight(user, right, context) == RightResult.RIGHT_ALLOW; 211 } 212 213 /** 214 * Compute the context to use.<br> 215 * Default implementation uses standard context <code>"/cms"</code>. 216 * @param transientVars variables that will not be persisted. 217 * @param args the properties for this function invocation. 218 * @param user the current user. 219 * @param right the needed right. 220 * @return the computed context. 221 * @throws WorkflowException if an error occurs. 222 */ 223 protected Object _computeContext(Map transientVars, Map args, UserIdentity user, String right) throws WorkflowException 224 { 225 // Retrieve the context to use 226 String context = (String) args.get(__CONTEXT_KEY); 227 228 if (context == null) 229 { 230 if (_logger.isDebugEnabled()) 231 { 232 _logger.debug(String.format("Missing 'context' argument, using default context: %s", 233 __DEFAULT_CONTEXT)); 234 } 235 236 return __DEFAULT_CONTEXT; 237 } 238 239 return context; 240 } 241 242 @Override 243 public void dispose() 244 { 245 _manager.release(_rightManager); 246 _rightManager = null; 247 _manager = null; 248 } 249 250 @SuppressWarnings("unchecked") 251 @Override 252 public List<WorkflowArgument> getArguments() 253 { 254 WorkflowArgument rights = WorkflowElementDefinitionHelper.getElementDefinition( 255 __RIGHT_KEY, 256 new I18nizableText("plugin.workflow", "PLUGINS_WORKFLOW_EDITOR_CHECK_RIGHTS_ARGUMENT_RIGHT_KEY_LABEL"), 257 new I18nizableText("plugin.workflow", "PLUGINS_WORKFLOW_EDITOR_CHECK_RIGHTS_ARGUMENT_RIGHT_KEY_DESCRIPTION"), 258 true, 259 false 260 ); 261 rights.setEnumerator(_getRightsEnumerator()); 262 263 return List.of( 264 rights, 265 WorkflowElementDefinitionHelper.getElementDefinition( 266 __CONTEXT_KEY, 267 new I18nizableText("plugin.workflow", "PLUGINS_WORKFLOW_EDITOR_CHECK_RIGHTS_ARGUMENT_CONTEXT_KEY_LABEL"), 268 new I18nizableText("plugin.workflow", "PLUGINS_WORKFLOW_EDITOR_CHECK_RIGHTS_ARGUMENT_CONTEXT_KEY_DESCRIPTION"), 269 false, 270 false 271 ) 272 ); 273 } 274 275 /** 276 * Get the rights enumerator 277 * @return the rights enumerator 278 */ 279 protected StaticEnumerator _getRightsEnumerator() 280 { 281 StaticEnumerator<String> rightsStaticEnumerator = new StaticEnumerator<>(); 282 for (String rightId : _rightsExtensionPoint.getExtensionsIds()) 283 { 284 Right right = _rightsExtensionPoint.getExtension(rightId); 285 Map<String, I18nizableTextParameter> params = Map.of("category", right.getCategory(), "label", right.getLabel()); 286 rightsStaticEnumerator.add(new I18nizableText("plugin.workflow", "PLUGINS_WORKFLOW_EDITOR_CHECK_RIGHTS_ARGUMENT_RIGHT_KEY_PARAMS_LABEL", params), right.getId()); 287 } 288 289 return rightsStaticEnumerator; 290 } 291 292 @Override 293 public I18nizableText getLabel() 294 { 295 return new I18nizableText("plugin.workflow", "PLUGINS_WORKFLOW_EDITOR_EDITOR_CHECK_RIGHTS_CONDITION_LABEL"); 296 } 297 298 /** 299 * Get condition description when there are multiple rights involved 300 * @param parameters list of rights 301 * @return the description 302 */ 303 protected I18nizableText _getMultipleConditionsDescriptionKey(List<String> parameters) 304 { 305 return new I18nizableText("plugin.workflow", "UITOOL_WORKFLOW_EDITOR_CHECK_RIGHTS_MULTIPLES_CONDITION_DESCRIPTION", parameters); 306 } 307 308 /** 309 * Get condition description when there is one right 310 * @param parameters the right label as a List 311 * @return the description 312 */ 313 protected I18nizableText _getSingleConditionDescriptionKey(List<String> parameters) 314 { 315 return new I18nizableText("plugin.workflow", "UITOOL_WORKFLOW_EDITOR_CHECK_RIGHTS_CONDITION_DESCRIPTION", parameters); 316 } 317 318 @Override 319 public I18nizableText getFullLabel(Map<String, String> argumentsValues) 320 { 321 @SuppressWarnings("cast") 322 String rightNeeded = (String) argumentsValues.get(__RIGHT_KEY); 323 String translatedRight = ""; 324 if (rightNeeded.contains("|")) 325 { 326 String[] rightsOr = StringUtils.split(rightNeeded, '|'); 327 translatedRight = "<strong>" + _i18nUtils.translate(_rightsExtensionPoint.getExtension(rightsOr[0]).getLabel()) + "</strong>"; 328 for (String rightId: rightsOr) 329 { 330 Right right = _rightsExtensionPoint.getExtension(rightId); 331 translatedRight += _i18nUtils.translate(new I18nizableText("plugin.workflow", "PLUGINS_WORKFLOW_EDITOR_CONDITION_OR")) + "<strong>" + _i18nUtils.translate(right.getDescription()) + "</strong>"; 332 } 333 return _getMultipleConditionsDescriptionKey(List.of(translatedRight)); 334 } 335 else if (rightNeeded.contains("&")) 336 { 337 String[] rightsAnd = StringUtils.split(rightNeeded, '&'); 338 translatedRight = "<strong>" + _i18nUtils.translate(_rightsExtensionPoint.getExtension(rightsAnd[0]).getLabel()) + "</strong>"; 339 for (String rightId: rightsAnd) 340 { 341 Right right = _rightsExtensionPoint.getExtension(rightId); 342 translatedRight += _i18nUtils.translate(new I18nizableText("plugin.workflow", "PLUGINS_WORKFLOW_EDITOR_CONDITION_AND")) + "<strong>" + _i18nUtils.translate(right.getDescription()) + "</strong>"; 343 } 344 return _getMultipleConditionsDescriptionKey(List.of(translatedRight)); 345 } 346 Right right = _rightsExtensionPoint.getExtension(rightNeeded); 347 translatedRight = "<strong>" + _i18nUtils.translate(right.getLabel()) + "</strong>"; 348 return _getSingleConditionDescriptionKey(List.of(translatedRight)); 349 } 350}