Skip to content

Commit

Permalink
updated ElytronHttpClient separating Authentication mechanism type me…
Browse files Browse the repository at this point in the history
…thods
  • Loading branch information
keshav-725 committed May 2, 2023
1 parent cbac3de commit 8f59c18
Show file tree
Hide file tree
Showing 10 changed files with 439 additions and 299 deletions.
4 changes: 4 additions & 0 deletions http/client/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@
<groupId>org.wildfly.security</groupId>
<artifactId>wildfly-elytron-digest</artifactId>
</dependency>
<dependency>
<groupId>org.wildfly.security</groupId>
<artifactId>wildfly-elytron-mechanism-digest</artifactId>
</dependency>

<dependency>
<groupId>org.wildfly.common</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,18 @@
package org.wildfly.security.http.client;

import org.wildfly.security.http.client.exception.ElytronHttpClientException;
import org.wildfly.security.http.client.mechanism.ElytronHttpClientAuthMechanism;
import org.wildfly.security.http.client.mechanism.basic.ElytronHttpClientBasicAuthMechanism;
import org.wildfly.security.http.client.mechanism.digest.ElytronHttpClientDigestAuthMechanism;
import org.wildfly.security.http.client.utils.DigestHttpMechanismUtil;
import org.wildfly.security.http.client.utils.HttpMechClientConfigUtil;
import org.wildfly.security.http.client.utils.ElytronMessages;
import static org.wildfly.security.http.HttpConstants.OK;

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.Base64;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

Expand All @@ -38,173 +40,62 @@
*/
public class ElytronHttpClient {

private static final String AUTHORIZATION = "Authorization";
private final HttpClient client;
private final HttpClient httpClient;
private final HttpMechClientConfigUtil httpMechClientConfigUtil;
private String lastURI;
private Map<String, String> authParams;
private String userName;
private String password;
private String previousMechanism;
private DigestHttpMechanismUtil digestHttpMechanismUtil;

private ElytronHttpClientAuthMechanism elytronHttpClientAuthMechanism;
public ElytronHttpClient() {
this.client = HttpClient.newHttpClient();
this.httpClient = HttpClient.newHttpClient();
this.httpMechClientConfigUtil = new HttpMechClientConfigUtil();
authParams = new HashMap<>();
previousMechanism = null;
lastURI = null;
digestHttpMechanismUtil = new DigestHttpMechanismUtil();
}

private static String basicAuth(String username, String password) {
return "Basic " + Base64.getEncoder().encodeToString((username + ":" + password).getBytes());
}

public HttpResponse evaluateDigestMechanism(String uri) throws Exception {

HttpRequest request;
request = digestHttpMechanismUtil.createDigestRequest(uri,userName,password,authParams);

if(lastURI==null || !(lastURI.equals(uri))){
lastURI = uri;
}

HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());

if (response.statusCode() == 401) {
String authHeader = response.headers().allValues("www-authenticate").get(0);
digestHttpMechanismUtil.updateAuthParams(authHeader,authParams);
request = digestHttpMechanismUtil.createDigestRequest(uri,userName,password,authParams);
response = client.send(request, HttpResponse.BodyHandlers.ofString());
}
return response;
}

