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