- Add openid-well-known-endpoint and treat it to automatically find the :

* issuer,
  * authorization_endpoint,
  * token_endpoint,
  * jwks_uri
- Replace openid-flow-type with openid-response-type
- Use an Enum for openid-response-type to limit to the values 'id_token',
  'token' or 'cod'
- Treat the response type as an implicit flow type allowing use of AWS
  Cognito
This commit is contained in:
David Bateman authored and adam committed 2026-04-13 15:49:01 +01:00
1 parent 4dfb4b5c66
commit 08bcd1f9e8
7 files changed
+476 -82

No files matched your search

@@ -63,6 +63,7 @@ public class AuthenticationProviderService implements SSOAuthenticationProviderS
/** /**
* The standard HTTP parameter which will be included within the URL by all * The standard HTTP parameter which will be included within the URL by all
* OpenID services upon successful implicit flow authentication. * OpenID services upon successful implicit flow authentication.
*
*/ */
public static final String IMPLICIT_TOKEN_PARAMETER_NAME = "id_token"; public static final String IMPLICIT_TOKEN_PARAMETER_NAME = "id_token";
@@ -136,13 +137,13 @@ public class AuthenticationProviderService implements SSOAuthenticationProviderS
Set<String> groups = null; Set<String> groups = null;
Map<String,String> tokens = Collections.emptyMap(); Map<String,String> tokens = Collections.emptyMap();
logger.debug("OpenID authentication with {} flow (ID: {}, Secret: {}, PKCE: {})", logger.debug("OpenID authentication with '{}' reponse type (ID: {}, Secret: {}, PKCE: {})",
confService.getFlowType(), confService.getResponseType(),
confService.getClientID(), confService.getClientID(),
confService.getClientSecret(), confService.getClientSecret(),
confService.isPKCERequired()); confService.isPKCERequired());
if (confService.getFlowType().equals("implicit")) { if (confService.isImplicitFlow()) {
String token = credentials.getParameter(IMPLICIT_TOKEN_PARAMETER_NAME); String token = credentials.getParameter(IMPLICIT_TOKEN_PARAMETER_NAME);
if (token != null) { if (token != null) {
JwtClaims claims = tokenService.validateToken(token); JwtClaims claims = tokenService.validateToken(token);
@@ -194,21 +195,15 @@ public class AuthenticationProviderService implements SSOAuthenticationProviderS
@Override @Override
public URI getLoginURI() throws GuacamoleException { public URI getLoginURI() throws GuacamoleException {
if (confService.getFlowType().equals("implicit")) { UriBuilder builder = UriBuilder.fromUri(confService.getAuthorizationEndpoint())
return UriBuilder.fromUri(confService.getAuthorizationEndpoint()) .queryParam("scope", confService.getScope())
.queryParam("scope", confService.getScope()) .queryParam("response_type", confService.getResponseType().toString())
.queryParam("response_type", IMPLICIT_TOKEN_PARAMETER_NAME) .queryParam("client_id", confService.getClientID())
.queryParam("client_id", confService.getClientID()) .queryParam("redirect_uri", confService.getRedirectURI());
.queryParam("redirect_uri", confService.getRedirectURI())
.queryParam("nonce", nonceService.generate(confService.getMaxNonceValidity() * 60000L))
.build();
} else {
UriBuilder builder = UriBuilder.fromUri(confService.getAuthorizationEndpoint())
.queryParam("scope", confService.getScope())
.queryParam("response_type", CODE_TOKEN_PARAMETER_NAME)
.queryParam("client_id", confService.getClientID())
.queryParam("redirect_uri", confService.getRedirectURI());
if (confService.isImplicitFlow()) {
builder.queryParam("nonce", nonceService.generate(confService.getMaxNonceValidity() * 60000L));
} else {
if (confService.isPKCERequired()) { if (confService.isPKCERequired()) {
String codeVerifier = PKCEUtil.generateCodeVerifier(); String codeVerifier = PKCEUtil.generateCodeVerifier();
String codeChallenge; String codeChallenge;
@@ -230,9 +225,9 @@ public class AuthenticationProviderService implements SSOAuthenticationProviderS
.queryParam("code_challenge_method", "S256") .queryParam("code_challenge_method", "S256")
.queryParam(AUTH_SESSION_QUERY_PARAM, identifier); .queryParam(AUTH_SESSION_QUERY_PARAM, identifier);
} }
return builder.build();
} }
return builder.build();
} }
@Override @Override
@@ -21,8 +21,10 @@ package org.apache.guacamole.auth.openid;
import com.google.inject.AbstractModule; import com.google.inject.AbstractModule;
import com.google.inject.Scopes; import com.google.inject.Scopes;
import com.google.inject.Singleton;
import org.apache.guacamole.auth.openid.conf.ConfigurationService; import org.apache.guacamole.auth.openid.conf.ConfigurationService;
import org.apache.guacamole.auth.openid.conf.OpenIDEnvironment; import org.apache.guacamole.auth.openid.conf.OpenIDEnvironment;
import org.apache.guacamole.auth.openid.conf.OpenIDWellKnown;
import org.apache.guacamole.auth.openid.OpenIDAuthenticationSessionManager; import org.apache.guacamole.auth.openid.OpenIDAuthenticationSessionManager;
import org.apache.guacamole.auth.sso.NonceService; import org.apache.guacamole.auth.sso.NonceService;
import org.apache.guacamole.auth.openid.token.TokenValidationService; import org.apache.guacamole.auth.openid.token.TokenValidationService;
@@ -20,13 +20,17 @@
package org.apache.guacamole.auth.openid.conf; package org.apache.guacamole.auth.openid.conf;
import com.google.inject.Inject; import com.google.inject.Inject;
import java.net.URI; import java.net.URI;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import org.apache.guacamole.auth.openid.conf.OpenIDResponseType;
import org.apache.guacamole.auth.openid.conf.OpenIDWellKnown;
import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.environment.Environment; import org.apache.guacamole.environment.Environment;
import org.apache.guacamole.properties.BooleanGuacamoleProperty; import org.apache.guacamole.properties.BooleanGuacamoleProperty;
import org.apache.guacamole.properties.EnumGuacamoleProperty;
import org.apache.guacamole.properties.IntegerGuacamoleProperty; import org.apache.guacamole.properties.IntegerGuacamoleProperty;
import org.apache.guacamole.properties.StringGuacamoleProperty; import org.apache.guacamole.properties.StringGuacamoleProperty;
import org.apache.guacamole.properties.URIGuacamoleProperty; import org.apache.guacamole.properties.URIGuacamoleProperty;
@@ -38,9 +42,9 @@ import org.apache.guacamole.properties.URIGuacamoleProperty;
public class ConfigurationService { public class ConfigurationService {
/** /**
* The default OICD flow type * The default OICD reponse type
*/ */
private static final String DEFAULT_FLOW_TYPE = "implicit"; private static final OpenIDResponseType DEFAULT_RESPONSE_TYPE = OpenIDResponseType.ID_TOKEN;
/** /**
* The default claim type to use to retrieve an authenticated user's * The default claim type to use to retrieve an authenticated user's
@@ -128,13 +132,13 @@ public class ConfigurationService {
}; };
/** /**
* The flow type of the OpenID service. * The reponse type of the OpenID service.
*/ */
private static final StringGuacamoleProperty OPENID_FLOW_TYPE = private static final EnumGuacamoleProperty<OpenIDResponseType> OPENID_RESPONSE_TYPE =
new StringGuacamoleProperty() { new EnumGuacamoleProperty<OpenIDResponseType>(OpenIDResponseType.class) {
@Override @Override
public String getName() { return "openid-flow-type"; } public String getName() { return "openid-response-type"; }
}; };
@@ -316,6 +320,12 @@ public class ConfigurationService {
*/ */
@Inject @Inject
private Environment environment; private Environment environment;
/**
* Service for retrieving OpenID well-known data.
*/
@Inject
private OpenIDWellKnown confWellKnown;
/** /**
* Returns the authorization endpoint (URI) of the OpenID service as * Returns the authorization endpoint (URI) of the OpenID service as
@@ -326,11 +336,15 @@ public class ConfigurationService {
* guacamole.properties. * guacamole.properties.
* *
* @throws GuacamoleException * @throws GuacamoleException
* If guacamole.properties cannot be parsed, or if the authorization
* endpoint property is missing. * endpoint property is missing.
*/ */
public URI getAuthorizationEndpoint() throws GuacamoleException { public URI getAuthorizationEndpoint() throws GuacamoleException {
return environment.getRequiredProperty(OPENID_AUTHORIZATION_ENDPOINT); URI authorization_endpoint = environment.getProperty(OPENID_AUTHORIZATION_ENDPOINT);
authorization_endpoint = authorization_endpoint == null ? confWellKnown.getAuthorizationEndpoint() : authorization_endpoint;
if (authorization_endpoint == null) {
throw new GuacamoleException("Property openid-authorization-endpoint or openid-well-known-endpoint is required");
}
return authorization_endpoint;
} }
/** /**
@@ -382,7 +396,12 @@ public class ConfigurationService {
* is missing. * is missing.
*/ */
public String getIssuer() throws GuacamoleException { public String getIssuer() throws GuacamoleException {
return environment.getRequiredProperty(OPENID_ISSUER); String issuer = environment.getProperty(OPENID_ISSUER);
issuer = issuer == null ? confWellKnown.getIssuer() : issuer;
if (issuer == null) {
throw new GuacamoleException("Property openid-issuer or openid-well-known-endpoint is required");
}
return issuer;
} }
/** /**
@@ -400,7 +419,12 @@ public class ConfigurationService {
* property is missing. * property is missing.
*/ */
public URI getJWKSEndpoint() throws GuacamoleException { public URI getJWKSEndpoint() throws GuacamoleException {
return environment.getRequiredProperty(OPENID_JWKS_ENDPOINT); URI jwks_uri = environment.getProperty(OPENID_JWKS_ENDPOINT);
jwks_uri = jwks_uri == null ? confWellKnown.getJWKSEndpoint() : jwks_uri;
if (jwks_uri == null) {
throw new GuacamoleException("Property openid-jwks-endpoint or openid-well-known-endpoint is required");
}
return jwks_uri;
} }
/** /**
@@ -412,25 +436,60 @@ public class ConfigurationService {
* guacamole.properties. * guacamole.properties.
* *
* @throws GuacamoleException * @throws GuacamoleException
* If guacamole.properties cannot be parsed, or if the authorization * If guacamole.properties cannot be parsed, or if the token
* endpoint property is missing. * endpoint property is missing.
*/ */
public URI getTokenEndpoint() throws GuacamoleException { public URI getTokenEndpoint() throws GuacamoleException {
return environment.getRequiredProperty(OPENID_TOKEN_ENDPOINT); URI token_endpoint = environment.getProperty(OPENID_TOKEN_ENDPOINT);
token_endpoint = token_endpoint == null ? confWellKnown.getTokenEndpoint() : token_endpoint;
if (token_endpoint == null) {
throw new GuacamoleException("Property openid-token-endpoint or openid-well-known-endpoint is required");
}
return token_endpoint;
} }
/** /**
* Returns the flow type of the OpenID service as configured with guacamole.properties. * Returns the well-known endpoint (URI) of the OIDC service as
* configured with guacamole.properties.
* *
* @return * @return
* The flow type of the OpenID service, as configured with guacamole.properties. Can * The well-known endpoint of the OIDC service, as configured with
* be either 'implicit' or 'code'. * guacamole.properties.
*
* @throws GuacamoleException
* If guacamole.properties cannot be parsed, or if the well-known
* endpoint property is missing.
*/
public URI getWellKnownEndpoint() throws GuacamoleException {
return confWellKnown.getWellKnownEndpoint();
}
/**
* Returns the reponse type of the OpenID service as configured with guacamole.properties.
*
* @return
* The reponse type of the OpenID service, as configured with guacamole.properties. Can
* be either 'id_token', 'token' or 'code'.
* *
* @throws GuacamoleException * @throws GuacamoleException
* If guacamole.properties cannot be parsed. * If guacamole.properties cannot be parsed.
*/ */
public String getFlowType() throws GuacamoleException { public OpenIDResponseType getResponseType() throws GuacamoleException {
return environment.getProperty(OPENID_FLOW_TYPE, DEFAULT_FLOW_TYPE); return environment.getProperty(OPENID_RESPONSE_TYPE, DEFAULT_RESPONSE_TYPE);
}
/**
* Returns true if the response type defines an implict flow
*
* @return
* The whether implicit flow is used or not, as configured with guacamole.properties.
*
* @throws GuacamoleException
* If guacamole.properties cannot be parsed.
*/
public boolean isImplicitFlow() throws GuacamoleException {
OpenIDResponseType response_type = environment.getProperty(OPENID_RESPONSE_TYPE, DEFAULT_RESPONSE_TYPE);
return response_type != OpenIDResponseType.CODE;
} }
/** /**
@@ -0,0 +1,67 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.guacamole.auth.openid.conf;
import org.apache.guacamole.properties.EnumGuacamoleProperty.PropertyValue;
/**
* This enum represents the valid OIDC reponse types that can be used
*/
public enum OpenIDResponseType {
/*
* Response type "id_token" used for implciit flow as specified in the OpenID standard
*/
@PropertyValue("id_token")
ID_TOKEN("id_token"),
/* Response type "token" used for implicit flow by certain Identity Providers, notably
* AWS Cognito. This corresponds to the official OIDC response_type "id_token token"
* that returns both the "id_token" and "access_token" parameters
*/
@PropertyValue("token")
TOKEN("token"),
/**
* Response type "code" used for code flow authentication
*/
@PropertyValue("code")
CODE("code");
/*
* The string value of the response type used
*/
public final String STRING_VALUE;
/**
* Initializes the response type such that it is associated with the
* given string value.
*
* @param value
* The string value that will be associated with the enum value.
*/
private OpenIDResponseType(String value) {
this.STRING_VALUE = value;
}
@Override
public String toString() {
return STRING_VALUE;
}
}
@@ -0,0 +1,214 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.guacamole.auth.openid.conf;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.IOException;
import java.net.URI;
import java.net.URL;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.Map;
import javax.ws.rs.core.UriBuilder;
import org.apache.guacamole.auth.openid.util.JsonUrlReader;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.environment.Environment;
import org.apache.guacamole.properties.URIGuacamoleProperty;
import org.jose4j.json.JsonUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Class for retrieving well-known endpoint data.
*/
@Singleton
public class OpenIDWellKnown {
/**
* Logger for this class.
*/
private final Logger logger = LoggerFactory.getLogger(OpenIDWellKnown.class);
/**
* The number of attempts to the well-known endpoint to get the values before giving up
*/
private static final int MAX_ATTEMPTS = 24;
/**
* The delay between each attempt to well-known endpoint in seconds
*/
private static final long DELAY_SECONDS = 5;
/**
* The detected issuer
*/
private static String issuer = null;
/**
* The detected authorization edpoint
*/
private static URI authorization_endpoint = null;
/**
* The detected token edpoint
*/
private static URI token_endpoint = null;
/**
* The detected jwks_uri
*/
private static URI jwks_uri = null;
/**
* The well-known endpoint (URI) of the OIDC service.
*/
private static final URIGuacamoleProperty OPENID_WELL_KNOWN_ENDPOINT =
new URIGuacamoleProperty() {
@Override
public String getName() {
return "openid-well-known-endpoint";
}
};
/**
* Returns the well-known endpoint (URI) of the OIDC service as
* configured with guacamole.properties.
*
* @return
* The well-known endpoint of the OIDC service, as configured with
* guacamole.properties.
*
* @throws GuacamoleException
* If guacamole.properties cannot be parsed, or if the authorization
* endpoint property is missing.
*/
public URI getWellKnownEndpoint() throws GuacamoleException {
return environment.getProperty(OPENID_WELL_KNOWN_ENDPOINT);
}
/**
* Returns the issuer to expect for all received ID tokens, as configured
* from the well_known endpoint.
*
* @return
* The issuer to expect for all received ID tokens, as returned by the
* well-known endpoint.
*/
public String getIssuer() {
return issuer;
}
/**
* Returns the authorization endpoint (URI) of the OpenID service as
* configured from the well_known endpoint.
*
* @return
* The authorization endpoint of the OpenID service, as returned by the
* well-known endpoint.
*/
public URI getAuthorizationEndpoint() {
return authorization_endpoint;
}
/**
* Returns the token endpoint (URI) of the OpenID service as
* configured from the well_known endpoint.
*
* @return
* The token endpoint of the OpenID service, as returned by the
* well-known endpoint.
*/
public URI getTokenEndpoint() {
return token_endpoint;
}
/**
* Returns the endpoint (URI) of the JWKS service which defines how
* received ID tokens (JWTs) shall be validated, as configured from
* the well-known endpoint.
*
* @return
* The endpoint (URI) of the JWKS service which defines how received ID
* tokens (JWTs) shall be validated, as configured from the
* well-known endpoint.
*/
public URI getJWKSEndpoint() {
return jwks_uri;
}
/**
* The Guacamole server environment.
*/
@Inject
private Environment environment;
/*
* Creates an OpenIDWellKnown class that reads the json from an OIDC
* well-known endpoint and saves these values for later use. Use Guice
* to ensure environment exists before initializing.
*/
public OpenIDWellKnown() {
}
@Inject
public void init() {
// Call to well-known endpoint might fail, so allow several tries before giving up
ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
Runnable task = new Runnable() {
int attempts = 0;
@Override
public void run() {
attempts++;
try {
Map<String,Object> json = JsonUrlReader.fetch("GET", getWellKnownEndpoint().toURL(), "");
issuer = (String) json.get("issuer");
authorization_endpoint = UriBuilder.fromUri((String) json.get("authorization_endpoint")).build();
token_endpoint = UriBuilder.fromUri((String) json.get("token_endpoint")).build();
jwks_uri = UriBuilder.fromUri((String) json.get("jwks_uri")).build();
logger.debug("OIDC well-known\n" +
" issuer : {}\n" +
" authorization_endpoint : {}\n" +
" token_endpoint : {}\n" +
" jwks_uri : {}\n",
issuer, authorization_endpoint, token_endpoint, jwks_uri);
scheduler.shutdown();
return;
} catch (Exception e) {
logger.debug("Rejecting well-known endpoint : {}", e.getMessage());
}
if (attempts >= MAX_ATTEMPTS) {
logger.info("Timeout on well-known on endpoint");
scheduler.shutdown();
}
}
};
scheduler.scheduleAtFixedRate(task, 0, DELAY_SECONDS, TimeUnit.SECONDS);
}
}
@@ -20,11 +20,6 @@
package org.apache.guacamole.auth.openid.token; package org.apache.guacamole.auth.openid.token;
import com.google.inject.Inject; import com.google.inject.Inject;
import java.io.BufferedReader;
import java.io.OutputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.net.URI; import java.net.URI;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
@@ -39,6 +34,7 @@ import java.util.Set;
import javax.ws.rs.core.UriBuilder; import javax.ws.rs.core.UriBuilder;
import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.auth.openid.conf.ConfigurationService; import org.apache.guacamole.auth.openid.conf.ConfigurationService;
import org.apache.guacamole.auth.openid.util.JsonUrlReader;
import org.apache.guacamole.auth.sso.NonceService; import org.apache.guacamole.auth.sso.NonceService;
import org.apache.guacamole.token.TokenName; import org.apache.guacamole.token.TokenName;
import org.jose4j.json.JsonUtil; import org.jose4j.json.JsonUtil;
@@ -243,46 +239,9 @@ public class TokenValidationService {
bodyBuilder.append("&").append(urlencode("code_verifier", verifier)); bodyBuilder.append("&").append(urlencode("code_verifier", verifier));
} }
// Build the final URI and convert to a URL Map<String,Object> json =
URL url = confService.getTokenEndpoint().toURL(); JsonUrlReader.fetch("POST", confService.getTokenEndpoint().toURL(),
bodyBuilder.toString());
// Open connection, using HttpURLConnection
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setDoOutput(true);
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8"
);
try (OutputStream out = conn.getOutputStream()) {
byte [] body = bodyBuilder.toString().getBytes(StandardCharsets.UTF_8);
out.write(body, 0, body.length);
}
// Read response
int status = conn.getResponseCode();
BufferedReader reader = new BufferedReader(
new InputStreamReader(
status >= 200 && status < 300
? conn.getInputStream()
: conn.getErrorStream(),
StandardCharsets.UTF_8
)
);
StringBuilder responseBody = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
responseBody.append(line);
}
reader.close();
Map<String,Object> json = JsonUtil.parseJson(responseBody.toString());
if (status < 200 || status >= 300) {
throw new GuacamoleException("Token endpoint error (" + status + "): " + json.toString());
}
return (String) json.get("id_token"); return (String) json.get("id_token");
@@ -0,0 +1,98 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.guacamole.auth.openid.util;
import java.io.BufferedReader;
import java.io.OutputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import org.apache.guacamole.GuacamoleException;
import org.jose4j.json.JsonUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/*
* Utility class to open a http connection to a URL, send a body
* and receive a response in the form of a parsed JSON
*/
public final class JsonUrlReader {
/**
* Logger for this class.
*/
private static final Logger logger = LoggerFactory.getLogger(JsonUrlReader.class);
private JsonUrlReader() {}
public static Map<String,Object> fetch(String method, URL url, String body) throws GuacamoleException {
if (url == null) {
throw new GuacamoleException("JsonUrlReader : Missing URL");
}
try {
// Open connection, using HttpURLConnection
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod(method);
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
if (! method.equalsIgnoreCase("GET")) {
conn.setDoOutput(true);
try (OutputStream out = conn.getOutputStream()) {
byte [] bodyBytes = body.getBytes(StandardCharsets.UTF_8);
out.write(bodyBytes, 0, bodyBytes.length);
}
}
// Read response
int status = conn.getResponseCode();
BufferedReader reader = new BufferedReader(
new InputStreamReader(
status >= 200 && status < 300
? conn.getInputStream()
: conn.getErrorStream(),
StandardCharsets.UTF_8
)
);
StringBuilder responseBody = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
responseBody.append(line);
}
reader.close();
logger.debug("Response body : {}", responseBody.toString());
Map<String,Object> json = JsonUtil.parseJson(responseBody.toString());
if (status < 200 || status >= 300) {
throw new GuacamoleException("(status: " + status + "): " + json.toString());
}
return json;
} catch (Exception e) {
throw new GuacamoleException("JsonUrlreader error : " + method + " : " + e.getMessage());
}
}
}