Java tutorial
/******************************************************************************* * Copyright (c) 2013, 2014 Lectorius, Inc. * Authors: * Vijay Pandurangan (vijayp@mitro.co) * Evan Jones (ej@mitro.co) * Adam Hilss (ahilss@mitro.co) * * * 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/>. * * You can contact the authors at inbound@mitro.co. *******************************************************************************/ package co.mitro.core.servlets; import java.io.IOException; import java.io.StringReader; import java.lang.reflect.Type; import java.util.List; import java.util.regex.Pattern; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.base.Strings; import com.google.gson.Gson; import com.google.gson.JsonSyntaxException; import com.google.gson.TypeAdapter; import com.google.gson.reflect.TypeToken; import com.google.gson.stream.JsonReader; @WebServlet("/ServerRejects") public class ServerRejectsServlet extends HttpServlet { private static final Logger logger = LoggerFactory.getLogger(ServerRejectsServlet.class); private static final long serialVersionUID = 1L; private static final Gson gson = new Gson(); // Magic to deserialize a generic typed list using Gson // See https://sites.google.com/site/gson/gson-user-guide#TOC-Serializing-and-Deserializing-Generic-Types private static final Type listType = new TypeToken<List<HintEntry>>() { }.getType(); private static String serverHintsJson = null; @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { if (serverHintsJson != null) { // allow cross origin requests for the web version Util.allowCrossOriginRequests(response); response.setContentType(Util.JSON_CONTENT_TYPE); response.getWriter().write(serverHintsJson); } else { response.sendError(500); } } /** * Sets the server hints to hintsString, after validation. Throws exceptions if the JSON data * is incorrect, although the validation isn't currently extremely careful. */ public static void setServerHintsJson(String hintsString) { try { // Parse and validate the hints // TODO: Unit test this more carefully // can't use gson.fromJson() because it always uses lenient parsing; copied from there // See https://code.google.com/p/google-gson/issues/detail?id=372 JsonReader jsonReader = new JsonReader(new StringReader(hintsString)); TypeAdapter<?> typeAdapter = gson.getAdapter(TypeToken.get(listType)); @SuppressWarnings("unchecked") List<HintEntry> hints = (List<HintEntry>) typeAdapter.read(jsonReader); for (HintEntry hint : hints) { @SuppressWarnings("unused") Pattern regexp = Pattern.compile(hint.regex); if (hint.additional_submit_button_ids != null) { // optional: just don't include it instead of including an empty list assert hint.additional_submit_button_ids.size() > 0; for (String submitIds : hint.additional_submit_button_ids) { assert !Strings.isNullOrEmpty(submitIds); } } if (hint.allow_empty_username != null) { assert hint.allow_empty_username : "omit allow_empty_username if false"; } if (hint.empty_password_username_selector != null) { // TODO: Validate that this is a valid CSS selector? assert !hint.empty_password_username_selector .isEmpty() : "omit empty_password_username_selector if there is no selector"; assert hint.allow_empty_username != null && hint.allow_empty_username .booleanValue() : "allow_empty_username must be true if empty_password_username_selector is present"; } validateRules(hint.reject.login_submit); validateRules(hint.reject.submit); validateRules(hint.reject.password); validateRules(hint.reject.username); validateRules(hint.reject.form); } logger.info("setting {} server hints", hints.size()); serverHintsJson = hintsString; } catch (IOException | IllegalStateException e) { // Rethrow the same way as gson.fromJson() throw new JsonSyntaxException(e); } } private static void validateRules(List<List<Attribute>> rules) { for (List<Attribute> andRule : rules) { // if an AND rule exists, it must be non-empty assert andRule.size() > 0; for (Attribute attribute : andRule) { assert !Strings.isNullOrEmpty(attribute.attributeName); // Exactly one of exactMatch or regexMatch must be non-empty; the other must be null if (!Strings.isNullOrEmpty(attribute.regexMatch)) { @SuppressWarnings("unused") Pattern regexp = Pattern.compile(attribute.regexMatch); assert attribute.exactMatch == null; } else { assert !Strings.isNullOrEmpty(attribute.exactMatch); assert attribute.regexMatch == null; } } } } private static class Attribute { public String attributeName; public String exactMatch; public String regexMatch; } private static class RejectEntry { public List<List<Attribute>> login_submit; public List<List<Attribute>> submit; public List<List<Attribute>> password; public List<List<Attribute>> username; public List<List<Attribute>> form; } private static class HintEntry { public String regex; public List<String> additional_submit_button_ids; /** Boolean so this is null if absent. */ public Boolean allow_empty_username; public String empty_password_username_selector; public RejectEntry reject; } }