Skip to content

Commit

Permalink
[ELY-2359] Support HTTP Digest when fronted by load balancer
Browse files Browse the repository at this point in the history
  • Loading branch information
Skyllarr committed Sep 5, 2023
1 parent 7bfea75 commit 4db7996
Show file tree
Hide file tree
Showing 11 changed files with 586 additions and 143 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,15 @@ public final class MechanismConfiguration {
private final RealmMapper realmMapper;
private final Map<String, MechanismRealmConfiguration> mechanismRealms;
private final CredentialSource serverCredentialSource;
private final boolean sessionDigest;

MechanismConfiguration(final Function<Principal, Principal> preRealmRewriter, final Function<Principal, Principal> postRealmRewriter, final Function<Principal, Principal> finalRewriter, final RealmMapper realmMapper, final Collection<MechanismRealmConfiguration> mechanismRealms, final CredentialSource serverCredentialSource) {
MechanismConfiguration(final Function<Principal, Principal> preRealmRewriter, final Function<Principal, Principal> postRealmRewriter, final Function<Principal, Principal> finalRewriter, final RealmMapper realmMapper, final Collection<MechanismRealmConfiguration> mechanismRealms, final CredentialSource serverCredentialSource, final boolean sessionDigest) {
checkNotNullParam("mechanismRealms", mechanismRealms);
this.preRealmRewriter = preRealmRewriter;
this.postRealmRewriter = postRealmRewriter;
this.finalRewriter = finalRewriter;
this.realmMapper = realmMapper;
this.sessionDigest = sessionDigest;
final Iterator<MechanismRealmConfiguration> iterator = mechanismRealms.iterator();
if (! iterator.hasNext()) {
// zero
Expand Down Expand Up @@ -146,6 +148,9 @@ public MechanismRealmConfiguration getMechanismRealmConfiguration(String realmNa
return mechanismRealms.get(realmName);
}

public boolean getSessionDigest() {
return sessionDigest;
}
/**
* Obtain a new {@link Builder} capable of building a {@link MechanismConfiguration}.
*
Expand All @@ -167,6 +172,7 @@ public static final class Builder {
private RealmMapper realmMapper;
private List<MechanismRealmConfiguration> mechanismRealms;
private CredentialSource serverCredentialSource = CredentialSource.NONE;
private boolean sessionDigest = false;

/**
* Construct a new instance.
Expand Down Expand Up @@ -271,6 +277,12 @@ public Builder setServerCredentialSource(final CredentialSource serverCredential
return this;
}

public Builder setSessionDigest(final Boolean sessionDigest) {
checkNotNullParam("sessionDigest", sessionDigest);
this.sessionDigest = sessionDigest;
return this;
}

/**
* Build a new instance. If no mechanism realms are offered, an empty collection should be provided for
* {@code mechanismRealms}; otherwise, if the mechanism only supports one realm, the first will be used. If the
Expand All @@ -285,12 +297,12 @@ public MechanismConfiguration build() {
} else {
mechanismRealms = unmodifiableList(asList(mechanismRealms.toArray(NO_REALM_CONFIGS)));
}
return new MechanismConfiguration(preRealmRewriter, postRealmRewriter, finalRewriter, realmMapper, mechanismRealms, serverCredentialSource);
return new MechanismConfiguration(preRealmRewriter, postRealmRewriter, finalRewriter, realmMapper, mechanismRealms, serverCredentialSource, sessionDigest);
}
}

/**
* An empty mechanism configuration..
*/
public static final MechanismConfiguration EMPTY = new MechanismConfiguration(Function.identity(), Function.identity(), Function.identity(), null, emptyList(), CredentialSource.NONE);
public static final MechanismConfiguration EMPTY = new MechanismConfiguration(Function.identity(), Function.identity(), Function.identity(), null, emptyList(), CredentialSource.NONE, false);
}
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ public String getMechanismName() {
public String getHostName() {
return null;
}

};

}
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ private HttpConstants() {

public static final String CONFIG_VALIDATE_DIGEST_URI = CONFIG_BASE + ".validate-digest-uri";
public static final String CONFIG_SKIP_CERTIFICATE_VERIFICATION = CONFIG_BASE + ".skip-certificate-verification";
public static final String CONFIG_SESSION_DIGEST = CONFIG_BASE + ".session-digest";

/**
* The context relative path of the login page.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
import org.wildfly.security.http.HttpServerMechanismsResponder;
import org.wildfly.security.http.HttpServerRequest;
import org.wildfly.security.http.HttpServerResponse;
import org.wildfly.security.http.Scope;
import org.wildfly.security.mechanism.AuthenticationMechanismException;
import org.wildfly.security.mechanism.digest.DigestQuote;
import org.wildfly.security.mechanism.digest.PasswordDigestObtainer;
Expand Down Expand Up @@ -114,6 +115,17 @@ public String getMechanismName() {

@Override
public void evaluateRequest(final HttpServerRequest request) throws HttpAuthenticationException {

if (nonceManager instanceof PersistentNonceManager) {
if (request.getScope(Scope.SESSION) == null || !request.getScope(Scope.SESSION).exists()) {
request.getScope(Scope.SESSION).create();
}
PersistentNonceManager persistentNonceManager = (PersistentNonceManager) request.getScope(Scope.SESSION).getAttachment("persistentNonceManager");
if (persistentNonceManager != null) {
((PersistentNonceManager) nonceManager).refreshInfoFromSessionNonceManager(persistentNonceManager);
}
}

List<String> authorizationValues = request.getRequestHeaderValues(AUTHORIZATION);

if (authorizationValues != null) {
Expand All @@ -126,14 +138,13 @@ public void evaluateRequest(final HttpServerRequest request) throws HttpAuthenti
return;
} catch (AuthenticationMechanismException e) {
httpDigest.trace("Failed to parse or validate the response", e);
request.badRequest(e.toHttpAuthenticationException(), response -> prepareResponse(selectRealm(), response, false));
request.badRequest(e.toHttpAuthenticationException(), response -> prepareResponse(selectRealm(), response, false, request));
return;
}
}
}
}

request.noAuthenticationInProgress(response -> prepareResponse(selectRealm(), response, false));
request.noAuthenticationInProgress(response -> prepareResponse(selectRealm(), response, false, request));
}

private void validateResponse(HashMap<String, byte[]> responseTokens, final HttpServerRequest request) throws AuthenticationMechanismException, HttpAuthenticationException {
Expand Down Expand Up @@ -211,7 +222,7 @@ private void validateResponse(HashMap<String, byte[]> responseTokens, final Http
if (username.length() == 0) {
httpDigest.trace("Failed: no username");
fail();
request.authenticationFailed(httpDigest.authenticationFailed(), httpResponse -> prepareResponse(selectedRealm, httpResponse, false));
request.authenticationFailed(httpDigest.authenticationFailed(), httpResponse -> prepareResponse(selectedRealm, httpResponse, false, request));
return;
}

Expand All @@ -220,7 +231,7 @@ private void validateResponse(HashMap<String, byte[]> responseTokens, final Http
if (hA1 == null) {
httpDigest.trace("Failed: unable to get expected proof");
fail();
request.authenticationFailed(httpDigest.authenticationFailed(), httpResponse -> prepareResponse(selectedRealm, httpResponse, false));
request.authenticationFailed(httpDigest.authenticationFailed(), httpResponse -> prepareResponse(selectedRealm, httpResponse, false, request));
return;
}

Expand All @@ -229,13 +240,13 @@ private void validateResponse(HashMap<String, byte[]> responseTokens, final Http
if (MessageDigest.isEqual(response, calculatedResponse) == false) {
httpDigest.trace("Failed: invalid proof");
fail();
request.authenticationFailed(httpDigest.mechResponseTokenMismatch(getMechanismName()), httpResponse -> prepareResponse(selectedRealm, httpResponse, false));
request.authenticationFailed(httpDigest.mechResponseTokenMismatch(getMechanismName()), httpResponse -> prepareResponse(selectedRealm, httpResponse, false, request));
return;
}

if (nonceValid == false) {
httpDigest.trace("Failed: invalid nonce");
request.authenticationInProgress(httpResponse -> prepareResponse(selectedRealm, httpResponse, true));
request.authenticationInProgress(httpResponse -> prepareResponse(selectedRealm, httpResponse, true, request));
return;
}

Expand Down Expand Up @@ -379,7 +390,7 @@ private String[] getAvailableRealms() throws AuthenticationMechanismException {
}
}

private void prepareResponse(String realmName, HttpServerResponse response, boolean stale) throws HttpAuthenticationException {
private void prepareResponse(String realmName, HttpServerResponse response, boolean stale, HttpServerRequest request) throws HttpAuthenticationException {
StringBuilder sb = new StringBuilder(CHALLENGE_PREFIX);
sb.append(REALM).append("=\"").append(DigestQuote.quote(realmName)).append("\"");

Expand All @@ -396,6 +407,10 @@ private void prepareResponse(String realmName, HttpServerResponse response, bool

response.addResponseHeader(WWW_AUTHENTICATE, sb.toString());
response.setStatusCode(UNAUTHORIZED);

if ((nonceManager instanceof PersistentNonceManager) && request.getScope(Scope.SESSION) != null) {
request.getScope(Scope.SESSION).setAttachment("persistentNonceManager", this.nonceManager);
}
}

private boolean authorize(String username) throws AuthenticationMechanismException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import static org.wildfly.common.Assert.checkNotNullParam;
import static org.wildfly.security.http.HttpConstants.CONFIG_CONTEXT_PATH;
import static org.wildfly.security.http.HttpConstants.CONFIG_REALM;
import static org.wildfly.security.http.HttpConstants.CONFIG_SESSION_DIGEST;
import static org.wildfly.security.http.HttpConstants.DIGEST_NAME;
import static org.wildfly.security.http.HttpConstants.DIGEST_SHA256_NAME;
import static org.wildfly.security.http.HttpConstants.DIGEST_SHA512_256_NAME;
Expand Down Expand Up @@ -58,7 +59,7 @@ public DigestMechanismFactory() {
}

public DigestMechanismFactory(final Provider provider) {
this(new Provider[] { provider });
this(new Provider[]{provider});
}

public DigestMechanismFactory(final Provider... providers) {
Expand Down Expand Up @@ -100,6 +101,8 @@ public HttpServerAuthenticationMechanism createAuthenticationMechanism(String me

if (properties.containsKey("nonceManager")) {
nonceManager = (NonceManager) properties.get("nonceManager");
} else if (properties.get(CONFIG_SESSION_DIGEST) != null) {
nonceManager = new PersistentNonceManager(300000, 900000, true, 20, SHA256, ElytronMessages.httpDigest);
}

switch (mechanismName) {
Expand Down

0 comments on commit 4db7996

Please sign in to comment.