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