//To test header values from ElytronHttpClientTest
public HttpRequest getResponseHeader(String responseHeader, String uri) throws Exception {

digestHttpMechanismUtil.updateAuthParams(responseHeader,authParams);

String realm = authParams.get("realm");
String nonce = authParams.get("nonce");
String opaque = authParams.get("opaque");
String algorithm = authParams.get("algorithm");
String qop = authParams.get("qop");

String path = digestHttpMechanismUtil.getUriPath(uri);

userName = httpMechClientConfigUtil.getUsername(new URI(uri));
password = httpMechClientConfigUtil.getPassword(new URI(uri));

String response;
if (qop == null) {
response = digestHttpMechanismUtil.computeDigestWithoutQop(path, nonce, userName, password, algorithm, realm, "GET");
} else {
response = digestHttpMechanismUtil.computeDigestWithQop(path, nonce, "0a4f113b", "00000001", userName, password, algorithm, realm, qop, "GET");
}

HttpRequest request2 = HttpRequest
.newBuilder()
.uri(new URI(uri))
.header(AUTHORIZATION, "Digest " +
"username=\"" + userName + "\", " +
"realm=\"" + realm + "\"," +
"nonce=\"" + nonce + "\", " +
"uri=\"" + path + "\", " +
"qop=\"" + qop + "\", " +
"nc=00000001, " +
"cnonce=\"0a4f113b\", " +
"response=\"" + response + "\", " +
"opaque=\"" + opaque + "\", " +
"algorithm=" + algorithm)
.build();
return request2;

private HttpResponse getResponse(HttpRequest request) throws Exception{
return httpClient.send(request, HttpResponse.BodyHandlers.ofString());
}

public HttpRequest getRequest(String uri) throws Exception {
String username = httpMechClientConfigUtil.getUsername(new URI(uri));
String password = httpMechClientConfigUtil.getPassword(new URI(uri));
String AuthType = httpMechClientConfigUtil.getHttpAuthenticationType(new URI(uri));
String AuthHeader = null;
if (AuthType.equalsIgnoreCase("basic")) {
AuthHeader = basicAuth(username, password);
}
private HttpRequest evaluateNoAuthMechanism(URI uri) {
HttpRequest request = HttpRequest
.newBuilder()
.uri(new URI(uri))
.header(AUTHORIZATION, AuthHeader)
.uri(uri)
.build();

return request;
}

public HttpResponse evaluateBasicMechanism(String uri) throws Exception {
HttpRequest request = HttpRequest
.newBuilder()
.uri(new URI(uri))
.header(AUTHORIZATION, basicAuth(userName, password))
.build();
HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString());
return response;
}
public HttpResponse evaluateNoAuthMechanism(String uri) throws Exception{
HttpRequest request = HttpRequest
.newBuilder()
.uri(new URI(uri))
.build();

HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString());
return response;
}

