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.RightManager; 026import org.ametys.core.right.RightManager.RightResult; 027import org.ametys.core.user.UserIdentity; 028import org.ametys.plugins.workflow.AbstractWorkflowComponent; 029import org.ametys.runtime.i18n.I18nizableText; 030 031import com.opensymphony.module.propertyset.PropertySet; 032import com.opensymphony.workflow.Condition; 033import com.opensymphony.workflow.WorkflowException; 034 035/** 036 * Condition for checking rights of an user for the current action.<p> 037 * The following configuration can be used for checking rights:<br> 038 * <pre> 039 * <condition type="avalon"> 040 * <arg name="role">org.ametys.plugins.workflow.component.CheckRightsCondition</arg> 041 * <arg name="right">Right_Edition</arg> 042 * [ <arg name="context">/cms</arg>] 043 * </condition> 044 * </pre> 045 */ 046public class CheckRightsCondition extends AbstractWorkflowComponent implements Condition, Initializable, Disposable 047{ 048 /** 049 * Boolean to force the CheckRightsCondition to returns ok. 050 */ 051 public static final String FORCE = CheckRightsCondition.class.getName() + "$force"; 052 053 /** Default context to use. */ 054 protected static final String __DEFAULT_CONTEXT = "/cms"; 055 /** Key for getting the right to check. */ 056 protected static final String __RIGHT_KEY = "right"; 057 /** Key for getting the context to use. */ 058 protected static final String __CONTEXT_KEY = "context"; 059 /** Rights manager available to subclasses. */ 060 protected RightManager _rightManager; 061 062 @Override 063 public void initialize() throws Exception 064 { 065 _rightManager = (RightManager) _manager.lookup(RightManager.ROLE); 066 } 067 068 @Override 069 public boolean passesCondition(Map transientVars, Map args, PropertySet ps) throws WorkflowException 070 { 071 // Retrieve the right to check 072 String rightNeeded = (String) args.get(__RIGHT_KEY); 073 if (rightNeeded == null) 074 { 075 if (_logger.isWarnEnabled()) 076 { 077 _logger.warn(String.format("Missing 'right' argument for workflow action id: %d, failing condition", 078 transientVars.get("actionId"))); 079 } 080 081 return false; 082 } 083 084 Boolean force = (Boolean) transientVars.get(FORCE); 085 if (force != null && force) 086 { 087 // Pass condition 088 return true; 089 } 090 091 // Retrieve the user 092 UserIdentity user = getUser(transientVars); 093 boolean passesCondition = _checkRights(transientVars, args, user, rightNeeded); 094 095 if (!passesCondition) 096 { 097 List<String> conditionFailures = getConditionFailures(transientVars); 098 if (conditionFailures != null) 099 { 100 conditionFailures.add(String.format("Check right condition failed for workflow action id %d, user '%s' and right '%s'", transientVars.get("actionId"), user, rightNeeded)); 101 } 102 103 addWorkflowError(transientVars, new I18nizableText("plugin.workflow", "WORKFLOW_CHECK_RIGHTS_CONDITION_FAILED")); 104 } 105 return passesCondition; 106 } 107 108 /** 109 * Check if the current user has the needed right. 110 * @param transientVars variables that will not be persisted. 111 * @param args the properties for this function invocation. 112 * @param user the current user. 113 * @param rightNeeded the needed right. 114 * @return <code>true</code> if the user has the right, <code>false</code> 115 * otherwise. 116 * @throws WorkflowException if an error occurs. 117 */ 118 protected boolean _checkRights(Map transientVars, Map args, UserIdentity user, String rightNeeded) throws WorkflowException 119 { 120 // Compute the context 121 Object context = _computeContext(transientVars, args, user, rightNeeded); 122 123 // Check the context presence 124 if (context == null) 125 { 126 if (_logger.isWarnEnabled()) 127 { 128 _logger.warn(String.format("Missing context for checking rights for workflow action id: %d, failing condition", 129 transientVars.get("actionId"))); 130 } 131 132 return false; 133 } 134 135 if (rightNeeded.contains("|")) 136 { 137 return checkMultipleOrRights(user, StringUtils.split(rightNeeded, '|'), context); 138 } 139 else if (rightNeeded.contains("&")) 140 { 141 return checkMultipleAndRights(user, StringUtils.split(rightNeeded, '&'), context); 142 } 143 144 // Check if current user has the right 145 return hasRight(user, rightNeeded, context); 146 } 147 148 /** 149 * Check that a user has all the given rights on a context (AND condition). 150 * @param user the user. 151 * @param rights the rights to check. 152 * @param context the right context. 153 * @return true if the user has all the rights, false otherwise. 154 */ 155 protected boolean checkMultipleAndRights(UserIdentity user, String[] rights, Object context) 156 { 157 for (String right : rights) 158 { 159 if (!hasRight(user, right, context)) 160 { 161 return false; 162 } 163 } 164 return true; 165 } 166 167 /** 168 * Check that a user has at least one of the given rights on a context (OR condition). 169 * @param user the user. 170 * @param rights the rights to check. 171 * @param context the right context. 172 * @return true if the user has at least one right, false otherwise. 173 */ 174 protected boolean checkMultipleOrRights(UserIdentity user, String[] rights, Object context) 175 { 176 for (String right : rights) 177 { 178 if (hasRight(user, right, context)) 179 { 180 return true; 181 } 182 } 183 return false; 184 } 185 186 /** 187 * Test if a user has a right on a context. 188 * @param user the user 189 * @param right the right to check. 190 * @param context the right context. 191 * @return true if the user has the right, false otherwise. 192 */ 193 protected boolean hasRight(UserIdentity user, String right, Object context) 194 { 195 return _rightManager.hasRight(user, right, context) == RightResult.RIGHT_ALLOW; 196 } 197 198 /** 199 * Compute the context to use.<br> 200 * Default implementation uses standard context <code>"/cms"</code>. 201 * @param transientVars variables that will not be persisted. 202 * @param args the properties for this function invocation. 203 * @param user the current user. 204 * @param right the needed right. 205 * @return the computed context. 206 * @throws WorkflowException if an error occurs. 207 */ 208 protected Object _computeContext(Map transientVars, Map args, UserIdentity user, String right) throws WorkflowException 209 { 210 // Retrieve the context to use 211 String context = (String) args.get(__CONTEXT_KEY); 212 213 if (context == null) 214 { 215 if (_logger.isDebugEnabled()) 216 { 217 _logger.debug(String.format("Missing 'context' argument, using default context: %s", 218 __DEFAULT_CONTEXT)); 219 } 220 221 return __DEFAULT_CONTEXT; 222 } 223 224 return context; 225 } 226 227 @Override 228 public void dispose() 229 { 230 _manager.release(_rightManager); 231 _rightManager = null; 232 _manager = null; 233 } 234}