001/* 002 * Stallion Core: A Modern Web Framework 003 * 004 * Copyright (C) 2015 - 2016 Stallion Software LLC. 005 * 006 * This program is free software: you can redistribute it and/or modify it under the terms of the 007 * GNU General Public License as published by the Free Software Foundation, either version 2 of 008 * the License, or (at your option) any later version. This program is distributed in the hope that 009 * it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 010 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public 011 * License for more details. You should have received a copy of the GNU General Public License 012 * along with this program. If not, see <http://www.gnu.org/licenses/gpl-2.0.html>. 013 * 014 * 015 * 016 */ 017 018package io.stallion.forms; 019 020import io.stallion.exceptions.ClientException; 021import io.stallion.requests.validators.SafeMerger; 022import io.stallion.restfulEndpoints.ObjectParam; 023import io.stallion.services.LocalMemoryCache; 024import io.stallion.utils.Encrypter; 025import org.apache.commons.lang3.StringUtils; 026 027import javax.ws.rs.POST; 028import javax.ws.rs.Path; 029 030import static io.stallion.utils.Literals.*; 031import static io.stallion.Context.*; 032 033 034public class SimpleFormEndpoints { 035 036 @POST 037 @Path("/contacts/submit-form") 038 public Boolean submitForm(@ObjectParam(targetClass = SimpleFormSubmission.class) SimpleFormSubmission rawSubmission) { 039 SimpleFormSubmission submission = SafeMerger 040 .with() 041 .nonEmpty("antiSpamToken", "pageUrl", "data") 042 .optionalEmail("email") 043 .optional("pageTitle", "formId") 044 .merge(rawSubmission); 045 046 /* The Anti-spam token is an encrypted token with a milliseconds timestamp and a randomly generated key 047 This prevents a spammer from simply hitting this endpoint over and over again with a script. A given 048 random key can only be used once within an hour, and all tokens expire after an hour, so all together 049 it is not possible to submit more than once. 050 051 This will not stop a spammer who is actually requesting a new copy of the page and who is 052 parsing out the spam token single every time. We would have to implement IP address throttling or 053 captchas to fix that. 054 */ 055 056 String token = Encrypter.decryptString(settings().getAntiSpamSecret(), submission.getAntiSpamToken()); 057 if (empty(token) || !token.contains("|")) { 058 throw new ClientException("Anti-spam token is not in the correct format"); 059 } 060 String[] parts = StringUtils.split(token, "|", 2); 061 Long time = Long.parseLong(parts[0]); 062 String randomKey = parts[1]; 063 064 if (time == null || ((time + 60 * 60 * 1000) < mils())) { 065 throw new ClientException("Anti-spam token has expired. Please reload the page and submit again."); 066 } 067 068 Integer submissionCount = or((Integer)LocalMemoryCache.get("form_submissions", randomKey), 0); 069 if (submissionCount > 0) { 070 throw new ClientException("You have already submitted this form once."); 071 } 072 073 074 submission 075 .setSubmittedAt(mils()); 076 SimpleFormSubmissionController.instance().save(submission); 077 if (!empty(submission.getEmail())) { 078 SimpleFormSubmissionEmailTask.enqueue(submission); 079 } 080 081 // Store a record of this token, so it cannot be reused 082 LocalMemoryCache.set("form_submissions", randomKey, submissionCount + 1, 90 * 60 * 1000); 083 084 return true; 085 } 086}