blob: 756260f1be2bbca96c98f56b18d2e6f41297343b [file] [log] [blame]
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
package org.apache.wiki;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.apache.wiki.auth.AuthenticationManager;
import org.apache.wiki.auth.GroupPrincipal;
import org.apache.wiki.auth.NoSuchPrincipalException;
import org.apache.wiki.auth.SessionMonitor;
import org.apache.wiki.auth.UserManager;
import org.apache.wiki.auth.WikiPrincipal;
import org.apache.wiki.auth.authorize.Group;
import org.apache.wiki.auth.authorize.GroupManager;
import org.apache.wiki.auth.authorize.Role;
import org.apache.wiki.auth.user.UserDatabase;
import org.apache.wiki.auth.user.UserProfile;
import org.apache.wiki.event.WikiEvent;
import org.apache.wiki.event.WikiEventListener;
import org.apache.wiki.event.WikiSecurityEvent;
import javax.security.auth.Subject;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.security.AccessControlException;
import java.security.Principal;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
/**
* <p>Represents a long-running wiki session, with an associated user Principal, user Subject, and authentication status. This class
* is initialized with minimal, default-deny values: authentication is set to <code>false</code>, and the user principal is set to
* <code>null</code>.</p>
* <p>The WikiSession class allows callers to:</p>
* <ul>
* <li>Obtain the authentication status of the user via
* {@link #isAnonymous()} and {@link #isAuthenticated()}</li>
* <li>Query the session for Principals representing the
* user's identity via {@link #getLoginPrincipal()},
* {@link #getUserPrincipal()} and {@link #getPrincipals()}</li>
* <li>Store, retrieve and clear UI messages via
* {@link #addMessage(String)}, {@link #getMessages(String)}
* and {@link #clearMessages(String)}</li>
* </ul>
* <p>To keep track of the Principals each user posseses, each WikiSession stores a JAAS Subject. Various login processes add or
* remove Principals when users authenticate or log out.</p>
* <p>WikiSession implements the {@link org.apache.wiki.event.WikiEventListener} interface and listens for group add/change/delete
* events fired by event sources the WikiSession is registered with. Normally, {@link org.apache.wiki.auth.AuthenticationManager}
* registers each WikiSession with the {@link org.apache.wiki.auth.authorize.GroupManager} so it can catch group events. Thus, when
* a user is added to a {@link org.apache.wiki.auth.authorize.Group}, a corresponding {@link org.apache.wiki.auth.GroupPrincipal} is
* injected into the Subject's Principal set. Likewise, when the user is removed from the Group or the Group is deleted, the
* GroupPrincipal is removed from the Subject. The effect that this strategy produces is extremely beneficial: when someone adds a user
* to a wiki group, that user <em>immediately</em> gains the privileges associated with that group; he or she does not need to
* re-authenticate.
* </p>
* <p>In addition to methods for examining individual <code>WikiSession</code> objects, this class also contains a number of static
* methods for managing WikiSessions for an entire wiki. These methods allow callers to find, query and remove WikiSession objects, and
* to obtain a list of the current wiki session users.</p>
* <p>WikiSession encloses a protected static class, {@link SessionMonitor}, to keep track of WikiSessions registered with each wiki.</p>
*/
public final class WikiSession implements WikiEventListener {
/** An anonymous user's session status. */
public static final String ANONYMOUS = "anonymous";
/** An asserted user's session status. */
public static final String ASSERTED = "asserted";
/** An authenticated user's session status. */
public static final String AUTHENTICATED = "authenticated";
private static final int ONE = 48;
private static final int NINE = 57;
private static final int DOT = 46;
private static final Logger log = Logger.getLogger( WikiSession.class );
private static final String ALL = "*";
private static ThreadLocal<WikiSession> c_guestSession = new ThreadLocal<>();
private final Subject m_subject = new Subject();
private final Map<String,Set<String>> m_messages = new HashMap<>();
/** The WikiEngine that created this session. */
private WikiEngine m_engine = null;
private String m_status = ANONYMOUS;
private Principal m_userPrincipal = WikiPrincipal.GUEST;
private Principal m_loginPrincipal = WikiPrincipal.GUEST;
private Locale m_cachedLocale = Locale.getDefault();
/**
* Returns <code>true</code> if one of this WikiSession's user Principals can be shown to belong to a particular wiki group. If
* the user is not authenticated, this method will always return <code>false</code>.
*
* @param group the group to test
* @return the result
*/
protected boolean isInGroup( final Group group ) {
for( final Principal principal : getPrincipals() ) {
if( isAuthenticated() && group.isMember( principal ) ) {
return true;
}
}
return false;
}
/**
* Private constructor to prevent WikiSession from being instantiated directly.
*/
private WikiSession() {
}
/**
* Returns <code>true</code> if the user is considered asserted via a session cookie; that is, the Subject contains the Principal
* Role.ASSERTED.
*
* @return Returns <code>true</code> if the user is asserted
*/
public boolean isAsserted()
{
return m_subject.getPrincipals().contains( Role.ASSERTED );
}
/**
* Returns the authentication status of the user's session. The user is considered authenticated if the Subject contains the
* Principal Role.AUTHENTICATED. If this method determines that an earlier LoginModule did not inject Role.AUTHENTICATED, it
* will inject one if the user is not anonymous <em>and</em> not asserted.
*
* @return Returns <code>true</code> if the user is authenticated
*/
public boolean isAuthenticated() {
// If Role.AUTHENTICATED is in principals set, always return true.
if ( m_subject.getPrincipals().contains( Role.AUTHENTICATED ) ) {
return true;
}
// With non-JSPWiki LoginModules, the role may not be there, so we need to add it if the user really is authenticated.
if ( !isAnonymous() && !isAsserted() ) {
m_subject.getPrincipals().add( Role.AUTHENTICATED );
return true;
}
return false;
}
/**
* <p>Determines whether the current session is anonymous. This will be true if any of these conditions are true:</p>
* <ul>
* <li>The session's Principal set contains
* {@link org.apache.wiki.auth.authorize.Role#ANONYMOUS}</li>
* <li>The session's Principal set contains
* {@link org.apache.wiki.auth.WikiPrincipal#GUEST}</li>
* <li>The Principal returned by {@link #getUserPrincipal()} evaluates
* to an IP address.</li>
* </ul>
* <p>The criteria above are listed in the order in which they are evaluated.</p>
* @return whether the current user's identity is equivalent to an IP address
*/
public boolean isAnonymous() {
final Set< Principal > principals = m_subject.getPrincipals();
return principals.contains( Role.ANONYMOUS ) ||
principals.contains( WikiPrincipal.GUEST ) ||
isIPV4Address( getUserPrincipal().getName() );
}
/**
* <p> Returns the Principal used to log in to an authenticated session. The login principal is determined by examining the
* Subject's Principal set for PrincipalWrappers or WikiPrincipals with type designator <code>LOGIN_NAME</code>; the first one
* found is the login principal. If one is not found, this method returns the first principal that isn't of type Role or
* GroupPrincipal. If neither of these conditions hold, this method returns {@link org.apache.wiki.auth.WikiPrincipal#GUEST}.
*
* @return the login Principal. If it is a PrincipalWrapper containing an externally-provided Principal, the object returned is the
* Principal, not the wrapper around it.
*/
public Principal getLoginPrincipal()
{
return m_loginPrincipal;
}
/**
* <p>Returns the primary user Principal associated with this session. The primary user principal is determined as follows:</p>
* <ol>
* <li>If the Subject's Principal set contains WikiPrincipals, the first WikiPrincipal with type designator
* <code>WIKI_NAME</code> or (alternatively) <code>FULL_NAME</code> is the primary Principal.</li>
* <li>For all other cases, the first Principal in the Subject's principal collection that that isn't of type Role or
* GroupPrincipal is the primary.</li>
* </ol>
* If no primary user Principal is found, this method returns {@link org.apache.wiki.auth.WikiPrincipal#GUEST}.
*
* @return the primary user Principal
*/
public Principal getUserPrincipal()
{
return m_userPrincipal;
}
/**
* Returns a cached Locale object for this user. It's better to use WikiContext's corresponding getBundle() method, since that
* will actually react if the user changes the locale in the middle, but if that's not available (or, for some reason, you need
* the speed), this method can also be used. The Locale expires when the WikiSession expires, and currently there is no way to
* reset the Locale.
*
* @return A cached Locale object
* @since 2.5.96
*/
public Locale getLocale()
{
return m_cachedLocale;
}
/**
* Adds a message to the generic list of messages associated with the session. These messages retain their order of insertion and
* remain until the {@link #clearMessages()} method is called.
*
* @param message the message to add; if <code>null</code> it is ignored.
*/
public void addMessage( final String message )
{
addMessage( ALL, message );
}
/**
* Adds a message to the specific set of messages associated with the session. These messages retain their order of insertion and
* remain until the {@link #clearMessages()} method is called.
*
* @param topic the topic to associate the message to;
* @param message the message to add
*/
public void addMessage( final String topic, final String message ) {
if ( topic == null ) {
throw new IllegalArgumentException( "addMessage: topic cannot be null." );
}
final Set< String > messages = m_messages.computeIfAbsent( topic, k -> new LinkedHashSet<>() );
messages.add( StringUtils.defaultString( message ) );
}
/**
* Clears all messages associated with this session.
*/
public void clearMessages()
{
m_messages.clear();
}
/**
* Clears all messages associated with a session topic.
*
* @param topic the topic whose messages should be cleared.
*/
public void clearMessages( final String topic ) {
final Set< String > messages = m_messages.get( topic );
if ( messages != null ) {
m_messages.clear();
}
}
/**
* Returns all generic messages associated with this session.
* The messages stored with the session persist throughout the
* session unless they have been reset with {@link #clearMessages()}.
* @return the current messages.
*/
public String[] getMessages()
{
return getMessages( ALL );
}
/**
* Returns all messages associated with a session topic.
* The messages stored with the session persist throughout the
* session unless they have been reset with {@link #clearMessages(String)}.
* @return the current messages.
* @param topic The topic
*/
public String[] getMessages( final String topic ) {
final Set< String > messages = m_messages.get( topic );
if( messages == null || messages.size() == 0 ) {
return new String[0];
}
return messages.toArray( new String[messages.size()] );
}
/**
* Returns all user Principals associated with this session. User principals are those in the Subject's principal collection that
* aren't of type Role or of type GroupPrincipal. This is a defensive copy.
*
* @return Returns the user principal
* @see org.apache.wiki.auth.AuthenticationManager#isUserPrincipal(Principal)
*/
public Principal[] getPrincipals() {
final ArrayList< Principal > principals = new ArrayList<>();
// Take the first non Role as the main Principal
for( final Principal principal : m_subject.getPrincipals() ) {
if ( AuthenticationManager.isUserPrincipal( principal ) ) {
principals.add( principal );
}
}
return principals.toArray( new Principal[principals.size()] );
}
/**
* Returns an array of Principal objects that represents the groups and roles that the user associated with a WikiSession possesses.
* The array is built by iterating through the Subject's Principal set and extracting all Role and GroupPrincipal objects into a
* list. The list is returned as an array sorted in the natural order implied by each Principal's <code>getName</code> method. Note
* that this method does <em>not</em> consult the external Authorizer or GroupManager; it relies on the Principals that have been
* injected into the user's Subject at login time, or after group creation/modification/deletion.
*
* @return an array of Principal objects corresponding to the roles the Subject possesses
*/
public Principal[] getRoles() {
final Set< Principal > roles = new HashSet<>();
// Add all of the Roles possessed by the Subject directly
roles.addAll( m_subject.getPrincipals( Role.class ) );
// Add all of the GroupPrincipals possessed by the Subject directly
roles.addAll( m_subject.getPrincipals( GroupPrincipal.class ) );
// Return a defensive copy
final Principal[] roleArray = roles.toArray( new Principal[roles.size()] );
Arrays.sort( roleArray, WikiPrincipal.COMPARATOR );
return roleArray;
}
/**
* Removes the wiki session associated with the user's HTTP request from the cache of wiki sessions, typically as part of a
* logout process.
*
* @param engine the wiki engine
* @param request the users's HTTP request
*/
public static void removeWikiSession( final WikiEngine engine, final HttpServletRequest request ) {
if ( engine == null || request == null ) {
throw new IllegalArgumentException( "Request or engine cannot be null." );
}
final SessionMonitor monitor = SessionMonitor.getInstance( engine );
monitor.remove( request.getSession() );
}
/**
* Returns <code>true</code> if the WikiSession's Subject possess a supplied Principal. This method eliminates the need to externally
* request and inspect the JAAS subject.
*
* @param principal the Principal to test
* @return the result
*/
public boolean hasPrincipal( final Principal principal ) {
return m_subject.getPrincipals().contains( principal );
}
/**
* Listens for WikiEvents generated by source objects such as the GroupManager. This method adds Principals to the private Subject
* managed by the WikiSession.
*
* @see org.apache.wiki.event.WikiEventListener#actionPerformed(org.apache.wiki.event.WikiEvent)
*/
@Override
public void actionPerformed( final WikiEvent event ) {
if ( event instanceof WikiSecurityEvent ) {
final WikiSecurityEvent e = (WikiSecurityEvent)event;
if ( e.getTarget() != null ) {
switch (e.getType() ) {
case WikiSecurityEvent.GROUP_ADD:
final Group groupAdd = (Group)e.getTarget();
if ( isInGroup( groupAdd ) ) {
m_subject.getPrincipals().add( groupAdd.getPrincipal() );
}
break;
case WikiSecurityEvent.GROUP_REMOVE:
final Group group = (Group)e.getTarget();
m_subject.getPrincipals().remove( group.getPrincipal() );
break;
case WikiSecurityEvent.GROUP_CLEAR_GROUPS:
m_subject.getPrincipals().removeAll( m_subject.getPrincipals( GroupPrincipal.class ) );
break;
case WikiSecurityEvent.LOGIN_INITIATED:
// Do nothing
break;
case WikiSecurityEvent.PRINCIPAL_ADD:
final WikiSession targetPA = (WikiSession)e.getTarget();
if ( this.equals( targetPA ) && m_status.equals(AUTHENTICATED) ) {
final Set<Principal> principals = m_subject.getPrincipals();
principals.add( ( Principal )e.getPrincipal() );
}
break;
case WikiSecurityEvent.LOGIN_ANONYMOUS:
final WikiSession targetLAN = (WikiSession)e.getTarget();
if( this.equals( targetLAN ) ) {
m_status = ANONYMOUS;
// Set the login/user principals and login status
final Set<Principal> principals = m_subject.getPrincipals();
m_loginPrincipal = (Principal)e.getPrincipal();
m_userPrincipal = m_loginPrincipal;
// Add the login principal to the Subject, and set the built-in roles
principals.clear();
principals.add( m_loginPrincipal );
principals.add( Role.ALL );
principals.add( Role.ANONYMOUS );
}
break;
case WikiSecurityEvent.LOGIN_ASSERTED:
final WikiSession targetLAS = (WikiSession)e.getTarget();
if ( this.equals( targetLAS ) ) {
m_status = ASSERTED;
// Set the login/user principals and login status
final Set<Principal> principals = m_subject.getPrincipals();
m_loginPrincipal = (Principal)e.getPrincipal();
m_userPrincipal = m_loginPrincipal;
// Add the login principal to the Subject, and set the built-in roles
principals.clear();
principals.add( m_loginPrincipal );
principals.add( Role.ALL );
principals.add( Role.ASSERTED );
}
break;
case WikiSecurityEvent.LOGIN_AUTHENTICATED:
final WikiSession targetLAU = (WikiSession)e.getTarget();
if ( this.equals( targetLAU ) ) {
m_status = AUTHENTICATED;
// Set the login/user principals and login status
final Set<Principal> principals = m_subject.getPrincipals();
m_loginPrincipal = (Principal)e.getPrincipal();
m_userPrincipal = m_loginPrincipal;
// Add the login principal to the Subject, and set the built-in roles
principals.clear();
principals.add( m_loginPrincipal );
principals.add( Role.ALL );
principals.add( Role.AUTHENTICATED );
// Add the user and group principals
injectUserProfilePrincipals(); // Add principals for the user profile
injectGroupPrincipals(); // Inject group principals
}
break;
case WikiSecurityEvent.PROFILE_SAVE:
final WikiSession sourcePS = e.getSrc();
if ( this.equals( sourcePS ) ) {
injectUserProfilePrincipals(); // Add principals for the user profile
injectGroupPrincipals(); // Inject group principals
}
break;
case WikiSecurityEvent.PROFILE_NAME_CHANGED:
// Refresh user principals based on new user profile
final WikiSession sourcePNC = e.getSrc();
if ( this.equals( sourcePNC ) && m_status.equals(AUTHENTICATED) ) {
// To prepare for refresh, set the new full name as the primary principal
final UserProfile[] profiles = (UserProfile[])e.getTarget();
final UserProfile newProfile = profiles[1];
if ( newProfile.getFullname() == null ) {
throw new IllegalStateException( "User profile FullName cannot be null." );
}
final Set<Principal> principals = m_subject.getPrincipals();
m_loginPrincipal = new WikiPrincipal( newProfile.getLoginName() );
// Add the login principal to the Subject, and set the built-in roles
principals.clear();
principals.add( m_loginPrincipal );
principals.add( Role.ALL );
principals.add( Role.AUTHENTICATED );
// Add the user and group principals
injectUserProfilePrincipals(); // Add principals for the user profile
injectGroupPrincipals(); // Inject group principals
}
break;
// No action, if the event is not recognized.
default: break;
}
}
}
}
/**
* Invalidates the WikiSession and resets its Subject's Principals to the equivalent of a "guest session".
*/
public void invalidate() {
m_subject.getPrincipals().clear();
m_subject.getPrincipals().add( WikiPrincipal.GUEST );
m_subject.getPrincipals().add( Role.ANONYMOUS );
m_subject.getPrincipals().add( Role.ALL );
m_userPrincipal = WikiPrincipal.GUEST;
m_loginPrincipal = WikiPrincipal.GUEST;
}
/**
* Injects GroupPrincipal objects into the user's Principal set based on the groups the user belongs to. For Groups, the algorithm
* first calls the {@link GroupManager#getRoles()} to obtain the array of GroupPrincipals the authorizer knows about. Then, the
* method {@link GroupManager#isUserInRole(WikiSession, Principal)} is called for each Principal. If the user is a member of the
* group, an equivalent GroupPrincipal is injected into the user's principal set. Existing GroupPrincipals are flushed and replaced.
* This method should generally be called after a user's {@link org.apache.wiki.auth.user.UserProfile} is saved. If the wiki session
* is null, or there is no matching user profile, the method returns silently.
*/
protected void injectGroupPrincipals() {
// Flush the existing GroupPrincipals
m_subject.getPrincipals().removeAll( m_subject.getPrincipals(GroupPrincipal.class) );
// Get the GroupManager and test for each Group
final GroupManager manager = m_engine.getGroupManager();
for( final Principal group : manager.getRoles() ) {
if ( manager.isUserInRole( this, group ) ) {
m_subject.getPrincipals().add( group );
}
}
}
/**
* Adds Principal objects to the Subject that correspond to the logged-in user's profile attributes for the wiki name, full name
* and login name. These Principals will be WikiPrincipals, and they will replace all other WikiPrincipals in the Subject. <em>Note:
* this method is never called during anonymous or asserted sessions.</em>
*/
protected void injectUserProfilePrincipals() {
// Search for the user profile
final String searchId = m_loginPrincipal.getName();
if ( searchId == null ) {
// Oh dear, this wasn't an authenticated user after all
log.info("Refresh principals failed because WikiSession had no user Principal; maybe not logged in?");
return;
}
// Look up the user and go get the new Principals
final UserDatabase database = m_engine.getUserManager().getUserDatabase();
if( database == null ) {
throw new IllegalStateException( "User database cannot be null." );
}
try {
final UserProfile profile = database.find( searchId );
final Principal[] principals = database.getPrincipals( profile.getLoginName() );
for( final Principal principal : principals ) {
// Add the Principal to the Subject
m_subject.getPrincipals().add( principal );
// Set the user principal if needed; we prefer FullName, but the WikiName will also work
final boolean isFullNamePrincipal = ( principal instanceof WikiPrincipal &&
( ( WikiPrincipal )principal ).getType().equals( WikiPrincipal.FULL_NAME ) );
if ( isFullNamePrincipal ) {
m_userPrincipal = principal;
} else if ( !( m_userPrincipal instanceof WikiPrincipal ) ) {
m_userPrincipal = principal;
}
}
} catch ( final NoSuchPrincipalException e ) {
// We will get here if the user has a principal but not a profile
// For example, it's a container-managed user who hasn't set up a profile yet
log.warn("User profile '" + searchId + "' not found. This is normal for container-auth users who haven't set up a profile yet.");
}
}
/**
* <p>Returns the status of the wiki session as a text string. Valid values are:</p>
* <ul>
* <li>{@link #AUTHENTICATED}</li>
* <li>{@link #ASSERTED}</li>
* <li>{@link #ANONYMOUS}</li>
* </ul>
* @return the user's session status
*/
public String getStatus() {
return m_status;
}
/**
* <p>Static factory method that returns the WikiSession object associated with the current HTTP request. This method looks up
* the associated HttpSession in an internal WeakHashMap and attempts to retrieve the WikiSession. If not found, one is created.
* This method is guaranteed to always return a WikiSession, although the authentication status is unpredictable until the user
* attempts to log in. If the servlet request parameter is <code>null</code>, a synthetic {@link #guestSession(WikiEngine)} is
* returned.</p>
* <p>When a session is created, this method attaches a WikiEventListener to the GroupManager so that changes to groups are detected
* automatically.</p>
*
* @param engine the wiki engine
* @param request the servlet request object
* @return the existing (or newly created) wiki session
*/
public static WikiSession getWikiSession( final WikiEngine engine, final HttpServletRequest request ) {
if ( request == null ) {
if ( log.isDebugEnabled() ) {
log.debug( "Looking up WikiSession for NULL HttpRequest: returning guestSession()" );
}
return staticGuestSession( engine );
}
// Look for a WikiSession associated with the user's Http Session and create one if it isn't there yet.
final HttpSession session = request.getSession();
final SessionMonitor monitor = SessionMonitor.getInstance( engine );
final WikiSession wikiSession = monitor.find( session );
// Attach reference to wiki engine
wikiSession.m_engine = engine;
wikiSession.m_cachedLocale = request.getLocale();
return wikiSession;
}
/**
* Static factory method that creates a new "guest" session containing a single user Principal
* {@link org.apache.wiki.auth.WikiPrincipal#GUEST}, plus the role principals {@link Role#ALL} and {@link Role#ANONYMOUS}. This
* method also adds the session as a listener for GroupManager, AuthenticationManager and UserManager events.
*
* @param engine the wiki engine
* @return the guest wiki session
*/
public static WikiSession guestSession( final WikiEngine engine ) {
final WikiSession session = new WikiSession();
session.m_engine = engine;
session.invalidate();
// Add the session as listener for GroupManager, AuthManager, UserManager events
final GroupManager groupMgr = engine.getGroupManager();
final AuthenticationManager authMgr = engine.getAuthenticationManager();
final UserManager userMgr = engine.getUserManager();
groupMgr.addWikiEventListener( session );
authMgr.addWikiEventListener( session );
userMgr.addWikiEventListener( session );
return session;
}
/**
* Returns a static guest session, which is available for this thread only. This guest session is used internally whenever
* there is no HttpServletRequest involved, but the request is done e.g. when embedding JSPWiki code.
*
* @param engine WikiEngine for this session
* @return A static WikiSession which is shared by all in this same Thread.
*/
// FIXME: Should really use WeakReferences to clean away unused sessions.
private static WikiSession staticGuestSession( final WikiEngine engine ) {
WikiSession session = c_guestSession.get();
if( session == null ) {
session = guestSession( engine );
c_guestSession.set( session );
}
return session;
}
/**
* Returns the total number of active wiki sessions for a particular wiki. This method delegates to the wiki's
* {@link SessionMonitor#sessions()} method.
*
* @param engine the wiki session
* @return the number of sessions
*/
public static int sessions( final WikiEngine engine ) {
final SessionMonitor monitor = SessionMonitor.getInstance( engine );
return monitor.sessions();
}
/**
* Returns Principals representing the current users known to a particular wiki. Each Principal will correspond to the
* value returned by each WikiSession's {@link #getUserPrincipal()} method. This method delegates to
* {@link SessionMonitor#userPrincipals()}.
*
* @param engine the wiki engine
* @return an array of Principal objects, sorted by name
*/
public static Principal[] userPrincipals( final WikiEngine engine ) {
final SessionMonitor monitor = SessionMonitor.getInstance( engine );
return monitor.userPrincipals();
}
/**
* Wrapper for
* {@link javax.security.auth.Subject#doAsPrivileged(Subject, java.security.PrivilegedExceptionAction, java.security.AccessControlContext)}
* that executes an action with the privileges posssessed by a WikiSession's Subject. The action executes with a <code>null</code>
* AccessControlContext, which has the effect of running it "cleanly" without the AccessControlContexts of the caller.
*
* @param session the wiki session
* @param action the privileged action
* @return the result of the privileged action; may be <code>null</code>
* @throws java.security.AccessControlException if the action is not permitted by the security policy
*/
public static Object doPrivileged( final WikiSession session, final PrivilegedAction<?> action ) throws AccessControlException {
return Subject.doAsPrivileged( session.m_subject, action, null );
}
/**
* Verifies whether a String represents an IPv4 address. The algorithm is
* extremely efficient and does not allocate any objects.
* @param name the address to test
* @return the result
*/
protected static boolean isIPV4Address( final String name ) {
if ( name.charAt( 0 ) == DOT || name.charAt( name.length() - 1 ) == DOT ) {
return false;
}
final int[] addr = new int[]
{ 0, 0, 0, 0 };
int currentOctet = 0;
for( int i = 0; i < name.length(); i++ ) {
final int ch = name.charAt( i );
final boolean isDigit = ch >= ONE && ch <= NINE;
final boolean isDot = ch == DOT;
if ( !isDigit && !isDot ) {
return false;
}
if( isDigit ) {
addr[currentOctet] = 10 * addr[currentOctet] + ( ch - ONE );
if ( addr[currentOctet] > 255 ) {
return false;
}
} else if( name.charAt( i - 1 ) == DOT ) {
return false;
} else {
currentOctet++;
}
}
return currentOctet == 3;
}
}