001/** 002 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 003 * 004 * Copyright (c) 2006 Sun Microsystems Inc. All Rights Reserved 005 * 006 * The contents of this file are subject to the terms 007 * of the Common Development and Distribution License 008 * (the License). You may not use this file except in 009 * compliance with the License. 010 * 011 * You can obtain a copy of the License at 012 * https://opensso.dev.java.net/public/CDDLv1.0.html or 013 * opensso/legal/CDDLv1.0.txt 014 * See the License for the specific language governing 015 * permission and limitations under the License. 016 * 017 * When distributing Covered Code, include this CDDL 018 * Header Notice in each file and include the License file 019 * at opensso/legal/CDDLv1.0.txt. 020 * If applicable, add the following below the CDDL Header, 021 * with the fields enclosed by brackets [] replaced by 022 * your own identifying information: 023 * "Portions Copyrighted [year] [name of copyright owner]" 024 * 025 * $Id: PolicyDecision.java,v 1.3 2008/06/25 05:43:44 qcheng Exp $ 026 * 027 */ 028 029/* 030 * Portions Copyrighted [2011] [ForgeRock AS] 031 */ 032package com.sun.identity.policy; 033 034import java.util.Map; 035import java.util.HashMap; 036import java.util.Set; 037import java.util.Iterator; 038import java.util.Collections; 039import com.sun.identity.shared.xml.XMLUtils; 040import com.sun.identity.sm.AttributeSchema; 041import org.w3c.dom.*; 042 043 044/** 045 * The <code>PolicyDecision</code> class represents the result of a policy 046 * evaluation. 047 * 048 * @supported.api 049 */ 050public class PolicyDecision { 051 052 static final String POLICY_DECISION = "PolicyDecision"; 053 static final String RESPONSE_DECISIONS = "ResponseDecisions"; 054 055 //added in 7.0 for attributes provided by policy response providers 056 static final String RESPONSE_ATTRIBUTES = "ResponseAttributes"; 057 058 Map actionDecisions = new HashMap(); 059 private Map responseDecisions; 060 private Map responseAttributes; 061 private long timeToLive = Long.MAX_VALUE; 062 private boolean advicesAreSet = false; 063 064 /** 065 * Default constructor. 066 * @supported.api 067 */ 068 public PolicyDecision() { 069 } 070 071 /** 072 * Gets the <code>Map</code> of action decisions associated 073 * with this policy decision. The 074 * action name is the key to the Map. The value for each key is an 075 * <code>ActionDecision</code>. 076 * 077 * @return the <code>Map</code> of action decisions associated 078 * with this policy decision. The 079 * action name is the key to the Map. The value for each key is an 080 * <code>ActionDecision</code>. 081 * @supported.api 082 */ 083 public Map getActionDecisions() { 084 return actionDecisions; 085 } 086 087 /** 088 * Add an <code>ActionDecision</code> to the <code>PolicyDecision</code> 089 * These are the rules followed to add action decision: 090 * If the action schema has boolean syntax, boolean false value 091 * overrides boolean true value. The time to live of boolean false 092 * value overrides the time to live of boolean true value. 093 * Otherwise, action values are simply aggregated. Time to live 094 * is set to the minimum of time to live(s) of all values of the 095 * action. 096 * 097 * @param newActionDecision an <code>ActionDecision</code> to be added. 098 * @param resourceType <code>ServiceType</code> representing the 099 * service which provides the schema for the action. 100 * 101 */ 102 103 void addActionDecision(ActionDecision newActionDecision, ServiceType 104 resourceType) { 105 String action = newActionDecision.getActionName(); 106 ActionDecision oldActionDecision 107 = (ActionDecision) actionDecisions.get(action); 108 if ( oldActionDecision == null ) { 109 addActionDecision(newActionDecision); 110 } else { 111 ActionSchema actionSchema = null; 112 AttributeSchema.Syntax actionSyntax = null; 113 try { 114 actionSchema = resourceType.getActionSchema(action); 115 actionSyntax = actionSchema.getSyntax(); 116 } catch(InvalidNameException e) { 117 PolicyManager.debug.error( 118 "can not find action schmea for action = " 119 + action, e ); 120 } 121 if (!AttributeSchema.Syntax.BOOLEAN.equals( 122 actionSyntax)) { 123 addActionDecision(newActionDecision); 124 } else { //boolean valued action 125 String falseValue = actionSchema.getFalseValue(); 126 String trueValue = actionSchema.getTrueValue(); 127 addActionDecision(newActionDecision, trueValue, falseValue); 128 } 129 } 130 } 131 132 /** 133 * Add an <code>ActionDecision</code> to the <code>PolicyDecision</code> 134 * using the provided <code>trueValue</code> and <code>falseValue</code> 135 * These are the rules followed to add action decision: 136 * Boolean false value overrides boolean true value. The time to live 137 * of boolean false value overrides the time to live of boolean true value. 138 * Otherwise, action values are simply aggregated. Time to live 139 * is set to the minimum of time to live(s) of all values of the 140 * action. 141 * 142 * @param newActionDecision an <code>ActionDecision</code> to be added. 143 * @param trueValue <code>String</code> representing the </code>true</code> 144 * value in the action schema. 145 * @param falseValue <code>String</code> representing the 146 * </code>false</code> value in the action schema. 147 * 148 */ 149 public void addActionDecision(ActionDecision newActionDecision, 150 String trueValue, String falseValue) { 151 String action = newActionDecision.getActionName(); 152 ActionDecision oldActionDecision 153 = (ActionDecision) actionDecisions.get(action); 154 if ( (oldActionDecision == null) 155 || (trueValue == null) || (falseValue == null)) { 156 addActionDecision(newActionDecision); 157 } else { //boolean valued action 158 long newTtl = newActionDecision.getTimeToLive(); 159 long oldTtl = oldActionDecision.getTimeToLive(); 160 Set oldActionValues = oldActionDecision.getValues(); 161 Set newActionValues = newActionDecision.getValues(); 162 Map advices = null; 163 Map oldAdvices = oldActionDecision.getAdvices(); 164 Map newAdvices = newActionDecision.getAdvices(); 165 advices = PolicyUtils.addMapToMap(oldAdvices, newAdvices); 166 if ( (oldActionValues != null) 167 && (oldActionValues.contains(falseValue)) ) { 168 if ( (newActionValues != null) 169 && newActionValues.contains(falseValue) ) { 170 //both old and new values are false 171 //get the ttl to max of newTtl and oldTtl 172 oldActionDecision.setTimeToLive(Math.max(newTtl, oldTtl)); 173 } 174 175 /* else block not required here since 176 oldActionDecision does not need to change as it is false 177 and newActionDecision is null or true 178 */ 179 } else if ( (oldActionValues != null) 180 && oldActionValues.contains(trueValue) ) { 181 if ( (newActionValues != null) 182 && newActionValues.contains(falseValue) ) { 183 actionDecisions.put(action, newActionDecision); 184 } else if ( newActionDecision.getValues().contains(trueValue) ) { 185 //get the ttl to max of newTtl and oldTtl 186 oldActionDecision.setTimeToLive(Math.max(newTtl, oldTtl)); 187 } 188 } else { 189 actionDecisions.put(action, newActionDecision); 190 } 191 ActionDecision ad = (ActionDecision) actionDecisions.get(action); 192 ad.setAdvices(advices); 193 setTimeToLive(); 194 } 195 } 196 197 /** 198 * Adds an action decision to this object 199 * if there is already an existing actionDecision associated with the 200 * action name in the param <code>actionDecision</code>, merges 201 * the values of the new decision with the existing one, 202 * changing the time to live for the decision appropriately. 203 * 204 * @param actionDecision action decision to be added 205 * @supported.api 206 */ 207 public void addActionDecision(ActionDecision actionDecision) { 208 ActionDecision oldDecision = 209 (ActionDecision) actionDecisions.get( 210 actionDecision.getActionName()); 211 if ( oldDecision == null ) { 212 actionDecisions.put(actionDecision.getActionName(), 213 actionDecision); 214 } else { 215 Set oldValues = oldDecision.getValues(); 216 if ( (oldValues == Collections.EMPTY_SET) 217 || ( oldValues == null) ) { 218 oldDecision.setValues(actionDecision.getValues()); 219 } else { 220 oldValues.addAll(actionDecision.getValues()); 221 } 222 if ( actionDecision.getTimeToLive() 223 < oldDecision.getTimeToLive() ) { 224 oldDecision.setTimeToLive(actionDecision.getTimeToLive()); 225 } 226 PolicyUtils.appendMapToMap(actionDecision.getAdvices(), 227 oldDecision.getAdvices()); 228 } 229 setTimeToLive(); 230 } 231 232 /** 233 * Gets a String representation of this <code>PolicyDecision</code> 234 * @return a String representation of this <code>PolicyDecision</code> 235 * 236 * @supported.api 237 */ 238 public String toString() { 239 StringBuilder sb = new StringBuilder(); 240 if ((responseAttributes != null) && 241 (responseAttributes != Collections.EMPTY_MAP)) { 242 Iterator attrNames = responseAttributes.keySet().iterator(); 243 while ( attrNames.hasNext() ) { 244 String attrName = (String) attrNames.next(); 245 Set attrValues = (Set) responseAttributes.get(attrName); 246 sb.append(attrName).append("=").append(attrValues).append("\n"); 247 } 248 } 249 Iterator actionNames = actionDecisions.keySet().iterator(); 250 while ( actionNames.hasNext() ) { 251 String actionName = (String) actionNames.next(); 252 ActionDecision actionDecision = 253 (ActionDecision) actionDecisions.get(actionName); 254 Set actionValues = (Set) actionDecision.getValues(); 255 sb.append(actionName).append("=").append(actionValues).append("\n"); 256 } 257 return sb.toString(); 258 } 259 260 261 /** 262 * Gets an XML representation of this object 263 * @return an XML representation of this object 264 * 265 * @supported.api 266 */ 267 public String toXML() { 268 StringBuilder sb = new StringBuilder(300); 269 sb.append("<").append(POLICY_DECISION) 270 /* 271 .append(" ").append("timeToLive") 272 .append("=\"").append(timeToLive).append("\"") 273 .append(" ").append("hasAdvices") 274 .append("=\"").append(hasAdvices()).append("\"") 275 */ 276 .append(">").append(PolicyUtils.CRLF); 277 if ((responseAttributes != null) && 278 (responseAttributes != Collections.EMPTY_MAP)) { 279 sb.append("<").append(RESPONSE_ATTRIBUTES); 280 sb.append(">").append(PolicyUtils.CRLF); 281 sb.append(PolicyUtils.mapToXMLString(responseAttributes)); 282 sb.append("<").append("/").append(RESPONSE_ATTRIBUTES); 283 sb.append(">").append(PolicyUtils.CRLF); 284 } 285 Iterator actionNames = actionDecisions.keySet().iterator(); 286 while ( actionNames.hasNext() ) { 287 String actionName = (String) actionNames.next(); 288 ActionDecision actionDecision = (ActionDecision) 289 actionDecisions.get(actionName); 290 sb.append(actionDecision.toXML()); 291 } 292 if (responseDecisions != null) { 293 sb.append("<").append(RESPONSE_DECISIONS).append(">") 294 .append(PolicyUtils.CRLF); 295 sb.append(PolicyUtils.mapToXMLString(responseDecisions)); 296 sb.append("</").append(RESPONSE_DECISIONS).append(">") 297 .append(PolicyUtils.CRLF); 298 } 299 sb.append("</").append(POLICY_DECISION).append(">"); 300 sb.append(PolicyUtils.CRLF); 301 return sb.toString(); 302 } 303 304 /** 305 * Gets a PolicyDecision given corresponding XML node 306 * @param policyDecisionNode XML node for the policy decision 307 * @return policy decision based on the XML node 308 */ 309 public static PolicyDecision parsePolicyDecision(Node policyDecisionNode) 310 throws PolicyException 311 { 312 PolicyDecision policyDecision = new PolicyDecision(); 313 Set nodeSet = XMLUtils.getChildNodes(policyDecisionNode, 314 ActionDecision.ACTION_DECISION); 315 if (nodeSet == null) { 316 PolicyManager.debug.error("parsePolicyDecision: Required element " 317 + "not found in policy decision node:" 318 + ActionDecision.ACTION_DECISION); 319 Object [] args = { ActionDecision.ACTION_DECISION }; 320 throw new PolicyException(ResBundleUtils.rbName, 321 "missing_element", args, null); 322 } else { 323 Iterator nodes = nodeSet.iterator(); 324 while (nodes.hasNext()) { 325 Node node = (Node)nodes.next(); 326 ActionDecision actionDecision = 327 ActionDecision.parseActionDecision(node); 328 policyDecision.addActionDecision(actionDecision); 329 } 330 } 331 Set resposeAttrsSet = XMLUtils.getChildNodes(policyDecisionNode, 332 RESPONSE_ATTRIBUTES); 333 if ( (resposeAttrsSet != null) && !resposeAttrsSet.isEmpty() ) { 334 Node node = (Node) resposeAttrsSet.iterator().next(); 335 Map responseAttrsMap = PolicyUtils.parseAttributeValuePairs(node); 336 policyDecision.setResponseAttributes(responseAttrsMap); 337 } 338 Set responseNodeSet = XMLUtils.getChildNodes(policyDecisionNode, 339 RESPONSE_DECISIONS); 340 if ( (responseNodeSet != null) && !responseNodeSet.isEmpty() ) { 341 Node node = (Node) responseNodeSet.iterator().next(); 342 Map responseMap = PolicyUtils.parseAttributeValuePairs(node); 343 policyDecision.setResponseDecisions(responseMap); 344 } 345 return policyDecision; 346 } 347 348 /** 349 * 350 * Gets response decisions associated with this policy decision 351 * @return <code>Map</code> representing the response decisions associated 352 * with this policy decision 353 * 354 */ 355 public Map getResponseDecisions() { 356 return responseDecisions; 357 } 358 359 /** 360 * 361 * Sets response decisions associated with this policy decision 362 * @param responseDecisions A <code>Map</code> representing response 363 * decisions associated with this policy decision 364 */ 365 public void setResponseDecisions(Map responseDecisions) { 366 this.responseDecisions = responseDecisions; 367 } 368 369 370 /** 371 * 372 * Gets response attributes associated with this policy decision. 373 * Response attributes are computed as an aggregation of the return 374 * <code>Map</code>(s) of the <code>ResponseProvider</code> objects 375 * associated with the policy obtained via the getResponseDecision() call. 376 * @return the <code>Map</code> of response attributes associated with 377 * this policy decision. 378 * 379 */ 380 public Map getResponseAttributes() { 381 return responseAttributes; 382 } 383 384 /** 385 * 386 * Sets response attributes associated with this policy decision 387 * @param responseAttributes <code>Map</code> of attribute value pairs 388 * associated with this policy decision. 389 */ 390 public void setResponseAttributes(Map responseAttributes) { 391 this.responseAttributes = responseAttributes; 392 } 393 /** 394 * Makes a copy of this object 395 * 396 * @return a copied instance 397 */ 398 public Object clone() { 399 PolicyDecision clone = new PolicyDecision(); 400 clone.actionDecisions = new HashMap(actionDecisions.size()); 401 Iterator actionDecisionIter = actionDecisions.keySet().iterator(); 402 while (actionDecisionIter.hasNext()) { 403 String key = (String) actionDecisionIter.next(); 404 ActionDecision ad = (ActionDecision) actionDecisions.get(key); 405 clone.addActionDecision((ActionDecision)ad.clone()); 406 } 407 408 if (responseDecisions != null) { 409 clone.responseDecisions = new HashMap(responseDecisions.size()); 410 Iterator responseDecisionsIter = 411 responseDecisions.keySet().iterator(); 412 while (responseDecisionsIter.hasNext()) { 413 String key = (String) responseDecisionsIter.next(); 414 clone.responseDecisions.put(key, responseDecisions.get(key)); 415 } 416 } 417 if (responseAttributes != null) { 418 clone.responseAttributes = new HashMap(responseAttributes.size()); 419 Iterator responseAttributesIter = 420 responseAttributes.keySet().iterator(); 421 while (responseAttributesIter.hasNext()) { 422 String key = (String) responseAttributesIter.next(); 423 clone.responseAttributes.put(key, responseAttributes.get(key)); 424 } 425 } 426 return clone; 427 } 428 429 430 /** 431 * Gets the GMT time in milliseconds since epoch when this object is to 432 * be treated as expired. That is the policy decision would likely 433 * be different after that time. 434 * This is computed as a result of <code>SimpleTimeCondition</code>s 435 * specified in the <code>Policy</code> definition. 436 * 437 * @return time to live 438 */ 439 public long getTimeToLive() { 440 return timeToLive; 441 } 442 443 /** 444 * Sets the <code>timeToLive</code> value of the policy decision to the 445 * smallest of <code>timeToLive(s)<code> of contained 446 * <code>ActionDecision(s)<code>. Also sets value of 447 * <code>advicesAreSet</code>. This is set to <code>true</code> 448 * if any of the contained action decision(s) has advice defined. 449 */ 450 private void setTimeToLive() { 451 timeToLive = Long.MAX_VALUE; 452 advicesAreSet = false; 453 Iterator actionDecisionIter = actionDecisions.keySet().iterator(); 454 while (actionDecisionIter.hasNext()) { 455 String key = (String) actionDecisionIter.next(); 456 ActionDecision ad = (ActionDecision) actionDecisions.get(key); 457 long actionTtl = ad.getTimeToLive(); 458 if ( actionTtl < timeToLive) { 459 timeToLive = actionTtl; 460 } 461 advicesAreSet = advicesAreSet || 462 ((ad.getAdvices()) != null) && (!(ad.getAdvices().isEmpty())); 463 } 464 } 465 466 /** 467 * Sets the timeToLive value of the policy decision. 468 * 469 * @param ttl timeToLive value to be set 470 */ 471 void setTimeToLive(long ttl) { 472 timeToLive = ttl; 473 } 474 475 /** 476 * Checks wether advices are set in this object 477 * @return <code>true</code>, if advices are set, else <code>false</code> 478 */ 479 public boolean hasAdvices() { 480 return advicesAreSet; 481 } 482}