Java tutorial
/* * SOFTWARE USE PERMISSION * * By downloading and accessing this software and associated documentation files ("Software") you are granted the * unrestricted right to deal in the Software, including, without limitation the right to use, copy, modify, publish, * sublicense and grant such rights to third parties, subject to the following conditions: * * The following copyright notice and this permission notice shall be included in all copies, modifications or * substantial portions of this Software: Copyright 2016 GSM Association. * * THE SOFTWARE IS PROVIDED "AS IS," WITHOUT WARRANTY OF ANY KIND, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. YOU * AGREE TO INDEMNIFY AND HOLD HARMLESS THE AUTHORS AND COPYRIGHT HOLDERS FROM AND AGAINST ANY SUCH LIABILITY. */ package com.gsma.mobileconnect.utils; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.MissingNode; import com.gsma.mobileconnect.oidc.ParsedIdToken; import com.gsma.mobileconnect.oidc.RequestTokenResponse; import com.gsma.mobileconnect.oidc.RequestTokenResponseData; import org.apache.commons.codec.binary.Base64; import java.io.IOException; import java.util.Calendar; import java.util.Iterator; /** * Class to hold Json utility functions. */ public class JsonUtils { /** * Extract the requested URL from the Operator Not Identified Discovery response. * * @param jsonDoc The json object to check. * @param relToFind The URL to find. * @return The requested URL if present, null otherwise. */ public static String extractUrl(JsonNode jsonDoc, String relToFind) { if (null == jsonDoc) { throw new IllegalArgumentException("Missing argument jsonDoc"); } if (null == relToFind) { throw new IllegalArgumentException("Missing argument relToFind"); } JsonNode linksNode = jsonDoc.get(Constants.LINKS_FIELD_NAME); if (null == linksNode) { return null; } Iterator<JsonNode> i = linksNode.elements(); while (i.hasNext()) { JsonNode node = i.next(); String rel = getOptionalStringValue(node, Constants.REL_FIELD_NAME); if (relToFind.equals(rel)) { return getOptionalStringValue(node, Constants.HREF_FIELD_NAME); } } return null; } /** * Extract an error response from the discovery response if any. * <p> * A discovery response has an error if the error field is present. * * @param jsonDoc The discovery response to examine. * @return The error response if present, null otherwise. */ public static ErrorResponse getErrorResponse(JsonNode jsonDoc) { if (null == jsonDoc) { throw new IllegalArgumentException("Missing argument jsonDoc"); } String error = getOptionalStringValue(jsonDoc, Constants.ERROR_NAME); String errorDescription = getOptionalStringValue(jsonDoc, Constants.ERROR_DESCRIPTION_NAME); // Sometimes "description" rather than "error_description" is seen String altErrorDescription = getOptionalStringValue(jsonDoc, Constants.ERROR_DESCRIPTION_ALT_NAME); String errorUri = getOptionalStringValue(jsonDoc, Constants.ERROR_URI_NAME); if (StringUtils.isNullOrEmpty(error)) { return null; } if (!StringUtils.isNullOrEmpty(altErrorDescription)) { if (StringUtils.isNullOrEmpty(errorDescription)) { errorDescription = altErrorDescription; } else { errorDescription += " " + altErrorDescription; } } ErrorResponse errorResponse = new ErrorResponse(); errorResponse.set_error(error); errorResponse.set_error_description(errorDescription); errorResponse.set_error_uri(errorUri); return errorResponse; } /** * Parse the string into a Jackson Tree * <p> * Returns a missing node if the string is empty. * * @param jsonStr The Json string to parse. * @return The Jackson Json Tree * @throws IOException */ public static JsonNode parseJson(String jsonStr) throws IOException { if (StringUtils.isNullOrEmpty(jsonStr)) { return MissingNode.getInstance(); } ObjectMapper objectMapper = new ObjectMapper(); JsonNode root = objectMapper.readTree(jsonStr); if (null == root) { return MissingNode.getInstance(); } return root; } /** * Parse an Operator Identified Discovery Result. * <p> * If the discovery result looks like an Operator Identified result a ParsedOperatorIdentifiedDiscoveryResult is returned. * * @param jsonDoc The discovery result to examine. * @return The parsed operator discovery result or null. */ public static ParsedOperatorIdentifiedDiscoveryResult parseOperatorIdentifiedDiscoveryResult(JsonNode jsonDoc) { if (null == jsonDoc) { throw new IllegalArgumentException("Missing parameter jsonDoc"); } try { ParsedOperatorIdentifiedDiscoveryResult parsedOperatorIdentifiedDiscoveryResult = new ParsedOperatorIdentifiedDiscoveryResult(); JsonNode responseNode = getExpectedNode(jsonDoc, Constants.RESPONSE_FIELD_NAME); parsedOperatorIdentifiedDiscoveryResult .setClientId(getExpectedStringValue(responseNode, Constants.CLIENT_ID_FIELD_NAME)); parsedOperatorIdentifiedDiscoveryResult .setClientSecret(getExpectedStringValue(responseNode, Constants.CLIENT_SECRET_FIELD_NAME)); JsonNode linkNode = responseNode.path(Constants.APIS_FIELD_NAME).path(Constants.OPERATORID_FIELD_NAME) .path(Constants.LINK_FIELD_NAME); if (linkNode.isMissingNode()) { return null; } Iterator<JsonNode> i = linkNode.elements(); while (i.hasNext()) { JsonNode node = i.next(); String rel = getExpectedStringValue(node, Constants.REL_FIELD_NAME); if (Constants.AUTHORIZATION_REL.equals(rel)) { parsedOperatorIdentifiedDiscoveryResult .setAuthorizationHref(getExpectedStringValue(node, Constants.HREF_FIELD_NAME)); } else if (Constants.TOKEN_REL.equals(rel)) { parsedOperatorIdentifiedDiscoveryResult .setTokenHref(getExpectedStringValue(node, Constants.HREF_FIELD_NAME)); } else if (Constants.USER_INFO_REL.equals(rel)) { parsedOperatorIdentifiedDiscoveryResult .setUserInfoHref(getExpectedStringValue(node, Constants.HREF_FIELD_NAME)); } else if (Constants.PREMIUM_INFO_REL.equals(rel)) { parsedOperatorIdentifiedDiscoveryResult .setPremiumInfoHref(getExpectedStringValue(node, Constants.HREF_FIELD_NAME)); } } return parsedOperatorIdentifiedDiscoveryResult; } catch (NoFieldException ex) { return null; } } /** * Parse the response from a request token call. * <p> * The json is expected to be either an error or a successful request token response. * * @param timeReceived The time the response was received, used to timestamp the returned object. * @param jsonStr The Json string to examine. * @return The parsed response. * @throws IOException */ public static RequestTokenResponse parseRequestTokenResponse(Calendar timeReceived, String jsonStr) throws IOException { RequestTokenResponse requestTokenResponse = new RequestTokenResponse(); ObjectMapper objectMapper = new ObjectMapper(); JsonNode jsonDoc = objectMapper.readTree(jsonStr); ErrorResponse errorResponse = getErrorResponse(jsonDoc); if (null != errorResponse) { requestTokenResponse.setErrorResponse(getErrorResponse(jsonDoc)); return requestTokenResponse; } RequestTokenResponseData responseData = new RequestTokenResponseData(); requestTokenResponse.setResponseData(responseData); responseData.setTimeReceived(timeReceived); responseData.setOriginalResponse(jsonStr); responseData.set_access_token(getOptionalStringValue(jsonDoc, Constants.ACCESS_TOKEN_FIELD_NAME)); responseData.set_token_type(getOptionalStringValue(jsonDoc, Constants.TOKEN_TYPE_FIELD_NAME)); responseData.set_refresh_token(getOptionalStringValue(jsonDoc, Constants.REFRESH_TOKEN_FIELD_NAME)); Integer expiresIn = getOptionalIntegerValue(jsonDoc, Constants.EXPIRES_IN_FIELD_NAME); responseData.set_expires_in(expiresIn); String idTokenStr = getOptionalStringValue(jsonDoc, Constants.ID_TOKEN_FIELD_NAME); if (!StringUtils.isNullOrEmpty(idTokenStr)) { ParsedIdToken parsedIdToken = createParsedIdToken(idTokenStr); responseData.setParsedIdToken(parsedIdToken); } return requestTokenResponse; } /** * Parse the passed string as an id token. * * @param idTokenStr The string to parse. * @return A ParsedIdToken. * @throws IOException */ public static ParsedIdToken createParsedIdToken(String idTokenStr) throws IOException { IdToken idToken = parseIdToken(idTokenStr); ParsedIdToken parsedIdToken = new ParsedIdToken(); parsedIdToken.set_id_token(idTokenStr); parsedIdToken.set_pcr(idToken.getPayload().get_sub()); parsedIdToken.set_nonce(idToken.getPayload().get_nonce()); parsedIdToken.set_id_token_claims(idToken.getPayload().getClaims()); parsedIdToken.set_id_token_verified(false); return parsedIdToken; } /** * Parse the string as an id_token. * * @param idToken The string to parse. * @return An IdToken. * @throws IOException */ private static IdToken parseIdToken(String idToken) throws IOException { String[] parts = idToken.split("\\."); if (parts.length != 3) { throw new IllegalArgumentException("Not an id_token"); } String header = new String(Base64.decodeBase64(parts[0].getBytes("UTF-8")), "UTF-8"); String payload = new String(Base64.decodeBase64(parts[1].getBytes("UTF-8")), "UTF-8"); IdToken parsedIdToken = new IdToken(); parsedIdToken.setHeader(createJwtHeader(header)); parsedIdToken.setPayload(createJwtPayload(payload)); parsedIdToken.setSignature(parts[2]); return parsedIdToken; } /** * Parse the string as a Jwt header. * * @param header The string to parse. * @return A Jwt header. * @throws IOException */ static private JwtHeader createJwtHeader(String header) throws IOException { ObjectMapper objectMapper = new ObjectMapper(); JsonNode jsonDoc = objectMapper.readTree(header); JwtHeader parsedJwtHeader = new JwtHeader(); parsedJwtHeader.set_typ(getOptionalStringValue(jsonDoc, Constants.TYP_FIELD_NAME)); parsedJwtHeader.set_alg(getOptionalStringValue(jsonDoc, Constants.ALG_FIELD_NAME)); return parsedJwtHeader; } /** * Parse the string as a Jwt payload. * * @param payload The string to parse. * @return A JwtPayload. * @throws IOException */ static private JwtPayload createJwtPayload(String payload) throws IOException { ObjectMapper objectMapper = new ObjectMapper(); JsonNode jsonDoc = objectMapper.readTree(payload); JwtPayload parsedJwtPayload = new JwtPayload(); parsedJwtPayload.setClaims(jsonDoc); return parsedJwtPayload; } /** * Query the parent node for the named child node. * * @param parentNode Node to check. * @param name Name of child node. * @return The child node if found. * @throws NoFieldException Thrown if field not found. */ static private JsonNode getExpectedNode(JsonNode parentNode, String name) throws NoFieldException { JsonNode childNode = parentNode.get(name); if (null == childNode) { throw new NoFieldException(name); } return childNode; } /** * Query the parent node for the named child node and return the text value of the child node * * @param parentNode Node to check. * @param name Name of the child node. * @return The text value of the child node. * @throws NoFieldException Thrown if field not found. */ static private String getExpectedStringValue(JsonNode parentNode, String name) throws NoFieldException { JsonNode childNode = parentNode.get(name); if (null == childNode) { throw new NoFieldException(name); } return childNode.textValue(); } /** * Return the string value of an optional child node. * <p> * Check the parent node for the named child, if found return the string contents of the child node, return null otherwise. * * @param parentNode The node to check. * @param name Name of the optional child node. * @return Text value of child node, if found, null otherwise. */ static String getOptionalStringValue(JsonNode parentNode, String name) { JsonNode childNode = parentNode.get(name); if (null == childNode) { return null; } else { return childNode.textValue(); } } /** * Return the integer value of an optional child node. * <p> * Check the parent node for the named child, if found return the integer contents of the child node, return null otherwise. * * @param parentNode The node to check * @param name The name of the optional child. * @return Integer value of the child node if present, null otherwise. */ static private Integer getOptionalIntegerValue(JsonNode parentNode, String name) { JsonNode childNode = parentNode.get(name); if (null == childNode) { return null; } else { return childNode.asInt(); } } /** * Return the long value of an optional child node. * <p> * Check the parent node for the named child, if found return the long contents of the child node, return null otherwise. * * @param parentNode The node to check * @param name The name of the optional child. * @return Long value of the child node if present, null otherwise. */ static private Long getOptionalLongValue(JsonNode parentNode, String name) { JsonNode childNode = parentNode.get(name); if (null == childNode) { return null; } else { return childNode.asLong(); } } /** * Return the ttl field in the Json object. * * @param node The Json object to examine. * @return The ttl value if present, null otherwise. */ static public Long getDiscoveryResponseTtl(JsonNode node) { return getOptionalLongValue(node, Constants.TTL_FIELD_NAME); } }