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