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