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 }