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.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 * @param defaultValue in case of no existing element with key * @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 getListOfStringElements(final JsonNode input) throws SlCommandoParserException { final List result = new ArrayList<>(); if (input != null) { if (input.isArray()) { final Iterator 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 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 getMapOfStringElements(final JsonNode input) throws SlCommandoParserException { final Map result = new HashMap<>(); if (input != null) { if (input.isArray()) { final Iterator arrayIterator = input.iterator(); while (arrayIterator.hasNext()) { final JsonNode next = arrayIterator.next(); final Iterator> entry = next.fields(); entitySetToMap(result, entry); } } else if (input.isObject()) { final Iterator> 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 result, final Iterator> entry) { while (entry.hasNext()) { final Entry 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 true, 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 * @param mustBeSigned Throw an error if the result was not signed * @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"); } } 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; } }