diff options
author | Thomas Lenz <thomas.lenz@egiz.gv.at> | 2019-12-04 19:43:32 +0100 |
---|---|---|
committer | Thomas Lenz <thomas.lenz@egiz.gv.at> | 2019-12-04 19:43:32 +0100 |
commit | 759ac5f42c6aff901dbeede4fbf1a1d2e08cad0f (patch) | |
tree | 2132024fc058b1ef5338bf50df575a3244cc3f9f /eaaf_modules/eaaf_module_auth_sl20/src/main/java/at/gv/egiz/eaaf/modules/auth/sl20/utils/SL20JsonExtractorUtils.java | |
parent | 4f15bdc45b08724d20c66c9fd74ea6a43a03c32f (diff) | |
download | EAAF-Components-759ac5f42c6aff901dbeede4fbf1a1d2e08cad0f.tar.gz EAAF-Components-759ac5f42c6aff901dbeede4fbf1a1d2e08cad0f.tar.bz2 EAAF-Components-759ac5f42c6aff901dbeede4fbf1a1d2e08cad0f.zip |
common EGIZ code-style refactoring
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.java | 407 |
1 files changed, 407 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..f4b5a724 --- /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,407 @@ +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 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; +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; + +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]))); + 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; + + } +} |