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>&#8212; 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>&#8212; 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    }