blob: 16bee6ae8b052a4308970c5653fe4d188dc5ce85 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.guacamole.event;
import javax.annotation.Nonnull;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.GuacamoleResourceNotFoundException;
import org.apache.guacamole.net.auth.AuthenticationProvider;
import org.apache.guacamole.net.auth.Credentials;
import org.apache.guacamole.net.auth.Identifiable;
import org.apache.guacamole.net.auth.User;
import org.apache.guacamole.net.auth.credentials.GuacamoleInsufficientCredentialsException;
import org.apache.guacamole.net.event.ApplicationShutdownEvent;
import org.apache.guacamole.net.event.ApplicationStartedEvent;
import org.apache.guacamole.net.event.AuthenticationFailureEvent;
import org.apache.guacamole.net.event.AuthenticationRequestReceivedEvent;
import org.apache.guacamole.net.event.AuthenticationSuccessEvent;
import org.apache.guacamole.net.event.DirectoryEvent;
import org.apache.guacamole.net.event.DirectoryFailureEvent;
import org.apache.guacamole.net.event.DirectorySuccessEvent;
import org.apache.guacamole.net.event.IdentifiableObjectEvent;
import org.apache.guacamole.net.event.UserSessionInvalidatedEvent;
import org.apache.guacamole.net.event.listener.Listener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Listener that records each event that occurs in the logs, such as changes
* made to objects via the REST API.
*/
public class EventLoggingListener implements Listener {
/**
* Logger for this class.
*/
private final Logger logger = LoggerFactory.getLogger(EventLoggingListener.class);
/**
* Returns whether the given event affects the password of a User object.
*
* @param event
* The event to check.
*
* @return
* true if a user's password is specifically set or modified by the
* given event, false otherwise.
*/
private boolean isPasswordAffected(IdentifiableObjectEvent<?> event) {
Identifiable object = event.getObject();
if (!(object instanceof User))
return false;
return ((User) object).getPassword() != null;
}
/**
* Logs that an operation was performed on an object within a Directory
* successfully.
*
* @param event
* The event describing the operation successfully performed on the
* object.
*/
private void logSuccess(DirectorySuccessEvent<?> event) {
DirectoryEvent.Operation op = event.getOperation();
switch (op) {
case GET:
logger.debug("{} successfully accessed/retrieved {}", new RequestingUser(event), new AffectedObject(event));
break;
case ADD:
if (isPasswordAffected(event))
logger.info("{} successfully created {}, setting their password", new RequestingUser(event), new AffectedObject(event));
else
logger.info("{} successfully created {}", new RequestingUser(event), new AffectedObject(event));
break;
case UPDATE:
if (isPasswordAffected(event))
logger.info("{} successfully updated {}, changing their password", new RequestingUser(event), new AffectedObject(event));
else
logger.info("{} successfully updated {}", new RequestingUser(event), new AffectedObject(event));
break;
case REMOVE:
logger.info("{} successfully deleted {}", new RequestingUser(event), new AffectedObject(event));
break;
default:
logger.warn("DirectoryEvent operation type has no corresponding log message implemented: {}", op);
logger.info("{} successfully performed an unknown action on {} {}", new RequestingUser(event), new AffectedObject(event));
}
}
/**
* Logs that an operation failed to be performed on an object within a
* Directory.
*
* @param event
* The event describing the operation that failed.
*/
private void logFailure(DirectoryFailureEvent<?> event) {
DirectoryEvent.Operation op = event.getOperation();
switch (op) {
case GET:
if (event.getFailure() instanceof GuacamoleResourceNotFoundException)
logger.debug("{} failed to access/retrieve {}: {}", new RequestingUser(event), new AffectedObject(event), new Failure(event));
else
logger.info("{} failed to access/retrieve {}: {}", new RequestingUser(event), new AffectedObject(event), new Failure(event));
break;
case ADD:
logger.info("{} failed to create {}: {}", new RequestingUser(event), new AffectedObject(event), new Failure(event));
break;
case UPDATE:
logger.info("{} failed to update {}: {}", new RequestingUser(event), new AffectedObject(event), new Failure(event));
break;
case REMOVE:
logger.info("{} failed to delete {}: {}", new RequestingUser(event), new AffectedObject(event), new Failure(event));
break;
default:
logger.warn("DirectoryEvent operation type has no corresponding log message implemented: {}", op);
logger.info("{} failed to perform an unknown action on {}: {}", new RequestingUser(event), new AffectedObject(event), new Failure(event));
}
}
/**
* Logs that authentication succeeded for a user.
*
* @param event
* The event describing the authentication attempt that succeeded.
*/
private void logSuccess(AuthenticationSuccessEvent event) {
if (!event.isExistingSession())
logger.info("{} successfully authenticated from {}",
new RequestingUser(event),
new RemoteAddress(event.getCredentials()));
else
logger.debug("{} successfully re-authenticated their existing "
+ "session from {}", new RequestingUser(event),
new RemoteAddress(event.getCredentials()));
}
/**
* Logs that authentication failed for a user.
*
* @param event
* The event describing the authentication attempt that failed.
*/
private void logFailure(AuthenticationFailureEvent event) {
AuthenticationProvider authProvider = event.getAuthenticationProvider();
Credentials creds = event.getCredentials();
String username = creds.getUsername();
if (creds.isEmpty())
logger.debug("Empty authentication attempt (login screen "
+ "initialization) from {} failed: {}",
new RemoteAddress(creds), new Failure(event));
else if (username == null || username.isEmpty())
logger.debug("Anonymous authentication attempt from {} failed: {}",
new RemoteAddress(creds), new Failure(event));
else if (event.getFailure() instanceof GuacamoleInsufficientCredentialsException) {
if (authProvider != null)
logger.debug("Authentication attempt from {} for user \"{}\" "
+ "requires additional credentials to continue: {} "
+ "(requested by \"{}\")", new RemoteAddress(creds),
username, new Failure(event), authProvider.getIdentifier());
else
logger.debug("Authentication attempt from {} for user \"{}\" "
+ "requires additional credentials to continue: {}",
new RemoteAddress(creds), username, new Failure(event));
}
else {
if (authProvider != null)
logger.warn("Authentication attempt from {} for user \"{}\" "
+ "failed: {} (rejected by \"{}\")", new RemoteAddress(creds),
username, new Failure(event), authProvider.getIdentifier());
else
logger.warn("Authentication attempt from {} for user \"{}\" "
+ "failed: {}", new RemoteAddress(creds), username,
new Failure(event));
}
}
@Override
public void handleEvent(@Nonnull Object event) throws GuacamoleException {
// General object creation/modification/deletion
if (event instanceof DirectorySuccessEvent)
logSuccess((DirectorySuccessEvent<?>) event);
else if (event instanceof DirectoryFailureEvent)
logFailure((DirectoryFailureEvent<?>) event);
// Login / logout / session expiration
else if (event instanceof AuthenticationSuccessEvent)
logSuccess((AuthenticationSuccessEvent) event);
else if (event instanceof AuthenticationFailureEvent)
logFailure((AuthenticationFailureEvent) event);
else if (event instanceof UserSessionInvalidatedEvent)
logger.info("{} has logged out, or their session has expired or "
+ "been terminated.", new RequestingUser((UserSessionInvalidatedEvent) event));
else if (event instanceof AuthenticationRequestReceivedEvent)
logger.trace("Authentication request received from {}",
new RemoteAddress(((AuthenticationRequestReceivedEvent) event).getCredentials()));
// Application startup/shutdown
else if (event instanceof ApplicationStartedEvent)
logger.info("The Apache Guacamole web application has started.");
else if (event instanceof ApplicationShutdownEvent)
logger.info("The Apache Guacamole web application has shut down.");
// Unknown events
else
logger.debug("Ignoring unknown/unimplemented event type: {}",
event.getClass());
}
}