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