Skip to content

Commit

Permalink
Merge pull request #940 from kchitalia/master
Browse files Browse the repository at this point in the history
Fixes for org split
  • Loading branch information
kchitalia committed Aug 24, 2015
2 parents f823c15 + 0be6959 commit 72e16d3
Show file tree
Hide file tree
Showing 5 changed files with 142 additions and 37 deletions.
Expand Up @@ -211,7 +211,7 @@ public Bundle getAuthToken(
resBundle.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
resBundle.putString(AccountManager.KEY_AUTHTOKEN, tr.authToken);
resBundle.putString(AuthenticatorService.KEY_LOGIN_URL, SalesforceSDKManager.encryptWithPasscode(loginServer, passcodeHash));
resBundle.putString(AuthenticatorService.KEY_INSTANCE_URL, SalesforceSDKManager.encryptWithPasscode(instServer, passcodeHash));
resBundle.putString(AuthenticatorService.KEY_INSTANCE_URL, SalesforceSDKManager.encryptWithPasscode(tr.instanceUrl, passcodeHash));
resBundle.putString(AuthenticatorService.KEY_CLIENT_ID, SalesforceSDKManager.encryptWithPasscode(clientId, passcodeHash));
resBundle.putString(AuthenticatorService.KEY_USERNAME, SalesforceSDKManager.encryptWithPasscode(username, passcodeHash));
resBundle.putString(AuthenticatorService.KEY_USER_ID, SalesforceSDKManager.encryptWithPasscode(userId, passcodeHash));
Expand Down
Expand Up @@ -441,7 +441,7 @@ private RestClient getRestClient(UserAccount account) {
if (cm != null) {
try {
final AccMgrAuthTokenProvider authTokenProvider = new AccMgrAuthTokenProvider(cm,
account.getAuthToken(), account.getRefreshToken());
account.getInstanceServer(), account.getAuthToken(), account.getRefreshToken());
final ClientInfo clientInfo = new ClientInfo(account.getClientId(),
new URI(account.getInstanceServer()), new URI(account.getLoginServer()),
new URI(account.getIdUrl()), account.getAccountName(), account.getUsername(),
Expand Down
Expand Up @@ -59,6 +59,7 @@ public class ClientManager {

public static final String ACCESS_TOKEN_REVOKE_INTENT = "access_token_revoked";
public static final String ACCESS_TOKEN_REFRESH_INTENT = "access_token_refeshed";
public static final String INSTANCE_URL_UPDATE_INTENT = "instance_url_updated";

private final AccountManager accountManager;
private final String accountType;
Expand Down Expand Up @@ -205,7 +206,7 @@ public RestClient peekRestClient(Account acc) {
throw new AccountInfoNotFoundException(AuthenticatorService.KEY_ORG_ID);

try {
AccMgrAuthTokenProvider authTokenProvider = new AccMgrAuthTokenProvider(this, authToken, refreshToken);
AccMgrAuthTokenProvider authTokenProvider = new AccMgrAuthTokenProvider(this, instanceServer, authToken, refreshToken);
ClientInfo clientInfo = new ClientInfo(clientId, new URI(instanceServer),
new URI(loginServer), new URI(idUrl), accountName, username,
userId, orgId, communityId, communityUrl);
Expand Down Expand Up @@ -490,17 +491,19 @@ public static class AccMgrAuthTokenProvider implements RestClient.AuthTokenProvi
private final ClientManager clientManager;
private static String lastNewAuthToken;
private final String refreshToken;
private static String lastNewInstanceUrl;
private long lastRefreshTime = -1 /* never refreshed */;

/**
* Constructor
* @param clientManager
* @param refreshToken
*/
public AccMgrAuthTokenProvider(ClientManager clientManager, String authToken, String refreshToken) {
public AccMgrAuthTokenProvider(ClientManager clientManager, String instanceUrl, String authToken, String refreshToken) {
this.clientManager = clientManager;
this.refreshToken = refreshToken;
lastNewAuthToken = authToken;
lastNewInstanceUrl = instanceUrl;
}

/**
Expand Down Expand Up @@ -531,12 +534,16 @@ public String getNewAuthToken() {
// Invalidate current auth token
clientManager.invalidateToken(lastNewAuthToken);
String newAuthToken = null;
String newInstanceUrl = null;

try {
final Bundle bundle = clientManager.accountManager.getAuthToken(acc, AccountManager.KEY_AUTHTOKEN, null, false, null, null).getResult();
if (bundle == null) {
Log.w("AccMgrAuthTokenProvider:fetchNewAuthToken", "accountManager.getAuthToken returned null bundle");
} else {
newAuthToken = bundle.getString(AccountManager.KEY_AUTHTOKEN);
newInstanceUrl = bundle.getString(AuthenticatorService.KEY_INSTANCE_URL);
Intent broadcastIntent;
if (newAuthToken == null) {
if (clientManager.revokedTokenShouldLogout) {

Expand All @@ -548,16 +555,18 @@ public String getNewAuthToken() {
}

// Broadcasts an intent that the access token has been revoked.
final Intent revokeIntent = new Intent(ACCESS_TOKEN_REVOKE_INTENT);
revokeIntent.setPackage(SalesforceSDKManager.getInstance().getAppContext().getPackageName());
SalesforceSDKManager.getInstance().getAppContext().sendBroadcast(revokeIntent);
broadcastIntent = new Intent(ACCESS_TOKEN_REVOKE_INTENT);
} else if (newInstanceUrl != null && !newInstanceUrl.equalsIgnoreCase(lastNewInstanceUrl)) {
// Broadcasts an intent that the instance server has changed (implicitly token refreshed too)
broadcastIntent = new Intent(INSTANCE_URL_UPDATE_INTENT);
} else {

// Broadcasts an intent that the access token has been refreshed.
final Intent refreshIntent = new Intent(ACCESS_TOKEN_REFRESH_INTENT);
refreshIntent.setPackage(SalesforceSDKManager.getInstance().getAppContext().getPackageName());
SalesforceSDKManager.getInstance().getAppContext().sendBroadcast(refreshIntent);
broadcastIntent = new Intent(ACCESS_TOKEN_REFRESH_INTENT);
}

broadcastIntent.setPackage(SalesforceSDKManager.getInstance().getAppContext().getPackageName());
SalesforceSDKManager.getInstance().getAppContext().sendBroadcast(broadcastIntent);
}
} catch (Exception e) {
Log.w("AccMgrAuthTokenProvider:fetchNewAuthToken:getNewAuthToken",
Expand All @@ -566,6 +575,7 @@ public String getNewAuthToken() {
synchronized (lock) {
gettingAuthToken = false;
lastNewAuthToken = newAuthToken;
lastNewInstanceUrl = newInstanceUrl;
lastRefreshTime = System.currentTimeMillis();
lock.notifyAll();
}
Expand All @@ -582,6 +592,9 @@ public String getRefreshToken() {
public long getLastRefreshTime() {
return lastRefreshTime;
}

@Override
public String getInstanceUrl() { return lastNewInstanceUrl; }
}

/**
Expand Down
107 changes: 80 additions & 27 deletions libs/SalesforceSDK/src/com/salesforce/androidsdk/rest/RestClient.java
Expand Up @@ -95,6 +95,7 @@ public class RestClient {
* RestClient will call its authTokenProvider to refresh its authToken once it has expired.
*/
public interface AuthTokenProvider {
String getInstanceUrl();
String getNewAuthToken();
String getRefreshToken();
long getLastRefreshTime();
Expand Down Expand Up @@ -122,7 +123,7 @@ public interface AsyncRequestCallback {
* @param authTokenProvider
*/
public RestClient(ClientInfo clientInfo, String authToken, HttpAccess httpAccessor, AuthTokenProvider authTokenProvider) {
this(clientInfo, new SalesforceHttpStack(authToken, httpAccessor, authTokenProvider));
this(clientInfo, new SalesforceHttpStack(clientInfo, authToken, httpAccessor, authTokenProvider));
}

public RestClient(ClientInfo clientInfo, SalesforceHttpStack httpStack) {
Expand Down Expand Up @@ -243,7 +244,7 @@ public RestResponse sendSync(RestMethod method, String path, HttpEntity httpEnti
* @throws IOException
*/
public RestResponse sendSync(RestMethod method, String path, HttpEntity httpEntity, Map<String, String> additionalHttpHeaders) throws IOException {
return new RestResponse(httpStack.performRequest(method.asVolleyMethod(), clientInfo.resolveUrl(path), httpEntity, additionalHttpHeaders, true));
return new RestResponse(httpStack.performRequest(method.asVolleyMethod(), path, httpEntity, additionalHttpHeaders, true));
}

/**
Expand Down Expand Up @@ -455,6 +456,7 @@ public static class SalesforceHttpStack implements HttpStack {
private final AuthTokenProvider authTokenProvider;
private HttpAccess httpAccessor;
private String authToken;
private ClientInfo clientInfo;

/**
* Constructs a SalesforceHttpStack with the given clientInfo, authToken, httpAccessor and authTokenProvider.
Expand All @@ -463,11 +465,13 @@ public static class SalesforceHttpStack implements HttpStack {
* <li> If authTokenProvider is not null, it will ask the authTokenProvider for a new access token and retry the request a second time.</li>
* <li> Otherwise it will return the 401 response.</li>
* </ul>
* @param authToken
* @param clientInfo
* @param authToken
* @param httpAccessor
* @param authTokenProvider
*/
public SalesforceHttpStack(String authToken, HttpAccess httpAccessor, AuthTokenProvider authTokenProvider) {
public SalesforceHttpStack(ClientInfo clientInfo, String authToken, HttpAccess httpAccessor, AuthTokenProvider authTokenProvider) {
this.clientInfo = clientInfo;
this.authToken = authToken;
this.httpAccessor = httpAccessor;
this.authTokenProvider = authTokenProvider;
Expand Down Expand Up @@ -499,6 +503,7 @@ public HttpResponse performRequest(Request<?> request, Map<String, String> addit
requestEntity = new ByteArrayEntity(request.getBody());
}
}

return performRequest(method, url, requestEntity, additionalHeaders, true);
}

Expand Down Expand Up @@ -565,12 +570,11 @@ public void setHttpAccessor(HttpAccess httpAccessor) {
* @param url
* @param httpEntity
* @param additionalHttpHeaders
* @param retryInvalidToken
* @return
* @throws IOException
*/
public HttpResponse performRequest(int method, URI url, HttpEntity httpEntity,
Map<String, String> additionalHttpHeaders, boolean retryInvalidToken) throws IOException {
public HttpResponse doRequest(int method, URI url, HttpEntity httpEntity,
Map<String, String> additionalHttpHeaders) throws IOException {
Execution exec = null;

// Prepare headers.
Expand All @@ -597,38 +601,87 @@ public HttpResponse performRequest(int method, URI url, HttpEntity httpEntity,
case Request.Method.PUT:
exec = httpAccessor.doPut(headers, url, httpEntity); break;
}
final HttpResponse response = exec.response;
int statusCode = response.getStatusLine().getStatusCode();

// 401 bad access token.
if (retryInvalidToken && statusCode == HttpStatus.SC_UNAUTHORIZED) {
final HttpEntity entity = response.getEntity();
if (entity != null && entity.isStreaming()) {
final InputStream instream = entity.getContent();
if (instream != null) {
instream.close();
}
}
refreshAccessToken();
return performRequest(method, url, httpEntity, additionalHttpHeaders, false);
}

// Done.
return response;
return exec.response;
}

/**
* Swaps the existing access token for a new one.
*/
private void refreshAccessToken() {
if (authTokenProvider != null) {
private void refreshAccessToken(HttpResponse response) throws IOException {
// If we haven't retried already and we have an accessTokenProvider
// Then let's try to get a new authToken
if (authTokenProvider != null) {
// remember to consume this response so the connection can get re-used
HttpEntity entity = response.getEntity();
if (entity != null && entity.isStreaming()) {
InputStream instream = entity.getContent();
if (instream != null) {
instream.close();
}
}
final String newAuthToken = authTokenProvider.getNewAuthToken();
if (newAuthToken != null) {
setAuthToken(newAuthToken);
}
// Check if the instanceUrl changed
String instanceUrl = authTokenProvider.getInstanceUrl();
if (!clientInfo.instanceUrl.toString().equalsIgnoreCase(instanceUrl)) {
try {
// Create a new ClientInfo
clientInfo = new ClientInfo(clientInfo.clientId, new URI(instanceUrl),
clientInfo.loginUrl, clientInfo.identityUrl,
clientInfo.accountName, clientInfo.username,
clientInfo.userId, clientInfo.orgId, clientInfo.communityId,
clientInfo.communityUrl);
} catch (URISyntaxException ex) {
Log.w("RestClient", "Invalid server URL", ex);
}
}
}
}

/**
* @param method
* @param url
* @param httpEntity
* @param additionalHttpHeaders
* @param retryInvalidToken
* @return
* @throws IOException
*/
public HttpResponse performRequest(int method, URI url, HttpEntity httpEntity, Map<String, String> additionalHttpHeaders, boolean retryInvalidToken) throws IOException {
HttpResponse response = doRequest(method, url, httpEntity, additionalHttpHeaders);
// 401 bad access token
if (retryInvalidToken && response.getStatusLine().getStatusCode() == HttpStatus.SC_UNAUTHORIZED) {
refreshAccessToken(response);
return performRequest(method, url, httpEntity, additionalHttpHeaders, false);
}
// Done
return response;
}

/**
* @param method
* @param path
* @param httpEntity
* @param additionalHttpHeaders
* @param retryInvalidToken
* @return
* @throws IOException
*/
public HttpResponse performRequest(int method, String path, HttpEntity httpEntity, Map<String, String> additionalHttpHeaders, boolean retryInvalidToken) throws IOException {
HttpResponse response = doRequest(method, clientInfo.resolveUrl(path), httpEntity, additionalHttpHeaders);
// 401 bad access token
if (retryInvalidToken && response.getStatusLine().getStatusCode() == HttpStatus.SC_UNAUTHORIZED) {
refreshAccessToken(response);
return performRequest(method, path, httpEntity, additionalHttpHeaders, false);
}

// Done
return response;
}

private RestResponse uploadFile(File theFile, String name, RestClient client,
String title, String description, boolean retryInvalidToken) {
HttpURLConnection conn = null;
Expand Down Expand Up @@ -719,7 +772,7 @@ private RestResponse uploadFile(File theFile, String name, RestClient client,
responseInputStream.close();
}
}
refreshAccessToken();
refreshAccessToken(response);
return uploadFile(theFile, name, client, title, description, false);
}
restResponse = new RestResponse(response);
Expand Down
Expand Up @@ -72,6 +72,7 @@ public class RestClientTest extends InstrumentationTestCase {
private HttpAccess httpAccess;
private RestClient restClient;
private String authToken;
private String instanceUrl;

@Override
public void setUp() throws Exception {
Expand All @@ -80,6 +81,7 @@ public void setUp() throws Exception {
httpAccess = new HttpAccess(null, null);
TokenEndpointResponse refreshResponse = OAuth2.refreshAuthToken(httpAccess, new URI(TestCredentials.INSTANCE_URL), TestCredentials.CLIENT_ID, TestCredentials.REFRESH_TOKEN);
authToken = refreshResponse.authToken;
instanceUrl = refreshResponse.instanceUrl;
clientInfo = new ClientInfo(TestCredentials.CLIENT_ID,
new URI(TestCredentials.INSTANCE_URL),
new URI(TestCredentials.LOGIN_URL),
Expand Down Expand Up @@ -189,6 +191,9 @@ public String getRefreshToken() {
public long getLastRefreshTime() {
return -1;
}

@Override
public String getInstanceUrl() { return instanceUrl; }
};
RestClient unauthenticatedRestClient = new RestClient(clientInfo, BAD_TOKEN, httpAccess, authTokenProvider);
assertEquals("RestClient should be using the bad token initially", BAD_TOKEN, unauthenticatedRestClient.getAuthToken());
Expand All @@ -198,6 +203,40 @@ public long getLastRefreshTime() {
checkResponse(response, HttpStatus.SC_OK, false);
}

/**
* Testing a call with a bad auth token when restClient has a token provider
* Expect token provider to be invoked and new token to be used and a new instance url to be returned.
* @throws URISyntaxException
* @throws IOException
*/
public void testCallWithBadInstanceUrl() throws URISyntaxException, IOException {
AuthTokenProvider authTokenProvider = new AuthTokenProvider() {
@Override
public String getNewAuthToken() {
return authToken;
}

@Override
public String getRefreshToken() {
return null;
}

@Override
public long getLastRefreshTime() {
return -1;
}

@Override
public String getInstanceUrl() { return instanceUrl; }
};
RestClient unauthenticatedRestClient = new RestClient(clientInfo, BAD_TOKEN, httpAccess, authTokenProvider);
assertEquals("RestClient has bad instance url", new URI(TestCredentials.INSTANCE_URL), unauthenticatedRestClient.getClientInfo().instanceUrl);
RestResponse response = unauthenticatedRestClient.sendSync(RestRequest.getRequestForResources(TestCredentials.API_VERSION));
assertEquals("RestClient should now have the correct instance url", new URI(instanceUrl), unauthenticatedRestClient.getClientInfo().instanceUrl);
assertTrue("Expected success", response.isSuccess());
checkResponse(response, HttpStatus.SC_OK, false);
}


/**
* Testing a get versions call to the server - check response
Expand Down

0 comments on commit 72e16d3

Please sign in to comment.