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 */
019 package org.apache.wiki.event;
020
021 import java.security.Principal;
022
023 import org.apache.commons.lang.ArrayUtils;
024 import org.apache.log4j.Level;
025 import org.apache.log4j.Logger;
026
027 import org.apache.wiki.event.WikiEvent;
028
029 /**
030 * <p>Event class for security events: login/logout, wiki group adds/changes, and
031 * authorization decisions. When a WikiSecurityEvent is constructed, the
032 * security logger {@link #log} is notified.</p>
033 * <p>These events are logged with priority <code>ERROR</code>:</p>
034 * <ul>
035 * <li>login failed - bad credential or password</li>
036 * </ul>
037 * <p>These events are logged with priority <code>WARN</code>:</p>
038 * <ul>
039 * <li>access denied</li>
040 * <li>login failed - credential expired</li>
041 * <li>login failed - account expired</li>
042 * </ul>
043 * <p>These events are logged with priority <code>INFO</code>:</p>
044 * <ul>
045 * <li>login succeeded</li>
046 * <li>logout</li>
047 * <li>user profile name changed</li>
048 * </ul>
049 * <p>These events are logged with priority <code>DEBUG</code>:</p>
050 * <ul>
051 * <li>access allowed</li>
052 * <li>add group</li>
053 * <li>remove group</li>
054 * <li>clear all groups</li>
055 * <li>add group member</li>
056 * <li>remove group member</li>
057 * <li>clear all members from group</li>
058 * </ul>
059 * @since 2.3.79
060 */
061 public final class WikiSecurityEvent extends WikiEvent
062 {
063
064 private static final long serialVersionUID = -6751950399721334496L;
065
066 /** When a user's attempts to log in as guest, via cookies, using a password or otherwise. */
067 public static final int LOGIN_INITIATED = 30;
068
069 /** When a user first accesses JSPWiki, but before logging in or setting a cookie. */
070 public static final int LOGIN_ANONYMOUS = 31;
071
072 /** When a user sets a cookie to assert their identity. */
073 public static final int LOGIN_ASSERTED = 32;
074
075 /** When a user authenticates with a username and password, or via container auth. */
076 public static final int LOGIN_AUTHENTICATED = 40;
077
078 /** When a login fails due to account expiration. */
079 public static final int LOGIN_ACCOUNT_EXPIRED = 41;
080
081 /** When a login fails due to credential expiration. */
082 public static final int LOGIN_CREDENTIAL_EXPIRED = 42;
083
084 /** When a login fails due to wrong username or password. */
085 public static final int LOGIN_FAILED = 43;
086
087 /** When a user logs out. */
088 public static final int LOGOUT = 44;
089
090 /** When a Principal should be added to the WikiSession */
091 public static final int PRINCIPAL_ADD = 35;
092
093 /** When a session expires. */
094 public static final int SESSION_EXPIRED = 45;
095
096 /** When a new wiki group is added. */
097 public static final int GROUP_ADD = 46;
098
099 /** When a wiki group is deleted. */
100 public static final int GROUP_REMOVE = 47;
101
102 /** When all wiki groups are removed from GroupDatabase. */
103 public static final int GROUP_CLEAR_GROUPS = 48;
104
105 /** When access to a resource is allowed. */
106 public static final int ACCESS_ALLOWED = 51;
107
108 /** When access to a resource is allowed. */
109 public static final int ACCESS_DENIED = 52;
110
111 /** When a user profile is saved. */
112 public static final int PROFILE_SAVE = 53;
113
114 /** When a user profile name changes. */
115 public static final int PROFILE_NAME_CHANGED = 54;
116
117 /** The security logging service. */
118 protected static final Logger log = Logger.getLogger( "SecurityLog" );
119
120 private final Principal m_principal;
121
122 private final Object m_target;
123
124 private static final int[] ERROR_EVENTS = { LOGIN_FAILED };
125
126 private static final int[] WARN_EVENTS = { LOGIN_ACCOUNT_EXPIRED,
127 LOGIN_CREDENTIAL_EXPIRED };
128
129 private static final int[] INFO_EVENTS = { LOGIN_AUTHENTICATED,
130 SESSION_EXPIRED, LOGOUT, PROFILE_NAME_CHANGED };
131
132 /**
133 * Constructs a new instance of this event type, which signals a security
134 * event has occurred. The <code>source</code> parameter is required, and
135 * may not be <code>null</code>. When the WikiSecurityEvent is
136 * constructed, the security logger {@link #log} is notified.
137 * @param src the source of the event, which can be any object: a wiki
138 * page, group or authentication/authentication/group manager.
139 * @param type the type of event
140 * @param principal the subject of the event, which may be <code>null</code>
141 * @param target the changed Object, which may be <code>null</code>
142 */
143 public WikiSecurityEvent( Object src, int type, Principal principal, Object target )
144 {
145 super( src, type );
146 if ( src == null )
147 {
148 throw new IllegalArgumentException( "Argument(s) cannot be null." );
149 }
150 this.m_principal = principal;
151 this.m_target = target;
152 if ( log.isEnabledFor( Level.ERROR ) && ArrayUtils.contains( ERROR_EVENTS, type ) )
153 {
154 log.error( this );
155 }
156 else if ( log.isEnabledFor( Level.WARN ) && ArrayUtils.contains( WARN_EVENTS, type ) )
157 {
158 log.warn( this );
159 }
160 else if ( log.isEnabledFor( Level.INFO ) && ArrayUtils.contains( INFO_EVENTS, type ) )
161 {
162 log.info( this );
163 }
164 log.debug( this );
165 }
166
167 /**
168 * Constructs a new instance of this event type, which signals a security
169 * event has occurred. The <code>source</code> parameter is required, and
170 * may not be <code>null</code>. When the WikiSecurityEvent is
171 * constructed, the security logger {@link #log} is notified.
172 * @param src the source of the event, which can be any object: a wiki
173 * page, group or authentication/authentication/group manager.
174 * @param type the type of event
175 * @param target the changed Object, which may be <code>null</code>.
176 */
177 public WikiSecurityEvent( Object src, int type, Object target )
178 {
179 this( src, type, null, target );
180 }
181
182 /**
183 * Returns the principal to whom the opeation applied, if supplied. This
184 * method may return <code>null</code>
185 * <em>— and calling methods should check for this condition</em>.
186 * @return the changed object
187 */
188 public Object getPrincipal()
189 {
190 return m_principal;
191 }
192
193 /**
194 * Returns the object that was operated on, if supplied. This method may
195 * return <code>null</code>
196 * <em>— and calling methods should check for this condition</em>.
197 * @return the changed object
198 */
199 public Object getTarget()
200 {
201 return m_target;
202 }
203
204 /**
205 * Prints a String (human-readable) representation of this object.
206 * @see java.lang.Object#toString()
207 */
208 public String toString()
209 {
210 StringBuffer msg = new StringBuffer();
211 msg.append( "WikiSecurityEvent." );
212 msg.append( eventName( getType() ) );
213 Object obj = getSrc(); // cfr. https://forums.oracle.com/forums/thread.jspa?threadID=1184115
214 msg.append( " [source=" + obj.toString() );
215 if( m_principal != null )
216 {
217 msg.append( ", princpal=" + m_principal.getClass().getName() );
218 msg.append( " " + m_principal.getName() );
219 }
220 msg.append( ", target=" + m_target );
221 msg.append( "]" );
222 return msg.toString();
223 }
224
225 /**
226 * Returns a textual representation of an event type.
227 * @param type the type
228 * @return the string representation
229 */
230 public String eventName( int type )
231 {
232 switch( type )
233 {
234 case LOGIN_AUTHENTICATED: return "LOGIN_AUTHENTICATED";
235 case LOGIN_ACCOUNT_EXPIRED: return "LOGIN_ACCOUNT_EXPIRED";
236 case LOGIN_CREDENTIAL_EXPIRED: return "LOGIN_ACCOUNT_EXPIRED";
237 case LOGIN_FAILED: return "LOGIN_FAILED";
238 case LOGOUT: return "LOGOUT";
239 case PRINCIPAL_ADD: return "PRINCIPAL_ADD";
240 case SESSION_EXPIRED: return "SESSION_EXPIRED";
241 case GROUP_ADD: return "GROUP_ADD";
242 case GROUP_REMOVE: return "GROUP_REMOVE";
243 case GROUP_CLEAR_GROUPS: return "GROUP_CLEAR_GROUPS";
244 case ACCESS_ALLOWED: return "ACCESS_ALLOWED";
245 case ACCESS_DENIED: return "ACCESS_DENIED";
246 case PROFILE_NAME_CHANGED: return "PROFILE_NAME_CHANGED";
247 case PROFILE_SAVE: return "PROFILE_SAVE";
248 default: return super.eventName();
249 }
250 }
251
252 /**
253 * Returns a human-readable description of the event type.
254 *
255 * @return a String description of the type
256 */
257 public String getTypeDescription()
258 {
259 switch ( getType() )
260 {
261 case LOGIN_AUTHENTICATED: return "login authenticated";
262 case LOGIN_ACCOUNT_EXPIRED: return "login failed: expired account";
263 case LOGIN_CREDENTIAL_EXPIRED: return "login failed: credential expired";
264 case LOGIN_FAILED: return "login failed";
265 case LOGOUT: return "user logged out";
266 case PRINCIPAL_ADD: return "new principal added";
267 case SESSION_EXPIRED: return "session expired";
268 case GROUP_ADD: return "new group added";
269 case GROUP_REMOVE: return "group removed";
270 case GROUP_CLEAR_GROUPS: return "all groups cleared";
271 case ACCESS_ALLOWED: return "access allowed";
272 case ACCESS_DENIED: return "access denied";
273 case PROFILE_NAME_CHANGED: return "user profile name changed";
274 case PROFILE_SAVE: return "user profile saved";
275 default: return super.getTypeDescription();
276 }
277 }
278
279 }