diff --git a/.prettierrc b/.prettierrc
new file mode 100644
index 000000000..0967ef424
--- /dev/null
+++ b/.prettierrc
@@ -0,0 +1 @@
+{}
diff --git a/package-lock.json b/package-lock.json
index a1f07947e..97f3075b1 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -58,6 +58,7 @@
"com.foxdebug.acode.rk.customtabs": "file:src/plugins/custom-tabs",
"com.foxdebug.acode.rk.exec.proot": "file:src/plugins/proot",
"com.foxdebug.acode.rk.exec.terminal": "file:src/plugins/terminal",
+ "com.foxdebug.acode.rk.plugin.plugincontext": "file:src/plugins/pluginContext",
"cordova-android": "^14.0.1",
"cordova-clipboard": "^1.3.0",
"cordova-plugin-advanced-http": "^3.3.1",
@@ -4010,6 +4011,10 @@
"resolved": "src/plugins/terminal",
"link": true
},
+ "node_modules/com.foxdebug.acode.rk.plugin.plugincontext": {
+ "resolved": "src/plugins/pluginContext",
+ "link": true
+ },
"node_modules/commander": {
"version": "2.20.3",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
@@ -8795,6 +8800,12 @@
"dev": true,
"license": "ISC"
},
+ "src/plugins/pluginContext": {
+ "name": "com.foxdebug.acode.rk.plugin.plugincontext",
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
"src/plugins/proot": {
"name": "com.foxdebug.acode.rk.exec.proot",
"version": "1.0.0",
diff --git a/package.json b/package.json
index 1475f227d..850aed7af 100644
--- a/package.json
+++ b/package.json
@@ -40,6 +40,7 @@
"com.foxdebug.acode.rk.exec.proot": {},
"com.foxdebug.acode.rk.exec.terminal": {},
"com.foxdebug.acode.rk.customtabs": {},
+ "com.foxdebug.acode.rk.plugin.plugincontext": {},
"com.foxdebug.acode.rk.auth": {}
},
"platforms": [
@@ -69,6 +70,7 @@
"com.foxdebug.acode.rk.customtabs": "file:src/plugins/custom-tabs",
"com.foxdebug.acode.rk.exec.proot": "file:src/plugins/proot",
"com.foxdebug.acode.rk.exec.terminal": "file:src/plugins/terminal",
+ "com.foxdebug.acode.rk.plugin.plugincontext": "file:src/plugins/pluginContext",
"cordova-android": "^14.0.1",
"cordova-clipboard": "^1.3.0",
"cordova-plugin-advanced-http": "^3.3.1",
diff --git a/src/lib/loadPlugin.js b/src/lib/loadPlugin.js
index 4078f29c9..bad8e81eb 100644
--- a/src/lib/loadPlugin.js
+++ b/src/lib/loadPlugin.js
@@ -56,6 +56,10 @@ export default async function loadPlugin(pluginId, justInstalled = false) {
cacheFileUrl: await helpers.toInternalUri(cacheFile),
cacheFile: fsOperation(cacheFile),
firstInit: justInstalled,
+ ctx: await PluginContext.generate(
+ pluginId,
+ JSON.stringify(pluginJson),
+ ),
});
resolve();
diff --git a/src/plugins/pluginContext/package.json b/src/plugins/pluginContext/package.json
new file mode 100644
index 000000000..301d27b99
--- /dev/null
+++ b/src/plugins/pluginContext/package.json
@@ -0,0 +1,17 @@
+{
+ "name": "com.foxdebug.acode.rk.plugin.plugincontext",
+ "version": "1.0.0",
+ "description": "PluginContext",
+ "cordova": {
+ "id": "com.foxdebug.acode.rk.plugin.plugincontext",
+ "platforms": [
+ "android"
+ ]
+ },
+ "keywords": [
+ "ecosystem:cordova",
+ "cordova-android"
+ ],
+ "author": "@RohitKushvaha01",
+ "license": "MIT"
+}
diff --git a/src/plugins/pluginContext/plugin.xml b/src/plugins/pluginContext/plugin.xml
new file mode 100644
index 000000000..2e7dd6ed5
--- /dev/null
+++ b/src/plugins/pluginContext/plugin.xml
@@ -0,0 +1,22 @@
+
+
+ PluginContext
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/plugins/pluginContext/src/android/Tee.java b/src/plugins/pluginContext/src/android/Tee.java
new file mode 100644
index 000000000..be6b75f4b
--- /dev/null
+++ b/src/plugins/pluginContext/src/android/Tee.java
@@ -0,0 +1,138 @@
+package com.foxdebug.acode.rk.plugin;
+
+import org.apache.cordova.CallbackContext;
+import org.apache.cordova.CordovaPlugin;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.UUID;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class Tee extends CordovaPlugin {
+
+ // pluginId : token
+ private /*static*/ final Map tokenStore = new ConcurrentHashMap<>();
+
+ //assigned tokens
+ private /*static*/ final Set disclosed = ConcurrentHashMap.newKeySet();
+
+ // token : list of permissions
+ private /*static*/ final Map> permissionStore = new ConcurrentHashMap<>();
+
+ @Override
+ public boolean execute(String action, JSONArray args, CallbackContext callback)
+ throws JSONException {
+
+ if ("requestToken".equals(action)) {
+ String pluginId = args.getString(0);
+ String pluginJson = args.getString(1);
+ handleTokenRequest(pluginId, pluginJson, callback);
+ return true;
+ }
+
+ if ("grantedPermission".equals(action)) {
+ String token = args.getString(0);
+ String permission = args.getString(1);
+
+ if (!permissionStore.containsKey(token)) {
+ callback.error("INVALID_TOKEN");
+ return true;
+ }
+
+ boolean granted = grantedPermission(token, permission);
+ callback.success(granted ? 1 : 0);
+ return true;
+ }
+
+ if ("listAllPermissions".equals(action)) {
+ String token = args.getString(0);
+
+ if (!permissionStore.containsKey(token)) {
+ callback.error("INVALID_TOKEN");
+ return true;
+ }
+
+ List permissions = listAllPermissions(token);
+ JSONArray result = new JSONArray(permissions);
+
+ callback.success(result);
+ return true;
+ }
+
+ return false;
+ }
+
+ //============================================================
+ //do not change function signatures
+ public boolean isTokenValid(String token, String pluginId) {
+ String storedToken = tokenStore.get(pluginId);
+ return storedToken != null && token.equals(storedToken);
+ }
+
+
+ public boolean grantedPermission(String token, String permission) {
+ List permissions = permissionStore.get(token);
+ return permissions != null && permissions.contains(permission);
+ }
+
+ public List listAllPermissions(String token) {
+ List permissions = permissionStore.get(token);
+
+ if (permissions == null) {
+ return new ArrayList<>();
+ }
+
+ return new ArrayList<>(permissions); // return copy (safe)
+ }
+ //============================================================
+
+
+ private synchronized void handleTokenRequest(
+ String pluginId,
+ String pluginJson,
+ CallbackContext callback
+ ) {
+
+ if (disclosed.contains(pluginId)) {
+ callback.error("TOKEN_ALREADY_ISSUED");
+ return;
+ }
+
+ String token = tokenStore.get(pluginId);
+
+ if (token == null) {
+ token = UUID.randomUUID().toString();
+ tokenStore.put(pluginId, token);
+ }
+
+ try {
+ JSONObject json = new JSONObject(pluginJson);
+ JSONArray permissions = json.optJSONArray("permissions");
+
+ List permissionList = new ArrayList<>();
+
+ if (permissions != null) {
+ for (int i = 0; i < permissions.length(); i++) {
+ permissionList.add(permissions.getString(i));
+ }
+ }
+
+ // Bind permissions to token
+ permissionStore.put(token, permissionList);
+
+ } catch (JSONException e) {
+ callback.error("INVALID_PLUGIN_JSON");
+ return;
+ }
+
+ disclosed.add(pluginId);
+ callback.success(token);
+ }
+}
diff --git a/src/plugins/pluginContext/www/PluginContext.js b/src/plugins/pluginContext/www/PluginContext.js
new file mode 100644
index 000000000..380dfbb9b
--- /dev/null
+++ b/src/plugins/pluginContext/www/PluginContext.js
@@ -0,0 +1,65 @@
+var exec = require("cordova/exec");
+
+const PluginContext = (function () {
+ //=============================
+ class _PluginContext {
+ constructor(uuid) {
+ this.created_at = Date.now();
+ this.uuid = uuid;
+ Object.freeze(this);
+ }
+
+ toString() {
+ return this.uuid;
+ }
+
+ [Symbol.toPrimitive](hint) {
+ if (hint === "number") {
+ return NaN; // prevent numeric coercion
+ }
+ return this.uuid;
+ }
+
+ grantedPermission(permission) {
+ return new Promise((resolve, reject) => {
+ exec(resolve, reject, "Tee", "grantedPermission", [
+ this.uuid,
+ permission,
+ ]);
+ });
+ }
+
+ listAllPermissions() {
+ return new Promise((resolve, reject) => {
+ exec(resolve, reject, "Tee", "listAllPermissions", [this.uuid]);
+ });
+ }
+ }
+
+ //Object.freeze(this);
+
+ //===============================
+
+ return {
+ generate: async function (pluginId, pluginJson) {
+ try {
+ function requestToken(pluginId) {
+ return new Promise((resolve, reject) => {
+ exec(resolve, reject, "Tee", "requestToken", [
+ pluginId,
+ pluginJson,
+ ]);
+ });
+ }
+
+ const uuid = await requestToken(pluginId);
+ return new _PluginContext(uuid);
+ } catch (err) {
+ console.warn(`PluginContext creation failed for pluginId ${pluginId}:`, err);
+ return null;
+ }
+ },
+ };
+})();
+
+module.exports = PluginContext;