/* * This file is part of ReadonlyREST. * * ReadonlyREST 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. * * ReadonlyREST 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 ReadonlyREST. If not, see http://www.gnu.org/licenses/ */ package org.elasticsearch.plugin.readonlyrest.requestcontext; import org.apache.logging.log4j.Logger; import org.elasticsearch.plugin.readonlyrest.ESContext; import java.util.HashMap; import java.util.Map; import java.util.Optional; import java.util.TreeMap; /** * Created by sscarduzio on 04/05/2017. */ public class VariablesManager { private static final char ESCAPE_CHAR = '@'; private static final char DELIMITER_BEGIN_CHAR = '{'; private static final char DELIMITER_END_CHAR = '}'; private static final String VAR_DETECTOR = String.valueOf(ESCAPE_CHAR) + DELIMITER_BEGIN_CHAR; private final Logger logger; private final IndicesRequestContext rc; private Map<String, String> headers; public VariablesManager(Map<String, String> headers, IndicesRequestContext rc, ESContext context) { this.logger = context.logger(getClass()); this.rc = rc; Map<String, String> map = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); headers.keySet().forEach(k -> map.put(k.toLowerCase(), headers.get(k))); this.headers = map; } private static boolean containsReplacements(String template) { return template.contains(VAR_DETECTOR); } public Optional<String> apply(String original) { if (!containsReplacements(original)) { return Optional.of(original); } // Replacing "user" is more frequent, take care of that first. String replaced = original; if (rc.getLoggedInUser().isPresent()) { Map<String, String> m = new HashMap<>(1); m.put("user", rc.getLoggedInUser().get().getId()); replaced = replace(m, replaced); } if (!containsReplacements(replaced)) { return Optional.of(replaced); } replaced = replace(headers, original); if (!containsReplacements(replaced)) { return Optional.of(replaced); } logger.debug("unable to replace all variables, failing. orig:" + original + " replaced: " + replaced); return Optional.empty(); } /** * Uber-fast regex-free template replacer * * @param map replacements pool * @param str haystack string * @return replaced or unchanged string. */ private String replace(Map<String, String> map, String str) { StringBuilder sb = new StringBuilder(); char[] strArray = str.toCharArray(); int i = 0; while (i < strArray.length - 1) { if (strArray[i] == ESCAPE_CHAR && strArray[i + 1] == DELIMITER_BEGIN_CHAR) { i = i + 2; int begin = i; while (strArray[i] != DELIMITER_END_CHAR) ++i; String key = str.substring(begin, i++); String replacement = map.get(key); if (replacement == null) { replacement = VAR_DETECTOR + key + DELIMITER_END_CHAR; } sb.append(replacement); } else { sb.append(strArray[i]); ++i; } } if (i < strArray.length) sb.append(strArray[i]); return sb.toString(); } }