8
0

improve plugin creation: add input sanitization, logging, and error handling

This commit is contained in:
andre 2026-02-13 13:21:07 +01:00
parent 39ba244e73
commit 0e35bdd5d0
5 changed files with 112 additions and 66 deletions

View File

@ -68,6 +68,8 @@ dependencies {
// For < 0.71, this will be from the local maven repo // For < 0.71, this will be from the local maven repo
// For > 0.71, this will be replaced by `com.facebook.react:react-android:$version` by react gradle plugin // For > 0.71, this will be replaced by `com.facebook.react:react-android:$version` by react gradle plugin
//noinspection GradleDynamicVersion //noinspection GradleDynamicVersion
implementation "com.google.code.gson:gson:2.10.1"
implementation("com.facebook.react:react-android") implementation("com.facebook.react:react-android")
def product_catalog_version = "15.1.1" def product_catalog_version = "15.1.1"

View File

@ -21,6 +21,11 @@ import de.webidsolutions.plugin_core.IProductPluginWebId
import de.webidsolutions.video_ident.plugin.videocall.VideoOptionsConfig import de.webidsolutions.video_ident.plugin.videocall.VideoOptionsConfig
import de.webidsolutions.video_ident_product_plugin.VideoIdentProductPlugin import de.webidsolutions.video_ident_product_plugin.VideoIdentProductPlugin
import java.net.URI import java.net.URI
import com.facebook.react.bridge.UiThreadUtil
import android.util.Log
import com.google.gson.Gson
private const val TAG = "WebIdMetaPlugin"
class WebIdMetaPluginModule( class WebIdMetaPluginModule(
private val reactContext: ReactApplicationContext private val reactContext: ReactApplicationContext
@ -63,48 +68,78 @@ class WebIdMetaPluginModule(
plugins: ReadableArray, plugins: ReadableArray,
promise: Promise promise: Promise
) { ) {
val cleanUri = uri.trim()
val cleanUsername = username.trim()
val cleanApiKey = apiKey.trim()
val shaPinsList = shaPins.toArrayList().map { it.toString() }
val pluginsList = plugins.toArrayList().map { it.toString() }
Log.i(TAG, "createMetaPlugin uri=$cleanUri user=$cleanUsername apiKeyLen=${cleanApiKey.length} apiKeyTail=${cleanApiKey.takeLast(4)}")
Log.i(TAG, "createMetaPlugin shaPins len=${shaPinsList.size} first=${shaPinsList.firstOrNull()?.take(20)}...")
Log.i(TAG, "createMetaPlugin plugins(raw) len=${pluginsList.size} values=$pluginsList")
try { try {
val environment = WebIdSdkEnvironment( val environment = WebIdSdkEnvironment(
URI.create(uri), URI.create(cleanUri),
*shaPins.toArrayList().map { it.toString() }.toTypedArray() *shaPinsList.toTypedArray()
) )
val selectedPlugins = ArrayList<de.webidsolutions.plugin_core.IProductPluginWebId>() val selectedPlugins = ArrayList<IProductPluginWebId>()
plugins.toArrayList().forEach { pluginsList.forEach { p ->
when (it.toString()) { when (p) {
"AutoIdOnServer" -> selectedPlugins.add(AutoIdentOnServerProductPlugin()) "AutoIdOnServer" -> selectedPlugins.add(AutoIdentOnServerProductPlugin())
"PayOnServer" -> selectedPlugins.add(PayOnServerProductPlugin()) "PayOnServer" -> selectedPlugins.add(PayOnServerProductPlugin())
"VideoId" -> selectedPlugins.add(VideoIdentProductPlugin(config = VideoOptionsConfig())) "VideoId" -> selectedPlugins.add(VideoIdentProductPlugin(config = VideoOptionsConfig()))
"EIdOnServer" -> selectedPlugins.add(EIdOnServerProductPlugin()) "EIdOnServer" -> selectedPlugins.add(EIdOnServerProductPlugin())
else -> Log.w(TAG, "createMetaPlugin unknown plugin string='$p'")
} }
} }
Log.i(TAG, "createMetaPlugin plugins(mapped) len=${selectedPlugins.size} classes=${selectedPlugins.map { it.javaClass.simpleName }}")
metaPlugin = WebIdMetaPlugin( metaPlugin = WebIdMetaPlugin(
environment, environment,
username, cleanUsername,
apiKey, cleanApiKey,
reactApplicationContext, reactApplicationContext,
selectedPlugins selectedPlugins
) )
promise.resolve("MetaPluginCreated") promise.resolve("MetaPluginCreated")
} catch (e: Exception) { } catch (e: Exception) {
promise.reject("CREATE_ERROR", e.message) Log.e(TAG, "createMetaPlugin failed: ${e.message}", e)
promise.reject("CREATE_ERROR", e.message, e)
} }
} }
// ------------------------------------------------ // ------------------------------------------------
// VERIFY // VERIFY
// ------------------------------------------------ // ------------------------------------------------
@ReactMethod @ReactMethod
fun verifyActionId(actionId: String, promise: Promise) { fun verifyActionId(actionId: String, promise: Promise) {
Log.i(TAG, "verifyActionId() called actionId=$actionId")
try { try {
val result = metaPlugin?.verify(actionId) val plugin = metaPlugin
promise.resolve(result.toString()) if (plugin == null) {
Log.w(TAG, "verifyActionId() blocked: SDK_NOT_INITIALIZED")
promise.reject("SDK_NOT_INITIALIZED", "MetaPlugin not created. Call createMetaPlugin() first.")
return
}
val result = plugin.verify(actionId)
val json = Gson().toJson(result)
Log.i(TAG, "verifyActionId() success, jsonLen=${json.length}")
promise.resolve(json)
} catch (e: Exception) { } catch (e: Exception) {
promise.reject("VERIFY_ERROR", e.message) Log.e(TAG, "verifyActionId() failed: ${e.message}", e)
promise.reject("VERIFY_ERROR", e.message, e)
} }
} }
@ -127,37 +162,39 @@ class WebIdMetaPluginModule(
return return
} }
try { UiThreadUtil.runOnUiThread {
val fragment = WebIdResultHostFragment.getOrCreate( try {
activity = activity, val fragment = WebIdResultHostFragment.getOrCreate(
onResult = { resultCode, data -> activity = activity,
try { onResult = { resultCode, data ->
val result = data?.let { try {
IProductPluginWebId.getProductPluginResult<IEPluginError>(it, resultCode) val result = data?.let {
} IProductPluginWebId.getProductPluginResult<IEPluginError>(it, resultCode)
}
if (result?.error == null) { if (result?.error == null) {
sendEvent("WebIdSdkEvent.finishedSuccess", result?.info ?: "") sendEvent("WebIdSdkEvent.finishedSuccess", result?.info ?: "")
} else { } else {
sendEvent("WebIdSdkEvent.finishedFailed", result.error.toString()) sendEvent("WebIdSdkEvent.finishedFailed", result.error.toString())
}
} catch (e: Exception) {
sendEvent("WebIdSdkEvent.finishedFailed", e.message ?: "Unknown")
} }
} catch (e: Exception) {
sendEvent("WebIdSdkEvent.finishedFailed", e.message ?: "Unknown")
} }
} )
)
val customTheme: Int? = null val customTheme: Int? = null
plugin.startPlugin( plugin.startPlugin(
activity, activity,
fragment.launcher, fragment.launcher,
customTheme customTheme
) )
promise.resolve("PluginStarted") promise.resolve("PluginStarted")
} catch (e: Exception) { } catch (e: Exception) {
promise.reject("START_ERROR", e.message) promise.reject("START_ERROR", e.message, e)
}
} }
} }

