From 39b632650a619c162497ee81db417d40a260dc9a Mon Sep 17 00:00:00 2001 From: "K. Shankari" Date: Sat, 20 Feb 2016 10:42:26 -0800 Subject: [PATCH 1/3] Make the javascript calls to get private data also go through native code The original idea was that the javascript implementation of the code would just use the javascript HTTP implementations, and make a call to the native code to get the JWT. As we can saw, this does not refresh the token correctly if it is expired, which is why we went back to the old hardcoded method. But then if we want to use the old hardcoded method from javascript, we need to make calls back to native code. This change implements plugin interfaces on both ios and android that make the appropriate calls to the native code, and change the javascript to call it appropriately. This has not yet been tested, but let's get an initial version checked in. --- plugin.xml | 7 ++- src/android/CommunicationHelper.java | 50 ++++++++++++++++++++++ src/android/CommunicationHelperPlugin.java | 34 +++++++++++++++ src/ios/BEMCommunicationHelper.m | 11 +++++ src/ios/BEMCommunicationHelperPlugin.h | 7 +++ src/ios/BEMCommunicationHelperPlugin.m | 44 +++++++++++++++++++ www/servercomm.js | 32 ++------------ 7 files changed, 154 insertions(+), 31 deletions(-) create mode 100644 src/android/CommunicationHelperPlugin.java create mode 100644 src/ios/BEMCommunicationHelperPlugin.h create mode 100644 src/ios/BEMCommunicationHelperPlugin.m diff --git a/plugin.xml b/plugin.xml index 47b4505..f4507ce 100644 --- a/plugin.xml +++ b/plugin.xml @@ -30,22 +30,25 @@ - + + - + + + diff --git a/src/android/CommunicationHelper.java b/src/android/CommunicationHelper.java index 66597ce..65ca029 100644 --- a/src/android/CommunicationHelper.java +++ b/src/android/CommunicationHelper.java @@ -69,6 +69,56 @@ public static String readResults(Context ctxt, String cacheControlProperty) return rawHTML; } + /* + * The other methods here take in a fullURL and return a string, + * respectively. The plugin interface passes in a relative URL and returns + * a JSONObject. For now, let us have this be consistent with the other + * calls here, and glue things together in the plugin. + */ + public static String pushGetJSON(Context ctxt, String fullURL, + Object filledJsonObject) + throws IOException, JSONException { + + // Initialize the message + String result = ""; + HttpPost msg = new HttpPost(fullURL); + System.out.println("Posting data to " + msg.getURI()); + msg.setHeader("Content-Type", "application/json"); + + // Fill in the object + final String userName = UserProfile.getInstance(ctxt).getUserEmail(); + final String userToken = GoogleAccountManagerAuth.getServerToken(ctxt, userName); + filledJsonObject.put("user", userToken); + msg.setEntity(new StringEntity(toPush.toString())); + + // Perform the operation + AndroidHttpClient connection = AndroidHttpClient.newInstance(ctxt.getString(R.string.app_name)); + HttpResponse response = connection.execute(msg); + StatusLine statusLine = response.getStatusLine(); + Log.i(ctxt, TAG, "Got response "+response+" with status "+statusLine); + int statusCode = statusLine.getStatusCode(); + + if(statusCode == 200){ + BufferedReader in = new BufferedReader(new InputStreamReader(response.getEntity().getContent())); + StringBuilder builder = new StringBuilder(); + String currLine = null; + while ((currLine = in.readLine()) != null) { + builder.append(currLine+"\n"); + } + result = builder.toString(); + System.out.println("Result Summary JSON = "+ + result.substring(0, Math.min(200, result.length())) + " length "+result.length()); + Log.i(ctxt, TAG, "Result Summary JSON = "+ + result.substring(0, Math.min(200, result.length())) + " length "+result.length()); + in.close(); + } else { + Log.e(ctxt, R.class.toString(),"Failed to get JSON object"); + throw new IOException(); + } + connection.close(); + return result; + } + public static void pushJSON(Context ctxt, String fullURL, String userToken, String objectLabel, Object jsonObjectOrArray) throws IOException, JSONException { diff --git a/src/android/CommunicationHelperPlugin.java b/src/android/CommunicationHelperPlugin.java new file mode 100644 index 0000000..e06a136 --- /dev/null +++ b/src/android/CommunicationHelperPlugin.java @@ -0,0 +1,34 @@ +package edu.berkeley.eecs.emission.cordova.comm; + +import org.apache.cordova.*; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import android.content.Context; +import edu.berkeley.eecs.emission.cordova.comm.CommunicationHelper; + +public class CommunicationHelperPlugin extends CordovaPlugin { + @Override + public boolean execute(String action, JSONArray data, CallbackContext callbackContext) throws JSONException { + if (action.equals("pushGetJSON")) { + try { + Context ctxt = cordova.getActivity(); + String relativeURL = data.getString(0); + JSONObject filledMessage = data.getJSONObject(1); + + String commuteTrackerHost = ConnectionSettings.getConnectURL(cachedContext); + String fullURL = commuteTrackerHost + relativeURL; + + String resultString = CommunicationHelper.pushGetJSON(ctxt, fullURL, filledMessage); + callbackContext.success(new JSONObject(resultString)); + } catch (Exception e) { + callbackContext.error("While pushing/getting from server "+e.getMessage()); + } + return true; + } else { + return false; + } + } +} + diff --git a/src/ios/BEMCommunicationHelper.m b/src/ios/BEMCommunicationHelper.m index c3d0c73..84f397a 100644 --- a/src/ios/BEMCommunicationHelper.m +++ b/src/ios/BEMCommunicationHelper.m @@ -114,6 +114,17 @@ +(void)phone_to_server:(NSArray *)entriesToPush completionHandler:(void (^)(NSDa [executor execute]; } ++(void)pushGetJSON:(NSDictionary*)toSend toURL:(NSURL*)relativeURL completionHandler:(void (^)(NSData *data, NSURLResponse *response, NSError *error))completionHandler { + NSMutableDictionary *toPush = [NSMutableDictionary dictionaryWithDictionary:toSend]; + + NSURL* kBaseURL = [[ConnectionSettings sharedInstance] getConnectUrl]; + NSURL* absoluteURL = [NSURL URLWithString:relativeURL + relativeToURL:kBaseURL]; + + CommunicationHelper *executor = [[CommunicationHelper alloc] initPost:kUsercachePutURL data:toPush completionHandler:completionHandler]; + [executor execute]; +} + +(void)getData:(NSURL*)url completionHandler:(void (^)(NSData *data, NSURLResponse *response, NSError *error))completionHandler { NSURLSession *sharedSession = [NSURLSession sharedSession]; NSURLSessionDataTask *task = [sharedSession dataTaskWithURL:url completionHandler:completionHandler]; diff --git a/src/ios/BEMCommunicationHelperPlugin.h b/src/ios/BEMCommunicationHelperPlugin.h new file mode 100644 index 0000000..3351134 --- /dev/null +++ b/src/ios/BEMCommunicationHelperPlugin.h @@ -0,0 +1,7 @@ +#import + +@interface BEMCommunicationHelperPlugin: CDVPlugin + +- (void) pushGetJSON:(CDVInvokedUrlCommand*)command; + +@end diff --git a/src/ios/BEMCommunicationHelperPlugin.m b/src/ios/BEMCommunicationHelperPlugin.m new file mode 100644 index 0000000..11ad919 --- /dev/null +++ b/src/ios/BEMCommunicationHelperPlugin.m @@ -0,0 +1,44 @@ +#import "BEMCommunicationHelperPlugin.h" +#import "BEMCommunicationHelper.h" + +@implementation BEMCommunicationHelperPlugin + +- (void)getSettings:(CDVInvokedUrlCommand*)command +{ + NSString* callbackId = [command callbackId]; + + @try { + NSString* relativeURL = [[command arguments] objectAtIndex:0]; + NSDictionary* filledMessage = [[command arguments] objectAtIndex:1]; + + [BEMCommunicationHelper pushGetJSON:filledMessage toURL:relativeURL completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { + if (error != NULL) { + [self sendError:error]; + } + NSError *parseError; + NSDictionary *parsedResult = [NSJSONSerialization JSONObjectWithData:data + options:kNilOptions + error: &parseError]; + if (parseError != NULL) { + [self sendError:parseError]; + } + CDVPluginResult* result = [CDVPluginResult + resultWithStatus:CDVCommandStatus_OK + messageAsDictionary:parsedResult]; + [self.commandDelegate sendPluginResult:result callbackId:callbackId]; + }]; + } + @catch (NSException *exception) { + [self sendError:exception]; + } +} + +- (void) sendError:(NSError*) error { + NSString* msg = [NSString stringWithFormat: @"While getting settings, error %@", error]; + CDVPluginResult* result = [CDVPluginResult + resultWithStatus:CDVCommandStatus_ERROR + messageAsString:msg]; + [self.commandDelegate sendPluginResult:result callbackId:callbackId]; +} + +@end diff --git a/www/servercomm.js b/www/servercomm.js index ad9df33..44620cb 100644 --- a/www/servercomm.js +++ b/www/servercomm.js @@ -21,35 +21,9 @@ var ServerCommunication = { * libraries from our javascript framework. */ pushGetJSON: function(relativeURL, messageFiller, successCallback, errorCallback) { - var request = new XMLHttpRequest(); - - request.onreadystatechange = function() { - if(request.readyState == request.DONE) { - if (request.status == 200) { - var resultObj = JSON.parse(request.responseText); - successCallback(resultObj); - } else { - errorCallback(request.statusText); - } - } else { - console.log("during HTTP post, state "+request.readyState); - } - }; - window.cordova.plugins.BEMConnectionSettings.getSettings(function(settings) { - var fullURL = settings.connectURL + relativeURL; - window.cordova.plugins.BEMJWTAuth.getJWT(function(token) { - message = {}; - message.user = token; - messageFiller(message); - request.open("POST", fullURL, true); - request.setRequestHeader("Content-Type", "application/json"); - request.send(JSON.stringify(message)); - }, function(error) { - errorCallback(error); - }) - }, function(error) { - errorCallback(error); - }); + filledMessage = {}; + messageFiller(filledMessage); + exec(successCallback, errorCallback, "ServerComm", "pushGetJSON", [relativeURL, filledMessage]); }, pushJSON: function(relativeUrl, objectLabel, objectJSON, successCallback, errorCallback) { var msgFiller = function(message) { From 10e907a65f97a6c48f0a599aa668b802e5846c0c Mon Sep 17 00:00:00 2001 From: "K. Shankari" Date: Sat, 20 Feb 2016 11:47:40 -0800 Subject: [PATCH 2/3] Minor compilation fixes after copying things over The iOS code still doesn't actually work. So there's at least one more checkin coming in. --- src/android/CommunicationHelper.java | 4 ++-- src/android/CommunicationHelperPlugin.java | 3 ++- src/ios/BEMCommunicationHelper.h | 1 + src/ios/BEMCommunicationHelper.m | 4 ++-- src/ios/BEMCommunicationHelperPlugin.m | 16 ++++++++-------- 5 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/android/CommunicationHelper.java b/src/android/CommunicationHelper.java index 65ca029..029ca79 100644 --- a/src/android/CommunicationHelper.java +++ b/src/android/CommunicationHelper.java @@ -76,7 +76,7 @@ public static String readResults(Context ctxt, String cacheControlProperty) * calls here, and glue things together in the plugin. */ public static String pushGetJSON(Context ctxt, String fullURL, - Object filledJsonObject) + JSONObject filledJsonObject) throws IOException, JSONException { // Initialize the message @@ -89,7 +89,7 @@ public static String pushGetJSON(Context ctxt, String fullURL, final String userName = UserProfile.getInstance(ctxt).getUserEmail(); final String userToken = GoogleAccountManagerAuth.getServerToken(ctxt, userName); filledJsonObject.put("user", userToken); - msg.setEntity(new StringEntity(toPush.toString())); + msg.setEntity(new StringEntity(filledJsonObject.toString())); // Perform the operation AndroidHttpClient connection = AndroidHttpClient.newInstance(ctxt.getString(R.string.app_name)); diff --git a/src/android/CommunicationHelperPlugin.java b/src/android/CommunicationHelperPlugin.java index e06a136..8496da6 100644 --- a/src/android/CommunicationHelperPlugin.java +++ b/src/android/CommunicationHelperPlugin.java @@ -7,6 +7,7 @@ import android.content.Context; import edu.berkeley.eecs.emission.cordova.comm.CommunicationHelper; +import edu.berkeley.eecs.emission.cordova.connectionsettings.ConnectionSettings; public class CommunicationHelperPlugin extends CordovaPlugin { @Override @@ -17,7 +18,7 @@ public boolean execute(String action, JSONArray data, CallbackContext callbackCo String relativeURL = data.getString(0); JSONObject filledMessage = data.getJSONObject(1); - String commuteTrackerHost = ConnectionSettings.getConnectURL(cachedContext); + String commuteTrackerHost = ConnectionSettings.getConnectURL(ctxt); String fullURL = commuteTrackerHost + relativeURL; String resultString = CommunicationHelper.pushGetJSON(ctxt, fullURL, filledMessage); diff --git a/src/ios/BEMCommunicationHelper.h b/src/ios/BEMCommunicationHelper.h index af0eb68..193eba6 100644 --- a/src/ios/BEMCommunicationHelper.h +++ b/src/ios/BEMCommunicationHelper.h @@ -22,6 +22,7 @@ +(void)phone_to_server:(NSArray*) entriesToPush completionHandler:(void (^)(NSData *data, NSURLResponse *response, NSError *error))completionHandler;; // Generic GET and POST methods ++(void)pushGetJSON:(NSDictionary*)toSend toURL:(NSString*)relativeURL completionHandler:(void (^)(NSData *data, NSURLResponse *response, NSError *error))completionHandler; +(void)getData:(NSURL *)url completionHandler:(void (^)(NSData *data, NSURLResponse *response, NSError *error))completionHandler; -(id)initPost:(NSURL *)url data:(NSMutableDictionary*)jsonDict completionHandler:(void (^)(NSData *data, NSURLResponse *response, NSError *error))completionHandler; diff --git a/src/ios/BEMCommunicationHelper.m b/src/ios/BEMCommunicationHelper.m index 84f397a..0e4679a 100644 --- a/src/ios/BEMCommunicationHelper.m +++ b/src/ios/BEMCommunicationHelper.m @@ -114,14 +114,14 @@ +(void)phone_to_server:(NSArray *)entriesToPush completionHandler:(void (^)(NSDa [executor execute]; } -+(void)pushGetJSON:(NSDictionary*)toSend toURL:(NSURL*)relativeURL completionHandler:(void (^)(NSData *data, NSURLResponse *response, NSError *error))completionHandler { ++(void)pushGetJSON:(NSDictionary*)toSend toURL:(NSString*)relativeURL completionHandler:(void (^)(NSData *data, NSURLResponse *response, NSError *error))completionHandler { NSMutableDictionary *toPush = [NSMutableDictionary dictionaryWithDictionary:toSend]; NSURL* kBaseURL = [[ConnectionSettings sharedInstance] getConnectUrl]; NSURL* absoluteURL = [NSURL URLWithString:relativeURL relativeToURL:kBaseURL]; - CommunicationHelper *executor = [[CommunicationHelper alloc] initPost:kUsercachePutURL data:toPush completionHandler:completionHandler]; + CommunicationHelper *executor = [[CommunicationHelper alloc] initPost:absoluteURL data:toPush completionHandler:completionHandler]; [executor execute]; } diff --git a/src/ios/BEMCommunicationHelperPlugin.m b/src/ios/BEMCommunicationHelperPlugin.m index 11ad919..4089e03 100644 --- a/src/ios/BEMCommunicationHelperPlugin.m +++ b/src/ios/BEMCommunicationHelperPlugin.m @@ -3,7 +3,7 @@ @implementation BEMCommunicationHelperPlugin -- (void)getSettings:(CDVInvokedUrlCommand*)command +- (void)pushGetJSON:(CDVInvokedUrlCommand *)command { NSString* callbackId = [command callbackId]; @@ -11,16 +11,16 @@ - (void)getSettings:(CDVInvokedUrlCommand*)command NSString* relativeURL = [[command arguments] objectAtIndex:0]; NSDictionary* filledMessage = [[command arguments] objectAtIndex:1]; - [BEMCommunicationHelper pushGetJSON:filledMessage toURL:relativeURL completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { + [CommunicationHelper pushGetJSON:filledMessage toURL:relativeURL completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { if (error != NULL) { - [self sendError:error]; + [self sendError:error callBackID:callbackId]; } NSError *parseError; NSDictionary *parsedResult = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error: &parseError]; if (parseError != NULL) { - [self sendError:parseError]; + [self sendError:parseError callBackID:callbackId]; } CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK @@ -29,16 +29,16 @@ - (void)getSettings:(CDVInvokedUrlCommand*)command }]; } @catch (NSException *exception) { - [self sendError:exception]; + [self sendError:exception callBackID:callbackId]; } } -- (void) sendError:(NSError*) error { - NSString* msg = [NSString stringWithFormat: @"While getting settings, error %@", error]; +- (void) sendError:(id) error callBackID:(NSString*)callbackID { + NSString* msg = [NSString stringWithFormat: @"During server call, error %@", error]; CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:msg]; - [self.commandDelegate sendPluginResult:result callbackId:callbackId]; + [self.commandDelegate sendPluginResult:result callbackId:callbackID]; } @end From 6a8ac0ca915168867de6d50b191911cadfa896b4 Mon Sep 17 00:00:00 2001 From: "K. Shankari" Date: Sat, 20 Feb 2016 12:19:35 -0800 Subject: [PATCH 3/3] Fix the issue with the authentication not working on startup Basically, if the currAuth is NULL, then we read it from the keychain. But with the previous structure, after reading it, we just exit since everything else was in a giant else. Now, once we read the currAuth, we use it in the rest of the code. Wow. Simple restructuring. Much working. --- src/ios/BEMCommunicationHelper.m | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/ios/BEMCommunicationHelper.m b/src/ios/BEMCommunicationHelper.m index 0e4679a..0214162 100644 --- a/src/ios/BEMCommunicationHelper.m +++ b/src/ios/BEMCommunicationHelper.m @@ -160,7 +160,10 @@ -(void)execute { GTMOAuth2Authentication* currAuth = [AuthCompletionHandler sharedInstance].currAuth; if (currAuth == NULL) { [self tryToAuthenticate:jsonData]; - } else { + currAuth = [AuthCompletionHandler sharedInstance].currAuth; + } + + if (currAuth != NULL) { BOOL expired = ([currAuth.expirationDate compare:[NSDate date]] == NSOrderedAscending); // The access token may not have expired, but the id token may not be available because the app has been restarted, // so it is not in memory, and the ID token is not stored in the keychain. It is a real pain to store the ID token @@ -226,7 +229,7 @@ -(void)execute { } } -- (void)tryToAuthenticate:(NSData*)jsonData { +- (BOOL)tryToAuthenticate:(NSData*)jsonData { [LocalNotificationManager addNotification:[NSString stringWithFormat: @"tryToAuthenticate called"] showUI:FALSE]; [[AuthCompletionHandler sharedInstance] registerFinishDelegate:self]; @@ -245,9 +248,8 @@ - (void)tryToAuthenticate:(NSData*)jsonData { self.mCompletionHandler(jsonData, NULL, authError); } else { [LocalNotificationManager addNotification:[NSString stringWithFormat: - @"callback should be called, we will deal with it there"] + @"ready to authenticate, checking expiration"] showUI:FALSE]; - NSLog(@"callback should be called, we will deal with it there"); // So far, callback has not taken a long time... // But callback may take a long time. In that case, we may want to return early. // Also, callback will invoke mCompletionHandler in a separate thread, which won't @@ -257,6 +259,7 @@ - (void)tryToAuthenticate:(NSData*)jsonData { // So we will be called again, and won't have to invoke this call then // mCompletionHandler(NULL, NULL, NULL); } + return silentAuthResult; } - (void)postToHost {