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: ISPermission.java,v 1.5 2008/08/19 19:09:17 veiming Exp $ 026 * 027 */ 028 029package com.sun.identity.policy.jaas; 030 031import com.iplanet.sso.SSOToken; 032import com.iplanet.sso.SSOTokenManager; 033import com.iplanet.sso.SSOException; 034 035import com.sun.identity.authentication.service.SSOTokenPrincipal; 036import com.sun.identity.policy.client.PolicyEvaluator; 037import com.sun.identity.policy.client.PolicyEvaluatorFactory; 038import com.sun.identity.shared.debug.Debug; 039 040import java.security.Permission; 041import java.security.CodeSource; 042import java.security.ProtectionDomain; 043import java.security.PermissionCollection; 044 045import javax.security.auth.Subject; 046import java.security.Principal; 047 048import java.util.Set; 049import java.util.HashSet; 050import java.util.StringTokenizer; 051import java.util.Map; 052import java.util.Collections; 053 054/** 055 * This class provides the support for JAAS Authorization service 056 * Its a new JAAS <code>Permission</code> which extends the 057 * {@link java.security.Permission} class. This is the only 058 * API which gets used by an application/container to evaluate policy against 059 * the OpenSSO Policy framework. This class provides implementations 060 * of all the required abstract methods of <code>java.security.Permission</code> 061 * , in a way that the policy evaluation is made against the OpenSSO 062 * Enterprise's Policy service. 063 * <p> 064 * For example, one would use this class as follows to evaluate policy 065 * permissions: 066 * <pre> 067 * ISPermission perm = new ISPermission("iPlanetAMWebAgentService", 068 * "http://www.sun.com:80","GET"); 069 * AccessController.checkPermission(perm); 070 * </pre> 071 * If OpenSSO has the policy service 072 * <code>iPlanetAMWebAgentService</code> which has a <code>Rule</code> defined 073 * for resource <code>http://www.sun.com:80</code> 074 * with action "GET" with allow privilege, this call will return quietly, if 075 * such a policy is not found then access is denied and Exception thrown 076 * accordingly. Also <code>ISPermission</code> co-exists with the 077 * permissions specified in the JDK policy store ( by default file <code> 078 * sun.security.provider.PolicyFile</code> or defined on the command line 079 * using the -D option. 080 * 081 * <p> 082 * @see java.security.Permission 083 * @see javax.security.auth.Subject 084 * @see java.security.ProtectionDomain 085 * <p> 086 * 087 * @supported.all.api 088 */ 089public class ISPermission extends Permission { 090 private Subject subject; 091 private CodeSource codesource; 092 private ProtectionDomain protectionDomain; 093 private String serviceName; 094 private String resourceName; 095 private String actions; 096 private Set actionSet; 097 private Map envParams = Collections.synchronizedMap(Collections.EMPTY_MAP); 098 private PolicyEvaluatorFactory policyEvalFactory; 099 static Debug debug = Debug.getInstance("amPolicy"); 100 101 /** 102 * Constructs an <code>ISPermission</code> instance, with the specified 103 * <code>ProtectionDomain</code>. 104 * 105 * @param pd <code>ProtectionDomain</code> for which this 106 * <code>ISPermission</code> is being created. 107 */ 108 protected ISPermission(ProtectionDomain pd) { 109 super("ISPermission"); 110 if (debug.messageEnabled()) { 111 debug.message("ISPermission(protectionDomain) constructor " 112 + "called "); 113 } 114 this.protectionDomain = pd; 115 } 116 117 /** 118 * Constructs an <code>ISPermission</code> instance, with the specified 119 * <code>Subject</code> and the <code>CodeSource</code>. 120 * 121 * @param subject <code>Subject</code> for which this 122 * <code>ISPermission</code> is being created. 123 * @param codesource <code>CodeSource</code> for which this permission is 124 * being created. 125 */ 126 public ISPermission(Subject subject,CodeSource codesource) { 127 super("ISPermission"); 128 if (debug.messageEnabled()) { 129 debug.message("ISPermission(subject,codesource) constructor " 130 + "called "); 131 } 132 this.subject = subject; 133 this.codesource = codesource; 134 } 135 136 /** 137 * Constructs an <code>ISPermission</code> instance, with the specified 138 * <code>CodeSource</code>. 139 * 140 * @param codesource <code>CodeSource</code> for which this permission is 141 * being created. 142 */ 143 public ISPermission(CodeSource codesource) { 144 super("ISPermission"); 145 if (debug.messageEnabled()) { 146 debug.message("ISPermission(codesource) constructor " 147 + "called "); 148 } 149 this.codesource = codesource; 150 } 151 152 /** 153 * Constructs an <code>ISPermission</code> instance, with the specified 154 * service name, resource name and action name. 155 * @param serviceName name of service for which this 156 * <code>ISPermission</code> is being created. This name needs to be 157 * one of the loaded services in the OpenSSO's policy 158 * engine. example: <code>iPlanetAMWegAgentService</code> 159 * 160 * @param resourceName name of the resource for which this 161 * <code>ISPermission</code> is being defined. 162 * 163 * @param actions name of the action that needs to be checked for. It 164 * may be a <code>String</code> like "GET", "POST" in case of 165 * service name <code>iPlanetAMWebAgentService</code>. 166 */ 167 public ISPermission(String serviceName,String resourceName, String 168 actions) 169 { 170 super("ISPermission"); 171 this.serviceName = serviceName; 172 this.resourceName = resourceName; 173 this.actions = actions; 174 debug.message("ISPermission:: Constructor called"); 175 } 176 177 178 /** 179 * Constructs an <code>ISPermission</code> instance, with the specified 180 * service name, resource name and action name. 181 * @param serviceName name of service for which this 182 * <code>ISPermission</code> is being created. This name needs to be 183 * one of the loaded policy services in the OpenSSO. 184 * example: 185 * <code>iPlanetAMWegAgentService</code> 186 * 187 * @param resourceName name of the resource for which this 188 * <code>ISPermission</code> is being defined. 189 * 190 * @param actions name of the action that needs to be checked for. It 191 * may be a <code>String</code> like "GET", "POST" in case of 192 * service name <code>iPlanetAMWebAgentService</code>. 193 * @param envParams a <code>java.util.Map</code> of environment parameters 194 * which are used by the 195 * <code>com.sun.identity.policy.client.PolicyEvaluator</code> 196 * to evaluate the <code>com.sun.identity.policy.Conditions</code> 197 * associated with the policy. This is a Map of attribute-value pairs 198 * representing the environment under which the policy needs to be 199 * evaluated. 200 */ 201 public ISPermission(String serviceName,String resourceName, 202 String actions, Map envParams) 203 { 204 super("ISPermission"); 205 this.serviceName = serviceName; 206 this.resourceName = resourceName; 207 this.actions = actions; 208 this.envParams = envParams; 209 debug.message("ISPermission:: Constructor called"); 210 } 211 212 /** 213 * returns the name of the service associated with this <code>ISPermission 214 * </code>. 215 * @return <code>String</code> representing the name of the service for this 216 * permission. 217 */ 218 public String getServiceName() { 219 debug.message("ISPermission: getServiceName called"); 220 return serviceName; 221 } 222 223 /** 224 * returns the name of the resource associated with this <code>ISPermission 225 * </code>. 226 * @return <code>String</code> representing the name of the resource for 227 * this permission. 228 */ 229 public String getResourceName() { 230 debug.message("ISPermission: getResourceName called"); 231 return resourceName; 232 } 233 234 /** 235 * returns environment parameters and their values associated with this 236 * <code>ISPermission</code>. 237 * @return <code>Map</code> representing the environment parameters of 238 * this permission. The <code>Map</code> consists of attribute 239 * value pairs. 240 */ 241 public Map getEnvParams() { 242 return envParams; 243 } 244 245 /** 246 * returns a comma separated list of actions associated with this 247 * <code>ISPermission</code>. 248 * @return a comma separated <code>String</code> representing the name 249 * of the action for this object. For example for: 250 * <pre> 251 * ISPermission isp = new ISPermission("iPlanetAMWebAgentService, 252 * "http://www.sun.com:80", "GET, POST"); 253 * getActions() would return "GET,POST" 254 * </pre> 255 */ 256 public String getActions() { 257 debug.message("ISPermission: getActions called"); 258 if (debug.messageEnabled()) { 259 debug.message("returning actions:"+actions); 260 } 261 return actions; 262 } 263 264 /** 265 * Returns true if two comma separated strings are equal. 266 * 267 * @param actions1 actions string. 268 * @param actions2 actions string. 269 * @return true if two comma separated strings are equal. 270 */ 271 private boolean actionEquals(String actions1, String actions2) { 272 Set actionSet1 = Collections.synchronizedSet(new HashSet()); 273 Set actionSet2 = Collections.synchronizedSet(new HashSet()); 274 if (actions1 != null) { 275 StringTokenizer st = new StringTokenizer(actions1,","); 276 while (st.hasMoreTokens()) { 277 String action = (String)st.nextToken().trim(); 278 actionSet1.add(action); 279 } 280 } 281 if (actions2 != null) { 282 StringTokenizer st = new StringTokenizer(actions2,","); 283 while (st.hasMoreTokens()) { 284 String action = (String)st.nextToken().trim(); 285 actionSet2.add(action); 286 } 287 } 288 return actionSet1.equals(actionSet2); 289 } 290 291 /** 292 * Returns a <code>Set</code> of actions for this Permission. 293 * @param actions comma separated actions string. 294 * @return set of actions in this permsision. 295 * 296 */ 297 private Set actionsInSet(String actions) { 298 if (actionSet == null) { 299 actionSet = Collections.synchronizedSet(new HashSet()); 300 } else { 301 return actionSet; 302 } 303 if (actions != null) { 304 StringTokenizer st = new StringTokenizer(actions,","); 305 while (st.hasMoreTokens()) { 306 String action = (String)st.nextToken(); 307 actionSet.add(action); 308 } 309 } 310 return actionSet; 311 } 312 313 /** 314 * returns the <code>Subject</code>associated with this <code>ISPermission 315 * </code>. 316 * @return <code>javax.security.auth.Subject</code> representing the 317 * subject of this permission. 318 */ 319 public Subject getSubject() { 320 debug.message("ISPermission:: getSubject called "); 321 return subject; 322 } 323 324 /** 325 * returns the <code>CodeSource</code>associated with this 326 * <code>ISPermission</code>. 327 * @return <code>java.security.CodeSource</code> representing the 328 * <code>codesource</code> of this permission. 329 */ 330 public CodeSource getCodeSource() { 331 debug.message("ISPermission:: getCodeSource called "); 332 return codesource; 333 } 334 335 /** 336 * returns the <code>ProtectionDomain</code>associated with this 337 * <code>ISPermission</code>. 338 * @return <code>java.security.ProtectionDomain</code> representing the 339 * <code>protectionDomain</code> of this permission. 340 */ 341 public ProtectionDomain getProtectionDomain() { 342 debug.message("ISPermission:: getProtectionDomain called "); 343 return protectionDomain; 344 } 345 346 /** 347 * Returns true if two <code>ISPermission</code> objects for equality. 348 * 349 * @param obj <code>ISPermission</code> object. 350 * @return true if subject, <code>codesource</code>, service name, resource 351 * name actions and environment parameters of both objects are 352 * equal. 353 */ 354 public boolean equals(Object obj){ 355 boolean result = true; 356 debug.message("ISPermission:: equals(Object) called "); 357 if (obj == this) { 358 if (debug.messageEnabled()) { 359 debug.message("ISPermission::equals::this " +result); 360 } 361 return true; 362 } 363 if (obj instanceof ISPermission) { 364 ISPermission perm = (ISPermission) obj; 365 Subject subject = perm.getSubject(); 366 if (subject != null) { 367 result = subject.equals(this.subject); 368 } else { 369 if (this.subject != null) { 370 result = false; // subject is null, while this.subject is 371 // not null. 372 } 373 } 374 if (debug.messageEnabled()) { 375 debug.message("ISPermission::subject equals:"+result); 376 } 377 if (result) { 378 CodeSource codesource = perm.getCodeSource(); 379 if (codesource != null) { 380 result = codesource.equals(this.codesource); 381 if (debug.messageEnabled()) { 382 debug.message("ISPermission::codesource equals:"+ 383 codesource.equals(this.codesource)); 384 } 385 } else { 386 if (this.codesource != null) { 387 result = false; 388 } 389 } 390 } 391 if (result) { 392 ProtectionDomain protectionDomain = perm.getProtectionDomain(); 393 if (protectionDomain != null) { 394 result = protectionDomain.equals(this.protectionDomain); 395 if (debug.messageEnabled()) { 396 debug.message("ISPermission::protectionDomain equals:" 397 + protectionDomain.equals(this.protectionDomain)); 398 } 399 } else { 400 if (this.protectionDomain != null) { 401 result = false; 402 } 403 } 404 } 405 if (result) { 406 String serviceName = perm.getServiceName(); 407 if (serviceName != null) { 408 result = serviceName.equals(this.serviceName); 409 if (debug.messageEnabled()) { 410 debug.message("ISPermission::servicename equals:"+ 411 serviceName.equals(this.serviceName)); 412 } 413 } else { 414 if (this.serviceName != null) { 415 result = false; 416 } 417 } 418 } 419 if (result) { 420 String resourceName = perm.getResourceName(); 421 if (resourceName != null) { 422 result = resourceName.equals(this.resourceName); 423 if (debug.messageEnabled()) { 424 debug.message("ISPermission::resourceName equals:"+ 425 resourceName.equals(this.resourceName)); 426 } 427 } else { 428 if (this.resourceName != null) { 429 result = false; 430 } 431 } 432 } 433 if (result) { 434 String actions = perm.getActions(); 435 if (actions != null) { 436 result = actionEquals(actions,this.actions); 437 if (debug.messageEnabled()) { 438 debug.message("ISPermission::Actions equals:"+ 439 actionEquals(actions,this.actions)); 440 } 441 } else { 442 if (this.actions != null) { 443 result = false; 444 } 445 } 446 } 447 if (result) { 448 Map envParams = perm.getEnvParams(); 449 if (envParams != null && !envParams.isEmpty()) { 450 result = envParams.equals(this.envParams); 451 if (debug.messageEnabled()) { 452 debug.message("ISPermission::equals::envMap" 453 + envParams.equals(this.envParams)); 454 } 455 } else { 456 if (this.envParams != null && !this.envParams.isEmpty()) { 457 result = false; 458 } 459 } 460 } 461 } 462 if (debug.messageEnabled()) { 463 debug.message("ISPermission::equals::returning " +result); 464 } 465 return result; 466 } 467 468 /** 469 * Returns the hash code value for this Permission object. 470 * <P> 471 * The required <code>hashCode</code> behavior for Permission Objects is 472 * the following: <p> 473 * <ul> 474 * <li>Whenever it is invoked on the same Permission object more than 475 * once during an execution of a Java application, the 476 * <code>hashCode</code> method 477 * must consistently return the same integer. This integer need not 478 * remain consistent from one execution of an application to another 479 * execution of the same application. <p> 480 * <li>If two Permission objects are equal according to the 481 * <code>equals</code> 482 * method, then calling the <code>hashCode</code> method on each of the 483 * two Permission objects must produce the same integer result. 484 * </ul> 485 * 486 * @return a hash code value for this object. 487 */ 488 public int hashCode() { 489 int hash = 0; 490 if (subject != null) { 491 hash = hash + this.subject.hashCode(); 492 } 493 if (codesource != null) { 494 hash = hash + this.codesource.hashCode(); 495 } 496 if (protectionDomain != null) { 497 hash = hash + this.protectionDomain.hashCode(); 498 } 499 if (serviceName != null) { 500 hash = hash + this.serviceName.hashCode(); 501 } 502 if (resourceName != null) { 503 hash = hash + this.resourceName.hashCode(); 504 } 505 if (actions != null) { 506 Set actionSet = actionsInSet(actions); 507 hash = hash + actionSet.hashCode(); 508 } 509 if (envParams != null) { 510 hash = hash + this.envParams.hashCode(); 511 } 512 if (debug.messageEnabled()) { 513 debug.message("ISPermission::hashCode::"+hash); 514 } 515 return hash; 516 } 517 518 /** 519 * Checks if the specified permission's actions are "implied by" 520 * this object's actions. 521 * <P> 522 * The <code>implies</code> method is used by the 523 * <code>AccessController</code> to determine whether or not a requested 524 * permission is implied by another permission that is known to be valid 525 * in the current execution context. 526 * 527 * @param perm the permission to check against. 528 * 529 * @return true if the specified permission is implied by this object, 530 * false if not. The check is made against the OpenSSO's 531 * policy service to determine this evaluation. 532 */ 533 public boolean implies(Permission perm) { 534 debug.message("ISPermission: implies called"); 535 boolean allowed = false; 536 if (perm instanceof ISPermission) { 537 debug.message("ISPermission:passed perm is of type ISPermission"); 538 if (protectionDomain != null) { 539 debug.message("ISPermission:implies:protectionDomain not null"); 540 if (debug.messageEnabled()) { 541 debug.message("ISPermission::implies: protectionDomain:" 542 +protectionDomain.toString()); 543 } 544 final String serviceName =((ISPermission)perm).getServiceName(); 545 final String resourceName = 546 ((ISPermission)perm).getResourceName(); 547 final String actions = ((ISPermission)perm).getActions(); 548 final Map envParams = ((ISPermission)perm).getEnvParams(); 549 if (debug.messageEnabled()) { 550 debug.message("ISPermission: resourceName=" 551 +resourceName); 552 debug.message("ISPermission: serviceName=" 553 +serviceName); 554 debug.message("ISPermission: actions="+actions); 555 } 556 SSOTokenPrincipal tokenPrincipal = null; 557 try { 558 Principal[] principals = protectionDomain.getPrincipals(); 559 // principals should have only one entry 560 Principal principal = (Principal)principals[0]; 561 if (principal.getName().equals("com.sun.identity." 562 +"authentication.service.SSOTokenPrincipal")) { 563 if (debug.messageEnabled()) { 564 debug.message("ISPermission::implies:principals:" 565 +principal.toString()); 566 } 567 tokenPrincipal = (SSOTokenPrincipal) principal; 568 } 569 if (tokenPrincipal == null) { 570 if (debug.messageEnabled()) { 571 debug.error("ISPermission::implies:" 572 + " Principal is null"); 573 } 574 } else { 575 SSOTokenManager ssomgr = SSOTokenManager.getInstance(); 576 final SSOToken token = 577 ssomgr.createSSOToken(tokenPrincipal.getName()); 578 /* TODO currently ISPermission uses remote policy 579 client API so if this class gets used from server side 580 , will always make remote call, need to make changes 581 in this code to to make a local/remote call accordingly. 582 */ 583 if (policyEvalFactory == null) { 584 policyEvalFactory = 585 PolicyEvaluatorFactory.getInstance(); 586 } 587 PolicyEvaluator policyEvaluator = 588 policyEvalFactory. 589 getPolicyEvaluator(serviceName); 590 if (debug.messageEnabled()) { 591 debug.message("ISPermission::implies::created " 592 + "PolicyEvaluator for "+serviceName); 593 } 594 if (actions != null) { 595 StringTokenizer st = 596 new StringTokenizer(actions,","); 597 while (st.hasMoreTokens()) { 598 String action = (String)st.nextToken(); 599 allowed = policyEvaluator.isAllowed(token, 600 resourceName , action ,envParams); 601 if (!allowed) { 602 break; // the final result is not allowwed 603 } 604 if (debug.messageEnabled()) { 605 debug.message("ISPermission::result for " 606 + action+" is :"+allowed); 607 } 608 } 609 if (debug.messageEnabled()) { 610 debug.message("ISPermission::result for " 611 + actions+" is :"+allowed); 612 } 613 } else { 614 if (debug.messageEnabled()) { 615 debug.message("ISPermission:: actions is null"); 616 } 617 } 618 } 619 } catch (SSOException ssoe ) { 620 if (debug.messageEnabled()) { 621 debug.error("ISPermission::SSOException:" 622 +ssoe.getMessage()); 623 ssoe.printStackTrace(); 624 } 625 } catch (Exception e ) { 626 if (debug.messageEnabled()) { 627 debug.error("ISPermission::Exception:" 628 +e.getMessage()); 629 e.printStackTrace(); 630 } 631 } 632 } else { 633 debug.message("ISPermission:: subject was null"); 634 } 635 } 636 if (debug.messageEnabled()) { 637 debug.message("ISPermission: allowed::"+allowed); 638 } 639 return allowed; 640 } 641 642 /** 643 * Returns a <code>java.security.PermissionCollection</code> to store this 644 * kind of Permission. 645 * 646 * @return an instance of <code>ISPermissionCollection</code> 647 */ 648 public PermissionCollection newPermissionCollection() { 649 debug.message("ISPermission:: newISPermissionCollection() called"); 650 return new ISPermissionCollection(); 651 } 652 653 654 /** 655 * Returns a string describing this Permission. 656 * @return <code>String</code> containing information about this Permission. 657 */ 658 public String toString() { 659 StringBuffer str = new StringBuffer(200); 660 str = str.append("(").append(getClass().getName()).append("\n"); 661 String actions = getActions(); 662 if (subject != null) { 663 str = str.append(subject.toString()).append("\n"); 664 } 665 if (codesource != null) { 666 str = str.append(codesource.toString()).append("\n"); 667 } 668 if ((serviceName != null) && (serviceName.length() != 0)) { 669 str = str.append("serviceName=").append(serviceName).append("\n"); 670 } 671 if ((resourceName != null) && (resourceName.length() != 0)) { 672 str = str.append("resourceName=").append(resourceName).append("\n"); 673 } 674 if ((actions != null) && (actions.length() != 0)) { 675 str = str.append("actions=").append(actions).append("\n"); 676 } 677 if ((envParams != null) && !(envParams.isEmpty())) { 678 str = str.append("envParams=").append(envParams.values()) 679 .append("\n"); 680 } 681 str.append(")"); 682 return str.toString(); 683 } 684}