View File

@ -10,8 +10,8 @@
"build:ios": "react-native build-ios --scheme WebIdMetaPluginExample --mode Debug --extra-params \"-sdk iphonesimulator CC=clang CPLUSPLUS=clang++ LD=clang LDPLUSPLUS=clang++ GCC_OPTIMIZATION_LEVEL=0 GCC_PRECOMPILE_PREFIX_HEADER=YES ASSETCATALOG_COMPILER_OPTIMIZATION=time DEBUG_INFORMATION_FORMAT=dwarf COMPILER_INDEX_STORE_ENABLE=NO\"" "build:ios": "react-native build-ios --scheme WebIdMetaPluginExample --mode Debug --extra-params \"-sdk iphonesimulator CC=clang CPLUSPLUS=clang++ LD=clang LDPLUSPLUS=clang++ GCC_OPTIMIZATION_LEVEL=0 GCC_PRECOMPILE_PREFIX_HEADER=YES ASSETCATALOG_COMPILER_OPTIMIZATION=time DEBUG_INFORMATION_FORMAT=dwarf COMPILER_INDEX_STORE_ENABLE=NO\""
}, },
"dependencies": { "dependencies": {
"react": "^19.2.4", "react": "19.2.3",
"react-dom": "^19.2.4", "react-dom": "19.2.3",
"react-native": "0.84.0", "react-native": "0.84.0",
"react-native-web-id-meta-plugin": "file:.." "react-native-web-id-meta-plugin": "file:.."
}, },

View File

