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. Also, the wiki context will be added to the request as attribute 408 * with the key name {@link org.apache.wiki.tags.WikiTagBase#ATTR_CONTEXT}. 409 * 410 * @param context wiki context to check if it is accesible 411 * @param response The servlet response object 412 * @param redirect If true, makes an automatic redirect to the response 413 * @return the result of the access check 414 * @throws IOException If something goes wrong 415 */ 416 public boolean hasAccess( WikiContext context, HttpServletResponse response, boolean redirect ) throws IOException 417 { 418 boolean allowed = checkPermission( context.getWikiSession(), context.requiredPermission() ); 419 ResourceBundle rb = Preferences.getBundle( context, InternationalizationManager.CORE_BUNDLE ); 420 421 // Stash the wiki context 422 if ( context.getHttpRequest() != null && context.getHttpRequest().getAttribute( WikiTagBase.ATTR_CONTEXT ) == null ) 423 { 424 context.getHttpRequest().setAttribute( WikiTagBase.ATTR_CONTEXT, context ); 425 } 426 427 // If access not allowed, redirect 428 if( !allowed && redirect ) 429 { 430 Principal currentUser = context.getWikiSession().getUserPrincipal(); 431 String pageurl = context.getPage().getName(); 432 if( context.getWikiSession().isAuthenticated() ) 433 { 434 log.info("User "+currentUser.getName()+" has no access - forbidden (permission=" + context.requiredPermission() + ")" ); 435 context.getWikiSession().addMessage( 436 MessageFormat.format( rb.getString("security.error.noaccess.logged"), context.getName()) ); 437 } 438 else 439 { 440 log.info("User "+currentUser.getName()+" has no access - redirecting (permission=" + context.requiredPermission() + ")"); 441 context.getWikiSession().addMessage( 442 MessageFormat.format( rb.getString("security.error.noaccess"), context.getName()) ); 443 } 444 response.sendRedirect( m_engine.getURL(WikiContext.LOGIN, pageurl, null, false ) ); 445 } 446 return allowed; 447 } 448 449 /** 450 * Initializes AuthorizationManager with an engine and set of properties. 451 * Expects to find property 'jspwiki.authorizer' with a valid Authorizer 452 * implementation name to take care of role lookup operations. 453 * @param engine the wiki engine 454 * @param properties the set of properties used to initialize the wiki engine 455 * @throws WikiException if the AuthorizationManager cannot be initialized 456 */ 457 public void initialize( WikiEngine engine, Properties properties ) throws WikiException 458 { 459 m_engine = engine; 460 461 // 462 // JAAS authorization continues 463 // 464 m_authorizer = getAuthorizerImplementation( properties ); 465 m_authorizer.initialize( engine, properties ); 466 467 // Initialize local security policy 468 try 469 { 470 String policyFileName = properties.getProperty( POLICY, DEFAULT_POLICY ); 471 URL policyURL = AuthenticationManager.findConfigFile( engine, policyFileName ); 472 473 if (policyURL != null) 474 { 475 File policyFile = new File( policyURL.toURI().getPath() ); 476 log.info("We found security policy URL: " + policyURL + " and transformed it to file " + policyFile.getAbsolutePath()); 477 m_localPolicy = new LocalPolicy( policyFile, engine.getContentEncoding() ); 478 m_localPolicy.refresh(); 479 log.info( "Initialized default security policy: " + policyFile.getAbsolutePath() ); 480 } 481 else 482 { 483 String sb = "JSPWiki was unable to initialize the default security policy (WEB-INF/jspwiki.policy) file. " + 484 "Please ensure that the jspwiki.policy file exists in the default location. " + 485 "This file should exist regardless of the existance of a global policy file. " + 486 "The global policy file is identified by the java.security.policy variable. "; 487 WikiSecurityException wse = new WikiSecurityException( sb ); 488 log.fatal( sb, wse ); 489 throw wse; 490 } 491 } 492 catch ( Exception e) 493 { 494 log.error("Could not initialize local security policy: " + e.getMessage() ); 495 throw new WikiException( "Could not initialize local security policy: " + e.getMessage(), e ); 496 } 497 } 498 499 /** 500 * Returns <code>true</code> if JSPWiki's JAAS authorization system 501 * is used for authorization in addition to container controls. 502 * @return the result 503 * @deprecated functionality deprecated - returns true always. To be removed on 2.11.0 504 */ 505 @Deprecated 506 protected boolean isJAASAuthorized() 507 { 508 return true; 509 } 510 511 /** 512 * Attempts to locate and initialize a Authorizer to use with this manager. 513 * Throws a WikiException if no entry is found, or if one fails to 514 * initialize. 515 * @param props jspwiki.properties, containing a 516 * 'jspwiki.authorization.provider' class name 517 * @return a Authorizer used to get page authorization information 518 * @throws WikiException 519 */ 520 private Authorizer getAuthorizerImplementation( Properties props ) throws WikiException 521 { 522 String authClassName = props.getProperty( PROP_AUTHORIZER, DEFAULT_AUTHORIZER ); 523 return (Authorizer) locateImplementation( authClassName ); 524 } 525 526 private Object locateImplementation( String clazz ) throws WikiException 527 { 528 if ( clazz != null ) 529 { 530 try 531 { 532 Class<?> authClass = ClassUtil.findClass( "org.apache.wiki.auth.authorize", clazz ); 533 Object impl = authClass.newInstance(); 534 return impl; 535 } 536 catch( ClassNotFoundException e ) 537 { 538 log.fatal( "Authorizer " + clazz + " cannot be found", e ); 539 throw new WikiException( "Authorizer " + clazz + " cannot be found", e ); 540 } 541 catch( InstantiationException e ) 542 { 543 log.fatal( "Authorizer " + clazz + " cannot be created", e ); 544 throw new WikiException( "Authorizer " + clazz + " cannot be created", e ); 545 } 546 catch( IllegalAccessException e ) 547 { 548 log.fatal( "You are not allowed to access this authorizer class", e ); 549 throw new WikiException( "You are not allowed to access this authorizer class", e ); 550 } 551 } 552 553 throw new NoRequiredPropertyException( "Unable to find a " + PROP_AUTHORIZER + " entry in the properties.", 554 PROP_AUTHORIZER ); 555 } 556 557 /** 558 * Checks to see if the local security policy allows a particular static Permission. 559 * Do not use this method for normal permission checks; use 560 * {@link #checkPermission(WikiSession, Permission)} instead. 561 * @param principals the Principals to check 562 * @param permission the Permission 563 * @return the result 564 */ 565 protected boolean allowedByLocalPolicy( Principal[] principals, Permission permission ) 566 { 567 for ( Principal principal : principals ) 568 { 569 // Get ProtectionDomain for this Principal from cache, or create new one 570 ProtectionDomain pd = m_cachedPds.get( principal ); 571 if ( pd == null ) 572 { 573 ClassLoader cl = this.getClass().getClassLoader(); 574 CodeSource cs = new CodeSource( null, (Certificate[])null ); 575 pd = new ProtectionDomain( cs, null, cl, new Principal[]{ principal } ); 576 m_cachedPds.put( principal, pd ); 577 } 578 579 // Consult the local policy and get the answer 580 if ( m_localPolicy.implies( pd, permission ) ) 581 { 582 return true; 583 } 584 } 585 return false; 586 } 587 588 /** 589 * Determines whether a Subject possesses a given "static" Permission as 590 * defined in the security policy file. This method uses standard Java 2 591 * security calls to do its work. Note that the current access control 592 * context's <code>codeBase</code> is effectively <em>this class</em>, 593 * not that of the caller. Therefore, this method will work best when what 594 * matters in the policy is <em>who</em> makes the permission check, not 595 * what the caller's code source is. Internally, this method works by 596 * executing <code>Subject.doAsPrivileged</code> with a privileged action 597 * that simply calls {@link java.security.AccessController#checkPermission(Permission)}. 598 * @see AccessController#checkPermission(java.security.Permission) . A 599 * caught exception (or lack thereof) determines whether the privilege 600 * is absent (or present). 601 * @param session the WikiSession whose permission status is being queried 602 * @param permission the Permission the Subject must possess 603 * @return <code>true</code> if the Subject possesses the permission, 604 * <code>false</code> otherwise 605 */ 606 protected boolean checkStaticPermission( final WikiSession session, final Permission permission ) 607 { 608 Boolean allowed = (Boolean) WikiSession.doPrivileged( session, new PrivilegedAction<Boolean>() 609 { 610 public Boolean run() 611 { 612 try 613 { 614 // Check the JVM-wide security policy first 615 AccessController.checkPermission( permission ); 616 return Boolean.TRUE; 617 } 618 catch( AccessControlException e ) 619 { 620 // Global policy denied the permission 621 } 622 623 // Try the local policy - check each Role/Group and User Principal 624 if ( allowedByLocalPolicy( session.getRoles(), permission ) || 625 allowedByLocalPolicy( session.getPrincipals(), permission ) ) 626 { 627 return Boolean.TRUE; 628 } 629 return Boolean.FALSE; 630 } 631 } ); 632 return allowed.booleanValue(); 633 } 634 635 /** 636 * <p>Given a supplied string representing a Principal's name from an Acl, this 637 * method resolves the correct type of Principal (role, group, or user). 638 * This method is guaranteed to always return a Principal. 639 * The algorithm is straightforward:</p> 640 * <ol> 641 * <li>If the name matches one of the built-in {@link org.apache.wiki.auth.authorize.Role} names, 642 * return that built-in Role</li> 643 * <li>If the name matches one supplied by the current 644 * {@link org.apache.wiki.auth.Authorizer}, return that Role</li> 645 * <li>If the name matches a group managed by the 646 * current {@link org.apache.wiki.auth.authorize.GroupManager}, return that Group</li> 647 * <li>Otherwise, assume that the name represents a user 648 * principal. Using the current {@link org.apache.wiki.auth.user.UserDatabase}, find the 649 * first user who matches the supplied name by calling 650 * {@link org.apache.wiki.auth.user.UserDatabase#find(String)}.</li> 651 * <li>Finally, if a user cannot be found, manufacture 652 * and return a generic {@link org.apache.wiki.auth.acl.UnresolvedPrincipal}</li> 653 * </ol> 654 * @param name the name of the Principal to resolve 655 * @return the fully-resolved Principal 656 */ 657 public Principal resolvePrincipal( String name ) 658 { 659 // Check built-in Roles first 660 Role role = new Role(name); 661 if ( Role.isBuiltInRole( role ) ) 662 { 663 return role; 664 } 665 666 // Check Authorizer Roles 667 Principal principal = m_authorizer.findRole( name ); 668 if ( principal != null ) 669 { 670 return principal; 671 } 672 673 // Check Groups 674 principal = m_engine.getGroupManager().findRole( name ); 675 if ( principal != null ) 676 { 677 return principal; 678 } 679 680 // Ok, no luck---this must be a user principal 681 Principal[] principals = null; 682 UserProfile profile = null; 683 UserDatabase db = m_engine.getUserManager().getUserDatabase(); 684 try 685 { 686 profile = db.find( name ); 687 principals = db.getPrincipals( profile.getLoginName() ); 688 for (int i = 0; i < principals.length; i++) 689 { 690 principal = principals[i]; 691 if ( principal.getName().equals( name ) ) 692 { 693 return principal; 694 } 695 } 696 } 697 catch( NoSuchPrincipalException e ) 698 { 699 // We couldn't find the user... 700 } 701 // Ok, no luck---mark this as unresolved and move on 702 return new UnresolvedPrincipal( name ); 703 } 704 705 706 // events processing ....................................................... 707 708 /** 709 * Registers a WikiEventListener with this instance. 710 * @param listener the event listener 711 */ 712 public synchronized void addWikiEventListener( WikiEventListener listener ) 713 { 714 WikiEventManager.addWikiEventListener( this, listener ); 715 } 716 717 /** 718 * Un-registers a WikiEventListener with this instance. 719 * @param listener the event listener 720 */ 721 public synchronized void removeWikiEventListener( WikiEventListener listener ) 722 { 723 WikiEventManager.removeWikiEventListener( this, listener ); 724 } 725 726 /** 727 * Fires a WikiSecurityEvent of the provided type, user, 728 * and permission to all registered listeners. 729 * 730 * @see org.apache.wiki.event.WikiSecurityEvent 731 * @param type the event type to be fired 732 * @param user the user associated with the event 733 * @param permission the permission the subject must possess 734 */ 735 protected void fireEvent( int type, Principal user, Object permission ) 736 { 737 if ( WikiEventManager.isListening(this) ) 738 { 739 WikiEventManager.fireEvent(this,new WikiSecurityEvent(this,type,user,permission)); 740 } 741 } 742 743}