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