package org.springframework.social.connect.jdbc; import static org.hamcrest.Matchers.hasItems; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; import org.junit.Before; import org.junit.Test; import org.springframework.social.connect.ApiAdapter; import org.springframework.social.connect.Connection; import org.springframework.social.connect.ConnectionData; import org.springframework.social.connect.ConnectionKey; import org.springframework.social.connect.ConnectionRepository; import org.springframework.social.connect.ConnectionSignUp; import org.springframework.social.connect.ConnectionValues; import org.springframework.social.connect.DuplicateConnectionException; import org.springframework.social.connect.NoSuchConnectionException; import org.springframework.social.connect.NotConnectedException; import org.springframework.social.connect.UserProfile; import org.springframework.social.connect.UserProfileBuilder; import org.springframework.social.connect.UsersConnectionRepository; import org.springframework.social.connect.support.ConnectionFactoryRegistry; import org.springframework.social.connect.support.OAuth1ConnectionFactory; import org.springframework.social.connect.support.OAuth2ConnectionFactory; import org.springframework.social.oauth1.OAuth1Operations; import org.springframework.social.oauth1.OAuth1ServiceProvider; import org.springframework.social.oauth2.AccessGrant; import org.springframework.social.oauth2.GrantType; import org.springframework.social.oauth2.OAuth2Operations; import org.springframework.social.oauth2.OAuth2Parameters; import org.springframework.social.oauth2.OAuth2ServiceProvider; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; public abstract class AbstractUsersConnectionRepositoryTest { protected static final String TWITTER_CONNECTION_1_PROVIDER_USER_ID = "8"; protected static final String FACEBOOK_CONNECTION_1_PROVIDER_USER_ID = "9"; protected static final String FACEBOOK_CONNECTION_2_PROVIDER_USER_ID = "10"; protected static final String FACEBOOK_CONNECTION_3_PROVIDER_USER_ID = "11"; protected static final ConnectionData TWITTER_DATA = new ConnectionData("twitter", TWITTER_CONNECTION_1_PROVIDER_USER_ID, "@kdonald", "http://twitter.com/kdonald", "http://twitter.com/kdonald/picture", "123456789", "987654321", "refresh_token", System.currentTimeMillis() + 3600000); protected static final ConnectionData FACEBOOK_DATA_1 = new ConnectionData("facebook", FACEBOOK_CONNECTION_1_PROVIDER_USER_ID, null, null, null, "234567890", null, "345678901", System.currentTimeMillis() + 3600000); protected static final ConnectionData FACEBOOK_DATA_2 = new ConnectionData("facebook", FACEBOOK_CONNECTION_2_PROVIDER_USER_ID, null, null, null, "456789012", null, "56789012", System.currentTimeMillis() + 3600000); protected static final ConnectionData FACEBOOK_DATA_3 = new ConnectionData("facebook", FACEBOOK_CONNECTION_3_PROVIDER_USER_ID, null, null, null, "234567890", null, "345678901", System.currentTimeMillis() + 3600000); protected static class TestFacebookConnectionFactory extends OAuth2ConnectionFactory<TestFacebookApi> { public TestFacebookConnectionFactory() { super("facebook", new TestFacebookServiceProvider(), new TestFacebookApiAdapter()); } } private TestFacebookConnectionFactory facebookConnectionFactory; private TestTwitterConnectionFactory twitterConnectionFactory; private ConnectionFactoryRegistry connectionFactoryRegistry = new ConnectionFactoryRegistry(); protected abstract void insertTwitterConnection(); protected abstract void insertFacebookConnection1(); protected abstract void insertFacebookConnection2(); protected abstract void insertFacebookConnection3(); protected abstract void insertFacebookConnectionSameFacebookUser(); protected abstract String getUserId1(); protected abstract String getUserId2(); protected abstract UsersConnectionRepository getUsersConnectionRepository(); protected abstract ConnectionRepository getConnectionRepository(); protected TestFacebookConnectionFactory getFacebookConnectionFactory() { return facebookConnectionFactory; } public TestTwitterConnectionFactory getTwitterConnectionFactory() { return twitterConnectionFactory; } public ConnectionFactoryRegistry getConnectionFactoryRegistry() { return this.connectionFactoryRegistry; } @Before public void setUpConnectionFactory() { facebookConnectionFactory = new TestFacebookConnectionFactory(); twitterConnectionFactory = new TestTwitterConnectionFactory(); connectionFactoryRegistry = new ConnectionFactoryRegistry(); connectionFactoryRegistry.addConnectionFactory(facebookConnectionFactory); connectionFactoryRegistry.addConnectionFactory(twitterConnectionFactory); } @Test public void findUserIdWithConnection() { insertFacebookConnection1(); List<String> userIds = getUsersConnectionRepository().findUserIdsWithConnection(getConnectionRepository().getPrimaryConnection(TestFacebookApi.class)); assertEquals(getUserId1(), userIds.get(0)); } @Test public void findUserIdWithConnectionNoSuchConnection() { Connection<TestFacebookApi> connection = getFacebookConnectionFactory().createConnection(new AccessGrant("12345")); assertEquals(0, getUsersConnectionRepository().findUserIdsWithConnection(connection).size()); } @Test public void findUserIdWithConnectionMultipleConnectionsToSameProviderUser() { insertFacebookConnection1(); insertFacebookConnectionSameFacebookUser(); List<String> localUserIds = getUsersConnectionRepository().findUserIdsWithConnection(getConnectionRepository().getPrimaryConnection(TestFacebookApi.class)); assertEquals(2, localUserIds.size()); assertThat(localUserIds, hasItems(getUserId1(), getUserId2())); } @Test public void findUserIdWithConnectionNoConnection_withWorkingConnectionSignUp() { Connection<TestFacebookApi> connection = facebookConnectionFactory.createConnection(new AccessGrant("12345")); getUsersConnectionRepository().setConnectionSignUp(new ConnectionSignUp() { @Override public String execute(Connection<?> connection) { return "batman"; } }); List<String> userIds = getUsersConnectionRepository().findUserIdsWithConnection(connection); assertEquals(1, userIds.size()); assertEquals("batman", userIds.get(0)); } @Test public void findUserIdWithConnectionNoConnection_withConnectionSignUpReturningNull() { Connection<TestFacebookApi> connection = facebookConnectionFactory.createConnection(new AccessGrant("12345")); getUsersConnectionRepository().setConnectionSignUp(new ConnectionSignUp() { @Override public String execute(Connection<?> connection) { return null; } }); List<String> userIds = getUsersConnectionRepository().findUserIdsWithConnection(connection); assertEquals(0, userIds.size()); } @Test public void findUserIdsConnectedTo() { insertFacebookConnection1(); insertFacebookConnection3(); Set<String> localUserIds = getUsersConnectionRepository().findUserIdsConnectedTo("facebook", new HashSet<String>(Arrays.asList(FACEBOOK_CONNECTION_1_PROVIDER_USER_ID, FACEBOOK_CONNECTION_3_PROVIDER_USER_ID))); assertEquals(2, localUserIds.size()); assertThat(localUserIds, hasItems(getUserId1(), getUserId2())); } @Test @SuppressWarnings("unchecked") public void findAllConnections() { insertTwitterConnection(); insertFacebookConnection1(); MultiValueMap<String, Connection<?>> connections = getConnectionRepository().findAllConnections(); assertEquals(2, connections.size()); Connection<TestFacebookApi> facebook = (Connection<TestFacebookApi>) connections.getFirst("facebook"); assertFacebookConnection(facebook); Connection<TestTwitterApi> twitter = (Connection<TestTwitterApi>) connections.getFirst("twitter"); assertTwitterConnection(twitter); } @Test public void findAllConnectionsMultipleConnectionResults() { insertTwitterConnection(); insertFacebookConnection1(); insertFacebookConnection2(); MultiValueMap<String, Connection<?>> connections = getConnectionRepository().findAllConnections(); assertEquals(2, connections.size()); assertEquals(2, connections.get("facebook").size()); assertEquals(1, connections.get("twitter").size()); } @Test public void findAllConnectionsEmptyResult() { MultiValueMap<String, Connection<?>> connections = getConnectionRepository().findAllConnections(); assertEquals(2, connections.size()); assertEquals(0, connections.get("facebook").size()); assertEquals(0, connections.get("twitter").size()); } @Test @SuppressWarnings("unchecked") public void findConnectionsByProviderId() { insertTwitterConnection(); List<Connection<?>> connections = getConnectionRepository().findConnections("twitter"); assertEquals(1, connections.size()); assertTwitterConnection((Connection<TestTwitterApi>) connections.get(0)); } @Test public void findConnectionsByProviderIdEmptyResult() { assertTrue(getConnectionRepository().findConnections("facebook").isEmpty()); } @Test public void findConnectionsByApi() { insertFacebookConnection1(); insertFacebookConnection2(); List<Connection<TestFacebookApi>> connections = getConnectionRepository().findConnections(TestFacebookApi.class); assertEquals(2, connections.size()); assertFacebookConnection(connections.get(0)); } @Test public void findConnectionsByApiEmptyResult() { assertTrue(getConnectionRepository().findConnections(TestFacebookApi.class).isEmpty()); } @Test @SuppressWarnings("unchecked") public void findConnectionsToUsers() { insertTwitterConnection(); insertFacebookConnection2(); insertFacebookConnection1(); MultiValueMap<String, String> providerUsers = new LinkedMultiValueMap<String, String>(); providerUsers.add("twitter", TWITTER_CONNECTION_1_PROVIDER_USER_ID); providerUsers.add("facebook", FACEBOOK_CONNECTION_2_PROVIDER_USER_ID); providerUsers.add("facebook", FACEBOOK_CONNECTION_1_PROVIDER_USER_ID); MultiValueMap<String, Connection<?>> connectionsForUsers = getConnectionRepository().findConnectionsToUsers(providerUsers); assertEquals(2, connectionsForUsers.size()); assertEquals(FACEBOOK_CONNECTION_2_PROVIDER_USER_ID, connectionsForUsers.getFirst("facebook").getKey().getProviderUserId()); assertFacebookConnection((Connection<TestFacebookApi>) connectionsForUsers.get("facebook").get(1)); assertTwitterConnection((Connection<TestTwitterApi>) connectionsForUsers.getFirst("twitter")); } @Test public void findConnectionsToUsersEmptyResult() { MultiValueMap<String, String> providerUsers = new LinkedMultiValueMap<String, String>(); providerUsers.add("facebook", FACEBOOK_CONNECTION_1_PROVIDER_USER_ID); assertTrue(getConnectionRepository().findConnectionsToUsers(providerUsers).isEmpty()); } @Test(expected = IllegalArgumentException.class) public void findConnectionsToUsersEmptyInput() { MultiValueMap<String, String> providerUsers = new LinkedMultiValueMap<String, String>(); getConnectionRepository().findConnectionsToUsers(providerUsers); } @Test @SuppressWarnings("unchecked") public void findConnectionByKey() { insertFacebookConnection1(); assertFacebookConnection((Connection<TestFacebookApi>) getConnectionRepository().getConnection(new ConnectionKey("facebook", "9"))); } @Test(expected = NoSuchConnectionException.class) public void findConnectionByKeyNoSuchConnection() { getConnectionRepository().getConnection(new ConnectionKey("facebook", "bogus")); } @Test public void findConnectionByApiToUser() { insertFacebookConnection1(); insertFacebookConnection2(); assertFacebookConnection(getConnectionRepository().getConnection(TestFacebookApi.class, FACEBOOK_CONNECTION_1_PROVIDER_USER_ID)); assertEquals(FACEBOOK_CONNECTION_2_PROVIDER_USER_ID, getConnectionRepository().getConnection(TestFacebookApi.class, FACEBOOK_CONNECTION_2_PROVIDER_USER_ID).getKey().getProviderUserId()); } @Test(expected = NoSuchConnectionException.class) public void findConnectionByApiToUserNoSuchConnection() { assertFacebookConnection(getConnectionRepository().getConnection(TestFacebookApi.class, FACEBOOK_CONNECTION_1_PROVIDER_USER_ID)); } @Test public void findPrimaryConnection() { insertFacebookConnection1(); assertFacebookConnection(getConnectionRepository().getPrimaryConnection(TestFacebookApi.class)); } @Test public void findPrimaryConnectionSelectFromMultipleByRank() { insertFacebookConnection1(); insertFacebookConnection2(); assertFacebookConnection(getConnectionRepository().getPrimaryConnection(TestFacebookApi.class)); } @Test(expected = NotConnectedException.class) public void findPrimaryConnectionNotConnected() { getConnectionRepository().getPrimaryConnection(TestFacebookApi.class); } @Test public void removeConnections() { insertFacebookConnection1(); insertFacebookConnection2(); assertEquals(2, getConnectionRepository().findConnections("facebook").size()); getConnectionRepository().removeConnections("facebook"); assertEquals(0, getConnectionRepository().findConnections("facebook").size()); } @Test public void removeConnectionsToProviderNoOp() { getConnectionRepository().removeConnections("twitter"); } @Test public void removeConnection() { insertFacebookConnection1(); insertFacebookConnection2(); assertEquals(2, getConnectionRepository().findConnections("facebook").size()); getConnectionRepository().removeConnection(new ConnectionKey("facebook", FACEBOOK_CONNECTION_1_PROVIDER_USER_ID)); assertEquals(1, getConnectionRepository().findConnections("facebook").size()); } @Test public void removeConnectionNoOp() { getConnectionRepository().removeConnection(new ConnectionKey("facebook", "1111111111")); } @Test public void addConnection() { Connection<TestFacebookApi> connection = facebookConnectionFactory.createConnection(new AccessGrant("123456789", null, "987654321", 3600L)); getConnectionRepository().addConnection(connection); Connection<TestFacebookApi> restoredConnection = getConnectionRepository().getPrimaryConnection(TestFacebookApi.class); assertEquals(connection, restoredConnection); assertNewConnection(restoredConnection); } @Test(expected = DuplicateConnectionException.class) public void addConnectionDuplicate() { Connection<TestFacebookApi> connection = facebookConnectionFactory.createConnection(new AccessGrant("123456789", null, "987654321", 3600L)); getConnectionRepository().addConnection(connection); getConnectionRepository().addConnection(connection); } @Test public void updateConnectionProfileFields() { insertTwitterConnection(); Connection<TestTwitterApi> twitter = getConnectionRepository().getPrimaryConnection(TestTwitterApi.class); assertEquals("http://twitter.com/kdonald/picture", twitter.getImageUrl()); twitter.sync(); assertEquals("http://twitter.com/kdonald/a_new_picture", twitter.getImageUrl()); getConnectionRepository().updateConnection(twitter); Connection<TestTwitterApi> twitter2 = getConnectionRepository().getPrimaryConnection(TestTwitterApi.class); assertEquals("http://twitter.com/kdonald/a_new_picture", twitter2.getImageUrl()); } @Test public void updateConnectionAccessFields() { insertFacebookConnection1(); Connection<TestFacebookApi> facebook = getConnectionRepository().getPrimaryConnection(TestFacebookApi.class); assertEquals("234567890", facebook.getApi().getAccessToken()); facebook.refresh(); getConnectionRepository().updateConnection(facebook); Connection<TestFacebookApi> facebook2 = getConnectionRepository().getPrimaryConnection(TestFacebookApi.class); assertEquals("765432109", facebook2.getApi().getAccessToken()); ConnectionData data = facebook.createData(); assertEquals("654321098", data.getRefreshToken()); } @Test public void findPrimaryConnection_afterRemove() { insertFacebookConnection1(); insertFacebookConnection2(); // 9 is the providerUserId of the first Facebook connection getConnectionRepository().removeConnection(new ConnectionKey("facebook", FACEBOOK_CONNECTION_1_PROVIDER_USER_ID)); assertEquals(1, getConnectionRepository().findConnections(TestFacebookApi.class).size()); assertNotNull(getConnectionRepository().findPrimaryConnection(TestFacebookApi.class)); } protected String getTablePrefix() { return ""; } protected String getSchemaSql() { return "JdbcUsersConnectionRepository.sql"; } private void assertNewConnection(Connection<TestFacebookApi> connection) { assertEquals("facebook", connection.getKey().getProviderId()); assertEquals(FACEBOOK_CONNECTION_1_PROVIDER_USER_ID, connection.getKey().getProviderUserId()); assertEquals("Keith Donald", connection.getDisplayName()); assertEquals("http://facebook.com/keith.donald", connection.getProfileUrl()); assertEquals("http://facebook.com/keith.donald/picture", connection.getImageUrl()); assertTrue(connection.test()); TestFacebookApi api = connection.getApi(); assertNotNull(api); assertEquals("123456789", api.getAccessToken()); assertEquals("123456789", connection.createData().getAccessToken()); assertEquals("987654321", connection.createData().getRefreshToken()); } private void assertTwitterConnection(Connection<TestTwitterApi> twitter) { assertEquals(new ConnectionKey("twitter", TWITTER_CONNECTION_1_PROVIDER_USER_ID), twitter.getKey()); assertEquals("@kdonald", twitter.getDisplayName()); assertEquals("http://twitter.com/kdonald", twitter.getProfileUrl()); assertEquals("http://twitter.com/kdonald/picture", twitter.getImageUrl()); TestTwitterApi twitterApi = twitter.getApi(); assertEquals("123456789", twitterApi.getAccessToken()); assertEquals("987654321", twitterApi.getSecret()); twitter.sync(); assertEquals("http://twitter.com/kdonald/a_new_picture", twitter.getImageUrl()); } private void assertFacebookConnection(Connection<TestFacebookApi> facebook) { assertEquals(new ConnectionKey("facebook", FACEBOOK_CONNECTION_1_PROVIDER_USER_ID), facebook.getKey()); assertEquals(null, facebook.getDisplayName()); assertEquals(null, facebook.getProfileUrl()); assertEquals(null, facebook.getImageUrl()); TestFacebookApi facebookApi = facebook.getApi(); assertEquals("234567890", facebookApi.getAccessToken()); facebook.sync(); assertEquals("Keith Donald", facebook.getDisplayName()); assertEquals("http://facebook.com/keith.donald", facebook.getProfileUrl()); assertEquals("http://facebook.com/keith.donald/picture", facebook.getImageUrl()); } protected static class TestFacebookServiceProvider implements OAuth2ServiceProvider<TestFacebookApi> { @Override public OAuth2Operations getOAuthOperations() { return new OAuth2Operations() { @Override public String buildAuthorizeUrl(GrantType grantType, OAuth2Parameters params) { return null; } @Override public String buildAuthenticateUrl(GrantType grantType, OAuth2Parameters params) { return null; } @Override public String buildAuthorizeUrl(OAuth2Parameters params) { return null; } @Override public String buildAuthenticateUrl(OAuth2Parameters params) { return null; } @Override public AccessGrant exchangeForAccess(String authorizationGrant, String redirectUri, MultiValueMap<String, String> additionalParameters) { return null; } @Override public AccessGrant exchangeCredentialsForAccess(String username, String password, MultiValueMap<String, String> additionalParameters) { return null; } @Override public AccessGrant refreshAccess(String refreshToken, MultiValueMap<String, String> additionalParameters) { return new AccessGrant("765432109", "read", "654321098", 3600L); } @Override public AccessGrant authenticateClient() { return null; } @Override public AccessGrant authenticateClient(String scope) { return null; } }; } @Override public TestFacebookApi getApi(final String accessToken) { return new TestFacebookApi() { @Override public String getAccessToken() { return accessToken; } }; } } public interface TestFacebookApi { String getAccessToken(); } protected static class TestFacebookApiAdapter implements ApiAdapter<TestFacebookApi> { private final String accountId = FACEBOOK_CONNECTION_1_PROVIDER_USER_ID; private final String name = "Keith Donald"; private final String profileUrl = "http://facebook.com/keith.donald"; private final String profilePictureUrl = "http://facebook.com/keith.donald/picture"; @Override public boolean test(TestFacebookApi api) { return true; } @Override public void setConnectionValues(TestFacebookApi api, ConnectionValues values) { values.setProviderUserId(accountId); values.setDisplayName(name); values.setProfileUrl(profileUrl); values.setImageUrl(profilePictureUrl); } @Override public UserProfile fetchUserProfile(TestFacebookApi api) { return new UserProfileBuilder().setName(name).setEmail("keith@interface21.com").setUsername("Keith.Donald").build(); } @Override public void updateStatus(TestFacebookApi api, String message) { } } protected static class TestTwitterConnectionFactory extends OAuth1ConnectionFactory<TestTwitterApi> { public TestTwitterConnectionFactory() { super("twitter", new TestTwitterServiceProvider(), new TestTwitterApiAdapter()); } } protected static class TestTwitterServiceProvider implements OAuth1ServiceProvider<TestTwitterApi> { @Override public OAuth1Operations getOAuthOperations() { return null; } @Override public TestTwitterApi getApi(final String accessToken, final String secret) { return new TestTwitterApi() { @Override public String getAccessToken() { return accessToken; } @Override public String getSecret() { return secret; } }; } } public interface TestTwitterApi { String getAccessToken(); String getSecret(); } protected static class TestTwitterApiAdapter implements ApiAdapter<TestTwitterApi> { private final String accountId = TWITTER_CONNECTION_1_PROVIDER_USER_ID; private final String name = "@kdonald"; private final String profileUrl = "http://twitter.com/kdonald"; private final String profilePictureUrl = "http://twitter.com/kdonald/a_new_picture"; @Override public boolean test(TestTwitterApi api) { return true; } @Override public void setConnectionValues(TestTwitterApi api, ConnectionValues values) { values.setProviderUserId(accountId); values.setDisplayName(name); values.setProfileUrl(profileUrl); values.setImageUrl(profilePictureUrl); } @Override public UserProfile fetchUserProfile(TestTwitterApi api) { return new UserProfileBuilder().setName(name).setUsername("kdonald").build(); } @Override public void updateStatus(TestTwitterApi api, String message) { } } public AbstractUsersConnectionRepositoryTest() { super(); } }