bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford * The contents of this file are subject to the terms of the Common Development and
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford * Distribution License (the License). You may not use this file except in compliance with the
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford * specific language governing permission and limitations under the License.
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford * When distributing Covered Software, include this CDDL Header Notice in each file and include
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford * Header, with the fields enclosed by brackets [] replaced by your own identifying
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford * information: "Portions copyright [year] [name of copyright owner]".
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford * Copyright 2016 ForgeRock AS.
6b642033251d6306c85cd6a38ea40f5a9907d278Tony Bamford * Portions Copyright 2008 Sun Microsystems Inc.
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford * Construct REST endpoints and call them.
e80c6451e400ef4956af170a3efaa0423aede68cTony Bamford public static final String AUTHENTICATION_URI = "/json/{REALM}authenticate";
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford public static final String AUTHENTICATION_URI_API_VERSION = "1.0";
e80c6451e400ef4956af170a3efaa0423aede68cTony Bamford public static final String CREATE_PROFILE_URI = "/json/{REALM}agents";
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford public static final String CREATE_PROFILE_URI_ACTION_VALUE = "create";
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford public static final String CREATE_PROFILE_URI_API_VERSION = "1.0";
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford public static final String SERVER_INFO_URI = "/json/serverinfo/*";
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford public static final String SERVER_INFO_URI_API_VERSION = "1.0";
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford private static final String AUTH_INDEX_TYPE_NAME = "authIndexType";
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford private static final String AUTH_INDEX_TYPE_VALUE = "module";
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford private static final String AUTH_INDEX_VALUE_NAME = "authIndexValue";
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford private static final String AUTH_INDEX_VALUE_VALUE = "Application";
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford private RESTEndpoint(RESTEndpointBuilder builder) {
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford * Call a REST endpoint, returning its response.
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford * @return RESTResponse object containing status and text of returned value.
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford * @throws IOException
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford public RESTResponse call() throws IOException {
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford URL serviceURL = new URL(path + paramsToString());
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford urlConnect = (HttpURLConnection) serviceURL.openConnection();
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford for (Map.Entry<String, String> entry : headers.entrySet()) {
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford urlConnect.setRequestProperty(entry.getKey(), entry.getValue());
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford // post data
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford try (DataOutputStream output = new DataOutputStream(urlConnect.getOutputStream())){
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford // read response
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford response.setResponseCode(urlConnect.getResponseCode());
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford try (BufferedReader reader = new BufferedReader(new InputStreamReader(urlConnect.getInputStream()))) {
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford } catch (FileNotFoundException | UnknownHostException | ConnectException ex) {
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford try (BufferedReader br = new BufferedReader(new InputStreamReader(is))) {
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford * Convert the parameters into a string of ?name=value&othername=othervalue
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford * @return the parameters as a string
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford for (Map.Entry<String, String> entry : parameters.entrySet()) {
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford * ONLY EVER USE THIS FUNCTION FOR DEBUGGING PURPOSES. IT HIDES PASSWORDS. This obviously won't be what you
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford * want in real life.
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford * @return A string representing the headers of this endpoint, with a clumsy attempt to knock out clear text
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford for (Map.Entry<String, String> entry : headers.entrySet()) {
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford if (entry.getKey().toLowerCase().contains("password")) {
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford * Turn this RESTEndpoint into a string - ONLY for debugging purposes.
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford * @return a representation of this endpoint.
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford if (postData.toLowerCase().contains("password")) {
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford result.append(postData.length() + " bytes of POST data (hidden as it appears to contain a password)");
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford * For a little less than total debugging, try this:
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford * @return The path of the endpoint
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford * Build a RESTEndpoint
e80c6451e400ef4956af170a3efaa0423aede68cTony Bamford * This is where we assemble the path (straightforward in itself) but substitute the realm. For a number of
e80c6451e400ef4956af170a3efaa0423aede68cTony Bamford * URLs we use, the realm is not involved (none of the OIDC calls use it) but for others (like the identity
e80c6451e400ef4956af170a3efaa0423aede68cTony Bamford * endpoint) it is very important. Unfortunately substituting it is painful as we can accidentally change
e80c6451e400ef4956af170a3efaa0423aede68cTony Bamford * path1/{REALM}/path2
e80c6451e400ef4956af170a3efaa0423aede68cTony Bamford * path1//path2
e80c6451e400ef4956af170a3efaa0423aede68cTony Bamford * when the realm is undefined (i.e. it is the root realm), or even worse:
e80c6451e400ef4956af170a3efaa0423aede68cTony Bamford * path1///path2
e80c6451e400ef4956af170a3efaa0423aede68cTony Bamford * when the realm is set to "/".
e80c6451e400ef4956af170a3efaa0423aede68cTony Bamford * @return the carefully assembled path
e80c6451e400ef4956af170a3efaa0423aede68cTony Bamford // Trim the realm. Note that in this way if the caller set the realm to "/" (root realm), we trim it
e80c6451e400ef4956af170a3efaa0423aede68cTony Bamford // such that it becomes zero length.
e80c6451e400ef4956af170a3efaa0423aede68cTony Bamford realm = realm.substring(0, realm.length() - 1);
e80c6451e400ef4956af170a3efaa0423aede68cTony Bamford result = result.replace("{REALM}/", "{REALM}");
e80c6451e400ef4956af170a3efaa0423aede68cTony Bamford result = result.replace("{REALM}", realm + "/");
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford * Add the specified value to the path carefully. We must never end up gluing together two "/" characters
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford * (one from the end of the previous path and another from the start of the next path).
e80c6451e400ef4956af170a3efaa0423aede68cTony Bamford * @param incoming the value to append to the path.
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford * @return the rest call builder object for fluency.
e80c6451e400ef4956af170a3efaa0423aede68cTony Bamford public RESTEndpointBuilder path(String incoming) {
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford return this;
e80c6451e400ef4956af170a3efaa0423aede68cTony Bamford incoming = incoming.substring(0, incoming.length() - 1);
e80c6451e400ef4956af170a3efaa0423aede68cTony Bamford return this;
e80c6451e400ef4956af170a3efaa0423aede68cTony Bamford * Add the specified realm.
e80c6451e400ef4956af170a3efaa0423aede68cTony Bamford * @param s The realm.
e80c6451e400ef4956af170a3efaa0423aede68cTony Bamford * @return the rest call builder object for fluency.
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford return this;
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford * Add the specified name/value pair to the list of parameters. The list of parameters is preserved in order,
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford * even though this technically may not be necessary.
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford * @param name the name of the parameter
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford * @param value the value of the parameter
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford * @return the rest call builder object for added fluency
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford public RESTEndpointBuilder parameter(String name, String value) {
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford if (name.startsWith("&") || name.startsWith("?")) {
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford return this;
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford * Add the auth index name and auth index value to the list of parameters.
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford * @return the current rest call builder object
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford public RESTEndpointBuilder addModuleParameters() {
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford parameters.put(AUTH_INDEX_TYPE_NAME, AUTH_INDEX_TYPE_VALUE);
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford parameters.put(AUTH_INDEX_VALUE_NAME, AUTH_INDEX_VALUE_VALUE);
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford return this;
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford * Add HTTP POST data. No checking is done in the case where we're actually building a GET and the post
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford * data will still be written.
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford * @return the current rest call builder object
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford public RESTEndpointBuilder postData(String s) {
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford return this;
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford * Set the HTTP method to GET.
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford * @return the current rest call builder object
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford return this;
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford * Set the HTTP method to POST
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford * @return the current rest call builder object
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford return this;
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford * Add the name/value pair to the outgoing headers.
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford * @param header The header name
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford * @param value The header value
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford * @return the current rest call builder object
e80c6451e400ef4956af170a3efaa0423aede68cTony Bamford public RESTEndpointBuilder header(String header, String value) {
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford return this;
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford * Set the API version for this endpoint.
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford * @param apiVersion The specified API version
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford * @return the current rest call builder object
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford public RESTEndpointBuilder apiVersion(String apiVersion) {
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford headers.put("Accept-API-Version", "protocol=1.0,resource=" + apiVersion);
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford return this;
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford * Build the rest endpoint object
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford * @return the built rest endpoint
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford return new RESTEndpoint(this);
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford * Class to encapsulate the response from REST API.
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford * @return All the text returned by the endpoint, as a list of lines
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford * Set the content, as a list of lines, returned by the endpoint
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford * @param content The content, as a list of lines
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford * @return the response code of the endpoint
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford * set the response code of the endpoint
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford * @param responseCode the response code to set
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford public void setResponseCode(int responseCode) {
bbe59b9e7b19daeef0e63ffa77aaefb67aceb85eTony Bamford * @return A string representation of the endpoint's response