/*
* JBoss, a division of Red Hat
* Copyright 2013, Red Hat Middleware, LLC, and individual
* contributors as indicated by the @authors tag. See the
* copyright.txt in the distribution for a full listing of
* individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.gatein.security.oauth.test;
import java.lang.reflect.UndeclaredThrowableException;
import com.google.api.client.googleapis.auth.oauth2.GoogleTokenResponse;
import org.exoplatform.component.test.AbstractKernelTest;
import org.exoplatform.component.test.ConfigurationUnit;
import org.exoplatform.component.test.ConfiguredBy;
import org.exoplatform.component.test.ContainerScope;
import org.exoplatform.container.PortalContainer;
import org.exoplatform.services.organization.OrganizationService;
import org.exoplatform.services.organization.User;
import org.exoplatform.services.organization.UserProfile;
import org.exoplatform.services.organization.impl.UserImpl;
import org.exoplatform.web.security.codec.AbstractCodec;
import org.exoplatform.web.security.codec.CodecInitializer;
import org.gatein.security.oauth.exception.OAuthExceptionCode;
import org.gatein.security.oauth.exception.OAuthException;
import org.gatein.security.oauth.spi.OAuthProviderType;
import org.gatein.security.oauth.common.OAuthConstants;
import org.gatein.security.oauth.spi.OAuthProviderTypeRegistry;
import org.gatein.security.oauth.spi.SocialNetworkService;
import org.gatein.security.oauth.facebook.FacebookAccessTokenContext;
import org.gatein.security.oauth.google.GoogleAccessTokenContext;
import org.gatein.security.oauth.twitter.TwitterAccessTokenContext;
/**
* Test persistence of data with usage of {@link SocialNetworkService}
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
@ConfiguredBy({
@ConfigurationUnit(scope = ContainerScope.PORTAL, path = "conf/exo.portal.component.identity-configuration.xml"),
@ConfigurationUnit(scope = ContainerScope.PORTAL, path = "conf/exo.portal.component.web.oauth-configuration.xml") })
public class TestSocialNetworkService extends AbstractKernelTest {
private OrganizationService orgService;
private SocialNetworkService socialNetworkService;
private OAuthProviderTypeRegistry oAuthProviderTypeRegistry;
private AbstractCodec codec;
@Override
protected void setUp() throws Exception {
PortalContainer portalContainer = PortalContainer.getInstance();
orgService = (OrganizationService) portalContainer.getComponentInstanceOfType(OrganizationService.class);
socialNetworkService = (SocialNetworkService) portalContainer.getComponentInstanceOfType(SocialNetworkService.class);
oAuthProviderTypeRegistry = (OAuthProviderTypeRegistry) portalContainer.getComponentInstanceOfType(OAuthProviderTypeRegistry.class);
CodecInitializer codecInitializer = (CodecInitializer) portalContainer.getComponentInstanceOfType(CodecInitializer.class);
this.codec = codecInitializer.getCodec();
begin();
}
@Override
protected void tearDown() throws Exception {
end();
}
public void testPersistOAuthProviderUsernames() throws Exception {
User user1 = new UserImpl("testUser1");
User user2 = new UserImpl("testUser2");
orgService.getUserHandler().createUser(user1, false);
orgService.getUserHandler().createUser(user2, false);
// Save facebook username and google username for user1
UserProfile userProfile1 = orgService.getUserProfileHandler().createUserProfileInstance(user1.getUserName());
userProfile1.setAttribute(OAuthConstants.PROFILE_FACEBOOK_USERNAME, "joseph.doyle");
userProfile1.setAttribute(OAuthConstants.PROFILE_GOOGLE_USERNAME, "joseph.something");
orgService.getUserProfileHandler().saveUserProfile(userProfile1, true);
// Save facebook username and google username for user2
UserProfile userProfile2 = orgService.getUserProfileHandler().createUserProfileInstance(user2.getUserName());
userProfile2.setAttribute(OAuthConstants.PROFILE_FACEBOOK_USERNAME, "john.doyle");
userProfile2.setAttribute(OAuthConstants.PROFILE_GOOGLE_USERNAME, "john.something");
orgService.getUserProfileHandler().saveUserProfile(userProfile2, true);
// Find user by facebook and google username
User foundUser = socialNetworkService.findUserByOAuthProviderUsername(getFacebookProvider(), "joseph.doyle");
assertNotNull(foundUser);
assertEquals(foundUser.getUserName(), user1.getUserName());
User foundUser2 = socialNetworkService.findUserByOAuthProviderUsername(getFacebookProvider(), "john.doyle");
assertNotNull(foundUser2);
assertEquals(foundUser2.getUserName(), user2.getUserName());
User foundUser3 = socialNetworkService.findUserByOAuthProviderUsername(getGoogleProvider(), "john.something");
assertNotNull(foundUser3);
assertEquals(foundUser3.getUserName(), user2.getUserName());
// Try to change facebook username for user1 with socialNetworkService
socialNetworkService.updateOAuthInfo(getFacebookProvider(), user1.getUserName(), "joseph.doyle.changed", createFacebookAccessToken("someToken"));
User foundUser4 = socialNetworkService.findUserByOAuthProviderUsername(getFacebookProvider(), "joseph.doyle.changed");
assertNotNull(foundUser4);
assertEquals(foundUser4.getUserName(), user1.getUserName());
try {
// This should fail because of duplicated facebook username
socialNetworkService.updateOAuthInfo(getFacebookProvider(), user2.getUserName(), "joseph.doyle.changed", createFacebookAccessToken("someToken"));
fail("Exception should occur because of duplicated facebook username");
} catch (OAuthException gtnOauthOAuthException) {
assertEquals(OAuthExceptionCode.DUPLICATE_OAUTH_PROVIDER_USERNAME, gtnOauthOAuthException.getExceptionCode());
assertEquals(OAuthConstants.PROFILE_FACEBOOK_USERNAME, gtnOauthOAuthException.getExceptionAttribute(OAuthConstants.EXCEPTION_OAUTH_PROVIDER_USERNAME_ATTRIBUTE_NAME));
assertEquals("joseph.doyle.changed", gtnOauthOAuthException.getExceptionAttribute(OAuthConstants.EXCEPTION_OAUTH_PROVIDER_USERNAME));
} catch (Exception e) {
throw new UndeclaredThrowableException(e);
}
orgService.getUserHandler().removeUser(user1.getUserName(), false);
orgService.getUserHandler().removeUser(user2.getUserName(), false);
}
public void testPersistOAuthAccessTokens() throws Exception {
User user1 = new UserImpl("testUser1");
User user2 = new UserImpl("testUser2");
orgService.getUserHandler().createUser(user1, false);
orgService.getUserHandler().createUser(user2, false);
// Update some facebook accessTokens
socialNetworkService.updateOAuthAccessToken(getFacebookProvider(), user1.getUserName(), createFacebookAccessToken("aaa123"));
socialNetworkService.updateOAuthAccessToken(getFacebookProvider(), user2.getUserName(), createFacebookAccessToken("bbb456"));
// Update some google accessToken
GoogleAccessTokenContext googleToken = createGoogleAccessToken("ccc789", "rfrc487", "http://someScope");
socialNetworkService.updateOAuthAccessToken(getGoogleProvider(), user1.getUserName(), googleToken);
// Update some twitter accessToken
TwitterAccessTokenContext twitterToken = new TwitterAccessTokenContext("tok1", "secret1");
socialNetworkService.updateOAuthAccessToken(getTwitterProvider(), user1.getUserName(), twitterToken);
// Verify that FB accessTokens could be obtained
assertEquals("aaa123", socialNetworkService.getOAuthAccessToken(getFacebookProvider(), user1.getUserName()).getAccessToken());
assertEquals("bbb456", socialNetworkService.getOAuthAccessToken(getFacebookProvider(), user2.getUserName()).getAccessToken());
// Verify that Google accessToken could be obtained
googleToken = createGoogleAccessToken("ccc789", "rfrc487", "http://someScope");
assertEquals(googleToken, socialNetworkService.getOAuthAccessToken(getGoogleProvider(), user1.getUserName()));
assertNull(socialNetworkService.getOAuthAccessToken(getGoogleProvider(), user2.getUserName()));
// Verify that twitter accessToken could be obtained
assertEquals(new TwitterAccessTokenContext("tok1", "secret1"), socialNetworkService.getOAuthAccessToken(getTwitterProvider(), user1.getUserName()));
assertNull(socialNetworkService.getOAuthAccessToken(getTwitterProvider(), user2.getUserName()));
// Directly obtain accessTokens from userProfile and verify that they are encoded
UserProfile userProfile1 = orgService.getUserProfileHandler().findUserProfileByName("testUser1");
UserProfile userProfile2 = orgService.getUserProfileHandler().findUserProfileByName("testUser2");
String encodedAccessToken1 = userProfile1.getAttribute(OAuthConstants.PROFILE_FACEBOOK_ACCESS_TOKEN + ".1");
String encodedAccessToken2 = userProfile2.getAttribute(OAuthConstants.PROFILE_FACEBOOK_ACCESS_TOKEN + ".1");
assertFalse("aaa123".equals(encodedAccessToken1));
assertFalse("bbb456".equals(encodedAccessToken2));
assertTrue(codec.encode("aaa123").equals(encodedAccessToken1));
assertTrue(codec.encode("bbb456").equals(encodedAccessToken2));
// Verify that tokens are null after invalidation
socialNetworkService.removeOAuthAccessToken(getFacebookProvider(), user1.getUserName());
socialNetworkService.removeOAuthAccessToken(getGoogleProvider(), user1.getUserName());
socialNetworkService.removeOAuthAccessToken(getTwitterProvider(), user1.getUserName());
assertNull(socialNetworkService.getOAuthAccessToken(getFacebookProvider(), user1.getUserName()));
assertNull(socialNetworkService.getOAuthAccessToken(getGoogleProvider(), user1.getUserName()));
assertNull(socialNetworkService.getOAuthAccessToken(getTwitterProvider(), user1.getUserName()));
assertNotNull(socialNetworkService.getOAuthAccessToken(getFacebookProvider(), user2.getUserName()));
orgService.getUserHandler().removeUser(user1.getUserName(), false);
orgService.getUserHandler().removeUser(user2.getUserName(), false);
}
public void testInvalidationOfAccessTokens() throws Exception {
User user1 = new UserImpl("testUser1");
orgService.getUserHandler().createUser(user1, false);
// Update some accessToken and verify that it's available
socialNetworkService.updateOAuthInfo(getFacebookProvider(), user1.getUserName(), "fbUsername1", createFacebookAccessToken("fbAccessToken1"));
assertEquals("fbAccessToken1", socialNetworkService.getOAuthAccessToken(getFacebookProvider(), user1.getUserName()).getAccessToken());
// Update some accessToken again
socialNetworkService.updateOAuthInfo(getFacebookProvider(), user1.getUserName(), "fbUsername1", createFacebookAccessToken("fbAccessToken2"));
assertEquals("fbAccessToken2", socialNetworkService.getOAuthAccessToken(getFacebookProvider(), user1.getUserName()).getAccessToken());
// Update userProfile and change FB username. AccessToken should be invalidated
UserProfile userProfile1 = orgService.getUserProfileHandler().findUserProfileByName(user1.getUserName());
userProfile1.setAttribute(getFacebookProvider().getUserNameAttrName(), "fbUsername2");
orgService.getUserProfileHandler().saveUserProfile(userProfile1, true);
assertNull(socialNetworkService.getOAuthAccessToken(getFacebookProvider(), user1.getUserName()));
// Update some accessToken and verify it's here now
socialNetworkService.updateOAuthAccessToken(getFacebookProvider(), user1.getUserName(), createFacebookAccessToken("fbAccessToken3"));
assertEquals("fbAccessToken3", socialNetworkService.getOAuthAccessToken(getFacebookProvider(), user1.getUserName()).getAccessToken());
// Null FB username and verify that accessToken is invalidated
userProfile1 = orgService.getUserProfileHandler().findUserProfileByName(user1.getUserName());
userProfile1.setAttribute(getFacebookProvider().getUserNameAttrName(), null);
orgService.getUserProfileHandler().saveUserProfile(userProfile1, true);
assertNull(socialNetworkService.getOAuthAccessToken(getFacebookProvider(), user1.getUserName()));
// Test this with Twitter
TwitterAccessTokenContext twitterToken = new TwitterAccessTokenContext("token1", "secret1");
socialNetworkService.updateOAuthInfo(getTwitterProvider(), user1.getUserName(), "twitterUsername1", twitterToken);
userProfile1 = orgService.getUserProfileHandler().findUserProfileByName(user1.getUserName());
userProfile1.setAttribute(getTwitterProvider().getUserNameAttrName(), "twitterUsername2");
orgService.getUserProfileHandler().saveUserProfile(userProfile1, true);
assertNull(socialNetworkService.getOAuthAccessToken(getTwitterProvider(), user1.getUserName()));
// Test this with Google
GoogleAccessTokenContext grc = createGoogleAccessToken("token1", "rf1", "http://someScope");
socialNetworkService.updateOAuthInfo(getGoogleProvider(), user1.getUserName(), "googleUsername1", grc);
userProfile1 = orgService.getUserProfileHandler().findUserProfileByName(user1.getUserName());
userProfile1.setAttribute(getGoogleProvider().getUserNameAttrName(), "googleUsername2");
orgService.getUserProfileHandler().saveUserProfile(userProfile1, true);
assertNull(socialNetworkService.getOAuthAccessToken(getGoogleProvider(), user1.getUserName()));
orgService.getUserHandler().removeUser(user1.getUserName(), false);
}
public void testLongAccessToken() throws Exception {
// Create some example token of length 800
User user1 = new UserImpl("testUser1");
orgService.getUserHandler().createUser(user1, false);
String longAccessToken = createLongString();
// FACEBOOK
// Update long accessToken and verify that it's available
socialNetworkService.updateOAuthInfo(getFacebookProvider(), user1.getUserName(), "fbUsername1", createFacebookAccessToken(longAccessToken));
assertEquals(longAccessToken, socialNetworkService.getOAuthAccessToken(getFacebookProvider(), user1.getUserName()).getAccessToken());
// Update with some short token now
String shortAccessToken = "someToken1";
socialNetworkService.updateOAuthInfo(getFacebookProvider(), user1.getUserName(), "fbUsername1", createFacebookAccessToken(shortAccessToken));
assertEquals(shortAccessToken, socialNetworkService.getOAuthAccessToken(getFacebookProvider(), user1.getUserName()).getAccessToken());
// GOOGLE
GoogleAccessTokenContext grc = createGoogleAccessToken(longAccessToken, longAccessToken, "http://someScope");
socialNetworkService.updateOAuthInfo(getGoogleProvider(), user1.getUserName(), "googleUsername1", grc);
assertEquals(grc, socialNetworkService.getOAuthAccessToken(getGoogleProvider(), user1.getUserName()));
socialNetworkService.removeOAuthAccessToken(getGoogleProvider(), user1.getUserName());
assertNull(socialNetworkService.getOAuthAccessToken(getGoogleProvider(), user1.getUserName()));
// TWITTER
TwitterAccessTokenContext twitterToken = new TwitterAccessTokenContext(longAccessToken, longAccessToken);
socialNetworkService.updateOAuthAccessToken(getTwitterProvider(), user1.getUserName(), twitterToken);
assertEquals(twitterToken, socialNetworkService.getOAuthAccessToken(getTwitterProvider(), user1.getUserName()));
twitterToken = new TwitterAccessTokenContext(shortAccessToken, shortAccessToken);
socialNetworkService.updateOAuthAccessToken(getTwitterProvider(), user1.getUserName(), twitterToken);
assertEquals(twitterToken, socialNetworkService.getOAuthAccessToken(getTwitterProvider(), user1.getUserName()));
orgService.getUserHandler().removeUser(user1.getUserName(), false);
}
public void testAccessTokensWithMoreScopes() throws Exception {
User user1 = new UserImpl("testUser1");
orgService.getUserHandler().createUser(user1, false);
// Update some google accessToken with two scopes
GoogleAccessTokenContext googleToken = createGoogleAccessToken("ccc789", "rfrc487", "http://someScope", "http://someScope2");
socialNetworkService.updateOAuthAccessToken(getGoogleProvider(), user1.getUserName(), googleToken);
// Verify that Google accessTokens could be obtained and are equals to saved access token
assertEquals(googleToken, socialNetworkService.getOAuthAccessToken(getGoogleProvider(), user1.getUserName()));
// Update some google accessToken with two scopes
FacebookAccessTokenContext facebookToken = createFacebookAccessToken("ddd789", "rfrc4876", "email", "publish_stream");
socialNetworkService.updateOAuthAccessToken(getFacebookProvider(), user1.getUserName(), facebookToken);
// Verify that Facebook accessTokens could be obtained and are equals to saved access token
assertEquals(facebookToken, socialNetworkService.getOAuthAccessToken(getFacebookProvider(), user1.getUserName()));
orgService.getUserHandler().removeUser(user1.getUserName(), false);
}
private OAuthProviderType<FacebookAccessTokenContext> getFacebookProvider() {
return oAuthProviderTypeRegistry.getOAuthProvider(OAuthConstants.OAUTH_PROVIDER_KEY_FACEBOOK, FacebookAccessTokenContext.class);
}
private OAuthProviderType<GoogleAccessTokenContext> getGoogleProvider() {
return oAuthProviderTypeRegistry.getOAuthProvider(OAuthConstants.OAUTH_PROVIDER_KEY_GOOGLE, GoogleAccessTokenContext.class);
}
private OAuthProviderType<TwitterAccessTokenContext> getTwitterProvider() {
return oAuthProviderTypeRegistry.getOAuthProvider(OAuthConstants.OAUTH_PROVIDER_KEY_TWITTER, TwitterAccessTokenContext.class);
}
private GoogleAccessTokenContext createGoogleAccessToken(String accessToken, String refreshToken, String... scope) {
GoogleTokenResponse grc = new GoogleTokenResponse();
grc.setAccessToken(accessToken);
grc.setRefreshToken(refreshToken);
grc.setExpiresInSeconds(1000L);
grc.setTokenType("Bearer");
grc.setIdToken("someTokenId");
return new GoogleAccessTokenContext(grc, scope);
}
private FacebookAccessTokenContext createFacebookAccessToken(String accessToken, String... scope) {
if (scope == null) {
scope = new String[] { "email" };
}
return new FacebookAccessTokenContext(accessToken, scope);
}
/**
* Get string of length 800
* @return
*/
private String createLongString() {
StringBuilder builder = new StringBuilder("1324567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
for (int i=0 ; i<3 ; i++) {
builder.append(builder);
}
return builder.toString();
}
}