| /* |
| 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.auth.authorize; |
| |
| import org.apache.commons.lang3.ArrayUtils; |
| import org.apache.wiki.WikiContext; |
| import org.apache.wiki.api.core.Session; |
| import org.apache.wiki.auth.Authorizer; |
| import org.apache.wiki.auth.NoSuchPrincipalException; |
| import org.apache.wiki.auth.WikiSecurityException; |
| import org.apache.wiki.event.WikiEventListener; |
| import org.apache.wiki.event.WikiEventManager; |
| import org.apache.wiki.event.WikiSecurityEvent; |
| import org.apache.wiki.ui.InputValidator; |
| |
| import javax.servlet.http.HttpServletRequest; |
| import java.security.Principal; |
| |
| |
| /** |
| * <p> |
| * Facade class for storing, retrieving and managing wiki groups on behalf of AuthorizationManager, JSPs and other presentation-layer |
| * classes. GroupManager works in collaboration with a back-end {@link GroupDatabase}, which persists groups to permanent storage. |
| * </p> |
| * <p> |
| * <em>Note: prior to JSPWiki 2.4.19, GroupManager was an interface; it is now a concrete, final class. The aspects of GroupManager |
| * which previously extracted group information from storage (e.g., wiki pages) have been refactored into the GroupDatabase interface.</em> |
| * </p> |
| * @since 2.4.19 |
| */ |
| public interface GroupManager extends Authorizer, WikiEventListener { |
| |
| /** Key used for adding UI messages to a user's Session. */ |
| String MESSAGES_KEY = "group"; |
| |
| String PROP_GROUPDATABASE = "jspwiki.groupdatabase"; |
| |
| /** |
| * Returns the Group matching a given name. If the group cannot be found, this method throws a <code>NoSuchPrincipalException</code>. |
| * |
| * @param name the name of the group to find |
| * @return the group |
| * @throws NoSuchPrincipalException if the group cannot be found |
| */ |
| Group getGroup( final String name ) throws NoSuchPrincipalException; |
| |
| /** |
| * Returns the current external {@link GroupDatabase} in use. This method is guaranteed to return a properly-initialized GroupDatabase, |
| * unless it could not be initialized. In that case, this method throws a {@link org.apache.wiki.api.exceptions.WikiException}. The |
| * GroupDatabase is lazily initialized. |
| * |
| * @throws org.apache.wiki.auth.WikiSecurityException if the GroupDatabase could not be initialized |
| * @return the current GroupDatabase |
| * @since 2.3 |
| */ |
| GroupDatabase getGroupDatabase() throws WikiSecurityException; |
| |
| /** |
| * <p> |
| * Extracts group name and members from passed parameters and populates an existing Group with them. The Group will either be a copy of |
| * an existing Group (if one can be found), or a new, unregistered Group (if not). Optionally, this method can throw a |
| * WikiSecurityException if the Group does not yet exist in the GroupManager cache. |
| * </p> |
| * <p> |
| * The <code>group</code> parameter in the HTTP request contains the Group name to look up and populate. The <code>members</code> |
| * parameter contains the member list. If these differ from those in the existing group, the passed values override the old values. |
| * </p> |
| * <p> |
| * This method does not commit the new Group to the GroupManager cache. To do that, use {@link #setGroup(Session, Group)}. |
| * </p> |
| * @param name the name of the group to construct |
| * @param memberLine the line of text containing the group membership list |
| * @param create whether this method should create a new, empty Group if one with the requested name is not found. If <code>false</code>, |
| * groups that do not exist will cause a <code>NoSuchPrincipalException</code> to be thrown |
| * @return a new, populated group |
| * @see org.apache.wiki.auth.authorize.Group#RESTRICTED_GROUPNAMES |
| * @throws WikiSecurityException if the group name isn't allowed, or if <code>create</code> is <code>false</code> |
| * and the Group named <code>name</code> does not exist |
| */ |
| Group parseGroup( String name, String memberLine, boolean create ) throws WikiSecurityException; |
| |
| /** |
| * <p> |
| * Extracts group name and members from the HTTP request and populates an existing Group with them. The Group will either be a copy of |
| * an existing Group (if one can be found), or a new, unregistered Group (if not). Optionally, this method can throw a |
| * WikiSecurityException if the Group does not yet exist in the GroupManager cache. |
| * </p> |
| * <p> |
| * The <code>group</code> parameter in the HTTP request contains the Group name to look up and populate. The <code>members</code> |
| * parameter contains the member list. If these differ from those in the existing group, the passed values override the old values. |
| * </p> |
| * <p> |
| * This method does not commit the new Group to the GroupManager cache. To do that, use {@link #setGroup(Session, Group)}. |
| * </p> |
| * @param context the current wiki context |
| * @param create whether this method should create a new, empty Group if one with the requested name is not found. If <code>false</code>, |
| * groups that do not exist will cause a <code>NoSuchPrincipalException</code> to be thrown |
| * @return a new, populated group |
| * @throws WikiSecurityException if the group name isn't allowed, or if <code>create</code> is <code>false</code> |
| * and the Group does not exist |
| */ |
| default Group parseGroup( final WikiContext context, final boolean create ) throws WikiSecurityException { |
| // Extract parameters |
| final HttpServletRequest request = context.getHttpRequest(); |
| final String name = request.getParameter( "group" ); |
| final String memberLine = request.getParameter( "members" ); |
| |
| // Create the named group; we pass on any NoSuchPrincipalExceptions |
| // that may be thrown if create == false, or WikiSecurityExceptions |
| final Group group = parseGroup( name, memberLine, create ); |
| |
| // If no members, add the current user by default |
| if( group.members().length == 0 ) { |
| group.add( context.getWikiSession().getUserPrincipal() ); |
| } |
| |
| return group; |
| } |
| |
| /** |
| * Removes a named Group from the group database. If not found, throws a <code>NoSuchPrincipalException</code>. After removal, this |
| * method will commit the delete to the back-end group database. It will also fire a |
| * {@link org.apache.wiki.event.WikiSecurityEvent#GROUP_REMOVE} event with the GroupManager instance as the source and the Group as target. |
| * If <code>index</code> is <code>null</code>, this method throws an {@link IllegalArgumentException}. |
| * |
| * @param index the group to remove |
| * @throws WikiSecurityException if the Group cannot be removed by the back-end |
| * @see org.apache.wiki.auth.authorize.GroupDatabase#delete(Group) |
| */ |
| void removeGroup( final String index ) throws WikiSecurityException; |
| |
| /** |
| * <p> |
| * Saves the {@link Group} created by a user in a wiki session. This method registers the Group with the GroupManager and saves it to |
| * the back-end database. If an existing Group with the same name already exists, the new group will overwrite it. After saving the |
| * Group, the group database changes are committed. |
| * </p> |
| * <p> |
| * This method fires the following events: |
| * </p> |
| * <ul> |
| * <li><strong>When creating a new Group</strong>, this method fires a {@link org.apache.wiki.event.WikiSecurityEvent#GROUP_ADD} with |
| * the GroupManager instance as its source and the new Group as the target.</li> |
| * <li><strong>When overwriting an existing Group</strong>, this method fires a new |
| * {@link org.apache.wiki.event.WikiSecurityEvent#GROUP_REMOVE} with this GroupManager instance as the source, and the new Group as the |
| * target. It then fires a {@link org.apache.wiki.event.WikiSecurityEvent#GROUP_ADD} event with the same source and target.</li> |
| * </ul> |
| * <p> |
| * In addition, if the save or commit actions fail, this method will attempt to restore the older version of the wiki group if it |
| * 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> |
| * event (to indicate restoration of the old version). |
| * </p> |
| * <p> |
| * This method will register the new Group with the GroupManager. For example, {@link org.apache.wiki.auth.AuthenticationManager} |
| * attaches each Session as a GroupManager listener. Thus, the act of registering a Group with <code>setGroup</code> means that |
| * all Sessions will automatically receive group add/change/delete events immediately. |
| * </p> |
| * |
| * @param session the wiki session, which may not be <code>null</code> |
| * @param group the Group, which may not be <code>null</code> |
| * @throws WikiSecurityException if the Group cannot be saved by the back-end |
| */ |
| void setGroup( final Session session, final Group group ) throws WikiSecurityException; |
| |
| /** |
| * Validates a Group, and appends any errors to the session errors list. Any validation errors are added to the wiki session's messages |
| * collection (see {@link Session#getMessages()}. |
| * |
| * @param context the current wiki context |
| * @param group the supplied Group |
| */ |
| default void validateGroup( final WikiContext context, final Group group ) { |
| final InputValidator validator = new InputValidator( MESSAGES_KEY, context ); |
| |
| // Name cannot be null or one of the restricted names |
| try { |
| checkGroupName( context, group.getName() ); |
| } catch( final WikiSecurityException e ) { |
| } |
| |
| // Member names must be "safe" strings |
| final Principal[] members = group.members(); |
| for( final Principal member : members ) { |
| validator.validateNotNull( member.getName(), "Full name", InputValidator.ID ); |
| } |
| } |
| |
| |
| /** |
| * Checks if a String is blank or a restricted Group name, and if it is, appends an error to the Session's message list. |
| * |
| * @param context the wiki context |
| * @param name the Group name to test |
| * @throws WikiSecurityException if <code>session</code> is <code>null</code> or the Group name is illegal |
| * @see Group#RESTRICTED_GROUPNAMES |
| */ |
| default void checkGroupName( final WikiContext context, final String name ) throws WikiSecurityException { |
| // TODO: groups cannot have the same name as a user |
| |
| // Name cannot be null |
| final InputValidator validator = new InputValidator( MESSAGES_KEY, context ); |
| validator.validateNotNull( name, "Group name" ); |
| |
| // Name cannot be one of the restricted names either |
| if( ArrayUtils.contains( Group.RESTRICTED_GROUPNAMES, name ) ) { |
| throw new WikiSecurityException( "The group name '" + name + "' is illegal. Choose another." ); |
| } |
| } |
| |
| // events processing ....................................................... |
| |
| /** |
| * Registers a WikiEventListener with this instance. This is a convenience method. |
| * |
| * @param listener the event listener |
| */ |
| void addWikiEventListener( WikiEventListener listener ); |
| |
| /** |
| * Un-registers a WikiEventListener with this instance. This is a convenience method. |
| * |
| * @param listener the event listener |
| */ |
| void removeWikiEventListener( WikiEventListener listener ); |
| |
| /** |
| * Fires a WikiSecurityEvent of the provided type, Principal and target Object to all registered listeners. |
| * |
| * @see org.apache.wiki.event.WikiSecurityEvent |
| * @param type the event type to be fired |
| * @param target the changed Object, which may be <code>null</code> |
| */ |
| default void fireEvent( final int type, final Object target ) { |
| if( WikiEventManager.isListening( this ) ) { |
| WikiEventManager.fireEvent( this, new WikiSecurityEvent( this, type, target ) ); |
| } |
| } |
| |
| } |