/**
* Used to connect to the secured uri and return the response based on that.
*/
public HttpResponse connect(String uri) throws Exception {
userName = httpMechClientConfigUtil.getUsername(new URI(uri));
password = httpMechClientConfigUtil.getPassword(new URI(uri));
HttpResponse response = null;

if (lastURI != null && lastURI.equals(uri)) {
switch (previousMechanism) {
case "basic":
response = evaluateBasicMechanism(uri);
break;
case "digest":
response = evaluateDigestMechanism(uri);
break;
case "noauth":
response = evaluateNoAuthMechanism(uri);
break;
}
} else {
response = evaluateNoAuthMechanism(uri);
URI uriPath = new URI(uri);
HttpRequest request = evaluateNoAuthMechanism(uriPath);
HttpResponse response = getResponse(request);

if(response.statusCode()==200){
previousMechanism = "noauth";
lastURI = uri;
return response;
}
String authHeader = null;
Map<String, List<String>> allHeaderValues = response.headers().map();
for(String headerKey : allHeaderValues.keySet()){
if(headerKey.toLowerCase().equals("www-authenticate")){
authHeader = allHeaderValues.get(headerKey).get(0);
}
}
if(response.statusCode() == OK){
return response;
}

if(authHeader == null){
throw new ElytronHttpClientException(ElytronMessages.log.responseHeaderExtractionFailed());
String authHeader = null;
Map<String, List<String>> allHeaderValues = response.headers().map();
for(String headerKey : allHeaderValues.keySet()){
if(headerKey.toLowerCase().equals("www-authenticate")){
authHeader = allHeaderValues.get(headerKey).get(0);
}
}

if (authHeader.toLowerCase().startsWith("basic")) {
response = evaluateBasicMechanism(uri);
previousMechanism = "basic";
} else if (authHeader.toLowerCase().startsWith("digest")) {
digestHttpMechanismUtil.updateAuthParams(authHeader,authParams);
response = evaluateDigestMechanism(uri);
previousMechanism = "digest";
}
if(authHeader == null){
throw new ElytronHttpClientException(ElytronMessages.log.responseHeaderExtractionFailed());
}

if (authHeader.toLowerCase().startsWith("basic")) {
elytronHttpClientAuthMechanism = new ElytronHttpClientBasicAuthMechanism();
request = elytronHttpClientAuthMechanism.evaluateMechanism(uriPath);
} else if (authHeader.toLowerCase().startsWith("digest")) {
elytronHttpClientAuthMechanism = new ElytronHttpClientDigestAuthMechanism(authHeader);
request = elytronHttpClientAuthMechanism.evaluateMechanism(uriPath);
}
response = getResponse(request);
return response;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* JBoss, Home of Professional Open Source.
* Copyright 2023 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* Licensed 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.wildfly.security.http.client.mechanism;

import java.net.URI;
import java.net.http.HttpRequest;

/**
* Elytron client for HTTP authentication
*
* @author <a href="mailto:kekumar@redhat.com">Keshav Kumar</a>
*/
public interface ElytronHttpClientAuthMechanism {

/**
* Add required header to request object based on authentication type and return it.
*/
HttpRequest evaluateMechanism(URI uri) throws Exception;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* JBoss, Home of Professional Open Source.
* Copyright 2023 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* Licensed 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.wildfly.security.http.client.mechanism.basic;

import org.wildfly.security.http.client.mechanism.ElytronHttpClientAuthMechanism;
import org.wildfly.security.http.client.utils.ElytronHttpClientCredentialProvider;

import java.net.URI;
import java.net.http.HttpRequest;
import java.util.Base64;

/**
* Elytron client for HTTP authentication
*
* @author <a href="mailto:kekumar@redhat.com">Keshav Kumar</a>
*/
public class ElytronHttpClientBasicAuthMechanism implements ElytronHttpClientAuthMechanism {
private static final String AUTHORIZATION = "Authorization";
private ElytronHttpClientCredentialProvider elytronHttpClientCredentialProvider = new ElytronHttpClientCredentialProvider();

@Override
public HttpRequest evaluateMechanism(URI uri) {
String userName = elytronHttpClientCredentialProvider.getUserName(uri);
String password = elytronHttpClientCredentialProvider.getPassword(uri);
HttpRequest request = HttpRequest
.newBuilder()
.uri(uri)
.header(AUTHORIZATION, basicAuth(userName, password))
.build();
return request;
}

private String basicAuth(String username, String password) {
return "Basic " + Base64.getEncoder().encodeToString((username + ":" + password).getBytes());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* JBoss, Home of Professional Open Source.
* Copyright 2023 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* Licensed 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.wildfly.security.http.client.mechanism.digest;

import org.wildfly.security.http.client.mechanism.ElytronHttpClientAuthMechanism;
import org.wildfly.security.http.client.utils.DigestHttpMechanismUtil;
import org.wildfly.security.http.client.utils.ElytronHttpClientCredentialProvider;

import java.net.URI;
import java.net.http.HttpRequest;

/**
* Elytron client for HTTP authentication
*
* @author <a href="mailto:kekumar@redhat.com">Keshav Kumar</a>
*/
public class ElytronHttpClientDigestAuthMechanism implements ElytronHttpClientAuthMechanism {
private DigestHttpMechanismUtil digestHttpMechanismUtil;
private String authHeader;
private ElytronHttpClientCredentialProvider elytronHttpClientCredentialProvider = new ElytronHttpClientCredentialProvider();

public ElytronHttpClientDigestAuthMechanism(String authHeader){
this.authHeader = authHeader;
this.digestHttpMechanismUtil = new DigestHttpMechanismUtil();
}

@Override
public HttpRequest evaluateMechanism(URI uri) throws Exception{
String userName = elytronHttpClientCredentialProvider.getUserName(uri);
String password = elytronHttpClientCredentialProvider.getPassword(uri);
return digestHttpMechanismUtil.createDigestRequest(uri,userName,password,authHeader);
}
}

0 comments on commit 8f59c18

Please sign in to comment.