/**
* Copyright 2005-2014 Restlet
*
* The contents of this file are subject to the terms of one of the following
* open source licenses: Apache 2.0 or or EPL 1.0 (the "Licenses"). You can
* select the license that you prefer but you may not use this file except in
* compliance with one of these Licenses.
*
* You can obtain a copy of the Apache 2.0 license at
* http://www.opensource.org/licenses/apache-2.0
*
* You can obtain a copy of the EPL 1.0 license at
* http://www.opensource.org/licenses/eclipse-1.0
*
* See the Licenses for the specific language governing permissions and
* limitations under the Licenses.
*
* Alternatively, you can obtain a royalty free commercial license with less
* limitations, transferable or non-transferable, directly at
* http://restlet.com/products/restlet-framework
*
* Restlet is a registered trademark of Restlet S.A.S.
*/
package org.restlet.data;
import org.restlet.Request;
import org.restlet.Response;
import org.restlet.engine.util.SystemUtils;
import org.restlet.util.Series;
/**
* Authentication response sent by client to an origin server. This is typically
* following a {@link ChallengeRequest} sent by the origin server to the client.<br>
* <br>
* Sometimes, it might be faster to preemptively issue a challenge response if
* the client knows for sure that the target resource will require
* authentication.<br>
* <br>
* Note that when used with HTTP connectors, this class maps to the
* "Authorization" header.
*
* @author Jerome Louvel
*/
public final class ChallengeResponse extends ChallengeMessage {
/** The client nonce value. */
private volatile String clientNonce;
/**
* The {@link Request#getResourceRef()} value duplicated here in case a
* proxy changed it.
*/
private volatile Reference digestRef;
/** The user identifier, such as a login name or an access key. */
private volatile String identifier;
/** The chosen quality of protection. */
private volatile String quality;
/** The user secret, such as a password or a secret key. */
private volatile char[] secret;
/** The digest algorithm name optionally applied on the user secret. */
private volatile String secretAlgorithm;
/** The server nonce count. */
private volatile int serverNounceCount;
/**
* The time when the response was issued, as returned by
* {@link System#currentTimeMillis()}.
*/
private volatile long timeIssued;
// [ifndef gwt] method
/**
* Constructor. It leverages the latest server response and challenge
* request in order to compute the credentials.
*
* @param challengeRequest
* The challenge request sent by the origin server.
* @param response
* The latest server response.
* @param identifier
* The user identifier, such as a login name or an access key.
* @param secret
* The user secret, such as a password or a secret key, with no
* digest applied.
*/
public ChallengeResponse(ChallengeRequest challengeRequest,
Response response, String identifier, char[] secret) {
this(challengeRequest, response, identifier, secret,
Digest.ALGORITHM_NONE);
}
// [ifndef gwt] method
/**
* Constructor. It leverages the latest server response and challenge
* request in order to compute the credentials.
*
* @param challengeRequest
* The challenge request sent by the origin server.
* @param response
* The latest server response.
* @param identifier
* The user identifier, such as a login name or an access key.
* @param secret
* The user secret used to compute the secret, with an optional
* digest applied.
* @param secretAlgorithm
* The digest algorithm of the user secret (see {@link Digest}
* class).
*/
public ChallengeResponse(ChallengeRequest challengeRequest,
Response response, String identifier, char[] secret,
String secretAlgorithm) {
this(challengeRequest.getScheme(), null, identifier, secret,
secretAlgorithm, null, null, null, null, null, null, null, 0,
0L);
org.restlet.engine.security.AuthenticatorUtils.update(this,
response.getRequest(), response);
}
// [ifndef gwt] method
/**
* Constructor. It leverages the latest server response and challenge
* request in order to compute the credentials.
*
* @param challengeRequest
* The challenge request sent by the origin server.
* @param response
* The latest server response.
* @param identifier
* The user identifier, such as a login name or an access key.
* @param secret
* The user secret, such as a password or a secret key.
*/
public ChallengeResponse(ChallengeRequest challengeRequest,
Response response, String identifier, String secret) {
this(challengeRequest, response, identifier, secret.toCharArray(),
Digest.ALGORITHM_NONE);
}
/**
* Constructor with no credentials.
*
* @param scheme
* The challenge scheme.
*/
public ChallengeResponse(ChallengeScheme scheme) {
this(scheme, null, (char[]) null);
}
/**
* Constructor.
*
* @param scheme
* The challenge scheme.
* @param parameters
* The additional scheme parameters.
* @param identifier
* The user identifier, such as a login name or an access key.
* @param secret
* The user secret, such as a password or a secret key.
* @param secretAlgorithm
* The digest algorithm name optionally applied on the user
* secret.
* @param realm
* The authentication realm.
* @param quality
* The chosen quality of protection.
* @param digestRef
* The {@link Request#getResourceRef()} value duplicated here in
* case a proxy changed it.
* @param digestAlgorithm
* The digest algorithm.
* @param opaque
* An opaque string of data which should be returned by the
* client unchanged.
* @param clientNonce
* The client nonce value.
* @param serverNonce
* The server nonce.
* @param serverNounceCount
* The server nonce count.
* @param timeIssued
* The time when the response was issued, as returned by
* {@link System#currentTimeMillis()}.
*/
public ChallengeResponse(ChallengeScheme scheme,
Series<Parameter> parameters, String identifier, char[] secret,
String secretAlgorithm, String realm, String quality,
Reference digestRef, String digestAlgorithm, String opaque,
String clientNonce, String serverNonce, int serverNounceCount,
long timeIssued) {
super(scheme, realm, parameters, digestAlgorithm, opaque, serverNonce);
this.clientNonce = clientNonce;
this.digestRef = digestRef;
this.identifier = identifier;
this.quality = quality;
this.secret = secret;
this.secretAlgorithm = secretAlgorithm;
this.serverNounceCount = serverNounceCount;
this.timeIssued = timeIssued;
}
/**
* Constructor.
*
* @param scheme
* The challenge scheme.
* @param identifier
* The user identifier, such as a login name or an access key.
* @param secret
* The user secret, such as a password or a secret key.
*/
public ChallengeResponse(ChallengeScheme scheme, String identifier,
char[] secret) {
this(scheme, identifier, secret, null);
}
/**
* Constructor.
*
* @param scheme
* The challenge scheme.
* @param identifier
* The user identifier, such as a login name or an access key.
* @param parameters
* The additional scheme parameters.
*/
public ChallengeResponse(ChallengeScheme scheme, String identifier,
char[] secret, Series<Parameter> parameters) {
this(scheme, parameters, identifier, secret, Digest.ALGORITHM_NONE,
null, null, null, null, null, null, null, 0, 0L);
}
/**
* Constructor.
*
* @param scheme
* The challenge scheme.
* @param identifier
* The user identifier, such as a login name or an access key.
* @param parameters
* The additional scheme parameters.
*/
public ChallengeResponse(ChallengeScheme scheme, String identifier,
Series<Parameter> parameters) {
this(scheme, identifier, null, parameters);
}
/**
* Constructor.
*
* @param scheme
* The challenge scheme.
* @param identifier
* The user identifier, such as a login name or an access key.
* @param secret
* The user secret, such as a password or a secret key.
*/
public ChallengeResponse(ChallengeScheme scheme, String identifier,
String secret) {
this(scheme, identifier, (secret != null) ? secret.toCharArray() : null);
}
/** {@inheritDoc} */
@Override
public boolean equals(Object obj) {
boolean result = (obj == this);
// if obj == this no need to go further
if (!result) {
// if obj isn't a challenge request or is null don't evaluate
// further
if (obj instanceof ChallengeResponse) {
ChallengeResponse that = (ChallengeResponse) obj;
if (getRawValue() != null) {
result = getRawValue().equals(that.getRawValue());
} else {
result = (that.getRawValue() == null);
}
if (result) {
if (getIdentifier() != null) {
result = getIdentifier().equals(that.getIdentifier());
} else {
result = (that.getIdentifier() == null);
}
if (result) {
if (getScheme() != null) {
result = getScheme().equals(that.getScheme());
} else {
result = (that.getScheme() == null);
}
if (result) {
if ((getSecret() == null)
|| (that.getSecret() == null)) {
// check if both are null
result = (getSecret() == that.getSecret());
} else {
if (getSecret().length == that.getSecret().length) {
boolean equals = true;
for (int i = 0; (i < getSecret().length)
&& equals; i++) {
equals = (getSecret()[i] == that
.getSecret()[i]);
}
result = equals;
}
}
}
}
}
}
}
return result;
}
/**
* Returns the client nonce.
*
* @return The client nonce.
*/
public String getClientNonce() {
return this.clientNonce;
}
/**
* Returns the {@link Request#getResourceRef()} value duplicated here in
* case a proxy changed it.
*
* @return The digest URI reference.
*/
public Reference getDigestRef() {
return digestRef;
}
/**
* Returns the user identifier, such as a login name or an access key.
*
* @return The user identifier, such as a login name or an access key.
*/
public String getIdentifier() {
return this.identifier;
}
// [ifndef gwt] method
/**
* Gets the principal associated to the identifier property.
*
* @return The principal associated to the identifier property.
*/
public java.security.Principal getPrincipal() {
return new java.security.Principal() {
public String getName() {
return getIdentifier();
};
};
}
/**
* Returns the chosen quality of protection.
*
* @return The chosen quality of protection.
*/
public String getQuality() {
return quality;
}
/**
* Returns the user secret, such as a password or a secret key.
*
* It is not recommended to use {@link String#String(char[])} for security
* reasons.
*
* @return The user secret, such as a password or a secret key.
*/
public char[] getSecret() {
return this.secret;
}
/**
* Returns the digest algorithm name optionally applied on the user secret.
*
* @return The digest algorithm name optionally applied on the user secret.
*/
public String getSecretAlgorithm() {
return secretAlgorithm;
}
/**
* Returns the server nonce count.
*
* @return The server nonce count.
*/
public int getServerNounceCount() {
return serverNounceCount;
}
// [ifndef gwt] method
/**
* Returns the server nonce count as an hexadecimal string of eight
* characters.
*
* @return The server nonce count as an hexadecimal string.
*/
public String getServerNounceCountAsHex() {
return org.restlet.engine.security.AuthenticatorUtils
.formatNonceCount(getServerNounceCount());
}
/**
* Returns the time when the response was issued, as returned by
* {@link System#currentTimeMillis()}.
*
* @return The time when the response was issued.
*/
public long getTimeIssued() {
return timeIssued;
}
/** {@inheritDoc} */
@Override
public int hashCode() {
// Note that the secret is simply discarded from hash code calculation
// because we don't want it to be materialized as a string
return SystemUtils
.hashCode(getScheme(), getIdentifier(), getRawValue());
}
/**
* Sets the client nonce.
*
* @param clientNonce
* The client nonce.
*/
public void setClientNonce(String clientNonce) {
this.clientNonce = clientNonce;
}
/**
* Sets the digest URI reference.
*
* @param digestRef
* The digest URI reference.
*/
public void setDigestRef(Reference digestRef) {
this.digestRef = digestRef;
}
/**
* Sets the user identifier, such as a login name or an access key.
*
* @param identifier
* The user identifier, such as a login name or an access key.
*/
public void setIdentifier(String identifier) {
this.identifier = identifier;
}
/**
* Sets the chosen quality of protection.
*
* @param quality
* The chosen quality of protection.
*/
public void setQuality(String quality) {
this.quality = quality;
}
/**
* Sets the user secret, such as a password or a secret key.
*
* @param secret
* The user secret, such as a password or a secret key.
*/
public void setSecret(char[] secret) {
this.secret = secret;
}
/**
* Sets the user secret, such as a password or a secret key.
*
* @param secret
* The user secret, such as a password or a secret key.
*/
public void setSecret(String secret) {
this.secret = (secret == null) ? null : secret.toCharArray();
}
/**
* Sets the digest algorithm name optionally applied on the user secret.
*
* @param secretDigestAlgorithm
* The digest algorithm name optionally applied on the user
* secret.
*/
public void setSecretAlgorithm(String secretDigestAlgorithm) {
this.secretAlgorithm = secretDigestAlgorithm;
}
/**
* Sets the server nonce count.
*
* @param serverNounceCount
* The server nonce count.
*/
public void setServerNounceCount(int serverNounceCount) {
this.serverNounceCount = serverNounceCount;
}
/**
* Sets the time when the response was issued, as returned by
* {@link System#currentTimeMillis()}.
*
* @param timeIssued
* The time when the response was issued.
*/
public void setTimeIssued(long timeIssued) {
this.timeIssued = timeIssued;
}
}