@ -25,11 +25,20 @@ import BouncyCheckbox from 'react-native-bouncy-checkbox';
export default function App() { export default function App() {
const [actionId, onChangeActionId] = React.useState(''); const [actionId, onChangeActionId] = React.useState('');
var selectedPlugins: Set<string> = new Set();
const [selectedPlugins, setSelectedPlugins] = React.useState<string[]>([]);
const togglePlugin = (name: string, isChecked: boolean) => {
setSelectedPlugins((prev) => {
const next = new Set(prev);
if (isChecked) next.add(name);
else next.delete(name);
return Array.from(next);
});
};
webIdEventEmitter.addListener(WebIdSdkEvent.finishedFailed, (eventData) => { webIdEventEmitter.addListener(WebIdSdkEvent.finishedFailed, (eventData) => {
let result = parseJSONToType<ProductPluginResultFailed>(eventData); let result = parseJSONToType<ProductPluginResultFailed>(eventData);
if (result != undefined) { if (result !== undefined) {
console.log( console.log(
'received failed event with result code: ', 'received failed event with result code: ',
result.genericResultCode result.genericResultCode
@ -39,7 +48,7 @@ export default function App() {
webIdEventEmitter.addListener(WebIdSdkEvent.finishedSuccess, (eventData) => { webIdEventEmitter.addListener(WebIdSdkEvent.finishedSuccess, (eventData) => {
let result = parseJSONToType<ProductPluginResultSuccess>(eventData); let result = parseJSONToType<ProductPluginResultSuccess>(eventData);
if (result != undefined) { if (result !== undefined) {
console.log( console.log(
'received failed event with process finished: ', 'received failed event with process finished: ',
result.processFinished result.processFinished
@ -63,13 +72,7 @@ export default function App() {
}; };
const onCreateMetaPlugin = () => { const onCreateMetaPlugin = () => {
createMetaPlugin( createMetaPlugin(URL, USERNAME, API_KEY, [CERT_BASE64], selectedPlugins)
URL,
USERNAME,
API_KEY,
[CERT_BASE64],
Array.from(selectedPlugins)
)
.then((data) => { .then((data) => {
showAlert('Success', data); showAlert('Success', data);
}) })
@ -125,44 +128,36 @@ export default function App() {
<View> <View>
<View> <View>
<BouncyCheckbox <BouncyCheckbox
isChecked={selectedPlugins.has('AutoIdOnServer')} isChecked={selectedPlugins.includes('AutoIdOnServer')}
onPress={(isChecked: boolean) => onPress={(isChecked: boolean) =>
isChecked === true togglePlugin('AutoIdOnServer', isChecked)
? selectedPlugins.add('AutoIdOnServer')
: selectedPlugins.delete('AutoIdOnServer')
} }
/> />
<Text>AutoIdOnServer</Text> <Text>AutoIdOnServer</Text>
</View> </View>
<View> <View>
<BouncyCheckbox <BouncyCheckbox
isChecked={selectedPlugins.has('PayOnServer')} isChecked={selectedPlugins.includes('PayOnServer')}
onPress={(isChecked: boolean) => onPress={(isChecked: boolean) =>
isChecked === true togglePlugin('PayOnServer', isChecked)
? selectedPlugins.add('PayOnServer')
: selectedPlugins.delete('PayOnServer')
} }
/> />
<Text>PayOnServer</Text> <Text>PayOnServer</Text>
</View> </View>
<View> <View>
<BouncyCheckbox <BouncyCheckbox
isChecked={selectedPlugins.has('VideoId')} isChecked={selectedPlugins.includes('VideoId')}
onPress={(isChecked: boolean) => onPress={(isChecked: boolean) =>
isChecked === true togglePlugin('VideoId', isChecked)
? selectedPlugins.add('VideoId')
: selectedPlugins.delete('VideoId')
} }
/> />
<Text>VideoId</Text> <Text>VideoId</Text>
</View> </View>
<View> <View>
<BouncyCheckbox <BouncyCheckbox
isChecked={selectedPlugins.has('EIdOnServer')} isChecked={selectedPlugins.includes('EIdOnServer')}
onPress={(isChecked: boolean) => onPress={(isChecked: boolean) =>
isChecked === true togglePlugin('EIdOnServer', isChecked)
? selectedPlugins.add('EIdOnServer')
: selectedPlugins.delete('EIdOnServer')
} }
/> />
<Text>EIdOnServer</Text> <Text>EIdOnServer</Text>

View File

@ -6532,6 +6532,13 @@ react-dom@^19.2.4:
dependencies: dependencies:
scheduler "^0.27.0" scheduler "^0.27.0"
react-dom@19.2.3:
version "19.2.3"
resolved "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz"
integrity sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==
dependencies:
scheduler "^0.27.0"
react-is@^16.13.1: react-is@^16.13.1:
version "16.13.1" version "16.13.1"
@ -6579,8 +6586,8 @@ react-native-dotenv@^3.4.9:
version "0.0.1" version "0.0.1"
resolved "file:example" resolved "file:example"
dependencies: dependencies:
react "^19.2.4" react "19.2.3"
react-dom "^19.2.4" react-dom "19.2.3"
react-native "0.84.0" react-native "0.84.0"
react-native-web-id-meta-plugin "file:.." react-native-web-id-meta-plugin "file:.."
@ -6638,6 +6645,11 @@ react@*, react@^19.2.3, react@^19.2.4:
resolved "https://registry.npmjs.org/react/-/react-19.2.4.tgz" resolved "https://registry.npmjs.org/react/-/react-19.2.4.tgz"
integrity sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ== integrity sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==
react@19.2.3:
version "19.2.3"
resolved "https://registry.npmjs.org/react/-/react-19.2.3.tgz"
integrity sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==
read-pkg-up@^3.0.0: read-pkg-up@^3.0.0:
version "3.0.0" version "3.0.0"
dependencies: dependencies: