    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.api.core;
    020
    021import org.apache.wiki.event.WikiEventListener;
    022
    023import javax.security.auth.Subject;
    024import java.security.AccessControlException;
    025import java.security.Principal;
    026import java.security.PrivilegedAction;
    027import java.util.Locale;
    028
    029
    030/**
    031 * <p>Represents a long-running wiki session, with an associated user Principal, user Subject, and authentication status. The session
    032 * is initialized with minimal, default-deny values: authentication is set to <code>false</code>, and the user principal is set to
    033 * <code>null</code>.</p>
    034 * <p>The Session allows callers to:</p>
    035 * <ul>
    036 *   <li>Obtain the authentication status of the user via
    037 *     {@link #isAnonymous()} and {@link #isAuthenticated()}</li>
    038 *   <li>Query the session for Principals representing the
    039 *     user's identity via {@link #getLoginPrincipal()},
    040 *     {@link #getUserPrincipal()} and {@link #getPrincipals()}</li>
    041 *   <li>Store, retrieve and clear UI messages via
    042 *     {@link #addMessage(String)}, {@link #getMessages(String)}
    043 *     and {@link #clearMessages(String)}</li>
    044 * </ul>
    045 * <p>To keep track of the Principals each user possesses, each Session stores a JAAS Subject. Various login processes add or
    046 * remove Principals when users authenticate or log out.</p>
    047 * <p>Session extends the {@link org.apache.wiki.event.WikiEventListener} interface and listens for group add/change/delete
    048 * events fired by event sources the Session is registered with: {@link org.apache.wiki.auth.AuthenticationManager},
    049 * {@link org.apache.wiki.auth.UserManager} and {@link org.apache.wiki.auth.authorize.GroupManager}, so it can catch group events. Thus,
    050 * when a user is added to a {@link org.apache.wiki.auth.authorize.Group}, a corresponding {@link org.apache.wiki.auth.GroupPrincipal} is
    051 * injected into the Subject's Principal set. Likewise, when the user is removed from the Group or the Group is deleted, the
    052 * GroupPrincipal is removed from the Subject. The effect that this strategy produces is extremely beneficial: when someone adds a user
    053 * to a wiki group, that user <em>immediately</em> gains the privileges associated with that group; he or she does not need to
    054 * re-authenticate.
    055 * </p>
    056 * <p>In addition to methods for examining individual <code>Session</code> objects, this class also contains a number of static
    057 * methods for managing Sessions for an entire wiki. These methods allow callers to find, query and remove Session objects, and
    058 * to obtain a list of the current wiki session users.</p>
    059 */
    060public interface Session extends WikiEventListener {
    061
    062    /** An anonymous user's session status. */
    063    String  ANONYMOUS = "anonymous";
    064
    065    /** An asserted user's session status. */
    066    String  ASSERTED = "asserted";
    067
    068    /** An authenticated user's session status. */
    069    String  AUTHENTICATED = "authenticated";
    070
    071    /**
    072     * Returns <code>true</code> if the user is considered asserted via a session cookie; that is, the Subject contains the Principal
    073     * Role.ASSERTED.
    074     *
    075     * @return Returns <code>true</code> if the user is asserted
    076     */
    077    boolean isAsserted();
    078
    079    /**
    080     * Returns the authentication status of the user's session. The user is considered authenticated if the Subject contains the
    081     * Principal Role.AUTHENTICATED. If this method determines that an earlier LoginModule did not inject Role.AUTHENTICATED, it
    082     * will inject one if the user is not anonymous <em>and</em> not asserted.
    083     *
    084     * @return Returns <code>true</code> if the user is authenticated
    085     */
    086    boolean isAuthenticated();
    087
    088    /**
    089     * <p>Determines whether the current session is anonymous. This will be true if any of these conditions are true:</p>
    090     * <ul>
    091     *   <li>The session's Principal set contains {@link org.apache.wiki.auth.authorize.Role#ANONYMOUS}</li>
    092     *   <li>The session's Principal set contains {@link org.apache.wiki.auth.WikiPrincipal#GUEST}</li>
    093     *   <li>The Principal returned by {@link #getUserPrincipal()} evaluates to an IP address.</li>
    094     * </ul>
    095     * <p>The criteria above are listed in the order in which they are evaluated.</p>
    096     * @return whether the current user's identity is equivalent to an IP address
    097     */
    098    boolean isAnonymous();
    099
    100    /**
    101     * <p> Returns the Principal used to log in to an authenticated session. The login principal is determined by examining the
    102     * Subject's Principal set for PrincipalWrappers or WikiPrincipals with type designator <code>LOGIN_NAME</code>; the first one
    103     * found is the login principal. If one is not found, this method returns the first principal that isn't of type Role or
    104     * GroupPrincipal. If neither of these conditions hold, this method returns
    105     * {@link org.apache.wiki.auth.WikiPrincipal#GUEST}.
    106     *
    107     * @return the login Principal. If it is a PrincipalWrapper containing an externally-provided Principal, the object returned is the
    108     * Principal, not the wrapper around it.
    109     */
    110    Principal getLoginPrincipal();
    111
    112    /**
    113     * <p>Returns the primary user Principal associated with this session. The primary user principal is determined as follows:</p>
    114     * <ol>
    115     *     <li>If the Subject's Principal set contains WikiPrincipals, the first WikiPrincipal with type designator
    116     *         <code>WIKI_NAME</code> or (alternatively) <code>FULL_NAME</code> is the primary Principal.</li>
    117     *     <li>For all other cases, the first Principal in the Subject's principal collection that that isn't of type Role or
    118     *         GroupPrincipal is the primary.</li>
    119     * </ol>
    120     * If no primary user Principal is found, this method returns {@link org.apache.wiki.auth.WikiPrincipal#GUEST}.
    121     *
    122     * @return the primary user Principal
    123     */
    124    Principal getUserPrincipal();
    125
    126    /**
    127     * Returns the CSRF protection Token associated with this wiki session.
    128     * @return the CSRF protection Token associated with this wiki session.
    129     */
    130    String antiCsrfToken();
    131
    132    /**
    133     *  Returns a cached Locale object for this user.  It's better to use WikiContext's corresponding getBundle() method, since that
    134     *  will actually react if the user changes the locale in the middle, but if that's not available (or, for some reason, you need
    135     *  the speed), this method can also be used.  The Locale expires when the Session expires, and currently there is no way to
    136     *  reset the Locale.
    137     *
    138     *  @return A cached Locale object
    139     *  @since 2.5.96
    140     */
    141    Locale getLocale();
    142
    143    /**
    144     * Adds a message to the generic list of messages associated with the session. These messages retain their order of insertion and
    145     * remain until the {@link #clearMessages()} method is called.
    146     *
    147     * @param message the message to add; if <code>null</code> it is ignored.
    148     */
    149    void addMessage( String message );
    150
    151    /**
    152     * Adds a message to the specific set of messages associated with the session. These messages retain their order of insertion and
    153     * remain until the {@link #clearMessages()} method is called.
    154     *
    155     * @param topic the topic to associate the message to;
    156     * @param message the message to add
    157     */
    158    void addMessage( String topic, String message );
    159
    160    /**
    161     * Clears all messages associated with this session.
    162     */
    163    void clearMessages();
    164
    165    /**
    166     * Clears all messages associated with a session topic.
    167     *
    168     * @param topic the topic whose messages should be cleared.
    169     */
    170    void clearMessages( String topic );
    171
    172    /**
    173     * Returns all generic messages associated with this session. The messages stored with the session persist throughout the
    174     * session unless they have been reset with {@link #clearMessages()}.
    175     *
    176     * @return the current messages.
    177     */
    178    String[] getMessages();
    179
    180    /**
    181     * Returns all messages associated with a session topic. The messages stored with the session persist throughout the
    182     * session unless they have been reset with {@link #clearMessages(String)}.
    183     *
    184     * @return the current messages.
    185     * @param topic The topic
    186     */
    187    String[] getMessages( String topic );
    188
    189    /**
    190     * Returns all user Principals associated with this session. User principals are those in the Subject's principal collection that
    191     * aren't of type Role or of type GroupPrincipal. This is a defensive copy.
    192     *
    193     * @return Returns the user principal
    194     * @see org.apache.wiki.auth.AuthenticationManager#isUserPrincipal(Principal)
    195     */
    196    Principal[] getPrincipals();
    197
    198    /**
    199     * Returns an array of Principal objects that represents the groups and roles that the user associated with a Session possesses.
    200     * The array is built by iterating through the Subject's Principal set and extracting all Role and GroupPrincipal objects into a
    201     * list. The list is returned as an array sorted in the natural order implied by each Principal's <code>getName</code> method. Note
    202     * that this method does <em>not</em> consult the external Authorizer or GroupManager; it relies on the Principals that have been
    203     * injected into the user's Subject at login time, or after group creation/modification/deletion.
    204     *
    205     * @return an array of Principal objects corresponding to the roles the Subject possesses
    206     */
    207    Principal[] getRoles();
    208
    209    /**
    210     * Returns <code>true</code> if the Session's Subject possess a supplied Principal. This method eliminates the need to externally
    211     * request and inspect the JAAS subject.
    212     *
    213     * @param principal the Principal to test
    214     * @return the result
    215     */
    216    boolean hasPrincipal( Principal principal );
    217
    218    /** Invalidates the Session and resets its Subject's Principals to the equivalent of a "guest session". */
    219    void invalidate();
    220
    221    /**
    222     * <p>Returns the status of the wiki session as a text string. Valid values are:</p>
    223     * <ul>
    224     *   <li>{@link #AUTHENTICATED}</li>
    225     *   <li>{@link #ASSERTED}</li>
    226     *   <li>{@link #ANONYMOUS}</li>
    227     * </ul>
    228     *
    229     * @return the user's session status
    230     */
    231    String getStatus();
    232
    233    /**
    234     * Returns the {@link Subject} associated to the session.
    235     *
    236     * @return {@link Subject} associated to the session.
    237     */
    238    Subject getSubject();
    239
    240    /**
    241     * Wrapper for {@link Subject#doAsPrivileged(Subject, PrivilegedAction, java.security.AccessControlContext)}
    242     * that executes an action with the privileges possessed by a Session's Subject. The action executes with a <code>null</code>
    243     * AccessControlContext, which has the effect of running it "cleanly" without the AccessControlContexts of the caller.
    244     *
    245     * @param session the wiki session
    246     * @param action the privileged action
    247     * @return the result of the privileged action; may be <code>null</code>
    248     * @throws java.security.AccessControlException if the action is not permitted by the security policy
    249     */
    250    static Object doPrivileged( final Session session, final PrivilegedAction<?> action ) throws AccessControlException {
    251        return Subject.doAsPrivileged( session.getSubject(), action, null );
    252    }
    253
    254}