Java tutorial
/** This file is part of Waarp Project. Copyright 2009, Frederic Bregier, and individual contributors by the @author tags. See the COPYRIGHT.txt in the distribution for a full listing of individual contributors. All Waarp Project 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. Waarp 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 Waarp . If not, see <http://www.gnu.org/licenses/>. */ package org.waarp.gateway.kernel.rest; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; import java.util.Map.Entry; import io.netty.buffer.Unpooled; import io.netty.handler.codec.http.Cookie; import io.netty.handler.codec.http.CookieDecoder; import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.HttpRequest; import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.QueryStringDecoder; import io.netty.handler.codec.http.QueryStringEncoder; import org.joda.time.DateTime; import org.joda.time.Duration; import org.waarp.common.crypto.HmacSha256; import org.waarp.common.json.JsonHandler; import org.waarp.common.logging.WaarpLogger; import org.waarp.common.logging.WaarpLoggerFactory; import org.waarp.common.role.RoleDefault; import org.waarp.common.role.RoleDefault.ROLE; import org.waarp.gateway.kernel.exception.HttpIncorrectRequestException; import org.waarp.gateway.kernel.exception.HttpInvalidAuthenticationException; import org.waarp.gateway.kernel.rest.DataModelRestMethodHandler.COMMAND_TYPE; import org.waarp.gateway.kernel.rest.HttpRestHandler.METHOD; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; /** * Rest object that contains all arguments or answers once all tasks are over:</br> * - ARG_HASBODY, ARG_METHOD, ARG_PATH, ARG_BASEPATH, ARGS_SUBPATH, ARG_X_AUTH_KEY, ARG_X_AUTH_USER, ARG_X_AUTH_TIMESTAMP, * ARG_X_AUTH_ROLE: root elements (query only)</br> * - ARG_METHOD, ARG_PATH, ARG_BASEPATH, ARGS_SUBPATH, ARG_X_AUTH_USER, JSON_STATUSMESSAGE, JSON_STATUSCODE, JSON_COMMAND: root * elements (answer only)</br> * - ARGS_URI: uri elements (query only)</br> * - ARGS_HEADER: header elements (query only)</br> * - ARG_COOKIE: cookie elements</br> * - ARGS_BODY: body elements (query only)</br> * - ARGS_ANSWER: answer part (answer only)</br> * * @author "Frederic Bregier" * */ public class RestArgument { /** * Internal Logger */ private static final WaarpLogger logger = WaarpLoggerFactory.getLogger(RestArgument.class); public static enum REST_GROUP { /** * arguments.path(ARGS_URI) main entry for URI arguments */ ARGS_URI("uri"), /** * arguments.path(ARGS_HEADER) main entry for HEADER arguments */ ARGS_HEADER("header"), /** * arguments.path(ARGS_COOKIE) main entry for COOKIE arguments */ ARGS_COOKIE("cookie"), /** * arguments.path(ARGS_BODY) main entry for BODY arguments */ ARGS_BODY("body"), /** * arguments.path(ARGS_ANSWER) main entry for ANSWER arguments */ ARGS_ANSWER("answer"); public String group; REST_GROUP(String group) { this.group = group; } } public static enum REST_ROOT_FIELD { /** * arguments.path(ARG_PATH) = uri path */ ARG_PATH("path"), /** * arguments.path(ARG_BASEPATH).asText() = uri base path */ ARG_BASEPATH("base"), /** * arguments.path(ARGS_SUBPATH) main entry for SUB-PATH arguments<br> * arguments.path(ARGS_SUBPATH).elements() for an iterator or .get(x) for xth SUB-PATH argument */ ARGS_SUBPATH("subpath"), /** * arguments.path(ARG_METHOD).asText() = method identified */ ARG_METHOD("X-method"), /** * arguments.path(ARG_HASBODY).asBoolean() = true if the body has content */ ARG_HASBODY("hasBody"), /** * arguments.path(ARG_X_AUTH_KEY).asText() = Key used */ ARG_X_AUTH_KEY("X-Auth-Key"), /** * arguments.path(ARG_X_AUTH_KEY).asText() = Key used */ ARG_X_AUTH_USER("X-Auth-User"), /** * Internal Key used (not to be passed through wire) */ ARG_X_AUTH_INTERNALKEY("X-Auth-InternalKey"), /** * arguments.path(ARG_X_AUTH_TIMESTAMP).asText() = Timestamp in ISO 8601 format */ ARG_X_AUTH_TIMESTAMP("X-Auth-Timestamp"), /** * arguments.path(ARG_X_AUTH_ROLE).asInt() = Role used */ ARG_X_AUTH_ROLE("X-Auth-Role"), JSON_STATUSCODE("code"), JSON_STATUSMESSAGE("message"), JSON_COMMAND( "command"), JSON_DETAIL("detail"); public String field; REST_ROOT_FIELD(String field) { this.field = field; } } public static enum REST_FIELD { JSON_RESULT("result"), JSON_PATH("path"), JSON_JSON("body"), X_DETAILED_ALLOW( "DetailedAllow"), X_ALLOW_URIS("UriAllowed"), JSON_ID("_id"); public String field; REST_FIELD(String field) { this.field = field; } } public static enum DATAMODEL { JSON_COUNT("count"), JSON_RESULTS("results"), JSON_FILTER("filter"), JSON_LIMIT("limit"); public String field; DATAMODEL(String field) { this.field = field; } } ObjectNode arguments; /** * Create a RestArgument * * @param emptyArgument * might be null, but might be also already initialized with some values */ public RestArgument(ObjectNode emptyArgument) { if (emptyArgument == null) { arguments = JsonHandler.createObjectNode(); } else { arguments = emptyArgument; } } /** * Clean all internal values */ public void clean() { arguments.removeAll(); } /** * Set values according to the request URI * * @param request */ public void setRequest(HttpRequest request) { arguments.put(REST_ROOT_FIELD.ARG_HASBODY.field, (request instanceof FullHttpRequest && ((FullHttpRequest) request).content() != Unpooled.EMPTY_BUFFER)); arguments.put(REST_ROOT_FIELD.ARG_METHOD.field, request.method().name()); QueryStringDecoder decoderQuery = new QueryStringDecoder(request.uri()); String path = decoderQuery.path(); arguments.put(REST_ROOT_FIELD.ARG_PATH.field, path); // compute path main uri String basepath = path; int pos = basepath.indexOf('/'); if (pos >= 0) { if (pos == 0) { int pos2 = basepath.indexOf('/', 1); if (pos2 < 0) { basepath = basepath.substring(1); } else { basepath = basepath.substring(1, pos2); } } else { basepath = basepath.substring(0, pos); } } arguments.put(REST_ROOT_FIELD.ARG_BASEPATH.field, basepath); // compute sub path args if (pos == 0) { pos = path.indexOf('/', 1); } if (pos >= 0) { int pos2 = path.indexOf('/', pos + 1); if (pos2 > 0) { ArrayNode array = arguments.putArray(REST_ROOT_FIELD.ARGS_SUBPATH.field); while (pos2 > 0) { array.add(path.substring(pos + 1, pos2)); pos = pos2; pos2 = path.indexOf('/', pos + 1); } } pos2 = path.indexOf('?', pos + 1); if (pos2 > 0 && pos2 > pos + 1) { ArrayNode array = (ArrayNode) arguments.get(REST_ROOT_FIELD.ARGS_SUBPATH.field); if (array == null) { array = arguments.putArray(REST_ROOT_FIELD.ARGS_SUBPATH.field); } array.add(path.substring(pos + 1, pos2)); } else { String last = path.substring(pos + 1); if (!last.isEmpty()) { ArrayNode array = (ArrayNode) arguments.get(REST_ROOT_FIELD.ARGS_SUBPATH.field); if (array == null) { array = arguments.putArray(REST_ROOT_FIELD.ARGS_SUBPATH.field); } array.add(last); } } } Map<String, List<String>> map = decoderQuery.parameters(); ObjectNode node = arguments.putObject(REST_GROUP.ARGS_URI.group); for (String key : map.keySet()) { if (key.equalsIgnoreCase(REST_ROOT_FIELD.ARG_X_AUTH_KEY.field)) { arguments.put(REST_ROOT_FIELD.ARG_X_AUTH_KEY.field, map.get(key).get(0)); continue; } if (key.equalsIgnoreCase(REST_ROOT_FIELD.ARG_X_AUTH_USER.field)) { arguments.put(REST_ROOT_FIELD.ARG_X_AUTH_USER.field, map.get(key).get(0)); continue; } if (key.equalsIgnoreCase(REST_ROOT_FIELD.ARG_X_AUTH_TIMESTAMP.field)) { arguments.put(REST_ROOT_FIELD.ARG_X_AUTH_TIMESTAMP.field, map.get(key).get(0)); continue; } List<String> list = map.get(key); if (list.size() > 1) { ArrayNode array = node.putArray(key); for (String val : map.get(key)) { array.add(val); } } else if (list.isEmpty()) { node.putNull(key); } else { //1 node.put(key, list.get(0)); } } } /** * Set X_AUTH_USER, Method, Path, Basepath and Cookie from source * * @param source */ public void setFromArgument(RestArgument source) { if (source.arguments.has(REST_ROOT_FIELD.ARG_X_AUTH_USER.field)) { arguments.put(REST_ROOT_FIELD.ARG_X_AUTH_USER.field, source.arguments.get(REST_ROOT_FIELD.ARG_X_AUTH_USER.field).asText()); } if (source.arguments.has(REST_ROOT_FIELD.ARG_METHOD.field)) { arguments.put(REST_ROOT_FIELD.ARG_METHOD.field, source.arguments.get(REST_ROOT_FIELD.ARG_METHOD.field).asText()); } if (source.arguments.has(REST_ROOT_FIELD.ARG_PATH.field)) { arguments.put(REST_ROOT_FIELD.ARG_PATH.field, source.arguments.get(REST_ROOT_FIELD.ARG_PATH.field).asText()); } if (source.arguments.has(REST_ROOT_FIELD.ARG_BASEPATH.field)) { arguments.put(REST_ROOT_FIELD.ARG_BASEPATH.field, source.arguments.get(REST_ROOT_FIELD.ARG_BASEPATH.field).asText()); } if (source.arguments.has(REST_ROOT_FIELD.ARGS_SUBPATH.field)) { arguments.putArray(REST_ROOT_FIELD.ARGS_SUBPATH.field) .addAll((ArrayNode) source.arguments.get(REST_ROOT_FIELD.ARGS_SUBPATH.field)); } if (source.arguments.has(REST_GROUP.ARGS_URI.group)) { arguments.putObject(REST_GROUP.ARGS_URI.group) .setAll((ObjectNode) source.arguments.get(REST_GROUP.ARGS_URI.group)); } if (source.arguments.has(REST_GROUP.ARGS_COOKIE.group)) { arguments.putObject(REST_GROUP.ARGS_COOKIE.group) .setAll((ObjectNode) source.arguments.get(REST_GROUP.ARGS_COOKIE.group)); } logger.debug("DEBUG: {}\n {}", arguments, source); } /** * * @return the full Path of the URI */ public String getUri() { return arguments.path(REST_ROOT_FIELD.ARG_PATH.field).asText(); } /** * * @return the base Path of the URI (first item between '/') */ public String getBaseUri() { return arguments.path(REST_ROOT_FIELD.ARG_BASEPATH.field).asText(); } /** * * @return An iterator of JsonNode, which values can be retrieved by item.asText() */ public Iterator<JsonNode> getSubUri() { return arguments.path(REST_ROOT_FIELD.ARGS_SUBPATH.field).elements(); } public int getSubUriSize() { return arguments.path(REST_ROOT_FIELD.ARGS_SUBPATH.field).size(); } public void addSubUriToUriArgs(String name, int rank) { ObjectNode node = getUriArgs(); JsonNode elt = arguments.path(REST_ROOT_FIELD.ARGS_SUBPATH.field).get(rank); if (elt != null) { node.set(name, elt); } } public void addIdToUriArgs() { addSubUriToUriArgs(REST_FIELD.JSON_ID.field, 0); } public JsonNode getId() { return getUriArgs().path(REST_FIELD.JSON_ID.field); } public static JsonNode getId(ObjectNode node) { return node.path(REST_FIELD.JSON_ID.field); } public long getLimitFromUri() { return getUriArgs().path(DATAMODEL.JSON_LIMIT.field).asLong(100); } public String getXAuthKey() { return arguments.path(REST_ROOT_FIELD.ARG_X_AUTH_KEY.field).asText(); } public String getXAuthUser() { return arguments.path(REST_ROOT_FIELD.ARG_X_AUTH_USER.field).asText(); } public String getXAuthTimestamp() { return arguments.path(REST_ROOT_FIELD.ARG_X_AUTH_TIMESTAMP.field).asText(); } public void setXAuthRole(RoleDefault role) { arguments.put(REST_ROOT_FIELD.ARG_X_AUTH_ROLE.field, role.getRoleAsByte()); } public ROLE getXAuthRole() { byte role = (byte) arguments.get(REST_ROOT_FIELD.ARG_X_AUTH_ROLE.field).asInt(); return ROLE.fromByte(role); } /** * * @return The ObjectNode containing all couples key/value */ public ObjectNode getUriArgs() { JsonNode node = arguments.path(REST_GROUP.ARGS_URI.group); if (node == null || node.isMissingNode()) { node = arguments.putObject(REST_GROUP.ARGS_URI.group); } return (ObjectNode) node; } /** * * @return the method or null */ public METHOD getMethod() { String text = arguments.path(REST_ROOT_FIELD.ARG_METHOD.field).asText(); if (text == null || text.isEmpty()) { return METHOD.TRACE; } try { return METHOD.valueOf(text); } catch (Exception e) { return METHOD.TRACE; } } /** * set values from Header into arguments.path(ARGS_HEADER) * * @throws HttpIncorrectRequestException */ public void setHeaderArgs(List<Entry<String, String>> list) { ObjectNode node = (ObjectNode) arguments.get(REST_GROUP.ARGS_HEADER.group); if (node == null || node.isMissingNode()) { node = arguments.putObject(REST_GROUP.ARGS_HEADER.group); } for (Entry<String, String> entry : list) { String key = entry.getKey(); if (!key.equals(HttpHeaders.Names.COOKIE)) { if (key.equalsIgnoreCase(REST_ROOT_FIELD.ARG_X_AUTH_KEY.field)) { arguments.put(REST_ROOT_FIELD.ARG_X_AUTH_KEY.field, entry.getValue()); continue; } if (key.equalsIgnoreCase(REST_ROOT_FIELD.ARG_X_AUTH_USER.field)) { arguments.put(REST_ROOT_FIELD.ARG_X_AUTH_USER.field, entry.getValue()); continue; } if (key.equalsIgnoreCase(REST_ROOT_FIELD.ARG_X_AUTH_TIMESTAMP.field)) { arguments.put(REST_ROOT_FIELD.ARG_X_AUTH_TIMESTAMP.field, entry.getValue()); continue; } node.put(entry.getKey(), entry.getValue()); } } } /** * * @return The ObjectNode containing all couples key/value */ public ObjectNode getHeaderArgs() { JsonNode node = arguments.path(REST_GROUP.ARGS_HEADER.group); if (node == null || node.isMissingNode()) { node = arguments.putObject(REST_GROUP.ARGS_HEADER.group); } return (ObjectNode) node; } /** * set method From URI */ public void methodFromUri() { JsonNode node = arguments.path(REST_GROUP.ARGS_URI.group).path(REST_ROOT_FIELD.ARG_METHOD.field); if (!node.isMissingNode()) { // override arguments.put(REST_ROOT_FIELD.ARG_METHOD.field, node.asText()); } } /** * set method From Header */ public void methodFromHeader() { JsonNode node = arguments.path(REST_GROUP.ARGS_HEADER.group).path(REST_ROOT_FIELD.ARG_METHOD.field); if (!node.isMissingNode()) { // override arguments.put(REST_ROOT_FIELD.ARG_METHOD.field, node.asText()); } } /** * set values from Cookies into arguments.path(ARGS_COOKIE) * */ public void setCookieArgs(String cookieString) { Set<Cookie> cookies; if (cookieString == null) { cookies = Collections.emptySet(); } else { cookies = CookieDecoder.decode(cookieString); } if (!cookies.isEmpty()) { ObjectNode node = arguments.putObject(REST_GROUP.ARGS_COOKIE.group); for (Cookie cookie : cookies) { node.put(cookie.name(), cookie.value()); } } } /** * * @return The ObjectNode containing all couples key/value */ public ObjectNode getCookieArgs() { JsonNode node = arguments.path(REST_GROUP.ARGS_COOKIE.group); if (node == null || node.isMissingNode()) { node = arguments.putObject(REST_GROUP.ARGS_COOKIE.group); } return (ObjectNode) node; } /** * * @return The ObjectNode containing all couples key/value */ public ObjectNode getBody() { JsonNode node = arguments.path(REST_GROUP.ARGS_BODY.group); if (node == null || node.isMissingNode()) { node = arguments.putObject(REST_GROUP.ARGS_BODY.group); } return (ObjectNode) node; } /** * * @return The ObjectNode containing all couples key/value */ public ObjectNode getAnswer() { JsonNode node = arguments.path(REST_GROUP.ARGS_ANSWER.group); if (node == null || node.isMissingNode()) { node = arguments.putObject(REST_GROUP.ARGS_ANSWER.group); } return (ObjectNode) node; } public void addAnswer(ObjectNode node) { getAnswer().setAll(node); } public void setResult(HttpResponseStatus status) { arguments.put(REST_ROOT_FIELD.JSON_STATUSMESSAGE.field, status.reasonPhrase()); arguments.put(REST_ROOT_FIELD.JSON_STATUSCODE.field, status.code()); } /** * * @return the Http Status code */ public int getStatusCode() { return arguments.path(REST_ROOT_FIELD.JSON_STATUSCODE.field).asInt(); } /** * * @return the Http Status message according to the Http Status code */ public String getStatusMessage() { return arguments.path(REST_ROOT_FIELD.JSON_STATUSMESSAGE.field).asText(); } public void setDetail(String detail) { arguments.put(REST_ROOT_FIELD.JSON_DETAIL.field, detail); } /** * * @return the detail information on error (mainly) */ public String getDetail() { return arguments.path(REST_ROOT_FIELD.JSON_DETAIL.field).asText(); } public void setCommand(COMMAND_TYPE command) { arguments.put(REST_ROOT_FIELD.JSON_COMMAND.field, command.name()); } public void setCommand(String cmd) { arguments.put(REST_ROOT_FIELD.JSON_COMMAND.field, cmd); } /** * * @return the COMMAND field, to be transformed either into COMMAND_TYPE or ACTIONS_TYPE */ public String getCommandField() { return arguments.path(REST_ROOT_FIELD.JSON_COMMAND.field).asText(); } /** * * @return the COMMAND_TYPE but might be null if not found or if of ACTIONS_TYPE */ public COMMAND_TYPE getCommand() { String cmd = arguments.path(REST_ROOT_FIELD.JSON_COMMAND.field).asText(); if (cmd != null && !cmd.isEmpty()) { try { return COMMAND_TYPE.valueOf(cmd); } catch (Exception e) { return null; } } else { return null; } } /** * * @param filter * the filter used in multi get */ public void addFilter(ObjectNode filter) { getAnswer().putObject(DATAMODEL.JSON_FILTER.field).setAll(filter); } /** * * @return the filter used in multi get */ public ObjectNode getFilter() { return (ObjectNode) getAnswer().path(DATAMODEL.JSON_FILTER.field); } /** * * @return the array of results (in DataModel multi get) */ public ArrayNode getResults() { JsonNode node = getAnswer().path(DATAMODEL.JSON_RESULTS.field); if (node == null || node.isMissingNode()) { node = getAnswer().putArray(DATAMODEL.JSON_RESULTS.field); } return (ArrayNode) node; } /** * * @param result * added to the array of results (in DataModel multi get) */ public void addResult(ObjectNode result) { getResults().add(result); } /** * * @param count * added to answer if > 0 * @param limit * added to answer */ public void addCountLimit(long count, long limit) { ObjectNode node = getAnswer(); if (count >= 0) { node.put(DATAMODEL.JSON_COUNT.field, count); } node.put(DATAMODEL.JSON_LIMIT.field, limit); } /** * * @return the count of element (-1 if not found) */ public long getCount() { return getAnswer().path(DATAMODEL.JSON_COUNT.field).asLong(-1); } public long getLimit() { return getAnswer().path(DATAMODEL.JSON_LIMIT.field).asLong(100); } /** * Add options in answer * * @param allow * @param path * @param detailedAllow */ public void addOptions(String allow, String path, ArrayNode detailedAllow) { ObjectNode node = getAnswer(); node.put(HttpHeaders.Names.ALLOW, allow); node.put(REST_FIELD.X_ALLOW_URIS.field, path); if (detailedAllow != null) { node.putArray(REST_FIELD.X_DETAILED_ALLOW.field).addAll(detailedAllow); } } public String getAllowOption() { return getAnswer().path(HttpHeaders.Names.ALLOW).asText(); } public String getAllowUrisOption() { return getAnswer().path(REST_FIELD.X_ALLOW_URIS.field).asText(); } public ArrayNode getDetailedAllowOption() { JsonNode node = getAnswer().path(REST_FIELD.X_DETAILED_ALLOW.field); if (node.isMissingNode()) { return JsonHandler.createArrayNode(); } else { return (ArrayNode) node; } } /** * The encoder is completed with extra necessary URI part containing ARG_X_AUTH_TIMESTAMP & ARG_X_AUTH_KEY * * @param hmacSha256 * SHA-256 key to create the signature * @param encoder * @param user * might be null * @param extraKey * might be null * @return an array of 2 value in order ARG_X_AUTH_TIMESTAMP and ARG_X_AUTH_KEY * @throws HttpInvalidAuthenticationException * if the computation of the authentication failed */ public static String[] getBaseAuthent(HmacSha256 hmacSha256, QueryStringEncoder encoder, String user, String extraKey) throws HttpInvalidAuthenticationException { QueryStringDecoder decoderQuery = new QueryStringDecoder(encoder.toString()); Map<String, List<String>> map = decoderQuery.parameters(); TreeMap<String, String> treeMap = new TreeMap<String, String>(); for (Entry<String, List<String>> entry : map.entrySet()) { String keylower = entry.getKey().toLowerCase(); List<String> values = entry.getValue(); if (values != null && !values.isEmpty()) { String last = values.get(values.size() - 1); treeMap.put(keylower, last); } } DateTime date = new DateTime(); treeMap.put(REST_ROOT_FIELD.ARG_X_AUTH_TIMESTAMP.field.toLowerCase(), date.toString()); if (user != null) { treeMap.put(REST_ROOT_FIELD.ARG_X_AUTH_USER.field.toLowerCase(), user); } try { String key = computeKey(hmacSha256, extraKey, treeMap, decoderQuery.path()); String[] result = { date.toString(), key }; return result; /* encoder.addParam(REST_ROOT_FIELD.ARG_X_AUTH_TIMESTAMP.field, date.toString()); encoder.addParam(REST_ROOT_FIELD.ARG_X_AUTH_KEY.field, key); */ } catch (Exception e) { throw new HttpInvalidAuthenticationException(e); } } /** * Check Time only (no signature) * * @param maxInterval * @throws HttpInvalidAuthenticationException */ public void checkTime(long maxInterval) throws HttpInvalidAuthenticationException { DateTime dateTime = new DateTime(); String date = getXAuthTimestamp(); if (date != null && !date.isEmpty()) { DateTime received = DateTime.parse(date); if (maxInterval > 0) { Duration duration = new Duration(received, dateTime); if (Math.abs(duration.getMillis()) >= maxInterval) { throw new HttpInvalidAuthenticationException( "timestamp is not compatible with the maximum delay allowed"); } } } else if (maxInterval > 0) { throw new HttpInvalidAuthenticationException("timestamp absent while required"); } } /** * This implementation of authentication is as follow: if X_AUTH is included in the URI or Header<br> * 0) Check that timestamp is correct (|curtime - timestamp| < maxinterval) from ARG_X_AUTH_TIMESTAMP, if maxInterval is 0, * not mandatory<br> * 1) Get all URI args (except ARG_X_AUTH_KEY itself, but including timestamp), lowered case, in alphabetic order<br> * 2) Add an extra Key if not null (from ARG_X_AUTH_INTERNALKEY)<br> * 3) Compute an hash (SHA-1 or SHA-256)<br> * 4) Compare this hash with ARG_X_AUTH_KEY<br> * * @param hmacSha256 * SHA-256 key to create the signature * @param extraKey * will be added as ARG_X_AUTH_INTERNALKEY might be null * @param maxInterval * ARG_X_AUTH_TIMESTAMP will be tested if value > 0 * @throws HttpInvalidAuthenticationException * if the authentication failed */ public void checkBaseAuthent(HmacSha256 hmacSha256, String extraKey, long maxInterval) throws HttpInvalidAuthenticationException { TreeMap<String, String> treeMap = new TreeMap<String, String>(); String argPath = getUri(); ObjectNode arguri = getUriArgs(); if (arguri == null) { throw new HttpInvalidAuthenticationException("Not enough argument"); } Iterator<Entry<String, JsonNode>> iterator = arguri.fields(); DateTime dateTime = new DateTime(); DateTime received = null; while (iterator.hasNext()) { Entry<String, JsonNode> entry = iterator.next(); String key = entry.getKey(); if (key.equalsIgnoreCase(REST_ROOT_FIELD.ARG_X_AUTH_KEY.field)) { continue; } JsonNode values = entry.getValue(); if (key.equalsIgnoreCase(REST_ROOT_FIELD.ARG_X_AUTH_TIMESTAMP.field)) { received = DateTime.parse(values.asText()); } String keylower = key.toLowerCase(); if (values != null) { String val = null; if (values.isArray()) { JsonNode jsonNode = values.get(values.size() - 1); val = jsonNode.asText(); } else { val = values.asText(); } treeMap.put(keylower, val); } } if (received == null) { String date = getXAuthTimestamp(); received = DateTime.parse(date); treeMap.put(REST_ROOT_FIELD.ARG_X_AUTH_TIMESTAMP.field.toLowerCase(), date); } String user = getXAuthUser(); if (user != null && !user.isEmpty()) { treeMap.put(REST_ROOT_FIELD.ARG_X_AUTH_USER.field.toLowerCase(), user); } if (maxInterval > 0 && received != null) { Duration duration = new Duration(received, dateTime); if (Math.abs(duration.getMillis()) >= maxInterval) { throw new HttpInvalidAuthenticationException( "timestamp is not compatible with the maximum delay allowed"); } } else if (maxInterval > 0) { throw new HttpInvalidAuthenticationException("timestamp absent while required"); } String key = computeKey(hmacSha256, extraKey, treeMap, argPath); if (!key.equalsIgnoreCase(getXAuthKey())) { throw new HttpInvalidAuthenticationException("Invalid Authentication Key"); } } /** * @param hmacSha256 * SHA-256 key to create the signature * @param extraKey * might be null * @param treeMap * @param argPath * @throws HttpInvalidAuthenticationException */ protected static String computeKey(HmacSha256 hmacSha256, String extraKey, TreeMap<String, String> treeMap, String argPath) throws HttpInvalidAuthenticationException { Set<String> keys = treeMap.keySet(); StringBuilder builder = new StringBuilder(argPath); if (!keys.isEmpty() || extraKey != null) { builder.append('?'); } boolean first = true; for (String keylower : keys) { if (first) { first = false; } else { builder.append('&'); } builder.append(keylower).append('=').append(treeMap.get(keylower)); } if (extraKey != null) { if (!keys.isEmpty()) { builder.append("&"); } builder.append(REST_ROOT_FIELD.ARG_X_AUTH_INTERNALKEY.field).append("=").append(extraKey); } try { return hmacSha256.cryptToHex(builder.toString()); } catch (Exception e) { throw new HttpInvalidAuthenticationException(e); } } @Override public String toString() { return JsonHandler.writeAsString(arguments); } public String prettyPrint() { return JsonHandler.prettyPrint(arguments); } public static ObjectNode fillDetailedAllow(METHOD method, String path, String command, ObjectNode body, JsonNode result) { ObjectNode node = JsonHandler.createObjectNode(); ObjectNode node2 = node.putObject(method.name()); node2.put(REST_FIELD.JSON_PATH.field, "/" + path); node2.put(REST_ROOT_FIELD.JSON_COMMAND.field, command); if (body != null) { node2.set(RestArgument.REST_FIELD.JSON_JSON.field, body); } if (result != null) { node2.set(RestArgument.REST_GROUP.ARGS_ANSWER.group, result); } return node; } }