summaryrefslogtreecommitdiff
path: root/eaaf_modules/eaaf_module_auth_sl20/src/main/java/at/gv/egiz/eaaf/modules/auth/sl20/utils/SL20JsonExtractorUtils.java
diff options
context:
space:
mode:
Diffstat (limited to 'eaaf_modules/eaaf_module_auth_sl20/src/main/java/at/gv/egiz/eaaf/modules/auth/sl20/utils/SL20JsonExtractorUtils.java')
-rw-r--r--eaaf_modules/eaaf_module_auth_sl20/src/main/java/at/gv/egiz/eaaf/modules/auth/sl20/utils/SL20JsonExtractorUtils.java389
1 files changed, 389 insertions, 0 deletions
diff --git a/eaaf_modules/eaaf_module_auth_sl20/src/main/java/at/gv/egiz/eaaf/modules/auth/sl20/utils/SL20JsonExtractorUtils.java b/eaaf_modules/eaaf_module_auth_sl20/src/main/java/at/gv/egiz/eaaf/modules/auth/sl20/utils/SL20JsonExtractorUtils.java
new file mode 100644
index 00000000..eb6de461
--- /dev/null
+++ b/eaaf_modules/eaaf_module_auth_sl20/src/main/java/at/gv/egiz/eaaf/modules/auth/sl20/utils/SL20JsonExtractorUtils.java
@@ -0,0 +1,389 @@
+package at.gv.egiz.eaaf.modules.auth.sl20.utils;
+
+import java.util.ArrayList;
+import java.util.Base64;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.apache.http.Header;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.utils.URIBuilder;
+import org.apache.http.util.EntityUtils;
+import org.jose4j.base64url.Base64Url;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+import at.gv.egiz.eaaf.modules.auth.sl20.data.VerificationResult;
+import at.gv.egiz.eaaf.modules.auth.sl20.exceptions.SL20Exception;
+import at.gv.egiz.eaaf.modules.auth.sl20.exceptions.SlCommandoParserException;
+
+public class SL20JsonExtractorUtils {
+ private static final Logger log = LoggerFactory.getLogger(SL20JsonExtractorUtils.class);
+ private static JsonMapper mapper = new JsonMapper();
+
+ /**
+ * Extract String value from JSON.
+ *
+ * @param input JSON
+ * @param keyID Element identifier
+ * @param isRequired true, if the element must not null
+ * @return Value of this element
+ * @throws SlCommandoParserException In case an error
+ */
+ public static String getStringValue(final JsonNode input, final String keyID, final boolean isRequired)
+ throws SlCommandoParserException {
+ try {
+ final JsonNode internal = getAndCheck(input, keyID, isRequired);
+
+ if (internal != null) {
+ return internal.asText();
+ } else {
+ return null;
+ }
+
+ } catch (final SlCommandoParserException e) {
+ throw e;
+
+ } catch (final Exception e) {
+ throw new SlCommandoParserException("Can not extract String value with keyId: " + keyID, e);
+
+ }
+ }
+
+ /**
+ * Extract Boolean value from JSON.
+ *
+ * @param input JSON
+ * @param keyID Element identifier
+ * @param isRequired true, if the element must not null
+ * @return Boolean
+ * @throws SlCommandoParserException In case of an error
+ */
+ public static boolean getBooleanValue(final ObjectNode input, final String keyID, final boolean isRequired,
+ final boolean defaultValue) throws SlCommandoParserException {
+ try {
+ final JsonNode internal = getAndCheck(input, keyID, isRequired);
+
+ if (internal != null) {
+ return internal.asBoolean();
+ } else {
+ return defaultValue;
+ }
+
+ } catch (final SlCommandoParserException e) {
+ throw e;
+
+ } catch (final Exception e) {
+ throw new SlCommandoParserException("Can not extract Boolean value with keyId: " + keyID, e);
+
+ }
+ }
+
+ /**
+ * Extract JSONObject value from JSON.
+ *
+ * @param input JSON
+ * @param keyID Element identifier
+ * @param isRequired true, if the element must not null
+ * @return JSON node
+ * @throws SlCommandoParserException In case of an error
+ */
+ public static JsonNode getJsonObjectValue(final JsonNode input, final String keyID, final boolean isRequired)
+ throws SlCommandoParserException {
+ try {
+ final JsonNode internal = getAndCheck(input, keyID, isRequired);
+
+ if (internal != null) {
+ return internal;
+ } else {
+ return null;
+ }
+
+ } catch (final SlCommandoParserException e) {
+ throw e;
+
+ } catch (final Exception e) {
+ throw new SlCommandoParserException("Can not extract Boolean value with keyId: " + keyID, e);
+
+ }
+ }
+
+ /**
+ * Extract a List of String elements from a JSON element.
+ *
+ * @param input JSON
+ * @return List of Elements in this node
+ * @throws SlCommandoParserException In case of an error
+ */
+ public static List<String> getListOfStringElements(final JsonNode input) throws SlCommandoParserException {
+ final List<String> result = new ArrayList<>();
+ if (input != null) {
+ if (input.isArray()) {
+ final Iterator<JsonNode> arrayIterator = input.iterator();
+ while (arrayIterator.hasNext()) {
+ final JsonNode next = arrayIterator.next();
+ if (next.isTextual()) {
+ result.add(next.asText());
+ }
+ }
+
+ } else if (input.isTextual()) {
+ result.add(input.asText());
+
+ } else {
+ log.warn("JSON Element IS NOT a JSON array or a JSON Primitive");
+ throw new SlCommandoParserException("JSON Element IS NOT a JSON array or a JSON Primitive");
+
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Extract Map of Key/Value pairs from a JSON Element.
+ *
+ * @param input parent JSON object
+ * @param keyID KeyId of the child that should be parsed
+ * @param isRequired true, if the element must not null
+ * @return Map of element pairs
+ * @throws SlCommandoParserException In case of an error
+ */
+ public static Map<String, String> getMapOfStringElements(final JsonNode input, final String keyID,
+ final boolean isRequired) throws SlCommandoParserException {
+ final JsonNode internal = getAndCheck(input, keyID, isRequired);
+ return getMapOfStringElements(internal);
+
+ }
+
+ /**
+ * Extract Map of Key/Value pairs from a JSON Element.
+ *
+ * @param input JSON
+ * @return Map of element pairs
+ * @throws SlCommandoParserException in case of an error
+ */
+ public static Map<String, String> getMapOfStringElements(final JsonNode input) throws SlCommandoParserException {
+ final Map<String, String> result = new HashMap<>();
+
+ if (input != null) {
+ if (input.isArray()) {
+ final Iterator<JsonNode> arrayIterator = input.iterator();
+ while (arrayIterator.hasNext()) {
+ final JsonNode next = arrayIterator.next();
+ final Iterator<Entry<String, JsonNode>> entry = next.fields();
+ entitySetToMap(result, entry);
+
+ }
+
+ } else if (input.isObject()) {
+ final Iterator<Entry<String, JsonNode>> objectKeys = input.fields();
+ entitySetToMap(result, objectKeys);
+
+ } else {
+ throw new SlCommandoParserException("JSON Element IS NOT a JSON array or a JSON object");
+ }
+
+ }
+
+ return result;
+ }
+
+ private static void entitySetToMap(final Map<String, String> result, final Iterator<Entry<String, JsonNode>> entry) {
+ while (entry.hasNext()) {
+ final Entry<String, JsonNode> el = entry.next();
+ if (result.containsKey(el.getKey())) {
+ log.info("Attr. Map already contains Element with Key: " + el.getKey() + ". Overwrite element ... ");
+ }
+
+ result.put(el.getKey(), el.getValue().asText());
+
+ }
+
+ }
+
+ /**
+ * Extract Security-Layer 2.0 result from response object.
+ *
+ * @param command SL2.0 command
+ * @param decrypter JWS decrypter implementation
+ * @param mustBeEncrypted if <code>true</code>, the result must be encrypted
+ * @return decrypted JSON
+ * @throws SL20Exception In case of an error
+ */
+ public static JsonNode extractSL20Result(final JsonNode command, final IJoseTools decrypter,
+ final boolean mustBeEncrypted) throws SL20Exception {
+ final JsonNode result = command.get(SL20Constants.SL20_COMMAND_CONTAINER_RESULT);
+ final JsonNode encryptedResult = command.get(SL20Constants.SL20_COMMAND_CONTAINER_ENCRYPTEDRESULT);
+
+ if (result == null && encryptedResult == null) {
+ throw new SlCommandoParserException("NO result OR encryptedResult FOUND.");
+ } else if (encryptedResult == null && mustBeEncrypted) {
+ throw new SlCommandoParserException("result MUST be encrypted.");
+ } else if (encryptedResult != null && encryptedResult.isTextual()) {
+ try {
+ return decrypter.decryptPayload(encryptedResult.asText());
+
+ } catch (final Exception e) {
+ log.info("Can NOT decrypt SL20 result. Reason:" + e.getMessage());
+ if (!mustBeEncrypted) {
+ log.warn("Decrypted results are disabled by configuration. Parse result in plain if it is possible");
+
+ // dummy code
+ try {
+ final String[] signedPayload = encryptedResult.toString().split("\\.");
+ final JsonNode payLoad = mapper.getMapper()
+ .readTree(new String(Base64.getUrlDecoder().decode(signedPayload[1]), "UTF-8"));
+ return payLoad;
+
+ } catch (final Exception e1) {
+ log.debug("DummyCode FAILED, Reason: " + e1.getMessage() + " Ignore it ...");
+ throw new SL20Exception(e.getMessage(), null, e);
+
+ }
+
+ } else {
+ throw e;
+ }
+
+ }
+
+ } else if (result != null) {
+ return result;
+
+ } else {
+ throw new SlCommandoParserException("Internal build error");
+ }
+
+ }
+
+ /**
+ * Extract payLoad from generic transport container.
+ *
+ * @param container JSON
+ * @param joseTools JWS implementation
+ * @return Signature verification result that contains the payLoad
+ * @throws SlCommandoParserException In case of an error
+ */
+ public static VerificationResult extractSL20PayLoad(final JsonNode container, final IJoseTools joseTools,
+ final boolean mustBeSigned) throws SL20Exception {
+
+ final JsonNode sl20Payload = container.get(SL20Constants.SL20_PAYLOAD);
+ final JsonNode sl20SignedPayload = container.get(SL20Constants.SL20_SIGNEDPAYLOAD);
+
+ if (mustBeSigned && joseTools == null) {
+ throw new SlCommandoParserException("'joseTools' MUST be set if 'mustBeSigned' is 'true'");
+ }
+
+ if (sl20Payload == null && sl20SignedPayload == null) {
+ throw new SlCommandoParserException("NO payLoad OR signedPayload FOUND.");
+ } else if (sl20SignedPayload == null && mustBeSigned) {
+ throw new SlCommandoParserException("payLoad MUST be signed.");
+ } else if (joseTools != null && sl20SignedPayload != null && sl20SignedPayload.isTextual()) {
+ return joseTools.validateSignature(sl20SignedPayload.asText());
+
+ } else if (sl20Payload != null) {
+ return new VerificationResult(sl20Payload);
+ } else {
+ throw new SlCommandoParserException("Internal build error");
+ }
+
+ }
+
+ /**
+ * Extract generic transport container from httpResponse.
+ *
+ * @param httpResp Http response object
+ * @return JSON with SL2.0 response
+ * @throws SlCommandoParserException In case of an error
+ */
+ public static JsonNode getSL20ContainerFromResponse(final HttpResponse httpResp) throws SlCommandoParserException {
+ try {
+ JsonNode sl20Resp = null;
+ if (httpResp.getStatusLine().getStatusCode() == 303 || httpResp.getStatusLine().getStatusCode() == 307) {
+ final Header[] locationHeader = httpResp.getHeaders("Location");
+ if (locationHeader == null) {
+ throw new SlCommandoParserException("Find Redirect statuscode but not Location header");
+ }
+
+ final String sl20RespString = new URIBuilder(locationHeader[0].getValue()).getQueryParams().get(0).getValue();
+ sl20Resp = mapper.getMapper().readTree(Base64Url.decode(sl20RespString));
+
+ } else if (httpResp.getStatusLine().getStatusCode() == 200) {
+ if (httpResp.getEntity().getContentType() == null) {
+ throw new SlCommandoParserException("SL20 response contains NO ContentType");
+ }
+
+ if (!httpResp.getEntity().getContentType().getValue().startsWith("application/json")) {
+ throw new SlCommandoParserException(
+ "SL20 response with a wrong ContentType: " + httpResp.getEntity().getContentType().getValue());
+ }
+ sl20Resp = parseSL20ResultFromResponse(httpResp.getEntity());
+
+ } else if (httpResp.getStatusLine().getStatusCode() == 500 || httpResp.getStatusLine().getStatusCode() == 401
+ || httpResp.getStatusLine().getStatusCode() == 400) {
+ log.info(
+ "SL20 response with http-code: " + httpResp.getStatusLine().getStatusCode() + ". Search for error message");
+
+ try {
+ sl20Resp = parseSL20ResultFromResponse(httpResp.getEntity());
+
+ } catch (final Exception e) {
+ log.warn("SL20 response contains no valid JSON", e);
+ throw new SlCommandoParserException("SL20 response with http-code: "
+ + httpResp.getStatusLine().getStatusCode() + " AND NO valid JSON errormsg", e);
+
+ }
+
+ } else {
+ throw new SlCommandoParserException(
+ "SL20 response with http-code: " + httpResp.getStatusLine().getStatusCode());
+ }
+
+ log.info("Find JSON object in http response");
+ return sl20Resp;
+
+ } catch (final Exception e) {
+ throw new SlCommandoParserException("SL20 response parsing FAILED! Reason: " + e.getMessage(), e);
+
+ }
+ }
+
+ private static JsonNode parseSL20ResultFromResponse(final HttpEntity resp) throws Exception {
+ if (resp != null && resp.getContent() != null) {
+ final String rawSL20Resp = EntityUtils.toString(resp);
+ final JsonNode sl20Resp = mapper.getMapper().readTree(rawSL20Resp);
+
+ // TODO: check sl20Resp type like && sl20Resp.isJsonObject()
+ if (sl20Resp != null) {
+ return sl20Resp;
+
+ } else {
+ throw new SlCommandoParserException("SL2.0 can NOT parse to a JSON object");
+ }
+
+ } else {
+ throw new SlCommandoParserException("Can NOT find content in http response");
+ }
+
+ }
+
+ private static JsonNode getAndCheck(final JsonNode input, final String keyID, final boolean isRequired)
+ throws SlCommandoParserException {
+ final JsonNode internal = input.get(keyID);
+
+ if (internal == null && isRequired) {
+ throw new SlCommandoParserException("REQUIRED Element with keyId: " + keyID + " does not exist");
+ }
+
+ return internal;
+
+ }
+}