Credentials interface should be able to represent different types of user credentials including token based with no password

This commit is contained in:
Oleg Kalnichevski 2022-11-26 18:28:36 +01:00
parent c0194331c3
commit 55f86b9fd7
7 changed files with 65 additions and 43 deletions

View File

@ -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();
}

View File

@ -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 class UsernamePasswordCredentials implements Credentials, Serializable {
return this.principal.getName();
}
/**
* @since 5.3
*/
public char[] getUserPassword() {
return password;
}
/**
* @deprecated Use {@link #getUserPassword()}.
*/
@Deprecated
@Override
public char[] getPassword() {
return password;

View File

@ -39,7 +39,6 @@ import java.util.List;
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.Credentials;
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 class BasicScheme implements AuthScheme, Serializable {
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 class BasicScheme implements AuthScheme, Serializable {
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 class BasicScheme implements AuthScheme, Serializable {
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 class BasicScheme implements AuthScheme, Serializable {
}
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 class BasicScheme implements AuthScheme, Serializable {
}
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();
}

View File

@ -53,6 +53,7 @@ import org.apache.hc.client5.http.auth.Credentials;
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 @@ public class DigestScheme implements AuthScheme, Serializable {
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 class DigestScheme implements AuthScheme, Serializable {
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 class DigestScheme implements AuthScheme, Serializable {
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 class DigestScheme implements AuthScheme, Serializable {
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 @@ public class DigestScheme implements AuthScheme, Serializable {
// ":" 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 @@ public class DigestScheme implements AuthScheme, Serializable {
buffer.append(StandardAuthScheme.DIGEST + " ");
final List<BasicNameValuePair> 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));

View File

@ -44,14 +44,14 @@ public class TestCredentials {
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());
}

View File

@ -260,6 +260,7 @@ public class TestDigestScheme {
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<String, String> table = parseAuthResponse(authResponse);
@ -281,6 +282,7 @@ public class TestDigestScheme {
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<String, String> table = parseAuthResponse(authResponse);

View File

@ -104,7 +104,6 @@ public class TestSystemDefaultCredentialsProvider {
RequestorType.SERVER);
Assertions.assertNotNull(receivedCredentials);
Assertions.assertEquals(AUTH1.getUserName(), receivedCredentials.getUserPrincipal().getName());
Assertions.assertEquals(AUTH1.getPassword(), receivedCredentials.getPassword());
}
@Test
@ -122,7 +121,6 @@ public class TestSystemDefaultCredentialsProvider {
RequestorType.SERVER);
Assertions.assertNotNull(receivedCredentials);
Assertions.assertEquals(AUTH1.getUserName(), receivedCredentials.getUserPrincipal().getName());
Assertions.assertEquals(AUTH1.getPassword(), receivedCredentials.getPassword());
}
private AuthenticatorDelegate installAuthenticator(final PasswordAuthentication returedAuthentication) {