    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.authorize;
    020
    021import org.apache.wiki.api.core.Context;
    022import org.apache.wiki.api.core.Session;
    023import org.apache.wiki.api.engine.Initializable;
    024import org.apache.wiki.auth.Authorizer;
    025import org.apache.wiki.auth.NoSuchPrincipalException;
    026import org.apache.wiki.auth.WikiSecurityException;
    027import org.apache.wiki.event.WikiEventListener;
    028import org.apache.wiki.event.WikiEventManager;
    029import org.apache.wiki.event.WikiSecurityEvent;
    030
    031import javax.servlet.http.HttpServletRequest;
    032
    033
    034/**
    035 * <p>
    036 * Facade class for storing, retrieving and managing wiki groups on behalf of AuthorizationManager, JSPs and other presentation-layer
    037 * classes. GroupManager works in collaboration with a back-end {@link GroupDatabase}, which persists groups to permanent storage.
    038 * </p>
    039 * <p>
    040 * <em>Note: prior to JSPWiki 2.4.19, GroupManager was an interface; it is now a concrete, final class. The aspects of GroupManager
    041 * which previously extracted group information from storage (e.g., wiki pages) have been refactored into the GroupDatabase interface.</em>
    042 * </p>
    043 * @since 2.4.19
    044 */
    045public interface GroupManager extends Initializable, Authorizer, WikiEventListener {
    046
    047    /** Key used for adding UI messages to a user's Session. */
    048    String MESSAGES_KEY = "group";
    049
    050    String PROP_GROUPDATABASE = "jspwiki.groupdatabase";
    051
    052    /**
    053     * Returns the Group matching a given name. If the group cannot be found, this method throws a <code>NoSuchPrincipalException</code>.
    054     *
    055     * @param name the name of the group to find
    056     * @return the group
    057     * @throws NoSuchPrincipalException if the group cannot be found
    058     */
    059    Group getGroup( final String name ) throws NoSuchPrincipalException;
    060
    061    /**
    062     * Returns the current external {@link GroupDatabase} in use. This method is guaranteed to return a properly-initialized GroupDatabase,
    063     * unless it could not be initialized. In that case, this method throws a {@link org.apache.wiki.api.exceptions.WikiException}. The
    064     * GroupDatabase is lazily initialized.
    065     *
    066     * @throws org.apache.wiki.auth.WikiSecurityException if the GroupDatabase could not be initialized
    067     * @return the current GroupDatabase
    068     * @since 2.3
    069     */
    070    GroupDatabase getGroupDatabase() throws WikiSecurityException;
    071
    072    /**
    073     * <p>
    074     * Extracts group name and members from passed parameters and populates an existing Group with them. The Group will either be a copy of
    075     * an existing Group (if one can be found), or a new, unregistered Group (if not). Optionally, this method can throw a
    076     * WikiSecurityException if the Group does not yet exist in the GroupManager cache.
    077     * </p>
    078     * <p>
    079     * The <code>group</code> parameter in the HTTP request contains the Group name to look up and populate. The <code>members</code>
    080     * parameter contains the member list. If these differ from those in the existing group, the passed values override the old values.
    081     * </p>
    082     * <p>
    083     * This method does not commit the new Group to the GroupManager cache. To do that, use {@link #setGroup(Session, Group)}.
    084     * </p>
    085     * @param name the name of the group to construct
    086     * @param memberLine the line of text containing the group membership list
    087     * @param create whether this method should create a new, empty Group if one with the requested name is not found. If <code>false</code>,
    088     *            groups that do not exist will cause a <code>NoSuchPrincipalException</code> to be thrown
    089     * @return a new, populated group
    090     * @see org.apache.wiki.auth.authorize.Group#RESTRICTED_GROUPNAMES
    091     * @throws WikiSecurityException if the group name isn't allowed, or if <code>create</code> is <code>false</code>
    092     *                               and the Group named <code>name</code> does not exist
    093     */
    094    Group parseGroup( String name, String memberLine, boolean create ) throws WikiSecurityException;
    095
    096    /**
    097     * <p>
    098     * Extracts group name and members from the HTTP request and populates an existing Group with them. The Group will either be a copy of
    099     * an existing Group (if one can be found), or a new, unregistered Group (if not). Optionally, this method can throw a
    100     * WikiSecurityException if the Group does not yet exist in the GroupManager cache.
    101     * </p>
    102     * <p>
    103     * The <code>group</code> parameter in the HTTP request contains the Group name to look up and populate. The <code>members</code>
    104     * parameter contains the member list. If these differ from those in the existing group, the passed values override the old values.
    105     * </p>
    106     * <p>
    107     * This method does not commit the new Group to the GroupManager cache. To do that, use {@link #setGroup(Session, Group)}.
    108     * </p>
    109     * @param context the current wiki context
    110     * @param create whether this method should create a new, empty Group if one with the requested name is not found. If <code>false</code>,
    111     *            groups that do not exist will cause a <code>NoSuchPrincipalException</code> to be thrown
    112     * @return a new, populated group
    113     * @throws WikiSecurityException if the group name isn't allowed, or if <code>create</code> is <code>false</code>
    114     *                               and the Group does not exist
    115     */
    116    default Group parseGroup( final Context context, final boolean create ) throws WikiSecurityException {
    117        // Extract parameters
    118        final HttpServletRequest request = context.getHttpRequest();
    119        final String name = request.getParameter( "group" );
    120        final String memberLine = request.getParameter( "members" );
    121
    122        // Create the named group; we pass on any NoSuchPrincipalExceptions
    123        // that may be thrown if create == false, or WikiSecurityExceptions
    124        final Group group = parseGroup( name, memberLine, create );
    125
    126        // If no members, add the current user by default
    127        if( group.members().length == 0 ) {
    128            group.add( context.getWikiSession().getUserPrincipal() );
    129        }
    130
    131        return group;
    132    }
    133
    134    /**
    135     * Removes a named Group from the group database. If not found, throws a <code>NoSuchPrincipalException</code>. After removal, this
    136     * method will commit the delete to the back-end group database. It will also fire a
    137     * {@link org.apache.wiki.event.WikiSecurityEvent#GROUP_REMOVE} event with the GroupManager instance as the source and the Group as target.
    138     * If <code>index</code> is <code>null</code>, this method throws an {@link IllegalArgumentException}.
    139     *
    140     * @param index the group to remove
    141     * @throws WikiSecurityException if the Group cannot be removed by the back-end
    142     * @see org.apache.wiki.auth.authorize.GroupDatabase#delete(Group)
    143     */
    144    void removeGroup( final String index ) throws WikiSecurityException;
    145
    146    /**
    147     * <p>
    148     * Saves the {@link Group} created by a user in a wiki session. This method registers the Group with the GroupManager and saves it to
    149     * the back-end database. If an existing Group with the same name already exists, the new group will overwrite it. After saving the
    150     * Group, the group database changes are committed.
    151     * </p>
    152     * <p>
    153     * This method fires the following events:
    154     * </p>
    155     * <ul>
    156     * <li><strong>When creating a new Group</strong>, this method fires a {@link org.apache.wiki.event.WikiSecurityEvent#GROUP_ADD} with
    157     * the GroupManager instance as its source and the new Group as the target.</li>
    158     * <li><strong>When overwriting an existing Group</strong>, this method fires a new
    159     * {@link org.apache.wiki.event.WikiSecurityEvent#GROUP_REMOVE} with this GroupManager instance as the source, and the new Group as the
    160     * target. It then fires a {@link org.apache.wiki.event.WikiSecurityEvent#GROUP_ADD} event with the same source and target.</li>
    161     * </ul>
    162     * <p>
    163     * In addition, if the save or commit actions fail, this method will attempt to restore the older version of the wiki group if it
    164     * exists. This will result in a <code>GROUP_REMOVE</code> event (for the new version of the Group) followed by a <code>GROUP_ADD</code>
    165     * event (to indicate restoration of the old version).
    166     * </p>
    167     * <p>
    168     * This method will register the new Group with the GroupManager. For example, {@link org.apache.wiki.auth.AuthenticationManager}
    169     * attaches each Session as a GroupManager listener. Thus, the act of registering a Group with <code>setGroup</code> means that
    170     * all Sessions will automatically receive group add/change/delete events immediately.
    171     * </p>
    172     *
    173     * @param session the wiki session, which may not be <code>null</code>
    174     * @param group the Group, which may not be <code>null</code>
    175     * @throws WikiSecurityException if the Group cannot be saved by the back-end
    176     */
    177    void setGroup( final Session session, final Group group ) throws WikiSecurityException;
    178
    179    /**
    180     * Validates a Group, and appends any errors to the session errors list. Any validation errors are added to the wiki session's messages
    181     * collection (see {@link Session#getMessages()}.
    182     *
    183     * @param context the current wiki context
    184     * @param group the supplied Group
    185     */
    186    void validateGroup( final Context context, final Group group );
    187
    188    /**
    189     * Checks if a String is blank or a restricted Group name, and if it is, appends an error to the Session's message list.
    190     *
    191     * @param context the wiki context
    192     * @param name the Group name to test
    193     * @throws WikiSecurityException if <code>session</code> is <code>null</code> or the Group name is illegal
    194     * @see Group#RESTRICTED_GROUPNAMES
    195     */
    196    void checkGroupName( final Context context, final String name ) throws WikiSecurityException;
    197
    198    // events processing .......................................................
    199
    200    /**
    201     * Registers a WikiEventListener with this instance. This is a convenience method.
    202     *
    203     * @param listener the event listener
    204     */
    205    void addWikiEventListener( WikiEventListener listener );
    206
    207    /**
    208     * Un-registers a WikiEventListener with this instance. This is a convenience method.
    209     *
    210     * @param listener the event listener
    211     */
    212    void removeWikiEventListener( WikiEventListener listener );
    213
    214    /**
    215     *  Fires a WikiSecurityEvent of the provided type, Principal and target Object to all registered listeners.
    216     *
    217     * @see org.apache.wiki.event.WikiSecurityEvent
    218     * @param type       the event type to be fired
    219     * @param target     the changed Object, which may be <code>null</code>
    220     */
    221    default void fireEvent( final int type, final Object target ) {
    222        if( WikiEventManager.isListening( this ) ) {
    223            WikiEventManager.fireEvent( this, new WikiSecurityEvent( this, type, target ) );
    224        }
    225    }
    226
    227}