package org.sigmah.server.dispatch;
/*
* #%L
* Sigmah
* %%
* Copyright (C) 2010 - 2016 URD
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program. If not, see
* <http://www.gnu.org/licenses/gpl-3.0.html>.
* #L%
*/
import javax.validation.ConstraintViolationException;
import org.sigmah.client.security.SecureDispatchAsync.CommandExecution;
import org.sigmah.client.security.SecureDispatchService;
import org.sigmah.server.domain.User;
import org.sigmah.server.security.SecureSessionValidator;
import org.sigmah.server.security.SecureSessionValidator.Access;
import org.sigmah.server.servlet.util.Servlets;
import org.sigmah.shared.command.base.Command;
import org.sigmah.shared.command.result.Result;
import org.sigmah.shared.dispatch.CommandException;
import org.sigmah.shared.dispatch.DispatchException;
import org.sigmah.shared.dispatch.FunctionalException;
import org.sigmah.shared.security.InvalidSessionException;
import org.sigmah.shared.security.UnauthorizedAccessException;
import org.sigmah.shared.validation.ValidationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import com.google.inject.Inject;
import com.google.inject.Singleton;
/**
* Abstract secure dispatch servlet.
*
* @author Denis Colliot (dcolliot@ideia.fr)
*/
@Singleton
public class SecureDispatchServlet extends RemoteServiceServlet implements SecureDispatchService {
/**
* Serial version UID.
*/
private static final long serialVersionUID = 8811128792493692516L;
/**
* Logger.
*/
private static final Logger LOG = LoggerFactory.getLogger(SecureDispatchServlet.class);
/**
* Injected {@link SecureSessionValidator} instance.
*/
private final SecureSessionValidator sessionValidator;
/**
* Injected server-side {@link Dispatch} instance.
*/
private final Dispatch dispatch;
/**
* Initializes the {@code SecureDispatchServlet} that uses injected arguments.
*
* @param sessionValidator
* The secure session validator service.
* @param dispatch
* The dispatch service.
*/
@Inject
public SecureDispatchServlet(final SecureSessionValidator sessionValidator, final Dispatch dispatch) {
this.sessionValidator = sessionValidator;
this.dispatch = dispatch;
}
/**
* {@inheritDoc}
*/
@Override
public final void log(final String msg) {
this.log(msg, null);
}
/**
* {@inheritDoc}
*/
@Override
public final void log(final String message, final Throwable t) {
if (t != null) {
if (LOG.isErrorEnabled()) {
LOG.error(message, t);
}
} else {
if (LOG.isDebugEnabled()) {
LOG.debug(message);
}
}
}
/**
* {@inheritDoc}
*/
@Override
public <C extends Command<R>, R extends Result> R execute(final CommandExecution<C, R> commandExecution) throws DispatchException {
if (LOG.isTraceEnabled()) {
LOG.trace("Executing dispatch command.");
}
if (sessionValidator == null) {
throw new CommandException("No session validator found for servlet '" + getServletName() + "'. Please verify your server-side configuration.");
}
if (dispatch == null) {
throw new CommandException("No dispatch found for servlet '" + getServletName() + "'. Please verify your server-side configuration.");
}
User user = null;
final String authToken = commandExecution.getAuthenticationToken();
// Roles validation case.
try {
// Validates the user session and user access.
final Access access = sessionValidator.validate(authToken, commandExecution);
user = access.getUser();
switch (access.getAccessType()) {
case INVALID_SESSION:
if (LOG.isDebugEnabled()) {
LOG.debug("COMMAND EXECUTION FAILED - Command execution: '{}' ; User: '{}' ; Error: Invalid auth token '{}'.", commandExecution,
Servlets.logUser(user), authToken);
}
throw new InvalidSessionException();
case UNAUTHORIZED_ACCESS:
if (LOG.isDebugEnabled()) {
LOG
.debug("COMMAND EXECUTION FAILED - Command execution: '{}' ; User: '{}' ; Error: Unauthorized access.", commandExecution, Servlets.logUser(user));
}
throw new UnauthorizedAccessException();
default:
// Access granted.
if (LOG.isTraceEnabled()) {
LOG.trace("COMMAND EXECUTION GRANTED - Command execution: '{}' ; User: '{}'.", commandExecution, Servlets.logUser(user));
}
// Command execution.
return dispatch.execute(commandExecution, user, getThreadLocalRequest());
}
} catch (final FunctionalException e) {
// Functional exception.
if (LOG.isWarnEnabled()) {
LOG.warn(
"COMMAND EXECUTION ABORTED: A functional exception has been raised - Command execution: '"
+ commandExecution
+ "' ; User: '"
+ Servlets.logUser(user)
+ "'.", e);
}
throw e;
} catch (final CommandException e) {
// Command execution exception.
if (LOG.isErrorEnabled()) {
LOG.error("COMMAND EXECUTION FAILED - Command execution: '" + commandExecution + "' ; User: '" + Servlets.logUser(user) + "'.", e);
}
throw e;
} catch (final ConstraintViolationException e) {
// Bean validation failed.
if (LOG.isErrorEnabled()) {
LOG.error("COMMAND EXECUTION FAILED - Command execution: '"
+ commandExecution
+ "' ; User: '"
+ Servlets.logUser(user)
+ "' ; Error: A bean validation failed while executing '"
+ commandExecution.getCommand()
+ "'. Consider performing the validation on client-side.\n"
+ Servlets.logConstraints(e.getConstraintViolations()), e);
}
throw new ValidationException("A bean validation failed while executing '" + commandExecution.getCommand() + "'.", e);
} catch (final Throwable e) {
// Server unknown error.
if (LOG.isErrorEnabled()) {
LOG.error("COMMAND EXECUTION FAILED - Command execution: '"
+ commandExecution
+ "' ; User: '"
+ Servlets.logUser(user)
+ "' ; RuntimeException while executing.", e);
}
throw new CommandException("Server error.", e);
}
}
}