diff options
author | Danny van Heumen <danny@dannyvanheumen.nl> | 2015-06-07 23:07:00 +0200 |
---|---|---|
committer | Danny van Heumen <danny@dannyvanheumen.nl> | 2015-07-20 22:29:45 +0200 |
commit | dcbeba9b6a262ce0ab99b96e673a7ef1ec8b5acd (patch) | |
tree | 74f38c0477f8a9b6f1c35b5e3c634d464c9263c4 /src/net/java/sip/communicator/impl | |
parent | 7d311ba178defc059ad22ee9371051915346b365 (diff) | |
download | jitsi-dcbeba9b6a262ce0ab99b96e673a7ef1ec8b5acd.zip jitsi-dcbeba9b6a262ce0ab99b96e673a7ef1ec8b5acd.tar.gz jitsi-dcbeba9b6a262ce0ab99b96e673a7ef1ec8b5acd.tar.bz2 |
Working prototype OAuth 2 authentication for Google Contacts plugin.
Diffstat (limited to 'src/net/java/sip/communicator/impl')
3 files changed, 177 insertions, 17 deletions
diff --git a/src/net/java/sip/communicator/impl/googlecontacts/GoogleContactsConnectionImpl.java b/src/net/java/sip/communicator/impl/googlecontacts/GoogleContactsConnectionImpl.java index 2722a86..46849aa 100644 --- a/src/net/java/sip/communicator/impl/googlecontacts/GoogleContactsConnectionImpl.java +++ b/src/net/java/sip/communicator/impl/googlecontacts/GoogleContactsConnectionImpl.java @@ -6,17 +6,25 @@ */ package net.java.sip.communicator.impl.googlecontacts; +import java.io.*; +import java.util.concurrent.atomic.*; + import net.java.sip.communicator.service.googlecontacts.*; import net.java.sip.communicator.util.*; -import com.google.gdata.client.*; +import com.google.api.client.auth.oauth2.*; +import com.google.api.client.http.*; +import com.google.api.client.http.javanet.*; +import com.google.api.client.json.jackson2.*; import com.google.gdata.client.contacts.*; +import com.google.gdata.data.contacts.*; import com.google.gdata.util.*; /** * Google Contacts credentials to connect to the service. * * @author Sebastien Vincent + * @author Danny van Heumen */ public class GoogleContactsConnectionImpl implements GoogleContactsConnection @@ -28,6 +36,39 @@ public class GoogleContactsConnectionImpl Logger.getLogger(GoogleContactsConnectionImpl.class); /** + * Google OAuth 2 token server. + */ + private static final GenericUrl GOOGLE_OAUTH2_TOKEN_SERVER = + new GenericUrl("https://accounts.google.com/o/oauth2/token"); + + /** + * Client ID for OAuth 2 based authentication. + */ + private static final String GOOGLE_API_CLIENT_ID = null; + + /** + * Client secret for OAuth 2 based authentication. + */ + private static final String GOOGLE_API_CLIENT_SECRET = null; + + // FIXME Actually use scopes! + private static final String[] GOOGLE_API_OAUTH2_SCOPES = new String[] + { "profile", "email", "https://www.google.com/m8/feeds" }; + + // FIXME Actually use the redirect URL! + private static final String GOOGLE_API_OAUTH2_REDIRECT_URI = + "urn:ietf:wg:oauth:2.0:oob"; + + // FIXME (Danny) Temporary stored refresh token during development... + private static final String TEMP_REFRESH_TOKEN = + "1/mjFeVE86qVmm-O2B6SSLweW6hBcj4qxuYSb0fGvJvH0"; + + /** + * The credential store to pass around. + */ + private final AtomicReference<Credential> credential = new AtomicReference<Credential>(null); + + /** * Login. */ private String login = null; @@ -121,26 +162,127 @@ public class GoogleContactsConnectionImpl * * @return connection status */ - public ConnectionStatus connect() + public synchronized ConnectionStatus connect() + { + createCredential(GOOGLE_OAUTH2_TOKEN_SERVER); + googleService.setOAuth2Credentials(this.credential.get()); + return ConnectionStatus.SUCCESS; + } + + /** + * Query for contacts using provided ContactQuery. + * + * Executes query. In case of failure, refresh OAuth2 token and retry query. + * If query fails again, throws FailedContactQueryException. + * + * @param query the contact query + * @return Returns the contact feed with matching contacts. + * @throws IOException + * @throws ServiceException + * @throws FailedContactQueryException Throws in case of failed query. + * @throws FailedTokenRefreshException Throws in case refreshing OAuth2 + * token fails. + */ + public synchronized ContactFeed query(final ContactQuery query) + throws IOException, + ServiceException, + FailedContactQueryException, + FailedTokenRefreshException { try { - googleService.setUserCredentials(login, password); + return this.googleService.query(query, ContactFeed.class); } - catch(AuthenticationException e) + catch (Exception e) { - logger.info("Google contacts connection failure: " + e); - if(e instanceof GoogleService.InvalidCredentialsException) + // FIXME if possible narrow down the exceptions on which to + // refresh token + logger.info("Failed to execute query. Going to refresh token" + + " and try again.", e); + refreshToken(); + } + try + { + return this.googleService.query(query, ContactFeed.class); + } + catch (Exception e) + { + throw new FailedContactQueryException(e); + } + } + + /** + * Refresh OAuth2 authentication token. + * + * @throws IOException + */ + private void refreshToken() throws IOException, FailedTokenRefreshException + { + final Credential credential = this.credential.get(); + if (!credential.refreshToken()) + { + logger.warn("Refresh of OAuth2 authentication token failed."); + throw new FailedTokenRefreshException(); + } + } + + /** + * Create credential instance suitable for use in Google Contacts API. + * @param tokenServer the token server URL + * @return Returns a Credential instance. + */ + private void createCredential(final GenericUrl tokenServerURL) + { + final Credential.Builder builder = + new Credential.Builder( + BearerToken.authorizationHeaderAccessMethod()); + builder.setTokenServerUrl(tokenServerURL); + builder.setTransport(new NetHttpTransport()); + builder.setJsonFactory(new JacksonFactory()); + builder.setClientAuthentication(new HttpExecuteInterceptor() + { + + @Override + public void intercept(HttpRequest request) throws IOException { - return ConnectionStatus.ERROR_INVALID_CREDENTIALS; + final RefreshTokenRequest content = + (RefreshTokenRequest) ((UrlEncodedContent) request + .getContent()).getData(); + content.put("client_id", GOOGLE_API_CLIENT_ID); + content.put("client_secret", GOOGLE_API_CLIENT_SECRET); + logger.warn("Refresh token request: " + content.toString()); } - else + }); + builder.addRefreshListener(new CredentialRefreshListener() + { + final AtomicReference<Credential> store = + GoogleContactsConnectionImpl.this.credential; + + @Override + public void onTokenResponse(Credential credential, + TokenResponse tokenResponse) throws IOException { - return ConnectionStatus.ERROR_UNKNOWN; + logger.debug("Successful token refresh response: " + + tokenResponse.toPrettyString()); + store.set(credential); } - } - return ConnectionStatus.SUCCESS; + @Override + public void onTokenErrorResponse(Credential credential, + TokenErrorResponse tokenErrorResponse) throws IOException + { + logger.debug("Failed token refresh response: " + + tokenErrorResponse.toPrettyString()); + logger.error("Failed to refresh OAuth2 token: " + + tokenErrorResponse.getError() + ": " + + tokenErrorResponse.getErrorDescription()); + } + }); + final Credential credential = builder.build(); + credential.setAccessToken("ya29.iwG37LYEgB4FCwfPLq8vV6Q-CX1vQ5sJrb_2AGydhLAiUT4wmz4iW4FlVkZE57s1B6NgA3BJAspLIw"); + credential.setRefreshToken(TEMP_REFRESH_TOKEN); + credential.setExpiresInSeconds(3600L); + this.credential.set(credential); } /** @@ -182,4 +324,24 @@ public class GoogleContactsConnectionImpl { return prefix; } + + public static class FailedContactQueryException + extends Exception + { + + private FailedContactQueryException(Throwable cause) + { + super("Failed to query Google Contacts API.", cause); + } + } + + public static class FailedTokenRefreshException + extends Exception + { + + private FailedTokenRefreshException() + { + super("Failed to refresh OAuth2 token."); + } + } } diff --git a/src/net/java/sip/communicator/impl/googlecontacts/GoogleContactsServiceImpl.java b/src/net/java/sip/communicator/impl/googlecontacts/GoogleContactsServiceImpl.java index c81814a..206f4aa 100644 --- a/src/net/java/sip/communicator/impl/googlecontacts/GoogleContactsServiceImpl.java +++ b/src/net/java/sip/communicator/impl/googlecontacts/GoogleContactsServiceImpl.java @@ -269,14 +269,11 @@ public class GoogleContactsServiceImpl try { - contactFeed = cnxImpl.getGoogleService().query( - query, ContactFeed.class); + contactFeed = cnxImpl.query(query); } catch(Exception e) { - logger.info( - "Problem occurred during Google Contacts retrievment", - e); + logger.warn("Problem occurred during Google Contacts query", e); return ret; } 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 6fbb11f..99337a8 100644 --- a/src/net/java/sip/communicator/impl/googlecontacts/googlecontacts.manifest.mf +++ b/src/net/java/sip/communicator/impl/googlecontacts/googlecontacts.manifest.mf @@ -24,4 +24,5 @@ Import-Package: org.osgi.framework, javax.swing.event, javax.swing.table, javax.swing.tree, - javax.swing.text + javax.swing.text, + javax.net.ssl |