From 9c945143c2e2fe715f5a9a778843d5b2ccc0d4c7 Mon Sep 17 00:00:00 2001 From: "Evan W. Patton" Date: Sat, 9 Mar 2024 22:31:51 -0500 Subject: [PATCH] Add server-side config for enabling some extensions for iOS Change-Id: I796017c5a8cada0cec41548bf5533b62e8892357 --- .../com/google/appinventor/client/Ode.java | 5 +++ .../server/UserInfoServiceImpl.java | 1 + .../server/storage/ObjectifyStorageIo.java | 31 ++++++++++++++++++- .../appinventor/server/storage/StorageIo.java | 2 ++ .../server/storage/StoredData.java | 10 ++++++ .../appinventor/shared/rpc/user/Config.java | 9 ++++++ appinventor/blocklyeditor/src/replmgr.js | 11 +++++-- 7 files changed, 65 insertions(+), 4 deletions(-) diff --git a/appinventor/appengine/src/com/google/appinventor/client/Ode.java b/appinventor/appengine/src/com/google/appinventor/client/Ode.java index 75edd186d2b..9e8968de5f9 100644 --- a/appinventor/appengine/src/com/google/appinventor/client/Ode.java +++ b/appinventor/appengine/src/com/google/appinventor/client/Ode.java @@ -690,6 +690,7 @@ public void onUncaughtException(Throwable e) { config = result; user = result.getUser(); isReadOnly = user.isReadOnly(); + registerIosExtensions(config.getIosExtensions()); return resolve(null); }) .then0(this::handleGalleryId) @@ -2416,4 +2417,8 @@ private static native void doCloseProxy() /*-{ } }-*/; + private static native void registerIosExtensions(String extensionJson)/*-{ + $wnd.ALLOWED_IOS_EXTENSIONS = JSON.parse(extensionJson); + }-*/; + } diff --git a/appinventor/appengine/src/com/google/appinventor/server/UserInfoServiceImpl.java b/appinventor/appengine/src/com/google/appinventor/server/UserInfoServiceImpl.java index 72c2efdb51e..7d5467bca0d 100644 --- a/appinventor/appengine/src/com/google/appinventor/server/UserInfoServiceImpl.java +++ b/appinventor/appengine/src/com/google/appinventor/server/UserInfoServiceImpl.java @@ -82,6 +82,7 @@ public Config getSystemConfig(String sessionId) { config.setGalleryReadOnly(Flag.createFlag("gallery.readonly", false).get()); config.setGalleryLocation(Flag.createFlag("gallery.location", "").get()); config.setDeleteAccountAllowed(deleteAccountAllowed); + config.setIosExtensions(storageIo.getIosExtensionsConfig()); if (!Flag.createFlag("build2.server.host", "").get().isEmpty()) { config.setSecondBuildserver(true); diff --git a/appinventor/appengine/src/com/google/appinventor/server/storage/ObjectifyStorageIo.java b/appinventor/appengine/src/com/google/appinventor/server/storage/ObjectifyStorageIo.java index c01559193f4..d32d93a32c6 100644 --- a/appinventor/appengine/src/com/google/appinventor/server/storage/ObjectifyStorageIo.java +++ b/appinventor/appengine/src/com/google/appinventor/server/storage/ObjectifyStorageIo.java @@ -26,6 +26,7 @@ import com.google.appinventor.server.GalleryExtensionException; import com.google.appinventor.server.Server; import com.google.appinventor.server.flags.Flag; +import com.google.appinventor.server.storage.StoredData.AllowedIosExtensions; import com.google.appinventor.server.storage.StoredData.AllowedTutorialUrls; import com.google.appinventor.server.storage.StoredData.Backpack; import com.google.appinventor.server.storage.StoredData.CorruptionRecord; @@ -114,7 +115,7 @@ * @author sharon@google.com (Sharon Perl) * */ -public class ObjectifyStorageIo implements StorageIo { +public class ObjectifyStorageIo implements StorageIo { static final Flag requireTos = Flag.createFlag("require.tos", false); private static final Logger LOG = Logger.getLogger(ObjectifyStorageIo.class.getName()); @@ -124,6 +125,7 @@ public class ObjectifyStorageIo implements StorageIo { private static final long MOTD_ID = 1; private static final long ALLOWEDURL_ID = 1; private static final long SPLASHDATA_ID = 1; + private static final long ALLOWED_IOS_EXTENSIONS_ID = 1; // TODO(user): need a way to modify this. Also, what is really a good value? private static final int MAX_JOB_RETRIES = 10; @@ -204,6 +206,7 @@ private class Result { ObjectifyService.register(SplashData.class); ObjectifyService.register(Backpack.class); ObjectifyService.register(AllowedTutorialUrls.class); + ObjectifyService.register(AllowedIosExtensions.class); // Learn GCS Bucket from App Configuration or App Engine Default // gcsBucket is where project storage goes @@ -2778,6 +2781,32 @@ public void run(Objectify datastore) { } } + @Override + public String getIosExtensionsConfig() { + final Result result = new Result<>(); + try { + runJobWithRetries(new JobRetryHelper() { + @Override + public void run(Objectify datastore) { + AllowedIosExtensions iosSettingsData = datastore.find(AllowedIosExtensions.class, + ALLOWED_IOS_EXTENSIONS_ID); + if (iosSettingsData != null) { + result.t = iosSettingsData.allowedExtensions; + } else { + AllowedIosExtensions firstIosSettingsData = new AllowedIosExtensions(); + firstIosSettingsData.id = ALLOWED_IOS_EXTENSIONS_ID; + firstIosSettingsData.allowedExtensions = "[]"; + datastore.put(firstIosSettingsData); + result.t = firstIosSettingsData.allowedExtensions; + } + } + }, false); + } catch (ObjectifyException e) { + throw CrashReport.createAndLogError(LOG, null, null, e); + } + return result.t; + } + /* * Determine which GCS Bucket to use based on filename. In particular * APK files go in a bucket with a short TTL, because they are really diff --git a/appinventor/appengine/src/com/google/appinventor/server/storage/StorageIo.java b/appinventor/appengine/src/com/google/appinventor/server/storage/StorageIo.java index 0f9572306c0..48f4a4c0b3a 100644 --- a/appinventor/appengine/src/com/google/appinventor/server/storage/StorageIo.java +++ b/appinventor/appengine/src/com/google/appinventor/server/storage/StorageIo.java @@ -671,6 +671,8 @@ void storeFeedback(final String notes, final String foundIn, final String faultD */ boolean deleteAccount(String userId); + String getIosExtensionsConfig(); + } diff --git a/appinventor/appengine/src/com/google/appinventor/server/storage/StoredData.java b/appinventor/appengine/src/com/google/appinventor/server/storage/StoredData.java index e309de80f3b..30a17fd7fc1 100644 --- a/appinventor/appengine/src/com/google/appinventor/server/storage/StoredData.java +++ b/appinventor/appengine/src/com/google/appinventor/server/storage/StoredData.java @@ -318,6 +318,16 @@ static final class AllowedTutorialUrls { } + @Cached(expirationSeconds = 120) + @Unindexed + public static final class AllowedIosExtensions { + // Unique Id - for now we expect there to be only 1 AllowedIosExtensions object. + @Id Long id; + + // list of allowed extension packages as JSON + String allowedExtensions; + } + public static final class ProjectNotFoundException extends IOException { ProjectNotFoundException(String message) { super(message); diff --git a/appinventor/appengine/src/com/google/appinventor/shared/rpc/user/Config.java b/appinventor/appengine/src/com/google/appinventor/shared/rpc/user/Config.java index 79b0a947439..f0a6abe114d 100644 --- a/appinventor/appengine/src/com/google/appinventor/shared/rpc/user/Config.java +++ b/appinventor/appengine/src/com/google/appinventor/shared/rpc/user/Config.java @@ -45,6 +45,7 @@ public class Config implements IsSerializable, Serializable { private List tutorialUrlAllowed; private boolean serverExpired; private boolean deleteAccountAllowed; + private String iosExtensions; public Config() { } @@ -249,4 +250,12 @@ public void setDeleteAccountAllowed(boolean value) { deleteAccountAllowed = value; } + public String getIosExtensions() { + return iosExtensions; + } + + public void setIosExtensions(String value) { + iosExtensions = value; + } + } diff --git a/appinventor/blocklyeditor/src/replmgr.js b/appinventor/blocklyeditor/src/replmgr.js index 77b230aae14..e90c6c61b04 100644 --- a/appinventor/blocklyeditor/src/replmgr.js +++ b/appinventor/blocklyeditor/src/replmgr.js @@ -1483,7 +1483,7 @@ Blockly.ReplMgr.getFromRendezvous = function() { rs.versionurl = 'http://' + json.ipaddr + ':8001/_getversion'; rs.baseurl = 'http://' + json.ipaddr + ':8001/'; rs.android = !(new RegExp('^i(pad)?os$').test((json.os || 'Android').toLowerCase())); - if (!(rs.android) && Blockly.ReplMgr.hasExtensions()) { + if (!(rs.android) && Blockly.ReplMgr.hasDisallowedIosExtensions()) { rs.dialog.hide(); top.ReplState.state = Blockly.ReplMgr.rsState.IDLE; top.BlocklyPanel_indicateDisconnect(); @@ -1532,9 +1532,14 @@ Blockly.ReplMgr.getFromRendezvous = function() { xmlhttp.send(); }; -Blockly.ReplMgr.hasExtensions = function() { +Blockly.ReplMgr.hasDisallowedIosExtensions = function() { var extensions = top.AssetManager_getExtensions(); - return extensions.length > 0; + for (var i = 0; i < extensions.length; i++) { + if (top.ALLOWED_IOS_EXTENSIONS.indexOf(extensions[i]) == -1) { + return true; + } + } + return false; }; Blockly.ReplMgr.rendezvousDone = function() {