diff options
Diffstat (limited to 'src/net')
5 files changed, 194 insertions, 56 deletions
diff --git a/src/net/java/sip/communicator/impl/googlecontacts/GoogleContactsActivator.java b/src/net/java/sip/communicator/impl/googlecontacts/GoogleContactsActivator.java index 7c33c7d..75c6f14 100644 --- a/src/net/java/sip/communicator/impl/googlecontacts/GoogleContactsActivator.java +++ b/src/net/java/sip/communicator/impl/googlecontacts/GoogleContactsActivator.java @@ -13,6 +13,7 @@ import net.java.sip.communicator.service.credentialsstorage.*; import net.java.sip.communicator.service.googlecontacts.*; import net.java.sip.communicator.service.gui.*; import net.java.sip.communicator.service.protocol.*; +import net.java.sip.communicator.service.resources.*; import net.java.sip.communicator.util.*; import org.jitsi.service.configuration.*; @@ -37,7 +38,7 @@ public class GoogleContactsActivator implements BundleActivator * The OSGi <tt>ServiceRegistration</tt> of * <tt>GoogleContactsServiceImpl</tt>. */ - private ServiceRegistration serviceRegistration = null; + private ServiceRegistration<?> serviceRegistration = null; /** * BundleContext from the OSGI bus. @@ -67,8 +68,8 @@ public class GoogleContactsActivator implements BundleActivator /** * List of contact source service registrations. */ - private static Map<GoogleContactsSourceService, ServiceRegistration> cssList - = new HashMap<GoogleContactsSourceService, ServiceRegistration>(); + private static Map<GoogleContactsSourceService, ServiceRegistration<?>> cssList + = new HashMap<GoogleContactsSourceService, ServiceRegistration<?>>(); /** * The registered PhoneNumberI18nService. @@ -86,12 +87,9 @@ public class GoogleContactsActivator implements BundleActivator { if(configService == null) { - ServiceReference confReference - = bundleContext.getServiceReference( - ConfigurationService.class.getName()); - configService - = (ConfigurationService) bundleContext.getService( - confReference); + configService = + ServiceUtils.getService(bundleContext, + ConfigurationService.class); } return configService; } @@ -120,12 +118,9 @@ public class GoogleContactsActivator implements BundleActivator { if(credentialsService == null) { - ServiceReference confReference - = bundleContext.getServiceReference( - CredentialsStorageService.class.getName()); - credentialsService - = (CredentialsStorageService) bundleContext.getService( - confReference); + credentialsService = + ServiceUtils.getService(bundleContext, + CredentialsStorageService.class); } return credentialsService; } @@ -142,12 +137,8 @@ public class GoogleContactsActivator implements BundleActivator { if(resourceService == null) { - ServiceReference confReference - = bundleContext.getServiceReference( - ResourceManagementService.class.getName()); - resourceService - = (ResourceManagementService) bundleContext.getService( - confReference); + resourceService = + ResourceManagementServiceUtils.getService(bundleContext); } return resourceService; } @@ -207,7 +198,7 @@ public class GoogleContactsActivator implements BundleActivator */ private void serviceChanged(ServiceEvent event) { - ServiceReference serviceRef = event.getServiceReference(); + ServiceReference<?> serviceRef = event.getServiceReference(); // if the event is caused by a bundle being stopped, we don't want to // know @@ -303,7 +294,7 @@ public class GoogleContactsActivator implements BundleActivator } /* remove contact source services */ - for(Map.Entry<GoogleContactsSourceService, ServiceRegistration> entry : + for(Map.Entry<GoogleContactsSourceService, ServiceRegistration<?>> entry : cssList.entrySet()) { if (entry.getValue() != null) @@ -338,7 +329,7 @@ public class GoogleContactsActivator implements BundleActivator { GoogleContactsSourceService css = new GoogleContactsSourceService( login, password); - ServiceRegistration cssServiceRegistration = null; + ServiceRegistration<?> cssServiceRegistration = null; css.setGoogleTalk(googleTalk); @@ -380,7 +371,7 @@ public class GoogleContactsActivator implements BundleActivator boolean googleTalk) { GoogleContactsSourceService css = new GoogleContactsSourceService(cnx); - ServiceRegistration cssServiceRegistration = null; + ServiceRegistration<?> cssServiceRegistration = null; css.setGoogleTalk(googleTalk); @@ -418,7 +409,7 @@ public class GoogleContactsActivator implements BundleActivator { GoogleContactsSourceService found = null; - for(Map.Entry<GoogleContactsSourceService, ServiceRegistration> entry : + for(Map.Entry<GoogleContactsSourceService, ServiceRegistration<?>> entry : cssList.entrySet()) { String cssName = entry.getKey().getLogin(); @@ -459,7 +450,7 @@ public class GoogleContactsActivator implements BundleActivator return; } - for(Map.Entry<GoogleContactsSourceService, ServiceRegistration> entry : + for(Map.Entry<GoogleContactsSourceService, ServiceRegistration<?>> entry : cssList.entrySet()) { String cssName = entry.getKey().getLogin(); diff --git a/src/net/java/sip/communicator/impl/googlecontacts/GoogleContactsConnectionImpl.java b/src/net/java/sip/communicator/impl/googlecontacts/GoogleContactsConnectionImpl.java index 503b413..52d03da 100644 --- a/src/net/java/sip/communicator/impl/googlecontacts/GoogleContactsConnectionImpl.java +++ b/src/net/java/sip/communicator/impl/googlecontacts/GoogleContactsConnectionImpl.java @@ -135,7 +135,7 @@ public class GoogleContactsConnectionImpl { try { - googleService.setOAuth2Credentials(this.store.get()); + googleService.setOAuth2Credentials(this.store.get(this.login)); return ConnectionStatus.SUCCESS; } catch (FailedAcquireCredentialException e) diff --git a/src/net/java/sip/communicator/impl/googlecontacts/GoogleContactsServiceImpl.java b/src/net/java/sip/communicator/impl/googlecontacts/GoogleContactsServiceImpl.java index 206f4aa..adcd198 100644 --- a/src/net/java/sip/communicator/impl/googlecontacts/GoogleContactsServiceImpl.java +++ b/src/net/java/sip/communicator/impl/googlecontacts/GoogleContactsServiceImpl.java @@ -68,7 +68,7 @@ public class GoogleContactsServiceImpl /** * Path where to store the account settings */ - private final static String CONFIGURATION_PATH = + final static String CONFIGURATION_PATH = "net.java.sip.communicator.impl.googlecontacts"; /** diff --git a/src/net/java/sip/communicator/impl/googlecontacts/OAuth2TokenStore.java b/src/net/java/sip/communicator/impl/googlecontacts/OAuth2TokenStore.java index cd04b39..3dfaaea 100644 --- a/src/net/java/sip/communicator/impl/googlecontacts/OAuth2TokenStore.java +++ b/src/net/java/sip/communicator/impl/googlecontacts/OAuth2TokenStore.java @@ -19,16 +19,27 @@ import java.awt.*; import java.awt.event.*; import java.io.*; import java.net.*; +import java.util.*; import java.util.concurrent.atomic.*; import javax.swing.*; import net.java.sip.communicator.plugin.desktoputil.*; +import net.java.sip.communicator.service.credentialsstorage.*; import net.java.sip.communicator.util.*; +import org.apache.http.HttpResponse; +import org.apache.http.client.*; +import org.apache.http.client.entity.*; +import org.apache.http.client.methods.*; +import org.apache.http.impl.client.*; +import org.apache.http.message.*; + import com.google.api.client.auth.oauth2.*; import com.google.api.client.http.*; +import com.google.api.client.http.HttpRequest; import com.google.api.client.http.javanet.*; +import com.google.api.client.json.*; import com.google.api.client.json.jackson2.*; /** @@ -42,7 +53,37 @@ public class OAuth2TokenStore /** * Logger. */ - private static final Logger LOGGER = Logger.getLogger(OAuth2TokenStore.class); + private static final Logger LOGGER = Logger + .getLogger(OAuth2TokenStore.class); + + /** + * Symbol for refresh token in token server response. + */ + private static final String REFRESH_TOKEN_SYMBOL = "refresh_token"; + + /** + * Symbol for access token in token server response. + */ + private static final String ACCESS_TOKEN_SYMBOL = "access_token"; + + /** + * Symbol for expiration time in token server response. + */ + private static final String EXPIRES_IN_SYMBOL = "expires_in"; + + /** + * Interesting token server response fields. + */ + private static final Set<String> TOKEN_RESPONSE_FIELDS; + + static + { + final HashSet<String> set = new HashSet<String>(); + set.add(REFRESH_TOKEN_SYMBOL); + set.add(ACCESS_TOKEN_SYMBOL); + set.add(EXPIRES_IN_SYMBOL); + TOKEN_RESPONSE_FIELDS = Collections.unmodifiableSet(set); + } /** * Google OAuth 2 token server. @@ -73,13 +114,17 @@ public class OAuth2TokenStore "urn:ietf:wg:oauth:2.0:oob"; /** + * Grant type for communication with token server. + */ + private static final String GOOGLE_API_GRANT_TYPE = "authorization_code"; + + /** * Approval URL. */ private static final String APPROVAL_URL = String.format( - "https://accounts.google.com/o/oauth2/auth?scope=%s&redirect_uri=%s&response_type=code&client_id=%s", - GOOGLE_API_OAUTH2_SCOPES, - GOOGLE_API_OAUTH2_REDIRECT_URI, - GOOGLE_API_CLIENT_ID); + "https://accounts.google.com/o/oauth2/auth?scope=%s&redirect_uri=%s" + + "&response_type=code&client_id=%s", GOOGLE_API_OAUTH2_SCOPES, + GOOGLE_API_OAUTH2_REDIRECT_URI, GOOGLE_API_CLIENT_ID); /** * The credential store. @@ -96,17 +141,19 @@ public class OAuth2TokenStore * exist, acquire one preferrably from the password store. Optionally, * involve the user if a credential is not yet stored. * + * @param identity The identity of the API token. * @return Returns the credential. * @throws FailedAcquireCredentialException * @throws MalformedURLException In case requesting authn token failed. */ - public Credential get() throws FailedAcquireCredentialException + public Credential get(final String identity) + throws FailedAcquireCredentialException { if (this.store.get() == null) { try { - acquireCredential(this.store); + acquireCredential(this.store, identity); } catch (Exception e) { @@ -122,23 +169,67 @@ public class OAuth2TokenStore * * @param store credential store to update upon refreshing and other * operations + * @param identity the identity to which the refresh token belongs * @return Acquires and returns the credential instance. - * @throws MalformedURLException In case of bad redirect URL. * @throws URISyntaxException In case of bad redirect URI. + * @throws IOException + * @throws ClientProtocolException */ private static void acquireCredential( - final AtomicReference<Credential> store) - throws MalformedURLException, - URISyntaxException + final AtomicReference<Credential> store, final String identity) + throws URISyntaxException, ClientProtocolException, IOException { - LOGGER.info("No credentials available yet. Requesting user to " - + "approve access to Contacts API using URL: " + APPROVAL_URL); - final OAuthApprovalDialog dialog = new OAuthApprovalDialog(); - dialog.setVisible(true); - final String approvalCode = dialog.getApprovalCode(); - LOGGER.debug("Approval code from user: " + approvalCode); - final TokenData data = requestAuthenticationToken(approvalCode); - store.set(createCredential(store, data)); + final TokenData token; + String refreshToken = restoreRefreshToken(identity); + if (refreshToken == null) + { + LOGGER.info("No credentials available yet. Requesting user to " + + "approve access to Contacts API for identity " + identity + + " using URL: " + APPROVAL_URL); + final OAuthApprovalDialog dialog = + new OAuthApprovalDialog(identity); + dialog.setVisible(true); + final String approvalCode = dialog.getApprovalCode(); + LOGGER.debug("Approval code from user: " + approvalCode); + token = requestAuthenticationToken(approvalCode); + saveRefreshToken(token, identity); + } + else + { + token = new TokenData("TOKEN_NOT_AVAILABLE", refreshToken, 0); + } + store.set(createCredential(store, token)); + } + + /** + * Restore refresh token from encrypted credentials store. + * + * @param identity The identity corresponding to the refresh token. + * @return Returns the refresh token. + */ + private static String restoreRefreshToken(final String identity) + { + final CredentialsStorageService credentials = + GoogleContactsActivator.getCredentialsService(); + return credentials + .loadPassword(GoogleContactsServiceImpl.CONFIGURATION_PATH + "." + + identity); + } + + /** + * Save refresh token for provided identity. + * + * @param token The refresh token. + * @param identity The identity. + * @throws IOException An IOException in case of errors. + */ + private static void saveRefreshToken(final TokenData token, + final String identity) throws IOException + { + final CredentialsStorageService credentials = + GoogleContactsActivator.getCredentialsService(); + credentials.storePassword(GoogleContactsServiceImpl.CONFIGURATION_PATH + + "." + identity, token.refreshToken); } /** @@ -174,7 +265,8 @@ public class OAuth2TokenStore * @throws URISyntaxException In case of bad OAuth 2 redirect URI. */ private static Credential createCredential( - final AtomicReference<Credential> store, final TokenData data) throws URISyntaxException + final AtomicReference<Credential> store, final TokenData data) + throws URISyntaxException { final Credential.Builder builder = new Credential.Builder( @@ -198,7 +290,7 @@ public class OAuth2TokenStore content.put("client_id", GOOGLE_API_CLIENT_ID); content.put("client_secret", GOOGLE_API_CLIENT_SECRET); LOGGER.info("Inserting client authentication data into " - + " refresh token request."); + + "refresh token request."); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Request: " + content.toString()); @@ -249,11 +341,57 @@ public class OAuth2TokenStore * * @param approvalCode the approval code * @return Returns the acquired token data from OAuth 2 token server. + * @throws IOException + * @throws ClientProtocolException */ - private static TokenData requestAuthenticationToken(final String approvalCode) + private static TokenData requestAuthenticationToken( + final String approvalCode) throws ClientProtocolException, IOException { - // FIXME actually acquire credential - return new TokenData("", "", 3600L); + final HttpClient client = new DefaultHttpClient(); + final HttpPost post = new HttpPost(GOOGLE_OAUTH2_TOKEN_SERVER.toURI()); + final UrlEncodedFormEntity entity = + new UrlEncodedFormEntity(Arrays.asList(new BasicNameValuePair( + "code", approvalCode), new BasicNameValuePair("client_id", + GOOGLE_API_CLIENT_ID), new BasicNameValuePair("client_secret", + GOOGLE_API_CLIENT_SECRET), new BasicNameValuePair( + "redirect_uri", GOOGLE_API_OAUTH2_REDIRECT_URI), + new BasicNameValuePair("grant_type", GOOGLE_API_GRANT_TYPE))); + post.setEntity(entity); + final HttpResponse httpResponse = client.execute(post); + final JsonParser parser = + JacksonFactory.getDefaultInstance().createJsonParser( + httpResponse.getEntity().getContent()); + try + { + // Token response components initialized with defaults in case + // fields are missing in the token server response. + String accessToken = ""; + String refreshToken = ""; + long expiresIn = 3600; + // Parse token server response. + String found; + while (parser.nextToken() != JsonToken.END_OBJECT) + { + found = parser.skipToKey(TOKEN_RESPONSE_FIELDS); + if (REFRESH_TOKEN_SYMBOL.equals(found)) + { + refreshToken = parser.getText(); + } + else if (ACCESS_TOKEN_SYMBOL.equals(found)) + { + accessToken = parser.getText(); + } + else if (EXPIRES_IN_SYMBOL.equals(found)) + { + expiresIn = parser.getLongValue(); + } + } + return new TokenData(accessToken, refreshToken, expiresIn); + } + finally + { + parser.close(); + } } /** @@ -270,10 +408,13 @@ public class OAuth2TokenStore private final SIPCommTextField code = new SIPCommTextField(""); - public OAuthApprovalDialog() + public OAuthApprovalDialog(final String identity) { this.setModal(true); - this.label = new SIPCommLinkButton("Click here to approve."); + this.label = + new SIPCommLinkButton( + "Click here to approve. Make sure you log in as " + + identity); this.label.addActionListener(new ActionListener() { diff --git a/src/net/java/sip/communicator/impl/googlecontacts/googlecontacts.manifest.mf b/src/net/java/sip/communicator/impl/googlecontacts/googlecontacts.manifest.mf index 99337a8..4fd9387 100644 --- a/src/net/java/sip/communicator/impl/googlecontacts/googlecontacts.manifest.mf +++ b/src/net/java/sip/communicator/impl/googlecontacts/googlecontacts.manifest.mf @@ -25,4 +25,10 @@ Import-Package: org.osgi.framework, javax.swing.table, javax.swing.tree, javax.swing.text, - javax.net.ssl + javax.net.ssl, + org.apache.http, + org.apache.http.client, + org.apache.http.client.entity, + org.apache.http.client.methods, + org.apache.http.impl.client, + org.apache.http.message |