package org.activityinfo.server.endpoint.gwtrpc;
/*
* #%L
* ActivityInfo Server
* %%
* Copyright (C) 2009 - 2013 UNICEF
* %%
* 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 com.google.gwt.user.server.rpc.RemoteServiceServlet;
import com.google.gwt.user.server.rpc.SerializationPolicy;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Singleton;
import org.activityinfo.legacy.shared.command.Command;
import org.activityinfo.legacy.shared.command.RemoteCommandService;
import org.activityinfo.legacy.shared.command.result.CommandResult;
import org.activityinfo.legacy.shared.exception.CommandException;
import org.activityinfo.legacy.shared.exception.InvalidAuthTokenException;
import org.activityinfo.model.auth.AnonymousUser;
import org.activityinfo.model.auth.AuthenticatedUser;
import org.activityinfo.server.authentication.ServerSideAuthProvider;
import org.activityinfo.server.database.hibernate.entity.DomainFilters;
import org.activityinfo.server.database.hibernate.entity.User;
import org.activityinfo.server.util.logging.LogException;
import javax.persistence.EntityManager;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Process command objects from the client and returns CommandResults.
* <p/>
* This servlet is at the heart of the command execution pipeline, but delegates
* all logic processing to the
* {@link org.activityinfo.server.command.handler.CommandHandler} corresponding
* to the given {@link org.activityinfo.legacy.shared.command.Command}s.
* <p/>
* CommandHandlers are loaded based on name from the
* org.activityinfo.server.command.handler package.
* <p/>
* E.g. UpdateEntity =>
* org.activityinfo.server.command.handler.UpdateEntityHandler
*/
@Singleton
public class CommandServlet extends RemoteServiceServlet implements RemoteCommandService {
private static final Logger LOGGER = Logger.getLogger(CommandServlet.class.getName());
@Inject
private Injector injector;
@Inject
private ServerSideAuthProvider authProvider;
@Inject(optional = true)
private PersistentPolicyProvider policyProvider;
@Override @LogException
public List<CommandResult> execute(String authToken, List<Command> commands) throws CommandException {
if (!checkAuthentication(authToken)) {
throw new InvalidAuthTokenException("Auth Tokens do not match, possible XSRF attack");
}
try {
return handleCommands(commands);
} catch (Exception caught) {
throw new CommandException();
}
}
public CommandResult execute(String authToken, Command command) throws CommandException {
if (!checkAuthentication(authToken)) {
throw new InvalidAuthTokenException("Auth Tokens do not match, possible XSRF attack");
}
applyUserFilters();
return handleCommand(command);
}
/**
* Publicly visible for testing *
*/
@LogException
public List<CommandResult> handleCommands(List<Command> commands) {
applyUserFilters();
List<CommandResult> results = new ArrayList<CommandResult>();
for (Command command : commands) {
LOGGER.log(Level.INFO, authProvider.get().getEmail() + ": " + command.getClass().getSimpleName());
try {
results.add(handleCommand(command));
} catch (CommandException e) {
results.add(e);
}
}
return results;
}
private void applyUserFilters() {
EntityManager em = injector.getInstance(EntityManager.class);
User user = em.getReference(User.class, authProvider.get().getUserId());
DomainFilters.applyUserFilter(user, em);
}
@LogException(emailAlert = true)
protected CommandResult handleCommand(Command command) throws CommandException {
RemoteExecutionContext context = null;
try {
long timeStart = System.currentTimeMillis();
context = new RemoteExecutionContext(injector);
CommandResult result = context.startExecute(command);
long timeElapsed = System.currentTimeMillis() - timeStart;
if (timeElapsed > 1000) {
LOGGER.warning("Command " + command.toString() + " completed in " + timeElapsed + "ms");
}
return result;
} catch (CommandException e) {
throw e;
} catch (Exception e) {
throw new CommandException(command, context, e);
}
}
@Override
public void log(String message, Throwable t) {
super.log(message, t);
LOGGER.log(Level.SEVERE, message, t);
}
@Override
public void log(String msg) {
super.log(msg);
LOGGER.log(Level.INFO, msg);
}
@Override
protected SerializationPolicy doGetSerializationPolicy(HttpServletRequest request,
String moduleBaseURL,
String strongName) {
if (policyProvider == null) {
return super.doGetSerializationPolicy(request, moduleBaseURL, strongName);
} else {
return policyProvider.getSerializationPolicy(moduleBaseURL, strongName);
}
}
private boolean checkAuthentication(String authToken) {
if (authToken.equals(AnonymousUser.AUTHTOKEN)) {
authProvider.set(AuthenticatedUser.getAnonymous());
} else {
// TODO(alex): renable this check once the authToken has been
// removed from the host page
// user is already authenticated, but ensure that the authTokens match
// if(!authToken.equals(authProvider.get().getAuthToken())) {
// throw new InvalidAuthTokenException("Auth Tokens do not match, possible XSRF attack");
// }
}
return true;
}
}