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