001 /* 002 Licensed to the Apache Software Foundation (ASF) under one 003 or more contributor license agreements. See the NOTICE file 004 distributed with this work for additional information 005 regarding copyright ownership. The ASF licenses this file 006 to you under the Apache License, Version 2.0 (the 007 "License"); you may not use this file except in compliance 008 with the License. You may obtain a copy of the License at 009 010 http://www.apache.org/licenses/LICENSE-2.0 011 012 Unless required by applicable law or agreed to in writing, 013 software distributed under the License is distributed on an 014 "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 KIND, either express or implied. See the License for the 016 specific language governing permissions and limitations 017 under the License. 018 */ 019 package org.apache.wiki.auth; 020 021 022 import java.io.File; 023 import java.io.IOException; 024 import java.net.URL; 025 import java.security.AccessControlException; 026 import java.security.AccessController; 027 import java.security.CodeSource; 028 import java.security.Permission; 029 import java.security.Principal; 030 import java.security.PrivilegedAction; 031 import java.security.ProtectionDomain; 032 import java.security.cert.Certificate; 033 import java.text.MessageFormat; 034 import java.util.Arrays; 035 import java.util.Map; 036 import java.util.Properties; 037 import java.util.ResourceBundle; 038 import java.util.WeakHashMap; 039 040 import javax.servlet.http.HttpServletResponse; 041 042 import org.apache.log4j.Logger; 043 import org.apache.wiki.WikiContext; 044 import org.apache.wiki.WikiEngine; 045 import org.apache.wiki.WikiPage; 046 import org.apache.wiki.WikiSession; 047 import org.apache.wiki.api.exceptions.NoRequiredPropertyException; 048 import org.apache.wiki.api.exceptions.WikiException; 049 import org.apache.wiki.auth.acl.Acl; 050 import org.apache.wiki.auth.acl.AclEntry; 051 import org.apache.wiki.auth.acl.UnresolvedPrincipal; 052 import org.apache.wiki.auth.authorize.Role; 053 import org.apache.wiki.auth.permissions.AllPermission; 054 import org.apache.wiki.auth.permissions.PagePermission; 055 import org.apache.wiki.auth.user.UserDatabase; 056 import org.apache.wiki.auth.user.UserProfile; 057 import org.apache.wiki.event.WikiEventListener; 058 import org.apache.wiki.event.WikiEventManager; 059 import org.apache.wiki.event.WikiSecurityEvent; 060 import org.apache.wiki.i18n.InternationalizationManager; 061 import org.apache.wiki.preferences.Preferences; 062 import org.apache.wiki.tags.WikiTagBase; 063 import org.apache.wiki.util.ClassUtil; 064 import org.freshcookies.security.policy.LocalPolicy; 065 import org.freshcookies.security.policy.PolicyException; 066 067 /** 068 * <p>Manages all access control and authorization; determines what authenticated 069 * users are allowed to do.</p> 070 * <p>Privileges in JSPWiki are expressed as Java-standard {@link java.security.Permission} 071 * classes. There are two types of permissions:</p> 072 * <ul> 073 * <li>{@link org.apache.wiki.auth.permissions.WikiPermission} - privileges that apply 074 * to an entire wiki instance: <em>e.g.,</em> editing user profiles, creating pages, creating groups</li> 075 * <li>{@link org.apache.wiki.auth.permissions.PagePermission} - privileges that apply 076 * to a single wiki page or range of pages: <em>e.g.,</em> reading, editing, renaming 077 * </ul> 078 * <p>Calling classes determine whether they are entitled to perform a particular action 079 * by constructing the appropriate permission first, then passing it and the current 080 * {@link org.apache.wiki.WikiSession} to the 081 * {@link #checkPermission(WikiSession, Permission)} method. If the session's 082 * Subject possesses the permission, the action is allowed.</p> 083 * <p>For WikiPermissions, the decision criteria is relatively simple: the caller either 084 * possesses the permission, as granted by the wiki security policy -- or not.</p> 085 * <p>For PagePermissions, the logic is exactly the same if the page being checked 086 * does not have an access control list. However, if the page does have an ACL, the 087 * authorization decision is made based the <em>union</em> of the permissions 088 * granted in the ACL and in the security policy. In other words, the user must 089 * be named in the ACL (or belong to a group or role that is named in the ACL) 090 * <em>and</em> be granted (at least) the same permission in the security policy. We 091 * do this to prevent a user from gaining more permissions than they already 092 * have, based on the security policy.</p> 093 * <p>See the {@link #checkPermission(WikiSession, Permission)} and 094 * {@link #hasRoleOrPrincipal(WikiSession, Principal)} methods for more information 095 * on the authorization logic.</p> 096 * @since 2.3 097 * @see AuthenticationManager 098 */ 099 public class AuthorizationManager { 100 101 private static final Logger log = Logger.getLogger( AuthorizationManager.class ); 102 /** 103 * The default external Authorizer is the {@link org.apache.wiki.auth.authorize.WebContainerAuthorizer} 104 */ 105 public static final String DEFAULT_AUTHORIZER = "org.apache.wiki.auth.authorize.WebContainerAuthorizer"; 106 107 /** Property that supplies the security policy file name, in WEB-INF. */ 108 protected static final String POLICY = "jspwiki.policy.file"; 109 110 /** Name of the default security policy file, in WEB-INF. */ 111 protected static final String DEFAULT_POLICY = "jspwiki.policy"; 112 113 /** 114 * The property name in jspwiki.properties for specifying the external {@link Authorizer}. 115 */ 116 public static final String PROP_AUTHORIZER = "jspwiki.authorizer"; 117 118 private Authorizer m_authorizer = null; 119 120 /** Cache for storing ProtectionDomains used to evaluate the local policy. */ 121 private Map<Principal, ProtectionDomain> m_cachedPds = new WeakHashMap<Principal, ProtectionDomain>(); 122 123 private WikiEngine m_engine = null; 124 125 private LocalPolicy m_localPolicy = null; 126 127 private boolean m_useJAAS = true; 128 129 /** 130 * Constructs a new AuthorizationManager instance. 131 */ 132 public AuthorizationManager() 133 { 134 } 135 136 /** 137 * Returns <code>true</code> or <code>false</code>, depending on 138 * whether a Permission is allowed for the Subject associated with 139 * a supplied WikiSession. The access control algorithm works this way: 140 * <ol> 141 * <li>The {@link org.apache.wiki.auth.acl.Acl} for the page is obtained</li> 142 * <li>The Subject associated with the current 143 * {@link org.apache.wiki.WikiSession} is obtained</li> 144 * <li>If the Subject's Principal set includes the Role Principal that is 145 * the administrator group, always allow the Permission</li> 146 * <li>For all permissions, check to see if the Permission is allowed according 147 * to the default security policy. If it isn't, deny the permission and halt 148 * further processing.</li> 149 * <li>If there is an Acl, get the list of Principals assigned this 150 * Permission in the Acl: these will be role, group or user Principals, or 151 * {@link org.apache.wiki.auth.acl.UnresolvedPrincipal}s (see below). 152 * Then iterate through the Subject's Principal set and determine whether 153 * the user (Subject) possesses any one of these specified Roles or 154 * Principals. The matching process delegates to 155 * {@link #hasRoleOrPrincipal(WikiSession, Principal)}. 156 * </ol> 157 * <p> 158 * Note that when iterating through the Acl's list of authorized Principals, 159 * it is possible that one or more of the Acl's Principal entries are of 160 * type <code>UnresolvedPrincipal</code>. This means that the last time 161 * the ACL was read, the Principal (user, built-in Role, authorizer Role, or 162 * wiki Group) could not be resolved: the Role was not valid, the user 163 * wasn't found in the UserDatabase, or the Group wasn't known to (e.g., 164 * cached) in the GroupManager. If an <code>UnresolvedPrincipal</code> is 165 * encountered, this method will attempt to resolve it first <em>before</em> 166 * checking to see if the Subject possesses this principal, by calling 167 * {@link #resolvePrincipal(String)}. If the (re-)resolution does not 168 * succeed, the access check for the principal will fail by definition (the 169 * Subject should never contain UnresolvedPrincipals). 170 * </p> 171 * <p> 172 * If security not set to JAAS, will return true. 173 * </p> 174 * @param session the current wiki session 175 * @param permission the Permission being checked 176 * @see #hasRoleOrPrincipal(WikiSession, Principal) 177 * @return the result of the Permission check 178 */ 179 public boolean checkPermission( WikiSession session, Permission permission ) 180 { 181 if( !m_useJAAS ) 182 { 183 // 184 // Nobody can login, if JAAS is turned off. 185 // 186 187 if( permission == null || "login".equals( permission.getActions() ) ) 188 return false; 189 190 return true; 191 } 192 193 // 194 // A slight sanity check. 195 // 196 if ( session == null || permission == null ) 197 { 198 fireEvent( WikiSecurityEvent.ACCESS_DENIED, null, permission ); 199 return false; 200 } 201 202 Principal user = session.getLoginPrincipal(); 203 204 // Always allow the action if user has AllPermission 205 Permission allPermission = new AllPermission( m_engine.getApplicationName() ); 206 boolean hasAllPermission = checkStaticPermission( session, allPermission ); 207 if ( hasAllPermission ) 208 { 209 fireEvent( WikiSecurityEvent.ACCESS_ALLOWED, user, permission ); 210 return true; 211 } 212 213 // If the user doesn't have *at least* the permission 214 // granted by policy, return false. 215 boolean hasPolicyPermission = checkStaticPermission( session, permission ); 216 if ( !hasPolicyPermission ) 217 { 218 fireEvent( WikiSecurityEvent.ACCESS_DENIED, user, permission ); 219 return false; 220 } 221 222 // If this isn't a PagePermission, it's allowed 223 if ( ! ( permission instanceof PagePermission ) ) 224 { 225 fireEvent( WikiSecurityEvent.ACCESS_ALLOWED, user, permission ); 226 return true; 227 } 228 229 // 230 // If the page or ACL is null, it's allowed. 231 // 232 String pageName = ((PagePermission)permission).getPage(); 233 WikiPage page = m_engine.getPage( pageName ); 234 Acl acl = ( page == null) ? null : m_engine.getAclManager().getPermissions( page ); 235 if ( page == null || acl == null || acl.isEmpty() ) 236 { 237 fireEvent( WikiSecurityEvent.ACCESS_ALLOWED, user, permission ); 238 return true; 239 } 240 241 // 242 // Next, iterate through the Principal objects assigned 243 // this permission. If the context's subject possesses 244 // any of these, the action is allowed. 245 246 Principal[] aclPrincipals = acl.findPrincipals( permission ); 247 248 log.debug( "Checking ACL entries..." ); 249 log.debug( "Acl for this page is: " + acl ); 250 log.debug( "Checking for principal: " + Arrays.toString( aclPrincipals ) ); 251 log.debug( "Permission: " + permission ); 252 253 for( Principal aclPrincipal : aclPrincipals ) 254 { 255 // If the ACL principal we're looking at is unresolved, 256 // try to resolve it here & correct the Acl 257 if ( aclPrincipal instanceof UnresolvedPrincipal ) 258 { 259 AclEntry aclEntry = acl.getEntry( aclPrincipal ); 260 aclPrincipal = resolvePrincipal( aclPrincipal.getName() ); 261 if ( aclEntry != null && !( aclPrincipal instanceof UnresolvedPrincipal ) ) 262 { 263 aclEntry.setPrincipal( aclPrincipal ); 264 } 265 } 266 267 if ( hasRoleOrPrincipal( session, aclPrincipal ) ) 268 { 269 fireEvent( WikiSecurityEvent.ACCESS_ALLOWED, user, permission ); 270 return true; 271 } 272 } 273 fireEvent( WikiSecurityEvent.ACCESS_DENIED, user, permission ); 274 return false; 275 } 276 277 /** 278 * <p>Determines if the Subject associated with a 279 * supplied WikiSession contains a desired Role or GroupPrincipal. 280 * The algorithm simply checks to see if the Subject possesses 281 * the Role or GroupPrincipal it in its Principal set. Note that 282 * any user (anonymous, asserted, authenticated) can possess 283 * a built-in role. But a user <em>must</em> be authenticated to 284 * possess a role other than one of the built-in ones. 285 * We do this to prevent privilege escalation.</p> 286 * <p>For all other cases, this method returns <code>false</code>.</p> 287 * <p>Note that this method does <em>not</em> consult the external 288 * Authorizer or GroupManager; it relies on the Principals that 289 * have been injected into the user's Subject at login time, or 290 * after group creation/modification/deletion.</p> 291 * @param session the current wiki session, which must be non-null. If null, 292 * the result of this method always returns <code>false</code> 293 * @param principal the Principal (role or group principal) to look 294 * for, which must be non-<code>null</code>. If <code>null</code>, 295 * the result of this method always returns <code>false</code> 296 * @return <code>true</code> if the Subject supplied with the WikiContext 297 * posesses the Role or GroupPrincipal, <code>false</code> otherwise 298 */ 299 public boolean isUserInRole( WikiSession session, Principal principal ) 300 { 301 if ( session == null || principal == null || 302 AuthenticationManager.isUserPrincipal( principal ) ) 303 { 304 return false; 305 } 306 307 // Any type of user can possess a built-in role 308 if ( principal instanceof Role && Role.isBuiltInRole( (Role)principal ) ) 309 { 310 return session.hasPrincipal( principal ); 311 } 312 313 // Only authenticated users can possess groups or custom roles 314 if ( session.isAuthenticated() && AuthenticationManager.isRolePrincipal( principal ) ) 315 { 316 return session.hasPrincipal( principal ); 317 } 318 return false; 319 } 320 321 /** 322 * Returns the current external {@link Authorizer} in use. This method 323 * is guaranteed to return a properly-initialized Authorizer, unless 324 * it could not be initialized. In that case, this method throws 325 * a {@link org.apache.wiki.auth.WikiSecurityException}. 326 * @throws org.apache.wiki.auth.WikiSecurityException if the Authorizer could 327 * not be initialized 328 * @return the current Authorizer 329 */ 330 public Authorizer getAuthorizer() throws WikiSecurityException 331 { 332 if ( m_authorizer != null ) 333 { 334 return m_authorizer; 335 } 336 throw new WikiSecurityException( "Authorizer did not initialize properly. Check the logs." ); 337 } 338 339 /** 340 * <p>Determines if the Subject associated with a supplied WikiSession contains 341 * a desired user Principal or built-in Role principal, OR is a member a 342 * Group or external Role. The rules are as follows:</p> 343 * <ol> 344 * <li>First, if desired Principal is a Role or GroupPrincipal, delegate to 345 * {@link #isUserInRole(WikiSession, Principal)} and 346 * return the result.</li> 347 * <li>Otherwise, we're looking for a user Principal, 348 * so iterate through the Principal set and see if 349 * any share the same name as the one we are looking for.</li> 350 * </ol> 351 * <p><em>Note: if the Principal parameter is a user principal, the session 352 * must be authenticated in order for the user to "possess it". Anonymous 353 * or asserted sessions will never posseess a named user principal.</em></p> 354 * @param session the current wiki session, which must be non-null. If null, 355 * the result of this method always returns <code>false</code> 356 * @param principal the Principal (role, group, or user principal) to look 357 * for, which must be non-null. If null, the result of this 358 * method always returns <code>false</code> 359 * @return <code>true</code> if the Subject supplied with the WikiContext 360 * posesses the Role, GroupPrincipal or desired 361 * user Principal, <code>false</code> otherwise 362 */ 363 protected boolean hasRoleOrPrincipal( WikiSession session, Principal principal ) 364 { 365 // If either parameter is null, always deny 366 if( session == null || principal == null ) 367 { 368 return false; 369 } 370 371 // If principal is role, delegate to isUserInRole 372 if( AuthenticationManager.isRolePrincipal( principal ) ) 373 { 374 return isUserInRole( session, principal ); 375 } 376 377 // We must be looking for a user principal, assuming that the user 378 // has been properly logged in. 379 // So just look for a name match. 380 if( session.isAuthenticated() && AuthenticationManager.isUserPrincipal( principal ) ) 381 { 382 String principalName = principal.getName(); 383 Principal[] userPrincipals = session.getPrincipals(); 384 for( Principal userPrincipal : userPrincipals ) 385 { 386 if( userPrincipal.getName().equals( principalName ) ) 387 { 388 return true; 389 } 390 } 391 } 392 return false; 393 } 394 395 /** 396 * Checks whether the current user has access to the wiki context, 397 * by obtaining the required Permission ({@link WikiContext#requiredPermission()}) 398 * and delegating the access check to {@link #checkPermission(WikiSession, Permission)}. 399 * If the user is allowed, this method returns <code>true</code>; 400 * <code>false</code> otherwise. If access is allowed, 401 * the wiki context will be added to the request as an attribute 402 * with the key name {@link org.apache.wiki.tags.WikiTagBase#ATTR_CONTEXT}. 403 * Note that this method will automatically redirect the user to 404 * a login or error page, as appropriate, if access fails. This is 405 * NOT guaranteed to be default behavior in the future. 406 * 407 * @param context wiki context to check if it is accesible 408 * @param response the http response 409 * @return the result of the access check 410 * @throws IOException In case something goes wrong 411 */ 412 public boolean hasAccess( WikiContext context, HttpServletResponse response ) throws IOException 413 { 414 return hasAccess( context, response, true ); 415 } 416 417 /** 418 * Checks whether the current user has access to the wiki context (and 419 * optionally redirects if not), by obtaining the required Permission ({@link WikiContext#requiredPermission()}) 420 * and delegating the access check to {@link #checkPermission(WikiSession, Permission)}. 421 * If the user is allowed, this method returns <code>true</code>; 422 * <code>false</code> otherwise. If access is allowed, 423 * the wiki context will be added to the request as attribute 424 * with the key name {@link org.apache.wiki.tags.WikiTagBase#ATTR_CONTEXT}. 425 * 426 * @param context wiki context to check if it is accesible 427 * @param response The servlet response object 428 * @param redirect If true, makes an automatic redirect to the response 429 * @return the result of the access check 430 * @throws IOException If something goes wrong 431 */ 432 public boolean hasAccess( WikiContext context, HttpServletResponse response, boolean redirect ) throws IOException 433 { 434 boolean allowed = checkPermission( context.getWikiSession(), context.requiredPermission() ); 435 ResourceBundle rb = Preferences.getBundle( context, InternationalizationManager.CORE_BUNDLE ); 436 437 // Stash the wiki context 438 if( allowed ) 439 { 440 if ( context.getHttpRequest() != null && context.getHttpRequest().getAttribute( WikiTagBase.ATTR_CONTEXT ) == null ) 441 { 442 context.getHttpRequest().setAttribute( WikiTagBase.ATTR_CONTEXT, context ); 443 } 444 } 445 446 // If access not allowed, redirect 447 if( !allowed && redirect ) 448 { 449 Principal currentUser = context.getWikiSession().getUserPrincipal(); 450 String pageurl = context.getPage().getName(); 451 if( context.getWikiSession().isAuthenticated() ) 452 { 453 log.info("User "+currentUser.getName()+" has no access - forbidden (permission=" + context.requiredPermission() + ")" ); 454 context.getWikiSession().addMessage( 455 MessageFormat.format( rb.getString("security.error.noaccess.logged"), context.getName()) ); 456 } 457 else 458 { 459 log.info("User "+currentUser.getName()+" has no access - redirecting (permission=" + context.requiredPermission() + ")"); 460 context.getWikiSession().addMessage( 461 MessageFormat.format( rb.getString("security.error.noaccess"), context.getName()) ); 462 } 463 response.sendRedirect( m_engine.getURL(WikiContext.LOGIN, pageurl, null, false ) ); 464 } 465 return allowed; 466 } 467 468 /** 469 * Initializes AuthorizationManager with an engine and set of properties. 470 * Expects to find property 'jspwiki.authorizer' with a valid Authorizer 471 * implementation name to take care of role lookup operations. 472 * @param engine the wiki engine 473 * @param properties the set of properties used to initialize the wiki engine 474 * @throws WikiException if the AuthorizationManager cannot be initialized 475 */ 476 @SuppressWarnings("deprecation") 477 public void initialize( WikiEngine engine, Properties properties ) throws WikiException 478 { 479 m_engine = engine; 480 481 m_useJAAS = AuthenticationManager.SECURITY_JAAS.equals( properties.getProperty(AuthenticationManager.PROP_SECURITY, AuthenticationManager.SECURITY_JAAS ) ); 482 483 if( !m_useJAAS ) return; 484 485 // 486 // JAAS authorization continues 487 // 488 m_authorizer = getAuthorizerImplementation( properties ); 489 m_authorizer.initialize( engine, properties ); 490 491 // Initialize local security policy 492 try 493 { 494 String policyFileName = properties.getProperty( POLICY, DEFAULT_POLICY ); 495 URL policyURL = AuthenticationManager.findConfigFile( engine, policyFileName ); 496 497 if (policyURL != null) 498 { 499 File policyFile = new File( policyURL.getPath() ); 500 log.info("We found security policy URL: " + policyURL + " and transformed it to file " + policyFile.getAbsolutePath()); 501 m_localPolicy = new LocalPolicy( policyFile, engine.getContentEncoding() ); 502 m_localPolicy.refresh(); 503 log.info( "Initialized default security policy: " + policyFile.getAbsolutePath() ); 504 } 505 else 506 { 507 StringBuffer sb = new StringBuffer( "JSPWiki was unable to initialize the " ); 508 sb.append( "default security policy (WEB-INF/jspwiki.policy) file. " ); 509 sb.append( "Please ensure that the jspwiki.policy file exists in the default location. " ); 510 sb.append( "This file should exist regardless of the existance of a global policy file. " ); 511 sb.append( "The global policy file is identified by the java.security.policy variable. " ); 512 WikiSecurityException wse = new WikiSecurityException( sb.toString() ); 513 log.fatal( sb.toString(), wse ); 514 throw wse; 515 } 516 } 517 catch ( PolicyException e ) 518 { 519 log.error("Could not initialize local security policy: " + e.getMessage() ); 520 throw new WikiException( "Could not initialize local security policy: " + e.getMessage(), e ); 521 } 522 } 523 524 /** 525 * Returns <code>true</code> if JSPWiki's JAAS authorization system 526 * is used for authorization in addition to container controls. 527 * @return the result 528 */ 529 protected boolean isJAASAuthorized() 530 { 531 return m_useJAAS; 532 } 533 534 /** 535 * Attempts to locate and initialize a Authorizer to use with this manager. 536 * Throws a WikiException if no entry is found, or if one fails to 537 * initialize. 538 * @param props jspwiki.properties, containing a 539 * 'jspwiki.authorization.provider' class name 540 * @return a Authorizer used to get page authorization information 541 * @throws WikiException 542 */ 543 private Authorizer getAuthorizerImplementation( Properties props ) throws WikiException 544 { 545 String authClassName = props.getProperty( PROP_AUTHORIZER, DEFAULT_AUTHORIZER ); 546 return (Authorizer) locateImplementation( authClassName ); 547 } 548 549 private Object locateImplementation( String clazz ) throws WikiException 550 { 551 if ( clazz != null ) 552 { 553 try 554 { 555 Class<?> authClass = ClassUtil.findClass( "org.apache.wiki.auth.authorize", clazz ); 556 Object impl = authClass.newInstance(); 557 return impl; 558 } 559 catch( ClassNotFoundException e ) 560 { 561 log.fatal( "Authorizer " + clazz + " cannot be found", e ); 562 throw new WikiException( "Authorizer " + clazz + " cannot be found", e ); 563 } 564 catch( InstantiationException e ) 565 { 566 log.fatal( "Authorizer " + clazz + " cannot be created", e ); 567 throw new WikiException( "Authorizer " + clazz + " cannot be created", e ); 568 } 569 catch( IllegalAccessException e ) 570 { 571 log.fatal( "You are not allowed to access this authorizer class", e ); 572 throw new WikiException( "You are not allowed to access this authorizer class", e ); 573 } 574 } 575 576 throw new NoRequiredPropertyException( "Unable to find a " + PROP_AUTHORIZER + " entry in the properties.", 577 PROP_AUTHORIZER ); 578 } 579 580 /** 581 * Checks to see if the local security policy allows a particular static Permission. 582 * Do not use this method for normal permission checks; use 583 * {@link #checkPermission(WikiSession, Permission)} instead. 584 * @param principals the Principals to check 585 * @param permission the Permission 586 * @return the result 587 */ 588 protected boolean allowedByLocalPolicy( Principal[] principals, Permission permission ) 589 { 590 for ( Principal principal : principals ) 591 { 592 // Get ProtectionDomain for this Principal from cache, or create new one 593 ProtectionDomain pd = m_cachedPds.get( principal ); 594 if ( pd == null ) 595 { 596 ClassLoader cl = this.getClass().getClassLoader(); 597 CodeSource cs = new CodeSource( null, (Certificate[])null ); 598 pd = new ProtectionDomain( cs, null, cl, new Principal[]{ principal } ); 599 m_cachedPds.put( principal, pd ); 600 } 601 602 // Consult the local policy and get the answer 603 if ( m_localPolicy.implies( pd, permission ) ) 604 { 605 return true; 606 } 607 } 608 return false; 609 } 610 611 /** 612 * Determines whether a Subject possesses a given "static" Permission as 613 * defined in the security policy file. This method uses standard Java 2 614 * security calls to do its work. Note that the current access control 615 * context's <code>codeBase</code> is effectively <em>this class</em>, 616 * not that of the caller. Therefore, this method will work best when what 617 * matters in the policy is <em>who</em> makes the permission check, not 618 * what the caller's code source is. Internally, this method works by 619 * executing <code>Subject.doAsPrivileged</code> with a privileged action 620 * that simply calls {@link java.security.AccessController#checkPermission(Permission)}. 621 * @see AccessController#checkPermission(java.security.Permission) . A 622 * caught exception (or lack thereof) determines whether the privilege 623 * is absent (or present). 624 * @param session the WikiSession whose permission status is being queried 625 * @param permission the Permission the Subject must possess 626 * @return <code>true</code> if the Subject possesses the permission, 627 * <code>false</code> otherwise 628 */ 629 protected boolean checkStaticPermission( final WikiSession session, final Permission permission ) 630 { 631 if( !m_useJAAS ) return true; 632 633 Boolean allowed = (Boolean) WikiSession.doPrivileged( session, new PrivilegedAction<Boolean>() 634 { 635 public Boolean run() 636 { 637 try 638 { 639 // Check the JVM-wide security policy first 640 AccessController.checkPermission( permission ); 641 return Boolean.TRUE; 642 } 643 catch( AccessControlException e ) 644 { 645 // Global policy denied the permission 646 } 647 648 // Try the local policy - check each Role/Group and User Principal 649 if ( allowedByLocalPolicy( session.getRoles(), permission ) || 650 allowedByLocalPolicy( session.getPrincipals(), permission ) ) 651 { 652 return Boolean.TRUE; 653 } 654 return Boolean.FALSE; 655 } 656 } ); 657 return allowed.booleanValue(); 658 } 659 660 /** 661 * <p>Given a supplied string representing a Principal's name from an Acl, this 662 * method resolves the correct type of Principal (role, group, or user). 663 * This method is guaranteed to always return a Principal. 664 * The algorithm is straightforward:</p> 665 * <ol> 666 * <li>If the name matches one of the built-in {@link org.apache.wiki.auth.authorize.Role} names, 667 * return that built-in Role</li> 668 * <li>If the name matches one supplied by the current 669 * {@link org.apache.wiki.auth.Authorizer}, return that Role</li> 670 * <li>If the name matches a group managed by the 671 * current {@link org.apache.wiki.auth.authorize.GroupManager}, return that Group</li> 672 * <li>Otherwise, assume that the name represents a user 673 * principal. Using the current {@link org.apache.wiki.auth.user.UserDatabase}, find the 674 * first user who matches the supplied name by calling 675 * {@link org.apache.wiki.auth.user.UserDatabase#find(String)}.</li> 676 * <li>Finally, if a user cannot be found, manufacture 677 * and return a generic {@link org.apache.wiki.auth.acl.UnresolvedPrincipal}</li> 678 * </ol> 679 * @param name the name of the Principal to resolve 680 * @return the fully-resolved Principal 681 */ 682 public Principal resolvePrincipal( String name ) 683 { 684 if( !m_useJAAS ) 685 { 686 return new UnresolvedPrincipal(name); 687 } 688 689 // Check built-in Roles first 690 Role role = new Role(name); 691 if ( Role.isBuiltInRole( role ) ) 692 { 693 return role; 694 } 695 696 // Check Authorizer Roles 697 Principal principal = m_authorizer.findRole( name ); 698 if ( principal != null ) 699 { 700 return principal; 701 } 702 703 // Check Groups 704 principal = m_engine.getGroupManager().findRole( name ); 705 if ( principal != null ) 706 { 707 return principal; 708 } 709 710 // Ok, no luck---this must be a user principal 711 Principal[] principals = null; 712 UserProfile profile = null; 713 UserDatabase db = m_engine.getUserManager().getUserDatabase(); 714 try 715 { 716 profile = db.find( name ); 717 principals = db.getPrincipals( profile.getLoginName() ); 718 for (int i = 0; i < principals.length; i++) 719 { 720 principal = principals[i]; 721 if ( principal.getName().equals( name ) ) 722 { 723 return principal; 724 } 725 } 726 } 727 catch( NoSuchPrincipalException e ) 728 { 729 // We couldn't find the user... 730 } 731 // Ok, no luck---mark this as unresolved and move on 732 return new UnresolvedPrincipal( name ); 733 } 734 735 736 // events processing ....................................................... 737 738 /** 739 * Registers a WikiEventListener with this instance. 740 * @param listener the event listener 741 */ 742 public synchronized void addWikiEventListener( WikiEventListener listener ) 743 { 744 WikiEventManager.addWikiEventListener( this, listener ); 745 } 746 747 /** 748 * Un-registers a WikiEventListener with this instance. 749 * @param listener the event listener 750 */ 751 public synchronized void removeWikiEventListener( WikiEventListener listener ) 752 { 753 WikiEventManager.removeWikiEventListener( this, listener ); 754 } 755 756 /** 757 * Fires a WikiSecurityEvent of the provided type, user, 758 * and permission to all registered listeners. 759 * 760 * @see org.apache.wiki.event.WikiSecurityEvent 761 * @param type the event type to be fired 762 * @param user the user associated with the event 763 * @param permission the permission the subject must possess 764 */ 765 protected void fireEvent( int type, Principal user, Object permission ) 766 { 767 if ( WikiEventManager.isListening(this) ) 768 { 769 WikiEventManager.fireEvent(this,new WikiSecurityEvent(this,type,user,permission)); 770 } 771 } 772 773 }