package org.pac4j.http.client.direct;
import org.pac4j.core.client.DirectClient;
import org.pac4j.core.context.WebContext;
import org.pac4j.core.credentials.authenticator.Authenticator;
import org.pac4j.core.exception.HttpAction;
import org.pac4j.core.profile.CommonProfile;
import org.pac4j.core.profile.creator.ProfileCreator;
import org.pac4j.core.util.CommonHelper;
import org.pac4j.http.credentials.CredentialUtil;
import org.pac4j.http.credentials.DigestCredentials;
import org.pac4j.http.credentials.extractor.DigestAuthExtractor;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
/**
* <p>This class is the client to authenticate users directly through HTTP digest auth.</p>
* <p>Add the <code>commons-codec</code> dependency to use this class.</p>
*
* @author Mircea Carasel
* @since 1.9.0
*/
public class DirectDigestAuthClient extends DirectClient<DigestCredentials, CommonProfile> {
private String realm = "pac4jRealm";
public DirectDigestAuthClient() {
}
public DirectDigestAuthClient(final Authenticator digestAuthenticator) {
defaultAuthenticator(digestAuthenticator);
}
public DirectDigestAuthClient(final Authenticator digestAuthenticator,
final ProfileCreator profileCreator) {
defaultAuthenticator(digestAuthenticator);
defaultProfileCreator(profileCreator);
}
@Override
protected void clientInit(final WebContext context) {
defaultCredentialsExtractor(new DigestAuthExtractor(getName()));
}
/** Per RFC 2617
* If a server receives a request for an access-protected object, and an
* acceptable Authorization header is not sent, the server responds with
* a "401 Unauthorized" status code, and a WWW-Authenticate header
*/
@Override
protected DigestCredentials retrieveCredentials(final WebContext context) throws HttpAction {
DigestCredentials credentials = super.retrieveCredentials(context);
if (credentials == null) {
String nonce = calculateNonce();
HttpAction.unauthorizedDigest("Digest required", context, realm, "auth", nonce);
}
return credentials;
}
/**
* A server-specified data string which should be uniquely generated each time a 401 response is made (RFC 2617)
* Based on current time including nanoseconds
*/
private String calculateNonce() {
LocalDateTime time = LocalDateTime.now();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy:MM:dd:HH:mm:ss.SSS");
String fmtTime = formatter.format(time);
return CredentialUtil.encryptMD5(fmtTime + CommonHelper.randomString(10));
}
public String getRealm() {
return realm;
}
public void setRealm(final String realm) {
this.realm = realm;
}
@Override
public String toString() {
return CommonHelper.toString(this.getClass(), "name", getName(), "realm", this.realm, "extractor", getCredentialsExtractor(),
"authenticator", getAuthenticator(), "profileCreator", getProfileCreator());
}
}