Skip to content

Commit

Permalink
Resolves eclipse-ee4j#365 AutoCloseable jakarta.ws.rs Object instance…
Browse files Browse the repository at this point in the history
…s not being closed after being used.
  • Loading branch information
darranl committed Jul 31, 2023
1 parent 54e8900 commit 5204b60
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 70 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
import org.glassfish.soteria.mechanisms.openid.controller.AuthenticationController;
import org.glassfish.soteria.mechanisms.openid.controller.StateController;
import org.glassfish.soteria.mechanisms.openid.controller.TokenController;
import org.glassfish.soteria.mechanisms.openid.controller.TokenController.TokensResponse;
import org.glassfish.soteria.mechanisms.openid.domain.LogoutConfiguration;
import org.glassfish.soteria.mechanisms.openid.domain.OpenIdConfiguration;
import org.glassfish.soteria.mechanisms.openid.domain.OpenIdContextImpl;
Expand All @@ -68,10 +69,8 @@
import jakarta.enterprise.inject.Instance;
import jakarta.enterprise.inject.Typed;
import jakarta.inject.Inject;
import jakarta.json.Json;
import jakarta.json.JsonNumber;
import jakarta.json.JsonObject;
import jakarta.json.JsonReader;
import jakarta.security.auth.message.callback.CallerPrincipalCallback;
import jakarta.security.enterprise.AuthenticationException;
import jakarta.security.enterprise.AuthenticationStatus;
Expand All @@ -83,7 +82,6 @@
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.UriBuilder;

