Author: [email protected]
Date: Mon Jul 11 10:09:34 2011
New Revision: 1210
Log:
[AMDATUAUTH-43] Added fixes to prevent session fixation attacks.
oauth_verifier, oauth_callback and oauth_callback_confirmed parameter handling
has been added according to the Revision A spec of 1.0.
Removed:
trunk/amdatu-auth/oauth-server/src/main/java/org/amdatu/authentication/oauth/server/service/OAuthPluggableValidator.java
Modified:
trunk/amdatu-auth/oauth-client/src/main/java/org/amdatu/authentication/oauth/client/OAuthServiceConsumerClient.java
trunk/amdatu-auth/oauth-server/src/main/java/org/amdatu/authentication/oauth/server/OAuthTokenProvider.java
trunk/amdatu-auth/oauth-server/src/main/java/org/amdatu/authentication/oauth/server/service/OAuthServerConfiguration.java
trunk/amdatu-auth/oauth-server/src/main/java/org/amdatu/authentication/oauth/server/service/OAuthServerUtil.java
trunk/amdatu-auth/oauth-server/src/main/java/org/amdatu/authentication/oauth/server/service/OAuthTokenProviderImpl.java
trunk/amdatu-auth/oauth-server/src/main/java/org/amdatu/authentication/oauth/server/servlet/OAuthAccessTokenServletImpl.java
trunk/amdatu-auth/oauth-server/src/main/java/org/amdatu/authentication/oauth/server/servlet/OAuthAuthorizeTokenServletImpl.java
trunk/amdatu-auth/oauth-server/src/main/java/org/amdatu/authentication/oauth/server/servlet/OAuthRequestTokenServletImpl.java
trunk/amdatu-auth/test-integration/tests/src/test/java/org/amdatu/auth/test/integration/tests/AuthTest.java
trunk/amdatu-auth/test-integration/tests/src/test/java/org/amdatu/auth/test/integration/tests/OAuthThreeLeggedTest.java
trunk/amdatu-auth/test-integration/tests/src/test/java/org/amdatu/auth/test/integration/tests/OAuthTwoLeggedTest.java
Modified:
trunk/amdatu-auth/oauth-client/src/main/java/org/amdatu/authentication/oauth/client/OAuthServiceConsumerClient.java
==============================================================================
---
trunk/amdatu-auth/oauth-client/src/main/java/org/amdatu/authentication/oauth/client/OAuthServiceConsumerClient.java
(original)
+++
trunk/amdatu-auth/oauth-client/src/main/java/org/amdatu/authentication/oauth/client/OAuthServiceConsumerClient.java
Mon Jul 11 10:09:34 2011
@@ -18,8 +18,10 @@
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
+import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
+import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
@@ -282,6 +284,18 @@
}
}
+ public String getVerifier(String callbackURL) throws MalformedURLException
{
+ URL uri = new URL(callbackURL);
+ String args = uri.getQuery();
+ String[] qp = args.split("&");
+ for (String q : qp) {
+ if (q.split("=")[0].equals(OAuth.OAUTH_VERIFIER)) {
+ return q.split("=")[1];
+ }
+ }
+ return null;
+ }
+
public OAuthMessage invoke(OAuthHttpRequest httpRequest) throws
OAuthException {
OAuthAccessor accessor = httpRequest.getAccessor();
String httpMethod = httpRequest.getHttpMethod();
Modified:
trunk/amdatu-auth/oauth-server/src/main/java/org/amdatu/authentication/oauth/server/OAuthTokenProvider.java
==============================================================================
---
trunk/amdatu-auth/oauth-server/src/main/java/org/amdatu/authentication/oauth/server/OAuthTokenProvider.java
(original)
+++
trunk/amdatu-auth/oauth-server/src/main/java/org/amdatu/authentication/oauth/server/OAuthTokenProvider.java
Mon Jul 11 10:09:34 2011
@@ -56,6 +56,16 @@
String LAST_USED_TIMESTAMP_PROPERTY = "oauth_last_used";
/**
+ * Indicates the callback URL associated with this request token.
+ */
+ String CALLBACK_URL_PROPERTY = "oauth_callback";
+
+ /**
+ * Indicates the oauth_verifier associated with this request token.
+ */
+ String VERIFIER_PROPERTY = "oauth_verifier";
+
+ /**
* Indicates a request token.
*/
String REQUEST_TOKEN_TYPE = "request";
@@ -147,14 +157,30 @@
* In case any exception occurred
*/
void markAsAuthorized(Token token, String userId) throws OAuthException;
+
+ /**
+ * Generates a new oath verifier, which is just a nonce. The nonce is
associated with the
+ * request token and also returned as additional query parameter in the
callback URL. From the callback
+ * URL the consumer receives the oauth_verifier and sends it along with
the exchange request for access
+ * token request. The OAuth server receives the request token and verifier
and verifies that the
+ * provided verifier matches the verifier stored with the request token.
This makes sure that the
+ * user who authorized the request is the same as the one initiated the
OAuth dance (preventing session
+ * fixation attacks).
+ * @param token The request token
+ * @return a newly generated verifier
+ * @throws OAuthException
+ */
+ String generateVerifier(Token token) throws OAuthException;
/**
* Exchange the request token for an access token. This call is typically
invoked after a request token
* has been authorized, however this method does not validate that. The
token servlets are supposed to
* perform all token validations.
+ * @param token The request token to exchange
+ * @param verifier The oauth_verifier provided by the service provider
when the token was authorized
*
* @throws OAuthException
* In case the oAuth token stored in the accessor is invalid.
*/
- Token exchangeForAccessToken(Token token) throws OAuthException;
+ Token exchangeForAccessToken(Token token, String verifier) throws
OAuthException;
}
Modified:
trunk/amdatu-auth/oauth-server/src/main/java/org/amdatu/authentication/oauth/server/service/OAuthServerConfiguration.java
==============================================================================
---
trunk/amdatu-auth/oauth-server/src/main/java/org/amdatu/authentication/oauth/server/service/OAuthServerConfiguration.java
(original)
+++
trunk/amdatu-auth/oauth-server/src/main/java/org/amdatu/authentication/oauth/server/service/OAuthServerConfiguration.java
Mon Jul 11 10:09:34 2011
@@ -15,18 +15,52 @@
*/
package org.amdatu.authentication.oauth.server.service;
+/**
+ * Provides the OAuth server configuration. This interface is not published as
it should
+ * only be used internally by services of this bundle.
+ *
+ * @author ivol
+ */
public interface OAuthServerConfiguration {
+ /**
+ * Returns the hostname of the OAuth server.
+ * @return the hostname of the OAuth server.
+ */
String getHostname();
+ /**
+ * Returns the portnr of the OAuth server.
+ * @return the portnr of the OAuth server.
+ */
String getPortNr();
+ /**
+ * Returns the URL of the authorization process.
+ * @return the URL of the authorization process.
+ */
String getAuthorizeUrl();
+ /**
+ * Returns the maximum age of request timestamps.
+ * @return the maximum age of request timestamps.
+ */
long getRequestTimestampMaxAge();
+ /**
+ * Returns the maximum age of request tokens.
+ * @return the maximum age of request tokens.
+ */
long getRequestTokenMaxAge();
+ /**
+ * Returns the maximum age of access tokens.
+ * @return the maximum age of access tokens.
+ */
long getAccessTokenMaxAge();
+ /**
+ * Returns the timeout of access tokens.
+ * @return the timeout of access tokens.
+ */
long getAccessTokenTimeout();
}
Modified:
trunk/amdatu-auth/oauth-server/src/main/java/org/amdatu/authentication/oauth/server/service/OAuthServerUtil.java
==============================================================================
---
trunk/amdatu-auth/oauth-server/src/main/java/org/amdatu/authentication/oauth/server/service/OAuthServerUtil.java
(original)
+++
trunk/amdatu-auth/oauth-server/src/main/java/org/amdatu/authentication/oauth/server/service/OAuthServerUtil.java
Mon Jul 11 10:09:34 2011
@@ -42,7 +42,7 @@
realm += request.getLocalName();
OAuthServlet.handleException(response, e, realm, sendBody);
}
-
+
public static OAuthConsumer copy(final OAuthServiceConsumer
serviceConsumer, OAuthServiceProvider serviceProvider) {
String name = serviceConsumer.getName();
String callbackUrl = serviceConsumer.getCallbackUrl();
@@ -57,15 +57,35 @@
return consumer;
}
+ public static OAuthProblemException createException(String problem, String
reason) {
+ OAuthProblemException e = new OAuthProblemException(problem);
+ e.setParameter("reason", reason);
+ return e;
+ }
+
+ public static OAuthProblemException createException(String problem, String
reason, Exception e) {
+ OAuthProblemException exc = createException(problem, reason);
+ exc.setStackTrace(e.getStackTrace());
+ return exc;
+ }
+
public static OAuthProblemException createException(Exception e,
OAuthMessage requestMessage) {
+ return createException(e, requestMessage, null);
+ }
+
+ public static OAuthProblemException createException(Exception e,
OAuthMessage requestMessage, String msg) {
OAuthProblemException exc = new OAuthProblemException(e.toString());
exc.setParameter("method", requestMessage.method);
exc.setParameter("URL", requestMessage.URL);
+ if (msg != null) {
+ exc.setParameter("reason", msg);
+ }
try {
exc.setParameter("parameters", requestMessage.getParameters());
}
- catch (IOException e1) {}
- exc.fillInStackTrace();
+ catch (IOException e1) {
+ exc.setStackTrace(e1.getStackTrace());
+ }
return exc;
}
}
Modified:
trunk/amdatu-auth/oauth-server/src/main/java/org/amdatu/authentication/oauth/server/service/OAuthTokenProviderImpl.java
==============================================================================
---
trunk/amdatu-auth/oauth-server/src/main/java/org/amdatu/authentication/oauth/server/service/OAuthTokenProviderImpl.java
(original)
+++
trunk/amdatu-auth/oauth-server/src/main/java/org/amdatu/authentication/oauth/server/service/OAuthTokenProviderImpl.java
Mon Jul 11 10:09:34 2011
@@ -79,33 +79,45 @@
// try to load from local cache if not throw exception
String consumerKey = requestMessage.getConsumerKey();
if (consumerKey == null || "".equals(consumerKey)) {
- throw new
OAuthProblemException(OAuth.Problems.CONSUMER_KEY_REJECTED);
+ String msg = "No oauth_consumer parameter send along with the
OAuth request";
+ throw
OAuthServerUtil.createException(OAuth.Problems.CONSUMER_KEY_REJECTED, msg);
}
- OAuthServiceConsumer serviceConsumer =
m_consumerRegistry.getConsumer(consumerKey);
- if (serviceConsumer == null) {
- throw new
OAuthProblemException(OAuth.Problems.CONSUMER_KEY_UNKNOWN);
+ try {
+ OAuthServiceConsumer serviceConsumer =
m_consumerRegistry.getConsumer(consumerKey);
+ if (serviceConsumer == null) {
+ String msg =
+ "The oauth_consumer with key '" + consumerKey + "'
does not exist in the consumer registry";
+ throw
OAuthServerUtil.createException(OAuth.Problems.CONSUMER_KEY_UNKNOWN, msg);
+ }
+ return OAuthServerUtil.copy(serviceConsumer,
m_serviceProvider);
+ }
+ catch (ConsumerRegistryStorageException e) {
+ String msg =
+ "Could not load oauth_consumer with key '" +
requestMessage.getConsumerKey()
+ + "' from the consumer registry";
+ throw OAuthServerUtil.createException(e, requestMessage, msg);
}
- return OAuthServerUtil.copy(serviceConsumer, m_serviceProvider);
- }
- catch (ConsumerRegistryStorageException e) {
- throw OAuthServerUtil.createException(e, requestMessage);
}
catch (IOException e) {
- throw OAuthServerUtil.createException(e, requestMessage);
+ String msg = "Could not load oauth_consumer from the consumer
registry";
+ throw OAuthServerUtil.createException(e, requestMessage, msg);
}
}
public synchronized OAuthConsumer getConsumer(final Token token) throws
OAuthException {
+ String consumerKey =
token.getProperty(OAuthTokenProvider.CONSUMER_PROPERTY);
try {
- String consumerKey =
token.getProperty(OAuthTokenProvider.CONSUMER_PROPERTY);
OAuthServiceConsumer serviceConsumer =
m_consumerRegistry.getConsumer(consumerKey);
if (serviceConsumer == null) {
- throw new
OAuthProblemException(OAuth.Problems.CONSUMER_KEY_UNKNOWN);
+ String msg = "The oauth_consumer with key '" + consumerKey +
"' stored in the request token "
+ + "does not exist in the consumer registry";
+ throw
OAuthServerUtil.createException(OAuth.Problems.CONSUMER_KEY_UNKNOWN, msg);
}
return OAuthServerUtil.copy(serviceConsumer, m_serviceProvider);
}
catch (ConsumerRegistryStorageException e) {
- throw new OAuthException(e.toString());
+ String msg = "Could not load oauth_consumer with key '" +
consumerKey + "' from the consumer registry";
+ throw
OAuthServerUtil.createException(OAuth.Problems.CONSUMER_KEY_UNKNOWN, msg, e);
}
}
@@ -118,16 +130,20 @@
}
// Check if the token is not expired in the meantime
- if
(REQUEST_TOKEN_TYPE.equals(token.getProperty(TOKEN_TYPE_PROPERTY)) &&
token.isExpired(m_config.getRequestTokenMaxAge())) {
+ if
(REQUEST_TOKEN_TYPE.equals(token.getProperty(TOKEN_TYPE_PROPERTY))
+ && token.isExpired(m_config.getRequestTokenMaxAge())) {
m_tokenStore.removeToken(token);
- throw new OAuthProblemException(OAuth.Problems.TOKEN_EXPIRED);
- } else if
(ACCESS_TOKEN_TYPE.equals(token.getProperty(TOKEN_TYPE_PROPERTY))) {
+ String msg = "The request token is expired (maximum age is " +
m_config.getRequestTokenMaxAge() + " ms)";
+ throw
OAuthServerUtil.createException(OAuth.Problems.TOKEN_EXPIRED, msg);
+ }
+ else if
(ACCESS_TOKEN_TYPE.equals(token.getProperty(TOKEN_TYPE_PROPERTY))) {
boolean expired = false;
if (token.isExpired(m_config.getAccessTokenMaxAge())) {
expired = true;
- } else if (m_config.getAccessTokenTimeout() >= 0) {
+ }
+ else if (m_config.getAccessTokenTimeout() >= 0) {
// Access token timeout is a special case. The timestamp
is set when the access token is created and
- // updated each time the token is used, but when it is not
used for more then the timeout period,
+ // updated each time the token is used, but when it is not
used for more then the timeout period,
// it expires. We store this timestamp in a special
property.
long lastUsed =
Long.parseLong(token.getProperty(OAuthTokenProvider.LAST_USED_TIMESTAMP_PROPERTY));
if (lastUsed < (System.currentTimeMillis() -
m_config.getAccessTokenTimeout())) {
@@ -136,16 +152,17 @@
}
if (expired) {
m_tokenStore.removeToken(token);
- throw new
OAuthProblemException(OAuth.Problems.TOKEN_EXPIRED);
+ String msg = "The access token is expired";
+ throw
OAuthServerUtil.createException(OAuth.Problems.TOKEN_EXPIRED, msg);
}
}
return token;
}
catch (IOException e) {
- m_logService.log(LogService.LOG_ERROR, "Could not parse request
token from OAuth request message", e);
+ String msg = "Could not read request or access token from the
request";
+ throw
OAuthServerUtil.createException(OAuth.Problems.TOKEN_REJECTED, msg);
}
- return null;
}
public synchronized Token validateOAuthMessage(final OAuthMessage
requestMessage) throws OAuthException {
@@ -157,19 +174,23 @@
String tokenType =
token.getProperty(OAuthTokenProvider.TOKEN_TYPE_PROPERTY);
if (OAuthTokenProvider.REQUEST_TOKEN_TYPE.equals(tokenType)) {
accessor.requestToken = token.getToken();
- } else if
(OAuthTokenProvider.ACCESS_TOKEN_TYPE.equals(tokenType)) {
+ }
+ else if
(OAuthTokenProvider.ACCESS_TOKEN_TYPE.equals(tokenType)) {
accessor.accessToken = token.getToken();
- }
+ }
accessor.tokenSecret = token.getTokenSecret();
}
m_oAuthValidator.validateMessage(requestMessage, accessor);
+
return token;
}
catch (IOException e) {
- throw OAuthServerUtil.createException(e, requestMessage);
+ String msg = "Could not validate OAuth message";
+ throw OAuthServerUtil.createException(e, requestMessage, msg);
}
catch (URISyntaxException e) {
- throw OAuthServerUtil.createException(e, requestMessage);
+ String msg = "Could not validate OAuth message";
+ throw OAuthServerUtil.createException(e, requestMessage, msg);
}
}
@@ -191,7 +212,25 @@
token.setProperty(OAuthTokenProvider.USERID_PROPERTY, userId);
}
- public Token exchangeForAccessToken(Token requestToken) throws
OAuthException {
+ public String generateVerifier(Token token) throws OAuthException {
+ String verifier = generateToken();
+ token.setProperty(OAuthTokenProvider.VERIFIER_PROPERTY, verifier);
+ return verifier;
+ }
+
+ public Token exchangeForAccessToken(Token requestToken, String verifier)
throws OAuthException {
+ // Verify the oauth_verifier, this check is only applicable for OAuth
flows in which actually
+ // a token was authorized by a user (so not in case of 2-legged
OAuth). Hence this validation
+ // is not part of the regular validateOAuthMessage process.
+ if (verifier == null) {
+ String msg = "The OAuth server did not receive an oauth_verifier";
+ throw
OAuthServerUtil.createException(OAuth.Problems.OAUTH_PARAMETERS_REJECTED, msg);
+ }
+ if
(!verifier.equals(requestToken.getProperty(OAuthTokenProvider.VERIFIER_PROPERTY)))
{
+ String msg = "The OAuth server received an invalid oauth_verifier";
+
OAuthServerUtil.createException(OAuth.Problems.OAUTH_PARAMETERS_REJECTED, msg);
+ }
+
// First we generate a new access token
String token = generateToken();
String tokenSecret = generateToken();
@@ -202,7 +241,8 @@
accessToken.setProperty(key, requestToken.getProperty(key));
}
accessToken.setProperty(OAuthTokenProvider.TOKEN_TYPE_PROPERTY,
OAuthTokenProvider.ACCESS_TOKEN_TYPE);
-
accessToken.setProperty(OAuthTokenProvider.LAST_USED_TIMESTAMP_PROPERTY, new
Long(System.currentTimeMillis()).toString());
+
accessToken.setProperty(OAuthTokenProvider.LAST_USED_TIMESTAMP_PROPERTY,
+ new Long(System.currentTimeMillis()).toString());
// Now remove the request token
m_tokenStore.removeToken(requestToken);
@@ -217,6 +257,7 @@
* Generates a nonce with default bytesize (32). A nonce is an
abbreviation of number used once, and is a
* unique randomly generated number. It is used by authentication
protocols to ensure that old
* requests cannot be used in replay attacks.
+ *
* @return A random generated nonce.
*/
private BigInteger generateNonce() {
@@ -227,7 +268,9 @@
* Generates a nonce with the specified bytesize. A nonce is an
abbreviation of number used once, and is a
* unique randomly generated number. It is used by authentication
protocols to ensure that old
* requests cannot be used in replay attacks.
- * @param byteSize the number of bytes from which the nonce is generated
+ *
+ * @param byteSize
+ * the number of bytes from which the nonce is generated
* @return A random generated nonce.
*/
private BigInteger generateNonce(int byteSize) {
@@ -238,13 +281,14 @@
}
/**
- * Generates a unique random token. The token is generated from a 32-bits
nonce and converted to a String
+ * Generates a unique random token. The token is generated from a 32-bits
nonce and converted to a String
* representation (with numbers 0-9 and letters a-z).
+ *
* @return A new randomly generated token.
*/
private String generateToken() {
// Generate a nonce and convert it to a String representation (using
numbers 0-9 and letters a-z)
BigInteger nonce = generateNonce();
return nonce.toString(36);
- }
+ }
}
Modified:
trunk/amdatu-auth/oauth-server/src/main/java/org/amdatu/authentication/oauth/server/servlet/OAuthAccessTokenServletImpl.java
==============================================================================
---
trunk/amdatu-auth/oauth-server/src/main/java/org/amdatu/authentication/oauth/server/servlet/OAuthAccessTokenServletImpl.java
(original)
+++
trunk/amdatu-auth/oauth-server/src/main/java/org/amdatu/authentication/oauth/server/servlet/OAuthAccessTokenServletImpl.java
Mon Jul 11 10:09:34 2011
@@ -25,7 +25,6 @@
import net.oauth.OAuth;
import net.oauth.OAuthMessage;
-import net.oauth.OAuthProblemException;
import net.oauth.server.OAuthServlet;
import org.amdatu.authentication.oauth.server.OAuthAccessTokenServlet;
@@ -76,19 +75,24 @@
// Make sure a request token was send along with the request
if (requestToken == null) {
- throw new OAuthProblemException(OAuth.Problems.TOKEN_REJECTED);
+ String msg = "The OAuth server did not receive the request
token in the request "
+ + "(the oauth_token parameter is missing)";
+ throw
OAuthServerUtil.createException(OAuth.Problems.TOKEN_REJECTED, msg);
}
// Validate that this is a request token that was marked as
authorized
if
(!OAuthTokenProvider.REQUEST_TOKEN_TYPE.equals(requestToken.getProperty(OAuthTokenProvider.TOKEN_TYPE_PROPERTY)))
{
- throw new OAuthProblemException(OAuth.Problems.TOKEN_REJECTED);
+ String msg = "The token received is not a request token but an
access token";
+ throw
OAuthServerUtil.createException(OAuth.Problems.TOKEN_REJECTED, msg);
}
- if
(!"true".equals(requestToken.getProperty(OAuthTokenProvider.TOKEN_AUTHORIZED_PROPERTY)))
{
- throw new
OAuthProblemException(OAuth.Problems.PERMISSION_DENIED);
+ if
(!"true".equals(requestToken.getProperty(OAuthTokenProvider.TOKEN_AUTHORIZED_PROPERTY)))
{
+ String msg = "The user did not authorize the service consumer";
+ throw
OAuthServerUtil.createException(OAuth.Problems.PERMISSION_DENIED, msg);
}
- // Now exchange the request token for an access token and secret
- Token accessToken =
m_tokenProvider.exchangeForAccessToken(requestToken);
+ // Now exchange the request token for an access token and secret
+ String verifier =
requestMessage.getParameter(OAuth.OAUTH_VERIFIER);
+ Token accessToken =
m_tokenProvider.exchangeForAccessToken(requestToken, verifier);
response.setContentType("text/plain");
OutputStream out = null;
Modified:
trunk/amdatu-auth/oauth-server/src/main/java/org/amdatu/authentication/oauth/server/servlet/OAuthAuthorizeTokenServletImpl.java
==============================================================================
---
trunk/amdatu-auth/oauth-server/src/main/java/org/amdatu/authentication/oauth/server/servlet/OAuthAuthorizeTokenServletImpl.java
(original)
+++
trunk/amdatu-auth/oauth-server/src/main/java/org/amdatu/authentication/oauth/server/servlet/OAuthAuthorizeTokenServletImpl.java
Mon Jul 11 10:09:34 2011
@@ -13,10 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.amdatu.authentication.oauth.server.servlet;
-
+package org.amdatu.authentication.oauth.server.servlet;
+
import java.io.IOException;
-import java.io.PrintWriter;
import java.util.Map;
import javax.servlet.ServletException;
@@ -26,8 +25,8 @@
import net.oauth.OAuth;
import net.oauth.OAuthConsumer;
+import net.oauth.OAuthException;
import net.oauth.OAuthMessage;
-import net.oauth.OAuthProblemException;
import net.oauth.server.OAuthServlet;
import org.amdatu.authentication.oauth.api.OAuthServiceProvider;
@@ -44,151 +43,135 @@
import org.apache.felix.dm.DependencyManager;
import org.osgi.framework.Constants;
import org.osgi.service.log.LogService;
-
-public class OAuthAuthorizeTokenServletImpl extends HttpServlet implements
OAuthAuthorizeTokenServlet {
- // The serial version UID of this servlet
- private static final long serialVersionUID = -3589350956712947896L;
-
- // Service dependencies, injected by the Felix dependency manager
- private volatile LogService m_logService;
- private volatile TokenProvider m_tokenProvider;
- private volatile OAuthTokenProvider m_oAuthTokenProvider;
- private volatile OAuthServiceProvider m_serviceProvider;
- private volatile DependencyManager m_dependencyManager;
-
- private String m_tenantId;
-
- public void init(final Component component) {
- m_tenantId =
component.getServiceProperties().get(Tenant.TENANT_ID_SERVICEPROPERTY).toString();
-
- // Create a service dependency on the token provider for 'our' tenant
- String tenantFilter =
- "(&(" + Tenant.TENANT_ID_SERVICEPROPERTY + "=" + m_tenantId + ")("
+ Constants.OBJECTCLASS
- + "=" + TokenProvider.class.getName() + "))";
- component.add(m_dependencyManager.createServiceDependency()
- .setService(TokenProvider.class,
tenantFilter).setRequired(true).setInstanceBound(true));
- }
-
- public void start() {
- m_logService.log(LogService.LOG_DEBUG, "OAuth Authorize Token servlet
started for tenant '" + m_tenantId + "'");
- }
-
- @Override
- public void doGet(final HttpServletRequest request, final
HttpServletResponse response) throws IOException,
- ServletException {
- try {
+
+public class OAuthAuthorizeTokenServletImpl extends HttpServlet implements
OAuthAuthorizeTokenServlet {
+ // The serial version UID of this servlet
+ private static final long serialVersionUID = -3589350956712947896L;
+
+ // Service dependencies, injected by the Felix dependency manager
+ private volatile LogService m_logService;
+ private volatile TokenProvider m_tokenProvider;
+ private volatile OAuthTokenProvider m_oAuthTokenProvider;
+ private volatile OAuthServiceProvider m_serviceProvider;
+ private volatile DependencyManager m_dependencyManager;
+
+ private String m_tenantId;
+
+ public void init(final Component component) {
+ m_tenantId =
component.getServiceProperties().get(Tenant.TENANT_ID_SERVICEPROPERTY).toString();
+
+ // Create a service dependency on the token provider for 'our' tenant
+ String tenantFilter =
+ "(&(" + Tenant.TENANT_ID_SERVICEPROPERTY + "=" + m_tenantId + ")("
+ Constants.OBJECTCLASS
+ + "=" + TokenProvider.class.getName() + "))";
+ component.add(m_dependencyManager.createServiceDependency()
+ .setService(TokenProvider.class,
tenantFilter).setRequired(true).setInstanceBound(true));
+ }
+
+ public void start() {
+ m_logService.log(LogService.LOG_DEBUG, "OAuth Authorize Token servlet
started for tenant '" + m_tenantId + "'");
+ }
+
+ @Override
+ public void doGet(final HttpServletRequest request, final
HttpServletResponse response) throws IOException,
+ ServletException {
+ try {
OAuthMessage requestMessage = OAuthServlet.getMessage(request,
null);
OAuthConsumer consumer =
m_oAuthTokenProvider.getConsumer(requestMessage);
Token token = m_oAuthTokenProvider.getToken(requestMessage);
-
- if
("true".equalsIgnoreCase(token.getProperty(OAuthTokenProvider.TOKEN_AUTHORIZED_PROPERTY)))
{
- // already authorized send the user back
- m_logService.log(LogService.LOG_DEBUG, "Token authorized,
redirecting user to callback url");
- returnToConsumer(request, response, token, consumer);
- }
- else {
- m_logService.log(LogService.LOG_DEBUG,
- "Authorize token request received, redirecting user to
authorization page");
- sendToAuthorizePage(request, response, token, consumer);
- }
- }
- catch (Exception e) {
- OAuthServerUtil.handleException(e, request, response, true);
- }
- }
-
- @Override
- public void doPost(final HttpServletRequest request, final
HttpServletResponse response) throws IOException,
- ServletException {
- try {
- OAuthMessage requestMessage = OAuthServlet.getMessage(request,
null);
- String userId = getUserId(request);
- if (userId == null) {
- // If there is no user id available now, we throw a permission
denied as it won't happen
- // in a normal situation! Maybe it's a hack attempt.
- OAuthProblemException problem = new
OAuthProblemException("permission_denied");
- throw problem;
+
+ if
("true".equalsIgnoreCase(token.getProperty(OAuthTokenProvider.TOKEN_AUTHORIZED_PROPERTY)))
{
+ // already authorized send the user back
+ m_logService.log(LogService.LOG_DEBUG, "Token authorized,
redirecting user to callback url");
+ returnToConsumer(response, token);
+ }
+ else {
+ m_logService.log(LogService.LOG_DEBUG,
+ "Authorize token request received, redirecting user to
authorization page");
+ sendToAuthorizePage(request, response, token, consumer);
+ }
+ }
+ catch (Exception e) {
+ OAuthServerUtil.handleException(e, request, response, true);
+ }
+ }
+
+ @Override
+ public void doPost(final HttpServletRequest request, final
HttpServletResponse response) throws IOException,
+ ServletException {
+ try {
+ OAuthMessage requestMessage = OAuthServlet.getMessage(request,
null);
+ String userId = getUserId(request);
+ if (userId == null) {
+ // If there is no user id available now, we throw a permission
denied as it won't happen
+ // in a normal situation! Maybe it's a hack attempt.
+ String msg = "No userid send along with the authorize request";
+ throw
OAuthServerUtil.createException(OAuth.Problems.PERMISSION_DENIED, msg);
}
-
+
// Retrieve the request token and consumer
Token token = m_oAuthTokenProvider.getToken(requestMessage);
- OAuthConsumer consumer = m_oAuthTokenProvider.getConsumer(token);
-
- // Now set the userId as property of the request token and mark it
as authorized
- m_oAuthTokenProvider.markAsAuthorized(token, userId);
- returnToConsumer(request, response, token, consumer);
- }
- catch (Exception e) {
- OAuthServerUtil.handleException(e, request, response, true);
- }
- }
-
- private void sendToAuthorizePage(final HttpServletRequest request, final
HttpServletResponse response,
- final Token token, final OAuthConsumer consumer)
- throws IOException, ServletException {
- String callback = request.getParameter(OAuth.OAUTH_CALLBACK);
- if (callback == null || callback.length() <= 0) {
- callback = "none";
- }
- String consumerDescription = (String) consumer.getProperty("name");
- request.setAttribute("CONS_DESC", consumerDescription);
- request.setAttribute("CALLBACK", callback);
- request.setAttribute("TOKEN", token.getToken());
- m_logService.log(LogService.LOG_DEBUG,
- "Forwarding authorize token request to " +
m_serviceProvider.getAuthorizeTokenURL()
- + ", token=" + token.getToken() + ", callback=" + callback);
-
+
+ // Now set the userId as property of the request token and mark it
as authorized
+ m_oAuthTokenProvider.markAsAuthorized(token, userId);
+ returnToConsumer(response, token);
+ }
+ catch (Exception e) {
+ OAuthServerUtil.handleException(e, request, response, true);
+ }
+ }
+
+ private void sendToAuthorizePage(final HttpServletRequest request, final
HttpServletResponse response,
+ final Token token, final OAuthConsumer consumer)
+ throws IOException, ServletException {
+ String callback = request.getParameter(OAuth.OAUTH_CALLBACK);
+ if (callback == null || callback.length() <= 0) {
+ callback = "none";
+ }
+ String consumerDescription = (String) consumer.getProperty("name");
+ request.setAttribute("CONS_DESC", consumerDescription);
+ request.setAttribute("CALLBACK", callback);
+ request.setAttribute("TOKEN", token.getToken());
+ m_logService.log(LogService.LOG_DEBUG,
+ "Forwarding authorize token request to " +
m_serviceProvider.getAuthorizeTokenURL()
+ + ", token=" + token.getToken() + ", callback=" + callback);
+
// Dispatch the request to the authorize JSP
- // The authorize URL
- request.getRequestDispatcher(((OAuthServiceProviderImpl)
m_serviceProvider).getAuthorizeURL()).forward(request, response);
- }
-
- private void returnToConsumer(final HttpServletRequest request,
- final HttpServletResponse response, final Token token, final
OAuthConsumer consumer)
- throws IOException, ServletException {
- // send the user back to site's callBackUrl
- String callback = request.getParameter(OAuth.OAUTH_CALLBACK);
- if ("none".equals(callback)
- && consumer.callbackURL != null
- && consumer.callbackURL.length() > 0) {
- // first check if we have something in our properties file
- callback = request.getContextPath() + consumer.callbackURL;
- }
-
- if ("none".equals(callback)) {
- // no call back it must be a client
- response.setContentType("text/plain");
- PrintWriter out = response.getWriter();
- out.println("You have successfully authorized '"
- + consumer.getProperty("description")
- + "'. Please close this browser window and click continue"
- + " in the client.");
- out.close();
- }
- else {
- // if callback is not passed in, use the callback from config
- if (callback == null || callback.length() <= 0) {
- callback = request.getContextPath() + consumer.callbackURL;
- }
-
- callback = OAuth.addParameters(callback, OAuth.OAUTH_TOKEN,
token.getToken());
-
- // FIXME: we should generate and append an oauth_verifier here!
-
- response.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY);
- response.setHeader("Location", callback);
- }
- }
-
- private String getUserId(final HttpServletRequest request) throws
TokenProviderException, InvalidTokenException {
- // Get the token, retrieve attributes stored in it and get the
USERNAME from it
- String token = m_tokenProvider.getTokenFromRequest(request);
- if (token != null) {
- Map<String, String> attributes =
m_tokenProvider.verifyToken(token);
- return attributes.get(TokenProvider.USERNAME);
- }
- else {
- return null;
- }
- }
-}
+ // The authorize URL
+ request.getRequestDispatcher(((OAuthServiceProviderImpl)
m_serviceProvider).getAuthorizeURL()).forward(request,
+ response);
+ }
+
+ private void returnToConsumer(final HttpServletResponse response, final
Token token) throws IOException,
+ OAuthException {
+ // Send the user back to site's callBackUrl
+ String callback =
token.getProperty(OAuthTokenProvider.CALLBACK_URL_PROPERTY);
+
+ // FIXME: we should also support OOB mode here (check if the callback
url equals 'oob')
+ // See http://jira.amdatu.org/jira/browse/AMDATUAUTH-69
+ if ("oob".equalsIgnoreCase(callback)) {
+ String msg = "The current OAuth server version does not implement
Out Of Band mode";
+ throw
OAuthServerUtil.createException(OAuth.Problems.PARAMETER_REJECTED, msg);
+ }
+
+ // Now append the oauth token and a new oauth_verifier to the callback
URL
+ String verifier = m_oAuthTokenProvider.generateVerifier(token);
+ callback = OAuth.addParameters(callback, OAuth.OAUTH_TOKEN,
token.getToken());
+ callback = OAuth.addParameters(callback, OAuth.OAUTH_VERIFIER,
verifier);
+
+ response.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY);
+ response.setHeader("Location", callback);
+ }
+
+ private String getUserId(final HttpServletRequest request) throws
TokenProviderException, InvalidTokenException {
+ // Get the token, retrieve attributes stored in it and get the
USERNAME from it
+ String token = m_tokenProvider.getTokenFromRequest(request);
+ if (token != null) {
+ Map<String, String> attributes =
m_tokenProvider.verifyToken(token);
+ return attributes.get(TokenProvider.USERNAME);
+ }
+ else {
+ return null;
+ }
+ }
+}
Modified:
trunk/amdatu-auth/oauth-server/src/main/java/org/amdatu/authentication/oauth/server/servlet/OAuthRequestTokenServletImpl.java
==============================================================================
---
trunk/amdatu-auth/oauth-server/src/main/java/org/amdatu/authentication/oauth/server/servlet/OAuthRequestTokenServletImpl.java
(original)
+++
trunk/amdatu-auth/oauth-server/src/main/java/org/amdatu/authentication/oauth/server/servlet/OAuthRequestTokenServletImpl.java
Mon Jul 11 10:09:34 2011
@@ -85,8 +85,27 @@
// and signature)
m_tokenProvider.validateOAuthMessage(requestMessage);
- // Generate a brand new request token
+ // According to OAuth Core 1.0 Revision A, we must determine the
callback URL at this moment,
+ // instead of allowing the callback URL to be provided by the
consumer. So we read the
+ // oauth_callback parameter from the request. If none was send, we
use the callback URL
+ // defined for the consumer in the consumer registry. If both are
empty, we throw a
+ // VERSION_REJECTED exception as in that case we would be
vulnerable to session fixation
+ // attacks, as described on
http://hueniverse.com/2009/04/explaining-the-oauth-session-fixation-attack/
+ String callback =
requestMessage.getParameter(OAuth.OAUTH_CALLBACK);
+ if (callback == null || "".equalsIgnoreCase(callback)) {
+ callback = consumer.callbackURL;
+ if (callback == null || "".equalsIgnoreCase(callback)) {
+ String errorMsg = "The consumer '" + consumer.consumerKey
+ "' did not send the "
+ + "oauth_callback parameter (which is required
according to the OAuth Core 1.0 "
+ + "Revision A spec) and no callback URL is defined for
this consumer in "
+ + "the consumer registry. To prevent session fixation
attacks providing a "
+ + "callback URL (while retrieving the request token or
by defining the "
+ + "callback URL in the consumer registry for this
consumer) is required.";
+ throw
OAuthServerUtil.createException(OAuth.Problems.VERSION_REJECTED, errorMsg);
+ }
+ }
Token requestToken =
m_tokenProvider.generateRequestToken(consumer);
+ requestToken.setProperty(OAuthTokenProvider.CALLBACK_URL_PROPERTY,
callback);
m_logService.log(LogService.LOG_DEBUG, "Request token '" +
requestToken.getToken()
+ "' generated for consumer '" + consumer.consumerKey + "'");
Modified:
trunk/amdatu-auth/test-integration/tests/src/test/java/org/amdatu/auth/test/integration/tests/AuthTest.java
==============================================================================
---
trunk/amdatu-auth/test-integration/tests/src/test/java/org/amdatu/auth/test/integration/tests/AuthTest.java
(original)
+++
trunk/amdatu-auth/test-integration/tests/src/test/java/org/amdatu/auth/test/integration/tests/AuthTest.java
Mon Jul 11 10:09:34 2011
@@ -28,6 +28,8 @@
import javax.servlet.Servlet;
+import net.oauth.OAuthException;
+
import org.amdatu.auth.test.integration.base.AuthFixture;
import org.amdatu.auth.test.integration.tests.util.AuthTestBase;
import org.amdatu.auth.test.integration.tests.util.AuthUtils;
@@ -61,25 +63,25 @@
@RunWith(JUnit4TestRunner.class)
@ExamReactorStrategy(AllConfinedStagedReactorFactory.class)
-public class AuthTest {
+public class AuthTest {
public final static String TEST_USERNAME = "georged";
public final static String TEST_PASSWORD = "georged";
-
+
public final static String ADMIN_USERNAME = "Administrator";
public final static String ADMIN_PASSWORD = "Administrator";
-
+
// Fixtures used by the Auth integration tests
private CoreFixture m_coreFixture = new CoreFixture();
private WebFixture m_webFixture = new WebFixture();
private AuthFixture m_authFixture = new AuthFixture();
-
+
private DependencyManager m_dependencyManager;
private LogService m_logService;
private OAuthServiceProvider m_oAuthServiceProvider;
private OAuthServiceConsumerRegistry m_consumerRegistry;
private UserAdmin m_userAdmin;
private TenantManagementService m_tenantService;
-
+
@Configuration
public Option[] config() {
return options(
@@ -92,7 +94,7 @@
public TestContext testContextSetUp(BundleContext bundleContext) throws
Exception {
assertThat(bundleContext, is(notNullValue()));
TestContext testContext = new TestContext(bundleContext);
-
+
// FIXME quickfix to let startlevel complete to
// prevent felix bundle locking issues
Thread.sleep(1000);
@@ -107,7 +109,7 @@
return testContext;
}
-
+
// FIXME: [AMDATU-379] This method is here since TestContext.getService
doesn't (always) work.
// For optimal performance we do this in 3 steps:
// 1. Try to get the service directly using getAllServiceReferences,
return it if available
@@ -120,19 +122,19 @@
if (servRef != null && servRef.length > 0) {
return (T)
bundleContext.getService(getServiceRefWithHighestRank(servRef));
}
-
+
T service = testContext.getService(clazz);
if (service != null) {
return service;
}
-
+
servRef = bundleContext.getAllServiceReferences(clazz.getName(), null);
if (servRef != null && servRef.length > 0) {
return (T)
bundleContext.getService(getServiceRefWithHighestRank(servRef));
}
return null;
}
-
+
private ServiceReference getServiceRefWithHighestRank(ServiceReference[]
servRefs) {
int maxRank = -1;
if (servRefs.length == 1) {
@@ -144,9 +146,11 @@
int rank;
if (prop == null) {
rank = 0;
- } else if (prop instanceof Integer) {
+ }
+ else if (prop instanceof Integer) {
rank = (Integer)
servRef.getProperty(Constants.SERVICE_RANKING);
- } else {
+ }
+ else {
rank =
Integer.parseInt(servRef.getProperty(Constants.SERVICE_RANKING).toString());
}
if (rank >= maxRank) {
@@ -156,7 +160,7 @@
}
return maxServRef;
}
-
+
private <T> T assertAvailable(TestContext testContext, Class<T>
serviceClass) throws Exception {
T service = getService(testContext, serviceClass);
assertThat("Expected a " + serviceClass.getName(), service,
is(notNullValue()));
@@ -167,10 +171,10 @@
public void runTest(BundleContext bundleContext) throws Exception {
// Setup test context
TestContext testContext = testContextSetUp(bundleContext);
-
+
// Create the dependency manager
m_dependencyManager = new
DependencyManager(testContext.getBundleContext());
-
+
// Initialize services
m_logService = assertAvailable(testContext, LogService.class);
assertAvailable(testContext, OAuthRequestTokenServlet.class);
@@ -178,47 +182,60 @@
assertAvailable(testContext, OAuthAccessTokenServlet.class);
m_consumerRegistry = assertAvailable(testContext,
OAuthServiceConsumerRegistry.class);
assertAvailable(testContext, HttpService.class);
- m_userAdmin = assertAvailable(testContext, UserAdmin.class);
+ m_userAdmin = assertAvailable(testContext, UserAdmin.class);
m_oAuthServiceProvider = assertAvailable(testContext,
OAuthServiceProvider.class);
m_tenantService = assertAvailable(testContext,
TenantManagementService.class);
-
+
// Register an oAuth protected test servlet we need during the tests
registerTestResource();
-
+
// Wait for the oAuth servlets to become available
waitForOAuthServlets();
-
+
// Execute the tests
- test(OAuthServiceConsumerRegistryTest.class);
- test(OAuthSignedRequestsTest.class);
- test(OAuthThreeLeggedTest.class);
- test(OAuthTwoLeggedTest.class);
- test(UserAdminRESTTest.class);
-
+ try {
+ test(OAuthServiceConsumerRegistryTest.class);
+ test(OAuthSignedRequestsTest.class);
+ test(OAuthThreeLeggedTest.class);
+ test(OAuthTwoLeggedTest.class);
+ test(UserAdminRESTTest.class);
+ }
+ catch (OAuthException e) {
+ System.out.println(e.toString());
+ e.printStackTrace();
+ throw e;
+ }
+
// And we are done
testContext.tearDown();
}
-
+
private void registerTestResource() {
// Now register a test servlet
OAuthProtectedTestServlet m_testServlet = new
OAuthProtectedTestServlet();
Dictionary<String, String> servletProperties = new Hashtable<String,
String>();
servletProperties.put("alias",
OAuthProtectedTestServlet.SERVLET_ALIAS);
- Component servletComponent = m_dependencyManager.createComponent()
- .setImplementation(m_testServlet)
- .setInterface(new String[] { Servlet.class.getName() },
servletProperties)
-
.add(m_dependencyManager.createServiceDependency().setService(LogService.class).setRequired(true))
-
.add(m_dependencyManager.createServiceDependency().setService(OAuthServiceConsumerRegistry.class).setRequired(true))
-
.add(m_dependencyManager.createServiceDependency().setService(OAuthTokenProvider.class).setRequired(true));
+ Component servletComponent =
+ m_dependencyManager
+ .createComponent()
+ .setImplementation(m_testServlet)
+ .setInterface(new String[] {Servlet.class.getName()},
servletProperties)
+
.add(m_dependencyManager.createServiceDependency().setService(LogService.class).setRequired(true))
+ .add(
+
m_dependencyManager.createServiceDependency().setService(OAuthServiceConsumerRegistry.class)
+ .setRequired(true))
+ .add(
+
m_dependencyManager.createServiceDependency().setService(OAuthTokenProvider.class)
+ .setRequired(true));
m_dependencyManager.add(servletComponent);
}
-
+
private <T extends AuthTestBase> void test(Class<T> testClass) throws
Exception {
T test = testClass.newInstance();
init(test);
test.execute();
}
-
+
private void init(AuthTestBase test) {
test.setLogService(m_logService);
test.setUserAdmin(m_userAdmin);
@@ -226,7 +243,7 @@
test.setOAuthServiceConsumerRegistry(m_consumerRegistry);
test.setTenantService(m_tenantService);
}
-
+
protected void waitForOAuthServlets() throws MalformedURLException,
IOException {
// First wait for the request servlet to become available
m_logService.log(LogService.LOG_DEBUG, "Waiting for '" +
m_oAuthServiceProvider.getRequestTokenURL()
Modified:
trunk/amdatu-auth/test-integration/tests/src/test/java/org/amdatu/auth/test/integration/tests/OAuthThreeLeggedTest.java
==============================================================================
---
trunk/amdatu-auth/test-integration/tests/src/test/java/org/amdatu/auth/test/integration/tests/OAuthThreeLeggedTest.java
(original)
+++
trunk/amdatu-auth/test-integration/tests/src/test/java/org/amdatu/auth/test/integration/tests/OAuthThreeLeggedTest.java
Mon Jul 11 10:09:34 2011
@@ -18,6 +18,7 @@
import static
org.amdatu.auth.test.integration.tests.util.OAuthProtectedTestServlet.OAUTH_TYPE_PARAM;
import static
org.amdatu.auth.test.integration.tests.util.OAuthProtectedTestServlet.OAUTH_TYPE_THREE_LEGGED;
import junit.framework.Assert;
+import net.oauth.OAuth;
import net.oauth.OAuthMessage;
import org.amdatu.auth.test.integration.base.AuthFixture;
@@ -75,6 +76,8 @@
// Step 6: Exchange our request token for an access token
m_logService.log(LogService.LOG_DEBUG, "*** Step 6: Get access token
***");
+ String verifier = consumerClient.getVerifier(callback);
+ accessor.setProperty(OAuth.OAUTH_VERIFIER, verifier);
OAuthMessage message =
consumerClient.exchangeRequestTokenForAccessToken(accessor);
accessor.accessToken = message.getToken();
accessor.tokenSecret = message.getParameter("oauth_token_secret");
Modified:
trunk/amdatu-auth/test-integration/tests/src/test/java/org/amdatu/auth/test/integration/tests/OAuthTwoLeggedTest.java
==============================================================================
---
trunk/amdatu-auth/test-integration/tests/src/test/java/org/amdatu/auth/test/integration/tests/OAuthTwoLeggedTest.java
(original)
+++
trunk/amdatu-auth/test-integration/tests/src/test/java/org/amdatu/auth/test/integration/tests/OAuthTwoLeggedTest.java
Mon Jul 11 10:09:34 2011
@@ -69,10 +69,12 @@
// Step 3d: Authorize the request token for a user we define
m_logService.log(LogService.LOG_DEBUG, "*** Step 3d: Authorize the
request token ***");
- userClient.authorizeToken(accessor, getCookieHeader());
-
+ String callbackURL = userClient.authorizeToken(accessor,
getCookieHeader());
+ String verifier = consumerClient.getVerifier(callbackURL);
+
// Step 3e: Exchange our request token for an access token
m_logService.log(LogService.LOG_DEBUG, "*** Step 3e: Get access token
***");
+ accessor.setProperty(OAuth.OAUTH_VERIFIER, verifier);
OAuthMessage message =
consumerClient.exchangeRequestTokenForAccessToken(accessor);
accessor.accessToken = message.getToken();
accessor.tokenSecret = message.getParameter(OAuth.OAUTH_TOKEN_SECRET);
_______________________________________________
Amdatu-commits mailing list
[email protected]
http://lists.amdatu.org/mailman/listinfo/amdatu-commits