package oauth.signpost;
import static org.junit.Assert.*;
import static org.hamcrest.CoreMatchers.*;
import static org.mockito.Matchers.argThat;
import static org.mockito.Matchers.eq;
import static org.mockito.Matchers.same;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import oauth.signpost.exception.OAuthExpectationFailedException;
import oauth.signpost.http.HttpParameters;
import oauth.signpost.http.HttpRequest;
import oauth.signpost.signature.HmacSha1MessageSigner;
import oauth.signpost.signature.OAuthMessageSigner;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentMatcher;
import org.mockito.ArgumentCaptor;
import org.mockito.runners.MockitoJUnit44Runner;
@RunWith(MockitoJUnit44Runner.class)
public abstract class OAuthConsumerTest extends SignpostTestBase {
protected OAuthConsumer consumer;
protected abstract OAuthConsumer buildConsumer(String consumerKey, String consumerSecret,
OAuthMessageSigner messageSigner);
@Test(expected = OAuthExpectationFailedException.class)
public void shouldThrowIfConsumerKeyNotSet() throws Exception {
OAuthConsumer consumer = buildConsumer(null, CONSUMER_SECRET, null);
consumer.setTokenWithSecret(TOKEN, TOKEN_SECRET);
consumer.sign(httpGetMock);
}
@Test(expected = OAuthExpectationFailedException.class)
public void shouldThrowIfConsumerSecretNotSet() throws Exception {
OAuthConsumer consumer = buildConsumer(CONSUMER_KEY, null, null);
consumer.setTokenWithSecret(TOKEN, TOKEN_SECRET);
consumer.sign(httpGetMock);
}
@Test
public void shouldSignHttpRequestMessage() throws Exception {
OAuthConsumer consumer = buildConsumer(CONSUMER_KEY, CONSUMER_SECRET, null);
consumer.setTokenWithSecret(TOKEN, TOKEN_SECRET);
consumer.sign(httpGetMock);
verify(httpGetMock).setHeader(eq("Authorization"),
argThat(new IsCompleteListOfOAuthParameters()));
}
@Test
public void shouldSignUrl() throws Exception {
OAuthConsumer consumer = buildConsumer(CONSUMER_KEY, CONSUMER_SECRET, null);
consumer.setTokenWithSecret(TOKEN, TOKEN_SECRET);
String result = consumer.sign("http://www.example.com?q=1");
assertNotNull(result);
String[] parts = result.split("\\?");
assertEquals("parameters are missing", 2, parts.length);
assertEquals("http://www.example.com", parts[0]);
HttpParameters params = OAuth.decodeForm(parts[1]);
assertAllOAuthParametersExist(params);
assertEquals("1", params.getFirst("q"));
}
@Test
public void shouldIncludeOAuthAndQueryAndBodyParams() throws Exception {
// mock a request that has custom query, body, and header params set
HttpRequest request = mock(HttpRequest.class);
when(request.getRequestUrl()).thenReturn("http://example.com?a=1+1");
ByteArrayInputStream body = new ByteArrayInputStream("b=2+2".getBytes());
when(request.getMessagePayload()).thenReturn(body);
when(request.getContentType()).thenReturn(
"application/x-www-form-urlencoded; charset=ISO-8859-1");
when(request.getHeader("Authorization")).thenReturn(
"OAuth realm=\"http%3A%2F%2Fexample.com\", oauth_token=\"12%25345\", oauth_signature=\"1234\"");
OAuthMessageSigner signer = mock(HmacSha1MessageSigner.class);
consumer.setMessageSigner(signer);
consumer.sign(request);
// verify that all custom params are properly read and passed to the
// message signer
ArgumentMatcher<HttpParameters> hasAllParameters = new ArgumentMatcher<HttpParameters>() {
public boolean matches(Object argument) {
HttpParameters params = (HttpParameters) argument;
assertEquals("1 1", params.getFirst("a", true));
assertEquals("2 2", params.getFirst("b", true));
assertEquals("http://example.com", params.getFirst("realm", true));
assertEquals("12%345", params.getFirst("oauth_token", true));
// signature should be dropped, not valid to pre-set
assertNull(params.getFirst("oauth_signature"));
return true;
}
};
verify(signer).sign(same(request), argThat(hasAllParameters));
}
@Test
public void shouldHonorManuallySetSigningParameters() throws Exception {
// mock a request that has custom query, body, and header params set
HttpRequest request = mock(HttpRequest.class);
when(request.getRequestUrl()).thenReturn("http://example.com?a=1");
OAuthMessageSigner signer = mock(HmacSha1MessageSigner.class);
consumer.setMessageSigner(signer);
HttpParameters params = new HttpParameters();
params.put("oauth_callback", "http://mycallback");
consumer.setAdditionalParameters(params);
consumer.sign(request);
// verify that all custom params are properly read and passed to the
// message signer
ArgumentMatcher<HttpParameters> hasParameters = new ArgumentMatcher<HttpParameters>() {
public boolean matches(Object argument) {
HttpParameters params = (HttpParameters) argument;
assertEquals("http://mycallback", params.getFirst("oauth_callback"));
assertEquals("1", params.getFirst("a"));
return true;
}
};
verify(signer).sign(same(request), argThat(hasParameters));
}
@Test
public void shouldPercentEncodeOAuthParameters() throws Exception {
OAuthConsumer consumer = buildConsumer("1%2", CONSUMER_SECRET, null);
consumer.setTokenWithSecret("3 4", TOKEN_SECRET);
consumer.sign(httpGetMock);
verify(httpGetMock).setHeader(eq("Authorization"), argThat(new HasValuesPercentEncoded()));
}
@Test
public void shouldBeSerializable() throws Exception {
OAuthConsumer consumer = buildConsumer(CONSUMER_KEY, CONSUMER_SECRET, null);
consumer.setTokenWithSecret(TOKEN, TOKEN_SECRET);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream ostream = new ObjectOutputStream(baos);
ostream.writeObject(consumer);
ObjectInputStream istream = new ObjectInputStream(new ByteArrayInputStream(
baos.toByteArray()));
consumer = (OAuthConsumer) istream.readObject();
assertEquals(CONSUMER_KEY, consumer.getConsumerKey());
assertEquals(CONSUMER_SECRET, consumer.getConsumerSecret());
assertEquals(TOKEN, consumer.getToken());
assertEquals(TOKEN_SECRET, consumer.getTokenSecret());
// signing messages should still work
consumer.sign(httpGetMock);
}
@Test
public void shoudNotCllideOnMultiThread() throws Exception {
for (int i = 0; i < 10; i++) {
initRequestMocks();
final OAuthConsumer consumer = buildConsumer(CONSUMER_KEY, CONSUMER_SECRET, null);
consumer.setTokenWithSecret(TOKEN, TOKEN_SECRET);
Thread t1 = new Thread() {
@Override
public void run() {
try {
consumer.sign(httpGetMock);
} catch (Exception e) {
throw new RuntimeException("sign error", e);
}
}
};
Thread t2 = new Thread() {
@Override
public void run() {
try {
consumer.sign(httpGetMockWithQueryString);
} catch (Exception e) {
throw new RuntimeException("sign error", e);
}
}
};
t1.start();
t2.start();
t1.join();
t2.join();
ArgumentCaptor<String> arg1 = ArgumentCaptor.forClass(String.class);
ArgumentCaptor<String> arg2 = ArgumentCaptor.forClass(String.class);
verify(httpGetMock).setHeader(eq("Authorization"), arg1.capture());
verify(httpGetMockWithQueryString).setHeader(eq("Authorization"), arg2.capture());
HttpParameters headerMap1 = OAuth.oauthHeaderToParamsMap(arg1.getValue());
HttpParameters headerMap2 = OAuth.oauthHeaderToParamsMap(arg2.getValue());
assertThat(headerMap1.getFirst(OAuth.OAUTH_NONCE), not(equalTo(headerMap2.getFirst(OAuth.OAUTH_NONCE))));
}
}
// @Test
// public void shouldSupport2LeggedOAuth() throws Exception {
// OAuthConsumer consumer = buildConsumer(CONSUMER_KEY, CONSUMER_SECRET,
// null);
//
// // note how we do not set a token and secret; should still include the
// // oauth_token parameter
//
// consumer.sign(httpGetMock);
//
// verify(httpGetMock).setHeader(eq("Authorization"),
// argThat(new IsCompleteListOfOAuthParameters()));
// }
private class IsCompleteListOfOAuthParameters extends ArgumentMatcher<String> {
@Override
public boolean matches(Object argument) {
String oauthHeader = (String) argument;
assertTrue(oauthHeader.startsWith("OAuth "));
assertAllOAuthParametersExist(OAuth.oauthHeaderToParamsMap(oauthHeader));
return true;
}
}
private void assertAllOAuthParametersExist(HttpParameters params) {
assertNotNull(params.getFirst("oauth_consumer_key"));
assertNotNull(params.getFirst("oauth_token"));
assertNotNull(params.getFirst("oauth_signature_method"));
assertNotNull(params.getFirst("oauth_signature"));
assertNotNull(params.getFirst("oauth_timestamp"));
assertNotNull(params.getFirst("oauth_nonce"));
assertNotNull(params.getFirst("oauth_version"));
}
private class HasValuesPercentEncoded extends ArgumentMatcher<String> {
@Override
public boolean matches(Object argument) {
String oauthHeader = (String) argument;
HttpParameters params = OAuth.oauthHeaderToParamsMap(oauthHeader);
assertEquals("1%252", params.getFirst("oauth_consumer_key"));
assertEquals("3%204", params.getFirst("oauth_token"));
return true;
}
}
}