/**
Expand Down Expand Up @@ -362,9 +360,9 @@ private AuthenticationStatus validateAuthorizationCode(HttpMessageContext httpCo

LOGGER.finer("Authorization Code received, now fetching Access token & Id token");

Response tokenResponse = tokenController.getTokens(request);
JsonObject tokensObject = readJsonObject(tokenResponse.readEntity(String.class));
if (tokenResponse.getStatus() == OK.getStatusCode()) {
TokensResponse tokensResponse = tokenController.getTokens(request);
JsonObject tokensObject = tokensResponse.getTokensObject();
if (tokensResponse.getStatus() == OK.getStatusCode()) {
// Successful Token Response
updateContext(tokensObject);
OpenIdCredential credential = new OpenIdCredential(tokensObject, httpContext, configuration.getTokenMinValidity());
Expand Down Expand Up @@ -423,10 +421,10 @@ private AuthenticationStatus reAuthenticate(HttpMessageContext httpContext) thro
}

private AuthenticationStatus refreshTokens(HttpMessageContext httpContext, RefreshToken refreshToken) {
Response response = tokenController.refreshTokens(refreshToken);
JsonObject tokensObject = readJsonObject(response.readEntity(String.class));
TokensResponse response = tokenController.refreshTokens(refreshToken);
JsonObject tokensObject = response.getTokensObject();

if (response.getStatus() == Response.Status.OK.getStatusCode()) {
if (response.getStatus() == OK.getStatusCode()) {
// Successful Token Response
updateContext(tokensObject);
OpenIdCredential credential = new OpenIdCredential(tokensObject, httpContext, configuration.getTokenMinValidity());
Expand Down Expand Up @@ -493,12 +491,6 @@ private static void redirect(HttpServletResponse response, String uri) {
}
}

private JsonObject readJsonObject(String tokensBody) {
try (JsonReader reader = Json.createReader(new StringReader(tokensBody))) {
return reader.readObject();
}
}

private void updateContext(JsonObject tokensObject) {
context.setTokenType(tokensObject.getString(TOKEN_TYPE, null));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,25 +76,25 @@ public JsonObject getDocument(String providerURI) {
}

// Call
Client client = ClientBuilder.newClient();
WebTarget target = client.target(providerURI);
Response response = target.request()
try (Client client = ClientBuilder.newClient()) {
WebTarget target = client.target(providerURI);
try (Response response = target.request()
.accept(APPLICATION_JSON)
.get();
.get()) {

if (response.getStatus() == Status.OK.getStatusCode()) {
// Get back the result of the REST request
String responseBody = response.readEntity(String.class);
try (JsonReader reader = Json.createReader(new StringReader(responseBody))) {
JsonObject responseObject = reader.readObject();
providerDocuments.put(providerURI, responseObject);
if (response.getStatus() == Status.OK.getStatusCode()) {
// Get back the result of the REST request
String responseBody = response.readEntity(String.class);
try (JsonReader reader = Json.createReader(new StringReader(responseBody))) {
JsonObject responseObject = reader.readObject();
providerDocuments.put(providerURI, responseObject);
}
} else {
throw new IllegalStateException(String.format(
"Unable to retrieve OpenID Provider's [%s] configuration document, HTTP respons code : [%s] ",
providerURI, response.getStatus()));
}
}
} else {
throw new IllegalStateException(String.format(
"Unable to retrieve OpenID Provider's [%s] configuration document, HTTP respons code : [%s] ",
providerURI,
response.getStatus()
));
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON;
import static java.util.Collections.emptyMap;

import java.io.StringReader;
import java.util.Map;

import org.glassfish.soteria.mechanisms.openid.domain.AccessTokenImpl;
Expand All @@ -32,6 +33,9 @@

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.json.Json;
import jakarta.json.JsonObject;
import jakarta.json.JsonReader;
import jakarta.security.enterprise.authentication.mechanism.http.HttpMessageContext;
import jakarta.security.enterprise.authentication.mechanism.http.openid.OpenIdConstant;
import jakarta.security.enterprise.identitystore.openid.IdentityToken;
Expand Down Expand Up @@ -68,10 +72,10 @@ public class TokenController {
* Provider responds with an ID Token and an Access Token.
*
* @param request
* @return a JSON object representation of OpenID Connect token response
* from the Token endpoint.
* @return a {@code TokenResponse} object containing the status code and a JSON object
* representation of the OpenID Connect token response from the Token endpoint.
*/
public Response getTokens(HttpServletRequest request) {
public TokensResponse getTokens(HttpServletRequest request) {
/*
* one-time authorization code that RP exchange for an Access / Id token
*/
Expand All @@ -94,11 +98,17 @@ public Response getTokens(HttpServletRequest request) {
.param(OpenIdConstant.REDIRECT_URI, configuration.buildRedirectURI(request));

// ID Token and Access Token Request
Client client = ClientBuilder.newClient();
WebTarget target = client.target(configuration.getProviderMetadata().getTokenEndpoint());
return target.request()
try (Client client = ClientBuilder.newClient()) {
WebTarget target = client.target(configuration.getProviderMetadata().getTokenEndpoint());
try (Response response = target.request()
.accept(APPLICATION_JSON)
.post(Entity.form(form));
.post(Entity.form(form))) {

JsonObject tokensObject = readJsonObject(response.readEntity(String.class));

return new TokensResponse(response.getStatus(), tokensObject);
}
}
}

/**
Expand Down Expand Up @@ -175,22 +185,55 @@ public Map<String, Object> validateAccessToken(AccessTokenImpl accessToken, Algo
* responds with a new (updated) Access Token and Refreshs Token.
*
* @param refreshToken Refresh Token received from previous token request.
* @return a JSON object representation of OpenID Connect token response
* from the Token endpoint.
* @return a {@code TokenResponse} object containing the status code and a JSON object
* representation of the OpenID Connect token response from the Token endpoint.
*/
public Response refreshTokens(RefreshToken refreshToken) {
public TokensResponse refreshTokens(RefreshToken refreshToken) {
Form form = new Form()
.param(OpenIdConstant.CLIENT_ID, configuration.getClientId())
.param(OpenIdConstant.CLIENT_SECRET, new String(configuration.getClientSecret()))
.param(OpenIdConstant.GRANT_TYPE, OpenIdConstant.REFRESH_TOKEN)
.param(OpenIdConstant.REFRESH_TOKEN, refreshToken.getToken());

// Access Token and RefreshToken Request
Client client = ClientBuilder.newClient();
try (Client client = ClientBuilder.newClient()) {
WebTarget target = client.target(configuration.getProviderMetadata().getTokenEndpoint());
return target.request()
try (Response response = target.request()
.accept(APPLICATION_JSON)
.post(Entity.form(form));
.post(Entity.form(form))) {

JsonObject tokensObject = readJsonObject(response.readEntity(String.class));

return new TokensResponse(response.getStatus(), tokensObject);

}
}
}

private JsonObject readJsonObject(String tokensBody) {
try (JsonReader reader = Json.createReader(new StringReader(tokensBody))) {
return reader.readObject();
}
}

public static class TokensResponse {
private final int status;
private final JsonObject tokensObject;

TokensResponse(int status, JsonObject tokensObject) {
super();
this.status = status;
this.tokensObject = tokensObject;
}

public int getStatus() {
return status;
}

public JsonObject getTokensObject() {
return tokensObject;
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -74,37 +74,39 @@ public JsonObject getUserInfo(OpenIdConfiguration configuration, AccessToken acc
LOGGER.finest("Sending the request to the userinfo endpoint");
JsonObject userInfo;

Client client = ClientBuilder.newClient();
WebTarget target = client.target(configuration.getProviderMetadata().getUserinfoEndpoint());
Response response = target.request()
try (Client client = ClientBuilder.newClient()) {
WebTarget target = client.target(configuration.getProviderMetadata().getUserinfoEndpoint());
try (Response response = target.request()
.accept(APPLICATION_JSON)
.header(AUTHORIZATION_HEADER, BEARER_TYPE + accessToken)
// 5.5. Requesting Claims using the "claims" Request Parameter ??
.get();

String responseBody = response.readEntity(String.class);

String contentType = response.getHeaderString(CONTENT_TYPE);
if (response.getStatus() == Status.OK.getStatusCode()) {
if (nonNull(contentType) && contentType.contains(APPLICATION_JSON)) {
// Successful UserInfo Response
try (JsonReader reader = Json.createReader(new StringReader(responseBody))) {
userInfo = reader.readObject();
.get()) {

String responseBody = response.readEntity(String.class);

String contentType = response.getHeaderString(CONTENT_TYPE);
if (response.getStatus() == Status.OK.getStatusCode()) {
if (nonNull(contentType) && contentType.contains(APPLICATION_JSON)) {
// Successful UserInfo Response
try (JsonReader reader = Json.createReader(new StringReader(responseBody))) {
userInfo = reader.readObject();
}
} else if (nonNull(contentType) && contentType.contains(APPLICATION_JWT)) {
throw new UnsupportedOperationException("application/jwt content-type not supported for userinfo endpoint");
//If the UserInfo Response is signed and/or encrypted, then the Claims are returned in a JWT and the content-type MUST be application/jwt. The response MAY be encrypted without also being signed. If both signing and encryption are requested, the response MUST be signed then encrypted, with the result being a Nested JWT, ??
//If signed, the UserInfo Response SHOULD contain the Claims iss (issuer) and aud (audience) as members. The iss value SHOULD be the OP's Issuer Identifier URL. The aud value SHOULD be or include the RP's Client ID value.
} else {
throw new IllegalStateException("Invalid response received from userinfo endpoint with content-type : " + contentType);
}
} else {
// UserInfo Error Response
JsonObject responseObject = Json.createReader(new StringReader(responseBody)).readObject();
String error = responseObject.getString(OpenIdConstant.ERROR_PARAM, "Unknown Error");
String errorDescription = responseObject.getString(ERROR_DESCRIPTION_PARAM, "Unknown");
LOGGER.log(WARNING, "Error occurred in fetching user info: {0} caused by {1}", new Object[]{error, errorDescription});
throw new IllegalStateException("Error occurred in fetching user info");
}
} else if (nonNull(contentType) && contentType.contains(APPLICATION_JWT)) {
throw new UnsupportedOperationException("application/jwt content-type not supported for userinfo endpoint");
//If the UserInfo Response is signed and/or encrypted, then the Claims are returned in a JWT and the content-type MUST be application/jwt. The response MAY be encrypted without also being signed. If both signing and encryption are requested, the response MUST be signed then encrypted, with the result being a Nested JWT, ??
//If signed, the UserInfo Response SHOULD contain the Claims iss (issuer) and aud (audience) as members. The iss value SHOULD be the OP's Issuer Identifier URL. The aud value SHOULD be or include the RP's Client ID value.
} else {
throw new IllegalStateException("Invalid response received from userinfo endpoint with content-type : " + contentType);
}
} else {
// UserInfo Error Response
JsonObject responseObject = Json.createReader(new StringReader(responseBody)).readObject();
String error = responseObject.getString(OpenIdConstant.ERROR_PARAM, "Unknown Error");
String errorDescription = responseObject.getString(ERROR_DESCRIPTION_PARAM, "Unknown");
LOGGER.log(WARNING, "Error occurred in fetching user info: {0} caused by {1}", new Object[]{error, errorDescription});
throw new IllegalStateException("Error occurred in fetching user info");
}

validateUserInfoClaims(userInfo);
Expand Down

0 comments on commit 5204b60

Please sign in to comment.