package in.partake.controller.base; import in.partake.base.DateTime; import in.partake.base.PartakeException; import in.partake.base.TimeUtil; import in.partake.base.Util; import in.partake.controller.PartakeActionContext; import in.partake.controller.PartakeTestContext; import in.partake.model.IPartakeDAOs; import in.partake.model.UserEx; import in.partake.model.access.DBAccess; import in.partake.model.dao.DAOException; import in.partake.model.dao.PartakeConnection; import in.partake.model.daofacade.UserDAOFacade; import in.partake.resource.Constants; import in.partake.resource.MessageCode; import in.partake.resource.ServerErrorCode; import in.partake.resource.UserErrorCode; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.UUID; import org.apache.commons.lang.StringUtils; import play.Logger; import play.mvc.Controller; import play.mvc.Result; import com.google.common.base.Strings; public abstract class AbstractPartakeController extends Controller { private PartakeActionContextImpl ctx; // ---------------------------------------------------------------------- // Execute public final Result execute() { Result result = executeInternal(); // For testing purpose, we would like to expose the action. PartakeTestContext.setAction(this); return result; } private final Result executeInternal() { long begin = System.currentTimeMillis(); Logger.info("processing... " + request().uri()); try { ensureContext(); return doExecute(); } catch (DAOException e) { return renderError(ServerErrorCode.DB_ERROR, null, e); } catch (PartakeException e) { return renderException(e); } catch (RuntimeException e) { return renderError(ServerErrorCode.UNKNOWN_ERROR, null, e); } catch (Exception e) { return renderError(ServerErrorCode.UNKNOWN_ERROR, null, e); } finally { long end = System.currentTimeMillis(); Logger.info(request().uri() + " took "+ (end - begin) + "[msec] to process."); } } private void ensureContext() throws DAOException, PartakeException { PartakeActionContextImpl impl = new PartakeActionContextImpl(); final String userId = session().get(Constants.Session.USER_ID_KEY); impl.setCurrentURL(request().uri()); if (userId != null) { impl.loginUser = new DBAccess<UserEx>() { @Override protected UserEx doExecute(PartakeConnection con, IPartakeDAOs daos) throws DAOException, PartakeException { return UserDAOFacade.getUserEx(con, daos, userId); } }.execute(); } if (!session().containsKey(Constants.Session.ID_KEY)) session().put(Constants.Session.ID_KEY, UUID.randomUUID().toString()); if (!session().containsKey(Constants.Session.TOKEN_KEY)) session().put(Constants.Session.TOKEN_KEY, UUID.randomUUID().toString()); impl.messageCodes = new ArrayList<MessageCode>(); if (flash().get(Constants.Flash.MESSAGE_ID) != null) { MessageCode code = MessageCode.safeValueOf(flash().get(Constants.Flash.MESSAGE_ID)); impl.messageCodes.add(code); } impl.sessionToken = session().get(Constants.Session.TOKEN_KEY); this.ctx = impl; } protected PartakeActionContext context() { return this.ctx; } protected abstract Result doExecute() throws PartakeException, DAOException; // ---------------------------------------------------------------------- // Render protected abstract Result renderInvalid(UserErrorCode ec, Map<String, String> additionalInfo, Throwable e); protected abstract Result renderError(ServerErrorCode ec, Map<String, String> additionalInfo, Throwable e); protected abstract Result renderLoginRequired(); protected abstract Result renderForbidden(); protected abstract Result renderNotFound(); protected Result renderException(PartakeException e) { if (e.getStatusCode() == 401) return renderLoginRequired(); if (e.getStatusCode() == 403) return renderForbidden(); if (e.getStatusCode() == 404) return renderNotFound(); if (e.isUserError()) return renderInvalid(e.getUserErrorCode(), e.getAdditionalInfo(), e.getCause()); assert e.isServerError(); return renderError(e.getServerErrorCode(), e.getAdditionalInfo(), e.getCause()); } protected Result renderInvalid(UserErrorCode ec) { return renderInvalid(ec, null, null); } protected Result renderInvalid(UserErrorCode ec, Throwable e) { return renderInvalid(ec, null, e); } protected Result renderInvalid(UserErrorCode ec, Map<String, String> additionalInfo) { return renderInvalid(ec, additionalInfo, null); } protected Result renderError(ServerErrorCode errorCode) { return renderError(errorCode, null, null); } protected Result renderError(ServerErrorCode errorCode, Throwable e) { return renderError(errorCode, null, e); } // ---------------------------------------------------------------------- // Parameter protected String getFormParameter(String key) { Map<String, String[]> map = request().body().asFormUrlEncoded(); if (map == null) return null; String[] params = map.get(key); if (params == null || params.length <= 0) return null; return params[0]; } protected String getQueryStringParameter(String key) { Map<String, String[]> map = request().queryString(); if (map == null) return null; String[] params = map.get(key); if (params == null) return null; if (params.length <= 0) return null; return params[0]; } protected Map<String, String[]> getFormParameters() { return request().body().asFormUrlEncoded(); } @Deprecated protected String getParameter(String key) { String[] values = getParameters(key); if (values == null || values.length == 0) return null; return values[0]; } /** * Gets boolean parameter. If parameter does not exist, null will be returned. * @param key * @param defaultValue * @return */ protected Boolean getBooleanParameter(String key) { String value = getParameter(key); if (value == null) return null; return Util.parseBooleanParameter(value); } protected boolean optBooleanParameter(String key, boolean defaultValue) { Boolean value = getBooleanParameter(key); if (value != null) return value; return defaultValue; } protected Integer getIntegerParameter(String key) { String value = getParameter(key); if (value == null) return null; try { return Integer.valueOf(value); } catch (NumberFormatException e) { return null; } } protected int optIntegerParameter(String key, int defaultValue) { Integer value = getIntegerParameter(key); if (value != null) return value; return defaultValue; } protected Long getLongParameter(String key) { String value = getParameter(key); if (value == null) return null; try { return Long.valueOf(value); } catch (NumberFormatException e) { return null; } } protected DateTime getDateTimeParameter(String key) { String value = getParameter(key); if (value == null) return null; DateTime date = TimeUtil.parseForEvent(value); if (date != null) return date; // Try parse it as long. try { long time = Long.valueOf(value); return new DateTime(time); } catch (NumberFormatException e) { // Do nothing. } return null; } protected UUID getValidUUIDParameter(String key, UserErrorCode missing, UserErrorCode invalid) throws PartakeException { String id = getParameter(key); if (id == null) throw new PartakeException(missing); if (!Util.isUUID(id)) throw new PartakeException(invalid); return UUID.fromString(id); } protected String getValidIdParameter(String key, UserErrorCode missing, UserErrorCode invalid) throws PartakeException { String id = getParameter(key); checkIdParameterIsValid(id, missing, invalid); return id; } protected void checkIdParameterIsValid(String id, UserErrorCode missing, UserErrorCode invalid) throws PartakeException { if (id == null) throw new PartakeException(missing); if (!Util.isUUID(id)) throw new PartakeException(invalid); } protected String optValidIdParameter(String key, UserErrorCode invalid, String defaultValue) throws PartakeException { String id = getParameter(key); if (id == null) return defaultValue; if (!Util.isUUID(id)) throw new PartakeException(invalid); return id; } protected String getValidUserIdParameter(UserErrorCode missing, UserErrorCode invalid) throws PartakeException { return getValidIdParameter("userId", missing, invalid); } protected String getValidUserIdParameter() throws PartakeException { return getValidIdParameter("userId", UserErrorCode.MISSING_USER_ID, UserErrorCode.INVALID_USER_ID); } protected String getValidEventIdParameter() throws PartakeException { return getValidIdParameter("eventId", UserErrorCode.MISSING_EVENT_ID, UserErrorCode.INVALID_EVENT_ID); } protected UUID getValidTicketIdParameter() throws PartakeException { return getValidUUIDParameter("ticketId", UserErrorCode.MISSING_TICKET_ID, UserErrorCode.INVALID_TICKET_ID); } protected UUID getValidTicketIdParameter(UserErrorCode missing, UserErrorCode invalid) throws PartakeException { return getValidUUIDParameter("ticketId", missing, invalid); } protected String getValidEventIdParameter(UserErrorCode missing, UserErrorCode invalid) throws PartakeException { return getValidIdParameter("eventId", missing, invalid); } protected String getValidImageIdParameter() throws PartakeException { return getValidIdParameter("imageId", UserErrorCode.MISSING_IMAGEID, UserErrorCode.INVALID_IMAGEID); } protected String getValidCommentIdParameter() throws PartakeException { return getValidIdParameter("commentId", UserErrorCode.MISSING_COMMENT_ID, UserErrorCode.INVALID_COMMENT_ID); } protected void ensureValidSessionToken() throws PartakeException { if (!checkCSRFToken()) throw new PartakeException(UserErrorCode.INVALID_SECURITY_CSRF); } protected String[] ensureParameters(String key, UserErrorCode ec) throws PartakeException { String[] params = getParameters(key); if (params == null) throw new PartakeException(ec); return params; } protected String[] ensureParameters(String key, int n, UserErrorCode ec) throws PartakeException { String[] params = getParameters(key); if (params == null) { if (n == 0) return new String[0]; else throw new PartakeException(ec); } if (params.length != n) throw new PartakeException(ec); return params; } /** * take multiple parameters. If there is a single parameter, a new array will be created to return. * @param key * @return */ @Deprecated protected String[] getParameters(String key) { String[] params = request().queryString().get(key); if (params != null) return params; if (request().body().asFormUrlEncoded() != null) return request().body().asFormUrlEncoded().get(key); if (request().body().asMultipartFormData() != null) return request().body().asMultipartFormData().asFormUrlEncoded().get(key); return null; } @Deprecated protected Map<String, String[]> getParameters() { return request().queryString(); } public void setRedirectURL(String url) { ctx.redirectURL = url; } public String getRedirectURL() { return ctx.redirectURL; } public void setCurrentURL(String url) { ctx.setCurrentURL(url); } public String getCurrentURL() { return ctx.currentURL(); } // ---------------------------------------------------------------------- // Utility function /** * get logged in user. If a user is not logged in, null is returned. * @return the logged in user. null if a user is not logged in. */ protected UserEx getLoginUser() { return ctx.loginUser; } protected UserEx ensureLogin() throws PartakeException { UserEx user = getLoginUser(); if (user == null) throw new PartakeException(UserErrorCode.INVALID_LOGIN_REQUIRED); return user; } protected UserEx ensureAdmin() throws PartakeException { UserEx user = ensureLogin(); if (!user.isAdministrator()) throw new PartakeException(UserErrorCode.INVALID_PROHIBITED); return user; } // ---------------------------------------------------------------------- // CSRF private boolean checkCSRFToken() { String sessionToken = getParameter(Constants.Parameter.SESSION_TOKEN); if (sessionToken == null) return false; String originalSessionToken = session().get(Constants.Session.TOKEN_KEY); if (originalSessionToken == null) return false; return StringUtils.equals(sessionToken, originalSessionToken); } } class PartakeActionContextImpl implements PartakeActionContext { UserEx loginUser; String sessionToken; String currentURL; String redirectURL; String thumbnailURL; List<MessageCode> messageCodes = new ArrayList<MessageCode>(); public void setLoginUser(UserEx loginUser) { this.loginUser = loginUser; } public void setSessionToken(String sessionToken) { this.sessionToken = sessionToken; } public void setCurrentURL(String currentURL) { this.currentURL = currentURL; } @Override public UserEx loginUser() { return loginUser; } @Override public String sessionToken() { return sessionToken; } @Override public String currentURL() { return currentURL; } @Override public String redirectURL() { if (Strings.isNullOrEmpty(redirectURL)) { return currentURL; } else { return redirectURL; } } @Override public void setRedirectURL(String redirectURL) { this.redirectURL = redirectURL; } @Override public void addMessage(MessageCode mc) { messageCodes.add(mc); } @Override public List<MessageCode> messages() { return Collections.unmodifiableList(messageCodes); } @Override public String thumbnailURL() { return thumbnailURL; } @Override public void setThumbnailURL(String thumbnailURL) { this.thumbnailURL = thumbnailURL; } }