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