From 55f86b9fd70b701e93481228bfbdfd25b273ff69 Mon Sep 17 00:00:00 2001 From: Oleg Kalnichevski Date: Sat, 26 Nov 2022 18:28:36 +0100 Subject: [PATCH] Credentials interface should be able to represent different types of user credentials including token based with no password --- .../hc/client5/http/auth/Credentials.java | 5 +++ .../auth/UsernamePasswordCredentials.java | 39 +++++++++++++++---- .../client5/http/impl/auth/BasicScheme.java | 33 +++++++--------- .../client5/http/impl/auth/DigestScheme.java | 23 ++++++----- .../hc/client5/http/auth/TestCredentials.java | 4 +- .../http/impl/auth/TestDigestScheme.java | 2 + .../TestSystemDefaultCredentialsProvider.java | 2 - 7 files changed, 65 insertions(+), 43 deletions(-) diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/auth/Credentials.java b/httpclient5/src/main/java/org/apache/hc/client5/http/auth/Credentials.java index 742ba9e7b..491ee0072 100644 --- a/httpclient5/src/main/java/org/apache/hc/client5/http/auth/Credentials.java +++ b/httpclient5/src/main/java/org/apache/hc/client5/http/auth/Credentials.java @@ -39,6 +39,11 @@ public interface Credentials { Principal getUserPrincipal(); + /** + * @deprecated Use specific credentials class that represent a username / password + * set of a security token. + */ + @Deprecated char[] getPassword(); } diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/auth/UsernamePasswordCredentials.java b/httpclient5/src/main/java/org/apache/hc/client5/http/auth/UsernamePasswordCredentials.java index 54fd469bc..f35f3fc5c 100644 --- a/httpclient5/src/main/java/org/apache/hc/client5/http/auth/UsernamePasswordCredentials.java +++ b/httpclient5/src/main/java/org/apache/hc/client5/http/auth/UsernamePasswordCredentials.java @@ -45,20 +45,34 @@ public class UsernamePasswordCredentials implements Credentials, Serializable { private static final long serialVersionUID = 243343858802739403L; - private final BasicUserPrincipal principal; + private final Principal principal; private final char[] password; /** * The constructor with the username and password arguments. * - * @param userName the user name + * @param principal the user principal + * @param password the password + * + * @since 5.3 + * + * @see BasicUserPrincipal + * @see NTUserPrincipal + */ + public UsernamePasswordCredentials(final Principal principal, final char[] password) { + super(); + this.principal = Args.notNull(principal, "User principal"); + this.password = password; + } + + /** + * The constructor with the username and password arguments. + * + * @param username the user name * @param password the password */ - public UsernamePasswordCredentials(final String userName, final char[] password) { - super(); - Args.notNull(userName, "Username"); - this.principal = new BasicUserPrincipal(userName); - this.password = password; + public UsernamePasswordCredentials(final String username, final char[] password) { + this(new BasicUserPrincipal(username), password); } @Override @@ -70,6 +84,17 @@ public String getUserName() { return this.principal.getName(); } + /** + * @since 5.3 + */ + public char[] getUserPassword() { + return password; + } + + /** + * @deprecated Use {@link #getUserPassword()}. + */ + @Deprecated @Override public char[] getPassword() { return password; diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/auth/BasicScheme.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/auth/BasicScheme.java index 87d11448c..d6d0d027c 100644 --- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/auth/BasicScheme.java +++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/auth/BasicScheme.java @@ -39,7 +39,6 @@ import java.util.Locale; import java.util.Map; -import org.apache.hc.client5.http.utils.Base64; import org.apache.hc.client5.http.auth.AuthChallenge; import org.apache.hc.client5.http.auth.AuthScheme; import org.apache.hc.client5.http.auth.AuthScope; @@ -49,7 +48,9 @@ import org.apache.hc.client5.http.auth.CredentialsProvider; import org.apache.hc.client5.http.auth.MalformedChallengeException; import org.apache.hc.client5.http.auth.StandardAuthScheme; +import org.apache.hc.client5.http.auth.UsernamePasswordCredentials; import org.apache.hc.client5.http.protocol.HttpClientContext; +import org.apache.hc.client5.http.utils.Base64; import org.apache.hc.client5.http.utils.ByteArrayBuilder; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; @@ -77,8 +78,7 @@ public class BasicScheme implements AuthScheme, Serializable { private transient Base64 base64codec; private boolean complete; - private String username; - private char[] password; + private UsernamePasswordCredentials credentials; /** * @since 4.3 @@ -93,21 +93,13 @@ public BasicScheme() { this(StandardCharsets.US_ASCII); } - private void applyCredentials(final Credentials credentials) { - this.username = credentials.getUserPrincipal().getName(); - this.password = credentials.getPassword(); - } - - private void clearCredentials() { - this.username = null; - this.password = null; - } - public void initPreemptive(final Credentials credentials) { if (credentials != null) { - applyCredentials(credentials); + Args.check(credentials instanceof UsernamePasswordCredentials, + "Unsupported credential type: " + credentials.getClass()); + this.credentials = (UsernamePasswordCredentials) credentials; } else { - clearCredentials(); + this.credentials = null; } } @@ -157,8 +149,8 @@ public boolean isResponseReady( final AuthScope authScope = new AuthScope(host, getRealm(), getName()); final Credentials credentials = credentialsProvider.getCredentials( authScope, context); - if (credentials != null) { - applyCredentials(credentials); + if (credentials instanceof UsernamePasswordCredentials) { + this.credentials = (UsernamePasswordCredentials) credentials; return true; } @@ -167,7 +159,7 @@ public boolean isResponseReady( final String exchangeId = clientContext.getExchangeId(); LOG.debug("{} No credentials found for auth scope [{}]", exchangeId, authScope); } - clearCredentials(); + this.credentials = null; return false; } @@ -177,9 +169,10 @@ public Principal getPrincipal() { } private void validateUsername() throws AuthenticationException { - if (username == null) { + if (credentials == null) { throw new AuthenticationException("User credentials not set"); } + final String username = credentials.getUserName(); for (int i = 0; i < username.length(); i++) { final char ch = username.charAt(i); if (Character.isISOControl(ch)) { @@ -204,7 +197,7 @@ public String generateAuthResponse( } final Charset charset = AuthSchemeSupport.parseCharset(paramMap.get("charset"), defaultCharset); this.buffer.charset(charset); - this.buffer.append(this.username).append(":").append(this.password); + this.buffer.append(this.credentials.getUserName()).append(":").append(this.credentials.getUserPassword()); if (this.base64codec == null) { this.base64codec = new Base64(); } diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/auth/DigestScheme.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/auth/DigestScheme.java index bcee7bb51..9500260e9 100644 --- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/auth/DigestScheme.java +++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/auth/DigestScheme.java @@ -53,6 +53,7 @@ import org.apache.hc.client5.http.auth.CredentialsProvider; import org.apache.hc.client5.http.auth.MalformedChallengeException; import org.apache.hc.client5.http.auth.StandardAuthScheme; +import org.apache.hc.client5.http.auth.UsernamePasswordCredentials; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.client5.http.utils.ByteArrayBuilder; import org.apache.hc.core5.annotation.Internal; @@ -118,8 +119,7 @@ private enum QualityOfProtection { private byte[] a1; private byte[] a2; - private String username; - private char[] password; + private UsernamePasswordCredentials credentials; public DigestScheme() { this(StandardCharsets.ISO_8859_1); @@ -133,8 +133,9 @@ public DigestScheme(final Charset charset) { public void initPreemptive(final Credentials credentials, final String cnonce, final String realm) { Args.notNull(credentials, "Credentials"); - this.username = credentials.getUserPrincipal().getName(); - this.password = credentials.getPassword(); + Args.check(credentials instanceof UsernamePasswordCredentials, + "Unsupported credential type: " + credentials.getClass()); + this.credentials = (UsernamePasswordCredentials) credentials; this.paramMap.put("cnonce", cnonce); this.paramMap.put("realm", realm); } @@ -190,9 +191,8 @@ public boolean isResponseReady( final AuthScope authScope = new AuthScope(host, getRealm(), getName()); final Credentials credentials = credentialsProvider.getCredentials( authScope, context); - if (credentials != null) { - this.username = credentials.getUserPrincipal().getName(); - this.password = credentials.getPassword(); + if (credentials instanceof UsernamePasswordCredentials) { + this.credentials = (UsernamePasswordCredentials) credentials; return true; } @@ -201,8 +201,7 @@ public boolean isResponseReady( final String exchangeId = clientContext.getExchangeId(); LOG.debug("{} No credentials found for auth scope [{}]", exchangeId, authScope); } - this.username = null; - this.password = null; + this.credentials = null; return false; } @@ -320,13 +319,13 @@ private String createDigestResponse(final HttpRequest request) throws Authentica // ":" unq(cnonce-value) // calculated one per session - buffer.append(username).append(":").append(realm).append(":").append(password); + buffer.append(credentials.getUserName()).append(":").append(realm).append(":").append(credentials.getUserPassword()); final String checksum = formatHex(digester.digest(this.buffer.toByteArray())); buffer.reset(); buffer.append(checksum).append(":").append(nonce).append(":").append(cnonce); } else { // unq(username-value) ":" unq(realm-value) ":" passwd - buffer.append(username).append(":").append(realm).append(":").append(password); + buffer.append(credentials.getUserName()).append(":").append(realm).append(":").append(credentials.getUserPassword()); } a1 = buffer.toByteArray(); @@ -387,7 +386,7 @@ private String createDigestResponse(final HttpRequest request) throws Authentica buffer.append(StandardAuthScheme.DIGEST + " "); final List params = new ArrayList<>(20); - params.add(new BasicNameValuePair("username", username)); + params.add(new BasicNameValuePair("username", credentials.getUserName())); params.add(new BasicNameValuePair("realm", realm)); params.add(new BasicNameValuePair("nonce", nonce)); params.add(new BasicNameValuePair("uri", uri)); diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/auth/TestCredentials.java b/httpclient5/src/test/java/org/apache/hc/client5/http/auth/TestCredentials.java index a3b1e7926..de14d7c48 100644 --- a/httpclient5/src/test/java/org/apache/hc/client5/http/auth/TestCredentials.java +++ b/httpclient5/src/test/java/org/apache/hc/client5/http/auth/TestCredentials.java @@ -44,14 +44,14 @@ public void testUsernamePasswordCredentialsBasics() { Assertions.assertEquals("name", creds1.getUserName()); Assertions.assertEquals(new BasicUserPrincipal("name"), creds1.getUserPrincipal()); - Assertions.assertArrayEquals("pwd".toCharArray(), creds1.getPassword()); + Assertions.assertArrayEquals("pwd".toCharArray(), creds1.getUserPassword()); Assertions.assertEquals("[principal: name]", creds1.toString()); final UsernamePasswordCredentials creds2 = new UsernamePasswordCredentials( "name", null); Assertions.assertEquals("name", creds2.getUserName()); Assertions.assertEquals(new BasicUserPrincipal("name"), creds2.getUserPrincipal()); - Assertions.assertNull(creds2.getPassword()); + Assertions.assertNull(creds2.getUserPassword()); Assertions.assertEquals("[principal: name]", creds2.toString()); } diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/auth/TestDigestScheme.java b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/auth/TestDigestScheme.java index 8a2671979..3f05886cc 100644 --- a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/auth/TestDigestScheme.java +++ b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/auth/TestDigestScheme.java @@ -260,6 +260,7 @@ public void testDigestAuthenticationNoAlgorithm() throws Exception { final DigestScheme authscheme = new DigestScheme(); authscheme.processChallenge(authChallenge, null); + Assertions.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null)); final String authResponse = authscheme.generateAuthResponse(host, request, null); final Map table = parseAuthResponse(authResponse); @@ -281,6 +282,7 @@ public void testDigestAuthenticationMD5Algorithm() throws Exception { final DigestScheme authscheme = new DigestScheme(); authscheme.processChallenge(authChallenge, null); + Assertions.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null)); final String authResponse = authscheme.generateAuthResponse(host, request, null); final Map table = parseAuthResponse(authResponse); diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/auth/TestSystemDefaultCredentialsProvider.java b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/auth/TestSystemDefaultCredentialsProvider.java index 2c452a3f9..265b4c980 100644 --- a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/auth/TestSystemDefaultCredentialsProvider.java +++ b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/auth/TestSystemDefaultCredentialsProvider.java @@ -104,7 +104,6 @@ public void testSystemCredentialsProviderCredentials() throws Exception { RequestorType.SERVER); Assertions.assertNotNull(receivedCredentials); Assertions.assertEquals(AUTH1.getUserName(), receivedCredentials.getUserPrincipal().getName()); - Assertions.assertEquals(AUTH1.getPassword(), receivedCredentials.getPassword()); } @Test @@ -122,7 +121,6 @@ public void testSystemCredentialsProviderNoContext() throws Exception { RequestorType.SERVER); Assertions.assertNotNull(receivedCredentials); Assertions.assertEquals(AUTH1.getUserName(), receivedCredentials.getUserPrincipal().getName()); - Assertions.assertEquals(AUTH1.getPassword(), receivedCredentials.getPassword()); } private AuthenticatorDelegate installAuthenticator(final PasswordAuthentication returedAuthentication) {