added onReceivedServerTrustAuthRequest and onReceivedClientCertRequest events to manage SSL requests, added clearSslPreferences and clearClientCertPreferences for Android, added nodejs_server_test_auth_basic_and_ssl for testing auth and ssl requests

This commit is contained in:
Lorenzo Pichilli 2019-10-31 23:09:54 +01:00
parent 68ff79c716
commit 1f67e98213
41 changed files with 2253 additions and 439 deletions

View File

@ -15,34 +15,43 @@
</component> </component>
<component name="ChangeListManager"> <component name="ChangeListManager">
<list default="true" id="9b41f7a2-a71e-4923-91fb-249d7815b3e7" name="Default" comment=""> <list default="true" id="9b41f7a2-a71e-4923-91fb-249d7815b3e7" name="Default" comment="">
<change afterPath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/CredentialDatabase/Credential.java" afterDir="false" /> <change afterPath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppWebView/PreferredContentModeOptionType.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/CredentialDatabase/CredentialContract.java" afterDir="false" /> <change afterPath="$PROJECT_DIR$/example/assets/certificate.pfx" afterDir="false" />
<change afterPath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/CredentialDatabase/CredentialDao.java" afterDir="false" /> <change afterPath="$PROJECT_DIR$/nodejs_server_test_auth_basic_and_ssl/.gitignore" afterDir="false" />
<change afterPath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/CredentialDatabase/CredentialDatabase.java" afterDir="false" /> <change afterPath="$PROJECT_DIR$/nodejs_server_test_auth_basic_and_ssl/ca-crt.pem" afterDir="false" />
<change afterPath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/CredentialDatabase/CredentialDatabaseHelper.java" afterDir="false" /> <change afterPath="$PROJECT_DIR$/nodejs_server_test_auth_basic_and_ssl/ca-crt.srl" afterDir="false" />
<change afterPath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/CredentialDatabase/ProtectionSpace.java" afterDir="false" /> <change afterPath="$PROJECT_DIR$/nodejs_server_test_auth_basic_and_ssl/ca-key.pem" afterDir="false" />
<change afterPath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/CredentialDatabase/ProtectionSpaceContract.java" afterDir="false" /> <change afterPath="$PROJECT_DIR$/nodejs_server_test_auth_basic_and_ssl/ca.cnf" afterDir="false" />
<change afterPath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/CredentialDatabase/ProtectionSpaceDao.java" afterDir="false" /> <change afterPath="$PROJECT_DIR$/nodejs_server_test_auth_basic_and_ssl/certificate.pfx" afterDir="false" />
<change afterPath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/CredentialDatabaseHandler.java" afterDir="false" /> <change afterPath="$PROJECT_DIR$/nodejs_server_test_auth_basic_and_ssl/client1-crt.pem" afterDir="false" />
<change afterPath="$PROJECT_DIR$/ios/Classes/CredentialDatabase.swift" afterDir="false" /> <change afterPath="$PROJECT_DIR$/nodejs_server_test_auth_basic_and_ssl/client1-csr.pem" afterDir="false" />
<change afterPath="$PROJECT_DIR$/lib/src/http_auth_credentials_database.dart" afterDir="false" /> <change afterPath="$PROJECT_DIR$/nodejs_server_test_auth_basic_and_ssl/client1-key.pem" afterDir="false" />
<change afterPath="$PROJECT_DIR$/nodejs_server_test_auth_basic_and_ssl/client1.cnf" afterDir="false" />
<change afterPath="$PROJECT_DIR$/nodejs_server_test_auth_basic_and_ssl/client2-crt.pem" afterDir="false" />
<change afterPath="$PROJECT_DIR$/nodejs_server_test_auth_basic_and_ssl/client2-csr.pem" afterDir="false" />
<change afterPath="$PROJECT_DIR$/nodejs_server_test_auth_basic_and_ssl/client2-key.pem" afterDir="false" />
<change afterPath="$PROJECT_DIR$/nodejs_server_test_auth_basic_and_ssl/client2.cnf" afterDir="false" />
<change afterPath="$PROJECT_DIR$/nodejs_server_test_auth_basic_and_ssl/index.js" afterDir="false" />
<change afterPath="$PROJECT_DIR$/nodejs_server_test_auth_basic_and_ssl/package-lock.json" afterDir="false" />
<change afterPath="$PROJECT_DIR$/nodejs_server_test_auth_basic_and_ssl/package.json" afterDir="false" />
<change afterPath="$PROJECT_DIR$/nodejs_server_test_auth_basic_and_ssl/server-crt.pem" afterDir="false" />
<change afterPath="$PROJECT_DIR$/nodejs_server_test_auth_basic_and_ssl/server-csr.pem" afterDir="false" />
<change afterPath="$PROJECT_DIR$/nodejs_server_test_auth_basic_and_ssl/server-key.pem" afterDir="false" />
<change afterPath="$PROJECT_DIR$/nodejs_server_test_auth_basic_and_ssl/server.cnf" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" /> <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/CHANGELOG.md" beforeDir="false" afterPath="$PROJECT_DIR$/CHANGELOG.md" afterDir="false" /> <change beforePath="$PROJECT_DIR$/CHANGELOG.md" beforeDir="false" afterPath="$PROJECT_DIR$/CHANGELOG.md" afterDir="false" />
<change beforePath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/FlutterWebView.java" beforeDir="false" afterPath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/FlutterWebView.java" afterDir="false" /> <change beforePath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/FlutterWebView.java" beforeDir="false" afterPath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/FlutterWebView.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppBrowserActivity.java" beforeDir="false" afterPath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppBrowserActivity.java" afterDir="false" /> <change beforePath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppBrowserActivity.java" beforeDir="false" afterPath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppBrowserActivity.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppBrowserFlutterPlugin.java" beforeDir="false" afterPath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppBrowserFlutterPlugin.java" afterDir="false" /> <change beforePath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppBrowserFlutterPlugin.java" beforeDir="false" afterPath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppBrowserFlutterPlugin.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppWebView/InAppWebChromeClient.java" beforeDir="false" afterPath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppWebView/InAppWebChromeClient.java" afterDir="false" /> <change beforePath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppWebView/InAppWebView.java" beforeDir="false" afterPath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppWebView/InAppWebView.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppWebView/InAppWebViewClient.java" beforeDir="false" afterPath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppWebView/InAppWebViewClient.java" afterDir="false" /> <change beforePath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppWebView/InAppWebViewClient.java" beforeDir="false" afterPath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppWebView/InAppWebViewClient.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppWebView/InAppWebViewOptions.java" beforeDir="false" afterPath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppWebView/InAppWebViewOptions.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/Util.java" beforeDir="false" afterPath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/Util.java" afterDir="false" /> <change beforePath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/Util.java" beforeDir="false" afterPath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/Util.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/example/lib/chrome_safari_example.screen.dart" beforeDir="false" afterPath="$PROJECT_DIR$/example/lib/chrome_safari_example.screen.dart" afterDir="false" />
<change beforePath="$PROJECT_DIR$/example/lib/inline_example.screen.dart" beforeDir="false" afterPath="$PROJECT_DIR$/example/lib/inline_example.screen.dart" afterDir="false" /> <change beforePath="$PROJECT_DIR$/example/lib/inline_example.screen.dart" beforeDir="false" afterPath="$PROJECT_DIR$/example/lib/inline_example.screen.dart" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ios/Classes/FlutterWebViewController.swift" beforeDir="false" afterPath="$PROJECT_DIR$/ios/Classes/FlutterWebViewController.swift" afterDir="false" /> <change beforePath="$PROJECT_DIR$/example/pubspec.yaml" beforeDir="false" afterPath="$PROJECT_DIR$/example/pubspec.yaml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ios/Classes/CredentialDatabase.swift" beforeDir="false" afterPath="$PROJECT_DIR$/ios/Classes/CredentialDatabase.swift" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ios/Classes/InAppWebView.swift" beforeDir="false" afterPath="$PROJECT_DIR$/ios/Classes/InAppWebView.swift" afterDir="false" /> <change beforePath="$PROJECT_DIR$/ios/Classes/InAppWebView.swift" beforeDir="false" afterPath="$PROJECT_DIR$/ios/Classes/InAppWebView.swift" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ios/Classes/MyCookieManager.swift" beforeDir="false" afterPath="$PROJECT_DIR$/ios/Classes/MyCookieManager.swift" afterDir="false" /> <change beforePath="$PROJECT_DIR$/ios/flutter_inappbrowser.podspec" beforeDir="false" afterPath="$PROJECT_DIR$/ios/flutter_inappbrowser.podspec" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ios/Classes/SwiftFlutterPlugin.swift" beforeDir="false" afterPath="$PROJECT_DIR$/ios/Classes/SwiftFlutterPlugin.swift" afterDir="false" />
<change beforePath="$PROJECT_DIR$/lib/flutter_inappbrowser.dart" beforeDir="false" afterPath="$PROJECT_DIR$/lib/flutter_inappbrowser.dart" afterDir="false" />
<change beforePath="$PROJECT_DIR$/lib/src/chrome_safari_browser.dart" beforeDir="false" afterPath="$PROJECT_DIR$/lib/src/chrome_safari_browser.dart" afterDir="false" />
<change beforePath="$PROJECT_DIR$/lib/src/cookie_manager.dart" beforeDir="false" afterPath="$PROJECT_DIR$/lib/src/cookie_manager.dart" afterDir="false" />
<change beforePath="$PROJECT_DIR$/lib/src/in_app_browser.dart" beforeDir="false" afterPath="$PROJECT_DIR$/lib/src/in_app_browser.dart" afterDir="false" /> <change beforePath="$PROJECT_DIR$/lib/src/in_app_browser.dart" beforeDir="false" afterPath="$PROJECT_DIR$/lib/src/in_app_browser.dart" afterDir="false" />
<change beforePath="$PROJECT_DIR$/lib/src/in_app_webview.dart" beforeDir="false" afterPath="$PROJECT_DIR$/lib/src/in_app_webview.dart" afterDir="false" /> <change beforePath="$PROJECT_DIR$/lib/src/in_app_webview.dart" beforeDir="false" afterPath="$PROJECT_DIR$/lib/src/in_app_webview.dart" afterDir="false" />
<change beforePath="$PROJECT_DIR$/lib/src/types.dart" beforeDir="false" afterPath="$PROJECT_DIR$/lib/src/types.dart" afterDir="false" /> <change beforePath="$PROJECT_DIR$/lib/src/types.dart" beforeDir="false" afterPath="$PROJECT_DIR$/lib/src/types.dart" afterDir="false" />
@ -69,20 +78,8 @@
<file pinned="false" current-in-tab="false"> <file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/CHANGELOG.md"> <entry file="file://$PROJECT_DIR$/CHANGELOG.md">
<provider selected="true" editor-type-id="text-editor"> <provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="360"> <state>
<caret line="24" column="42" selection-start-line="24" selection-start-column="42" selection-end-line="24" selection-end-column="42" /> <caret column="8" selection-start-column="8" selection-end-column="8" />
</state>
</provider>
</entry>
</file>
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/lib/src/types.dart">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="147">
<caret line="327" column="89" selection-start-line="327" selection-start-column="89" selection-end-line="327" selection-end-column="89" />
<folding>
<element signature="e#0#32#0" expanded="true" />
</folding>
</state> </state>
</provider> </provider>
</entry> </entry>
@ -90,8 +87,8 @@
<file pinned="false" current-in-tab="false"> <file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/example/assets/index.html"> <entry file="file://$PROJECT_DIR$/example/assets/index.html">
<provider selected="true" editor-type-id="text-editor"> <provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="416"> <state relative-caret-position="-6">
<caret line="73" column="12" selection-start-line="73" selection-start-column="12" selection-end-line="73" selection-end-column="12" /> <caret line="22" column="17" selection-start-line="22" selection-start-column="17" selection-end-line="22" selection-end-column="17" />
</state> </state>
</provider> </provider>
</entry> </entry>
@ -99,8 +96,8 @@
<file pinned="false" current-in-tab="false"> <file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/lib/src/in_app_webview.dart"> <entry file="file://$PROJECT_DIR$/lib/src/in_app_webview.dart">
<provider selected="true" editor-type-id="text-editor"> <provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="-492"> <state relative-caret-position="623">
<caret line="881" column="26" selection-start-line="881" selection-start-column="22" selection-end-line="881" selection-end-column="26" /> <caret line="397" column="54" selection-start-line="397" selection-start-column="54" selection-end-line="397" selection-end-column="54" />
<folding> <folding>
<element signature="e#0#17#0" expanded="true" /> <element signature="e#0#17#0" expanded="true" />
</folding> </folding>
@ -109,22 +106,25 @@
</entry> </entry>
</file> </file>
<file pinned="false" current-in-tab="false"> <file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/lib/src/in_app_browser.dart"> <entry file="file://$PROJECT_DIR$/lib/src/webview_options.dart">
<provider selected="true" editor-type-id="text-editor"> <provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="419"> <state relative-caret-position="175">
<caret line="411" column="2" selection-start-line="411" selection-start-column="2" selection-end-line="411" selection-end-column="106" /> <caret line="42" column="83" selection-start-line="42" selection-start-column="83" selection-end-line="42" selection-end-column="83" />
<folding> <folding>
<element signature="e#0#20#0" expanded="true" /> <element signature="e#0#17#0" expanded="true" />
</folding> </folding>
</state> </state>
</provider> </provider>
</entry> </entry>
</file> </file>
<file pinned="false" current-in-tab="false"> <file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/lib/src/cookie_manager.dart"> <entry file="file://$PROJECT_DIR$/lib/src/types.dart">
<provider selected="true" editor-type-id="text-editor"> <provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="395"> <state relative-caret-position="170">
<caret line="61" column="51" selection-start-line="61" selection-start-column="21" selection-end-line="61" selection-end-column="51" /> <caret line="24" column="25" selection-start-line="24" selection-start-column="6" selection-end-line="24" selection-end-column="25" />
<folding>
<element signature="e#0#25#0" expanded="true" />
</folding>
</state> </state>
</provider> </provider>
</entry> </entry>
@ -132,8 +132,8 @@
<file pinned="false" current-in-tab="true"> <file pinned="false" current-in-tab="true">
<entry file="file://$PROJECT_DIR$/example/lib/inline_example.screen.dart"> <entry file="file://$PROJECT_DIR$/example/lib/inline_example.screen.dart">
<provider selected="true" editor-type-id="text-editor"> <provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="253"> <state relative-caret-position="85">
<caret line="88" column="12" selection-start-line="88" selection-start-column="12" selection-end-line="88" selection-end-column="12" /> <caret line="144" column="16" lean-forward="true" selection-start-line="144" selection-start-column="16" selection-end-line="146" selection-end-column="21" />
<folding> <folding>
<element signature="e#0#22#0" expanded="true" /> <element signature="e#0#22#0" expanded="true" />
</folding> </folding>
@ -141,27 +141,6 @@
</provider> </provider>
</entry> </entry>
</file> </file>
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/example/pubspec.yaml">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="256">
<caret line="43" column="9" selection-start-line="43" selection-start-column="9" selection-end-line="43" selection-end-column="9" />
</state>
</provider>
</entry>
</file>
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/lib/src/http_auth_credentials_database.dart">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="60">
<caret line="4" column="32" selection-start-line="4" selection-start-column="6" selection-end-line="4" selection-end-column="32" />
<folding>
<element signature="e#0#10#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
</file>
</leaf> </leaf>
</component> </component>
<component name="FileTemplateManagerImpl"> <component name="FileTemplateManagerImpl">
@ -174,25 +153,8 @@
</component> </component>
<component name="FindInProjectRecents"> <component name="FindInProjectRecents">
<findStrings> <findStrings>
<find>_channel</find>
<find>uuid_</find>
<find>javaScriptHandlersMap</find>
<find>onJsAlertResponseCallback</find>
<find>_jsResultCallbackMap</find>
<find>onJs</find>
<find>onJsAlert</find>
<find>defaultValue</find>
<find>onJsA</find>
<find>toMap()</find>
<find>.toMap()</find>
<find>onSafeBrowsingHitCallback</find>
<find>onSafeBrowsingHit&quot;</find>
<find>ConsoleMessageLevel</find>
<find>ConsoleMessage</find>
<find>appCa</find>
<find>onSafeBrowsingHit</find> <find>onSafeBrowsingHit</find>
<find>SafeBrowsingResponse</find> <find>SafeBrowsingResponse</find>
<find>iOSInAppWebViewUserPreferredContentMode</find>
<find>getDomainName</find> <find>getDomainName</find>
<find>a</find> <find>a</find>
<find>as</find> <find>as</find>
@ -200,10 +162,27 @@
<find>clearCache</find> <find>clearCache</find>
<find>Auth</find> <find>Auth</find>
<find>onReceivedHttpAuthRequestCallback</find> <find>onReceivedHttpAuthRequestCallback</find>
<find>onReceivedHttpAuthRequest</find>
<find>cast</find>
<find>Protection</find> <find>Protection</find>
<find>cast</find>
<find>clear</find> <find>clear</find>
<find>HttpAuthResponse</find>
<find>ClientCertResponse</find>
<find>onReceivedServerTrustAuthRequest</find>
<find>on</find>
<find>ServerTrustAuthResponse</find>
<find>certific</find>
<find>CER</find>
<find>ServerTrustChallenge</find>
<find>cONTENTMODE</find>
<find>iOSInAppWebViewUserPreferredContentMode</find>
<find>InAppWebViewUserPreferredContentMode</find>
<find>serverCertificate</find>
<find>onReceivedHttpAuthRequest</find>
<find>preferredContentMode</find>
<find>print</find>
<find>ConsoleMessageLevel</find>
<find>_findLocalPath</find>
<find>onConsoleMessage</find>
</findStrings> </findStrings>
<replaceStrings> <replaceStrings>
<replace>activity.getPreferences(0)</replace> <replace>activity.getPreferences(0)</replace>
@ -216,6 +195,7 @@
</replaceStrings> </replaceStrings>
<dirStrings> <dirStrings>
<dir>$PROJECT_DIR$/example/android</dir> <dir>$PROJECT_DIR$/example/android</dir>
<dir>$PROJECT_DIR$/lib/src</dir>
</dirStrings> </dirStrings>
</component> </component>
<component name="Git.Settings"> <component name="Git.Settings">
@ -224,7 +204,6 @@
<component name="IdeDocumentHistory"> <component name="IdeDocumentHistory">
<option name="CHANGED_PATHS"> <option name="CHANGED_PATHS">
<list> <list>
<option value="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutterwebview/InAppBrowser.java" />
<option value="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutterwebview/InAppBrowserClient.java" /> <option value="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutterwebview/InAppBrowserClient.java" />
<option value="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutterwebview/InAppBrowserDialog.java" /> <option value="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutterwebview/InAppBrowserDialog.java" />
<option value="$PROJECT_DIR$/android/settings.gradle" /> <option value="$PROJECT_DIR$/android/settings.gradle" />
@ -248,7 +227,6 @@
<option value="$PROJECT_DIR$/example/assets/page-1.html" /> <option value="$PROJECT_DIR$/example/assets/page-1.html" />
<option value="$PROJECT_DIR$/example/assets/css/style.css" /> <option value="$PROJECT_DIR$/example/assets/css/style.css" />
<option value="$PROJECT_DIR$/ios/Classes/InAppWebView.swift" /> <option value="$PROJECT_DIR$/ios/Classes/InAppWebView.swift" />
<option value="$PROJECT_DIR$/ios/flutter_inappbrowser.podspec" />
<option value="$PROJECT_DIR$/README.md" /> <option value="$PROJECT_DIR$/README.md" />
<option value="$PROJECT_DIR$/android/src/main/AndroidManifest.xml" /> <option value="$PROJECT_DIR$/android/src/main/AndroidManifest.xml" />
<option value="$PROJECT_DIR$/lib/in_app_browser.dart" /> <option value="$PROJECT_DIR$/lib/in_app_browser.dart" />
@ -259,7 +237,6 @@
<option value="$PROJECT_DIR$/example/lib/chrome_safari_example.screen.dart" /> <option value="$PROJECT_DIR$/example/lib/chrome_safari_example.screen.dart" />
<option value="$PROJECT_DIR$/lib/src/chrome_safari_browser.dart" /> <option value="$PROJECT_DIR$/lib/src/chrome_safari_browser.dart" />
<option value="$PROJECT_DIR$/example/ios/Runner/Info.plist" /> <option value="$PROJECT_DIR$/example/ios/Runner/Info.plist" />
<option value="$PROJECT_DIR$/pubspec.yaml" />
<option value="$PROJECT_DIR$/example/lib/main.dart" /> <option value="$PROJECT_DIR$/example/lib/main.dart" />
<option value="$PROJECT_DIR$/lib/src/content_blocker.dart" /> <option value="$PROJECT_DIR$/lib/src/content_blocker.dart" />
<option value="$PROJECT_DIR$/example/assets/index.html" /> <option value="$PROJECT_DIR$/example/assets/index.html" />
@ -267,18 +244,22 @@
<option value="$PROJECT_DIR$/lib/src/cookie_manager.dart" /> <option value="$PROJECT_DIR$/lib/src/cookie_manager.dart" />
<option value="$PROJECT_DIR$/lib/src/credentials_database.dart" /> <option value="$PROJECT_DIR$/lib/src/credentials_database.dart" />
<option value="$PROJECT_DIR$/lib/flutter_inappbrowser.dart" /> <option value="$PROJECT_DIR$/lib/flutter_inappbrowser.dart" />
<option value="$PROJECT_DIR$/lib/src/webview_options.dart" />
<option value="$PROJECT_DIR$/lib/src/in_app_browser.dart" />
<option value="$PROJECT_DIR$/lib/src/in_app_webview.dart" />
<option value="$PROJECT_DIR$/lib/src/types.dart" />
<option value="$PROJECT_DIR$/lib/src/http_auth_credentials_database.dart" /> <option value="$PROJECT_DIR$/lib/src/http_auth_credentials_database.dart" />
<option value="$PROJECT_DIR$/CHANGELOG.md" />
<option value="$PROJECT_DIR$/example/pubspec.yaml" /> <option value="$PROJECT_DIR$/example/pubspec.yaml" />
<option value="$PROJECT_DIR$/lib/src/in_app_browser.dart" />
<option value="$PROJECT_DIR$/ios/flutter_inappbrowser.podspec" />
<option value="$PROJECT_DIR$/pubspec.yaml" />
<option value="$PROJECT_DIR$/lib/src/types.dart" />
<option value="$PROJECT_DIR$/lib/src/webview_options.dart" />
<option value="$PROJECT_DIR$/CHANGELOG.md" />
<option value="$PROJECT_DIR$/nodejs_server_test_auth_basic_and_ssl/index.js" />
<option value="$PROJECT_DIR$/lib/src/in_app_webview.dart" />
<option value="$PROJECT_DIR$/example/lib/inline_example.screen.dart" /> <option value="$PROJECT_DIR$/example/lib/inline_example.screen.dart" />
</list> </list>
</option> </option>
</component> </component>
<component name="ProjectFrameBounds"> <component name="ProjectFrameBounds" extendedState="6">
<option name="x" value="1" />
<option name="y" value="23" /> <option name="y" value="23" />
<option name="width" value="1920" /> <option name="width" value="1920" />
<option name="height" value="1057" /> <option name="height" value="1057" />
@ -326,23 +307,6 @@
<item name="flutter_inappbrowser" type="b2602c69:ProjectViewProjectNode" /> <item name="flutter_inappbrowser" type="b2602c69:ProjectViewProjectNode" />
<item name="flutter_inappbrowser" type="462c0819:PsiDirectoryNode" /> <item name="flutter_inappbrowser" type="462c0819:PsiDirectoryNode" />
</path> </path>
<path>
<item name="flutter_inappbrowser" type="b2602c69:ProjectViewProjectNode" />
<item name="flutter_inappbrowser" type="462c0819:PsiDirectoryNode" />
<item name="example" type="462c0819:PsiDirectoryNode" />
</path>
<path>
<item name="flutter_inappbrowser" type="b2602c69:ProjectViewProjectNode" />
<item name="flutter_inappbrowser" type="462c0819:PsiDirectoryNode" />
<item name="example" type="462c0819:PsiDirectoryNode" />
<item name="assets" type="462c0819:PsiDirectoryNode" />
</path>
<path>
<item name="flutter_inappbrowser" type="b2602c69:ProjectViewProjectNode" />
<item name="flutter_inappbrowser" type="462c0819:PsiDirectoryNode" />
<item name="example" type="462c0819:PsiDirectoryNode" />
<item name="lib" type="462c0819:PsiDirectoryNode" />
</path>
<path> <path>
<item name="flutter_inappbrowser" type="b2602c69:ProjectViewProjectNode" /> <item name="flutter_inappbrowser" type="b2602c69:ProjectViewProjectNode" />
<item name="flutter_inappbrowser" type="462c0819:PsiDirectoryNode" /> <item name="flutter_inappbrowser" type="462c0819:PsiDirectoryNode" />
@ -523,11 +487,11 @@
</todo-panel> </todo-panel>
</component> </component>
<component name="ToolWindowManager"> <component name="ToolWindowManager">
<frame x="0" y="23" width="1920" height="1057" extended-state="0" /> <frame x="1" y="23" width="1920" height="1057" extended-state="6" />
<editor active="true" /> <editor active="true" />
<layout> <layout>
<window_info active="true" content_ui="combo" id="Project" order="0" sideWeight="0.59210527" visible="true" weight="0.15867944" /> <window_info active="true" content_ui="combo" id="Project" order="0" sideWeight="0.59067357" visible="true" weight="0.15867944" />
<window_info id="Structure" order="1" sideWeight="0.40789473" side_tool="true" visible="true" weight="0.15867944" /> <window_info id="Structure" order="1" sideWeight="0.40932643" side_tool="true" weight="0.15867944" />
<window_info id="Designer" order="2" /> <window_info id="Designer" order="2" />
<window_info id="Build Variants" order="3" side_tool="true" /> <window_info id="Build Variants" order="3" side_tool="true" />
<window_info id="Captures" order="4" side_tool="true" weight="0.32936507" /> <window_info id="Captures" order="4" side_tool="true" weight="0.32936507" />
@ -537,7 +501,7 @@
<window_info id="Resources Explorer" order="8" /> <window_info id="Resources Explorer" order="8" />
<window_info anchor="bottom" id="Message" order="0" /> <window_info anchor="bottom" id="Message" order="0" />
<window_info anchor="bottom" id="Find" order="1" weight="0.32642487" /> <window_info anchor="bottom" id="Find" order="1" weight="0.32642487" />
<window_info anchor="bottom" id="Run" order="2" sideWeight="0.49307775" visible="true" weight="0.5274611" /> <window_info anchor="bottom" id="Run" order="2" sideWeight="0.49307775" visible="true" weight="0.4" />
<window_info anchor="bottom" id="Debug" order="3" weight="0.34196892" /> <window_info anchor="bottom" id="Debug" order="3" weight="0.34196892" />
<window_info anchor="bottom" id="Cvs" order="4" weight="0.25" /> <window_info anchor="bottom" id="Cvs" order="4" weight="0.25" />
<window_info anchor="bottom" id="Inspection" order="5" weight="0.4" /> <window_info anchor="bottom" id="Inspection" order="5" weight="0.4" />
@ -545,7 +509,7 @@
<window_info anchor="bottom" id="Android Profiler" order="7" show_stripe_button="false" /> <window_info anchor="bottom" id="Android Profiler" order="7" show_stripe_button="false" />
<window_info anchor="bottom" id="Event Log" order="8" sideWeight="0.50692225" side_tool="true" weight="0.38445595" /> <window_info anchor="bottom" id="Event Log" order="8" sideWeight="0.50692225" side_tool="true" weight="0.38445595" />
<window_info anchor="bottom" id="Version Control" order="9" weight="0.32953367" /> <window_info anchor="bottom" id="Version Control" order="9" weight="0.32953367" />
<window_info anchor="bottom" id="Terminal" order="10" sideWeight="0.49533224" weight="0.29430053" /> <window_info anchor="bottom" id="Terminal" order="10" sideWeight="0.49533224" weight="0.3968912" />
<window_info anchor="bottom" id="Logcat" order="11" weight="0.32953367" /> <window_info anchor="bottom" id="Logcat" order="11" weight="0.32953367" />
<window_info anchor="bottom" id="Messages" order="12" weight="0.226943" /> <window_info anchor="bottom" id="Messages" order="12" weight="0.226943" />
<window_info anchor="bottom" id="Dependency Viewer" order="13" weight="0.32800853" /> <window_info anchor="bottom" id="Dependency Viewer" order="13" weight="0.32800853" />
@ -573,13 +537,6 @@
</ignored-roots> </ignored-roots>
</component> </component>
<component name="editorHistoryManager"> <component name="editorHistoryManager">
<entry file="file://$PROJECT_DIR$/android/.idea/gradle.xml">
<provider selected="true" editor-type-id="text-editor" />
</entry>
<entry file="file://$PROJECT_DIR$/android/.idea/misc.xml">
<provider selected="true" editor-type-id="text-editor" />
</entry>
<entry file="file://$PROJECT_DIR$/example/build/app/intermediates/incremental/mergeDebugResources/merged.dir/values-v28/values-v28.xml" />
<entry file="file://$PROJECT_DIR$/android/gradle.properties"> <entry file="file://$PROJECT_DIR$/android/gradle.properties">
<provider selected="true" editor-type-id="text-editor"> <provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="45"> <state relative-caret-position="45">
@ -655,13 +612,6 @@
</state> </state>
</provider> </provider>
</entry> </entry>
<entry file="file://$PROJECT_DIR$/ios/flutter_inappbrowser.podspec">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="345">
<caret line="23" selection-start-line="23" selection-end-line="23" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/android/src/main/AndroidManifest.xml"> <entry file="file://$PROJECT_DIR$/android/src/main/AndroidManifest.xml">
<provider selected="true" editor-type-id="text-editor"> <provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="180"> <state relative-caret-position="180">
@ -796,13 +746,6 @@
</state> </state>
</provider> </provider>
</entry> </entry>
<entry file="file://$PROJECT_DIR$/pubspec.yaml">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="30">
<caret line="2" column="14" selection-start-line="2" selection-start-column="14" selection-end-line="2" selection-end-column="14" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/lib/flutter_inappbrowser.dart"> <entry file="file://$PROJECT_DIR$/lib/flutter_inappbrowser.dart">
<provider selected="true" editor-type-id="text-editor"> <provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="195"> <state relative-caret-position="195">
@ -830,43 +773,8 @@
</state> </state>
</provider> </provider>
</entry> </entry>
<entry file="file://$PROJECT_DIR$/lib/src/webview_options.dart"> <entry file="file://$PROJECT_DIR$/example/assets/client1-crt.pem" />
<provider selected="true" editor-type-id="text-editor"> <entry file="file://$PROJECT_DIR$/example/assets/client1-crt.crt" />
<state relative-caret-position="4635">
<caret line="309" column="1" selection-start-line="309" selection-start-column="1" selection-end-line="309" selection-end-column="1" />
<folding>
<element signature="e#0#17#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/lib/src/in_app_browser.dart">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="419">
<caret line="411" column="2" selection-start-line="411" selection-start-column="2" selection-end-line="411" selection-end-column="106" />
<folding>
<element signature="e#0#20#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/example/assets/index.html">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="416">
<caret line="73" column="12" selection-start-line="73" selection-start-column="12" selection-end-line="73" selection-end-column="12" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/lib/src/in_app_webview.dart">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="-492">
<caret line="881" column="26" selection-start-line="881" selection-start-column="22" selection-end-line="881" selection-end-column="26" />
<folding>
<element signature="e#0#17#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/lib/src/cookie_manager.dart"> <entry file="file://$PROJECT_DIR$/lib/src/cookie_manager.dart">
<provider selected="true" editor-type-id="text-editor"> <provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="395"> <state relative-caret-position="395">
@ -874,54 +782,105 @@
</state> </state>
</provider> </provider>
</entry> </entry>
<entry file="file://$PROJECT_DIR$/lib/src/types.dart"> <entry file="file://$PROJECT_DIR$/pubspec.lock">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="147">
<caret line="327" column="89" selection-start-line="327" selection-start-column="89" selection-end-line="327" selection-end-column="89" />
<folding>
<element signature="e#0#32#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/CHANGELOG.md">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="360">
<caret line="24" column="42" selection-start-line="24" selection-start-column="42" selection-end-line="24" selection-end-column="42" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/lib/src/http_auth_credentials_database.dart">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="60">
<caret line="4" column="32" selection-start-line="4" selection-start-column="6" selection-end-line="4" selection-end-column="32" />
<folding>
<element signature="e#0#10#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/example/assets/client1-crt.pem">
<provider selected="true" editor-type-id="text-editor" /> <provider selected="true" editor-type-id="text-editor" />
</entry> </entry>
<entry file="file://$PROJECT_DIR$/example/assets/client1-crt.crt"> <entry file="file://$PROJECT_DIR$/.packages">
<provider selected="true" editor-type-id="text-editor" />
</entry>
<entry file="file://$PROJECT_DIR$/ios/flutter_inappbrowser.podspec">
<provider selected="true" editor-type-id="text-editor"> <provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="72"> <state relative-caret-position="315">
<caret line="11" column="57" lean-forward="true" selection-start-line="11" selection-start-column="57" selection-end-line="11" selection-end-column="57" /> <caret line="21" column="25" selection-start-line="21" selection-start-column="25" selection-end-line="21" selection-end-column="25" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/pubspec.yaml">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="210">
<caret line="14" column="16" selection-start-line="14" selection-start-column="16" selection-end-line="14" selection-end-column="16" />
</state> </state>
</provider> </provider>
</entry> </entry>
<entry file="file://$PROJECT_DIR$/example/pubspec.yaml"> <entry file="file://$PROJECT_DIR$/example/pubspec.yaml">
<provider selected="true" editor-type-id="text-editor"> <provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="256"> <state relative-caret-position="315">
<caret line="43" column="9" selection-start-line="43" selection-start-column="9" selection-end-line="43" selection-end-column="9" /> <caret line="21" column="28" selection-start-line="21" selection-start-column="28" selection-end-line="22" selection-end-column="23" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/nodejs_server_test_auth_basic_and_ssl/index.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="225">
<caret line="15" column="2" lean-forward="true" selection-start-line="15" selection-start-column="2" selection-end-line="15" selection-end-column="2" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/lib/src/in_app_browser.dart">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="170">
<caret line="66" column="68" selection-start-line="66" selection-start-column="66" selection-end-line="66" selection-end-column="68" />
<folding>
<element signature="e#0#20#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/lib/src/http_auth_credentials_database.dart">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="175">
<caret line="28" column="30" selection-start-line="28" selection-start-column="30" selection-end-line="28" selection-end-column="30" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/CHANGELOG.md">
<provider selected="true" editor-type-id="text-editor">
<state>
<caret column="8" selection-start-column="8" selection-end-column="8" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/example/assets/index.html">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="-6">
<caret line="22" column="17" selection-start-line="22" selection-start-column="17" selection-end-line="22" selection-end-column="17" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/lib/src/webview_options.dart">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="175">
<caret line="42" column="83" selection-start-line="42" selection-start-column="83" selection-end-line="42" selection-end-column="83" />
<folding>
<element signature="e#0#17#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/lib/src/types.dart">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="170">
<caret line="24" column="25" selection-start-line="24" selection-start-column="6" selection-end-line="24" selection-end-column="25" />
<folding>
<element signature="e#0#25#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/lib/src/in_app_webview.dart">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="623">
<caret line="397" column="54" selection-start-line="397" selection-start-column="54" selection-end-line="397" selection-end-column="54" />
<folding>
<element signature="e#0#17#0" expanded="true" />
</folding>
</state> </state>
</provider> </provider>
</entry> </entry>
<entry file="file://$PROJECT_DIR$/example/lib/inline_example.screen.dart"> <entry file="file://$PROJECT_DIR$/example/lib/inline_example.screen.dart">
<provider selected="true" editor-type-id="text-editor"> <provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="253"> <state relative-caret-position="85">
<caret line="88" column="12" selection-start-line="88" selection-start-column="12" selection-end-line="88" selection-end-column="12" /> <caret line="144" column="16" lean-forward="true" selection-start-line="144" selection-start-column="16" selection-end-line="146" selection-end-column="21" />
<folding> <folding>
<element signature="e#0#22#0" expanded="true" /> <element signature="e#0#22#0" expanded="true" />
</folding> </folding>

View File

@ -13,16 +13,19 @@
- Added `onLoadResourceCustomScheme` event and `resourceCustomSchemes` option to set custom schemes that WebView must handle to load resources - Added `onLoadResourceCustomScheme` event and `resourceCustomSchemes` option to set custom schemes that WebView must handle to load resources
- Added `onTargetBlank` event and `useOnTargetBlank` option to manage links with `target="_blank"` - Added `onTargetBlank` event and `useOnTargetBlank` option to manage links with `target="_blank"`
- Added `ContentBlocker`, `ContentBlockerTrigger` and `ContentBlockerAction` classes and the `contentBlockers` option that allows to define a set of rules to use to block content in the WebView - Added `ContentBlocker`, `ContentBlockerTrigger` and `ContentBlockerAction` classes and the `contentBlockers` option that allows to define a set of rules to use to block content in the WebView
- Added new WebView options: `minimumFontSize`, `debuggingEnabled` - Added new WebView options: `minimumFontSize`, `debuggingEnabled`, `preferredContentMode`
- Added new Android WebView options: `allowContentAccess`, `allowFileAccess`, `allowFileAccessFromFileURLs`, `allowUniversalAccessFromFileURLs`, `appCacheEnabled`, `appCachePath`, `blockNetworkImage`, `blockNetworkLoads`, `cacheMode`, `cursiveFontFamily`, `defaultFixedFontSize`, `defaultFontSize`, `defaultTextEncodingName`, `disabledActionModeMenuItems`, `fantasyFontFamily`, `fixedFontFamily`, `forceDark`, `geolocationEnabled`, `layoutAlgorithm`, `loadWithOverviewMode`, `loadsImagesAutomatically`, `minimumLogicalFontSize`, `needInitialFocus`, `offscreenPreRaster`, `sansSerifFontFamily`, `serifFontFamily`, `standardFontFamily` - Added new Android WebView options: `allowContentAccess`, `allowFileAccess`, `allowFileAccessFromFileURLs`, `allowUniversalAccessFromFileURLs`, `appCacheEnabled`, `appCachePath`, `blockNetworkImage`, `blockNetworkLoads`, `cacheMode`, `cursiveFontFamily`, `defaultFixedFontSize`, `defaultFontSize`, `defaultTextEncodingName`, `disabledActionModeMenuItems`, `fantasyFontFamily`, `fixedFontFamily`, `forceDark`, `geolocationEnabled`, `layoutAlgorithm`, `loadWithOverviewMode`, `loadsImagesAutomatically`, `minimumLogicalFontSize`, `needInitialFocus`, `offscreenPreRaster`, `sansSerifFontFamily`, `serifFontFamily`, `standardFontFamily`
- Added new iOS WebView options: `applicationNameForUserAgent`, `isFraudulentWebsiteWarningEnabled`, `selectionGranularity`, `dataDetectorTypes`, `preferredContentMode` - Added new iOS WebView options: `applicationNameForUserAgent`, `isFraudulentWebsiteWarningEnabled`, `selectionGranularity`, `dataDetectorTypes`
- Added `onGeolocationPermissionsShowPrompt` event and `GeolocationPermissionShowPromptResponse` class (available only for Android) - Added `onGeolocationPermissionsShowPrompt` event and `GeolocationPermissionShowPromptResponse` class (available only for Android)
- Added `startSafeBrowsing`, `setSafeBrowsingWhitelist` and `getSafeBrowsingPrivacyPolicyUrl` methods (available only for Android) - Added `startSafeBrowsing`, `setSafeBrowsingWhitelist` and `getSafeBrowsingPrivacyPolicyUrl` methods (available only for Android)
- Added `clearSslPreferences` and `clearClientCertPreferences` methods (available only for Android)
- Added `onSafeBrowsingHit` event (available only for Android) - Added `onSafeBrowsingHit` event (available only for Android)
- Added `onJsAlert`, `onJsConfirm` and `onJsPrompt` events to manage javascript popup dialogs - Added `onJsAlert`, `onJsConfirm` and `onJsPrompt` events to manage javascript popup dialogs
- Added `onReceivedHttpAuthRequest` event - Added `onReceivedHttpAuthRequest` event
- Added `clearCache()` method - Added `clearCache()` method
- Added `HttpAuthCredentialDatabase` class - Added `HttpAuthCredentialDatabase` class
- Added `onReceivedServerTrustAuthRequest` and `onReceivedClientCertRequest` events to manage SSL requests
### BREAKING CHANGES ### BREAKING CHANGES
- Deleted `WebResourceRequest` class - Deleted `WebResourceRequest` class
@ -30,7 +33,7 @@
- Updated `ConsoleMessageLevel` class - Updated `ConsoleMessageLevel` class
- Updated `onLoadResource` event - Updated `onLoadResource` event
- Updated `CookieManager` class - Updated `CookieManager` class
- WebView options are now available with the new corresponding classes: `InAppWebViewOptions`, `AndroidInAppWebViewOptions`, `iOSInAppWebViewOptions`, `InAppBrowserOptions`, `AndroidInAppBrowserOptions`, `iOSInAppBrowserOptions`, `AndroidChromeCustomTabsOptions` and `iOSChromeCustomTabsOptions` - WebView options are now available with the new corresponding classes: `InAppWebViewOptions`, `AndroidInAppWebViewOptions`, `iOSInAppWebViewOptions`, `InAppBrowserOptions`, `AndroidInAppBrowserOptions`, `iOSInAppBrowserOptions`, `AndroidChromeCustomTabsOptions` and `iOSSafariOptions`
## 1.2.1 ## 1.2.1

View File

@ -93,7 +93,7 @@ public class FlutterWebView implements PlatformView, MethodCallHandler {
} }
@Override @Override
public void onMethodCall(MethodCall call, Result result) { public void onMethodCall(MethodCall call, final Result result) {
String source; String source;
String urlFile; String urlFile;
switch (call.method) { switch (call.method) {
@ -250,6 +250,23 @@ public class FlutterWebView implements PlatformView, MethodCallHandler {
webView.clearAllCache(); webView.clearAllCache();
result.success(true); result.success(true);
break; break;
case "clearSslPreferences":
if (webView != null)
webView.clearSslPreferences();
result.success(true);
break;
case "clearClientCertPreferences":
if (webView != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
webView.clearClientCertPreferences(new Runnable() {
@Override
public void run() {
result.success(true);
}
});
} else {
result.success(false);
}
break;
case "dispose": case "dispose":
dispose(); dispose();
result.success(true); result.success(true);

View File

@ -9,6 +9,8 @@ import android.graphics.Picture;
import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.ColorDrawable;
import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.util.Log; import android.util.Log;
import android.view.KeyEvent; import android.view.KeyEvent;
@ -479,4 +481,22 @@ public class InAppBrowserActivity extends AppCompatActivity {
if (webView != null) if (webView != null)
webView.clearAllCache(); webView.clearAllCache();
} }
public void clearSslPreferences() {
if (webView != null)
webView.clearSslPreferences();
}
public void clearClientCertPreferences(final MethodChannel.Result result) {
if (webView != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
webView.clearClientCertPreferences(new Runnable() {
@Override
public void run() {
result.success(true);
}
});
}
else
result.success(false);
}
} }

View File

@ -311,6 +311,13 @@ public class InAppBrowserFlutterPlugin implements MethodCallHandler {
clearCache(uuid); clearCache(uuid);
result.success(true); result.success(true);
break; break;
case "clearSslPreferences":
clearSslPreferences(uuid);
result.success(true);
break;
case "clearClientCertPreferences":
clearClientCertPreferences(uuid, result);
break;
default: default:
result.notImplemented(); result.notImplemented();
} }
@ -699,4 +706,17 @@ public class InAppBrowserFlutterPlugin implements MethodCallHandler {
if (inAppBrowserActivity != null) if (inAppBrowserActivity != null)
inAppBrowserActivity.clearCache(); inAppBrowserActivity.clearCache();
} }
public void clearSslPreferences(String uuid) {
InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
if (inAppBrowserActivity != null)
inAppBrowserActivity.clearSslPreferences();
}
private void clearClientCertPreferences(String uuid, Result result) {
InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
if (inAppBrowserActivity != null)
inAppBrowserActivity.clearClientCertPreferences(result);
result.success(false);
}
} }

View File

@ -17,7 +17,6 @@ import android.webkit.ValueCallback;
import android.webkit.WebBackForwardList; import android.webkit.WebBackForwardList;
import android.webkit.WebHistoryItem; import android.webkit.WebHistoryItem;
import android.webkit.WebSettings; import android.webkit.WebSettings;
import android.webkit.WebView;
import com.pichillilorenzo.flutter_inappbrowser.ContentBlocker.ContentBlocker; import com.pichillilorenzo.flutter_inappbrowser.ContentBlocker.ContentBlocker;
import com.pichillilorenzo.flutter_inappbrowser.ContentBlocker.ContentBlockerAction; import com.pichillilorenzo.flutter_inappbrowser.ContentBlocker.ContentBlockerAction;
@ -41,6 +40,8 @@ import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.PluginRegistry; import io.flutter.plugin.common.PluginRegistry;
import okhttp3.OkHttpClient; import okhttp3.OkHttpClient;
import static com.pichillilorenzo.flutter_inappbrowser.InAppWebView.PreferredContentModeOptionType.*;
final public class InAppWebView extends InputAwareWebView { final public class InAppWebView extends InputAwareWebView {
static final String LOG_TAG = "InAppWebView"; static final String LOG_TAG = "InAppWebView";
@ -221,6 +222,18 @@ final public class InAppWebView extends InputAwareWebView {
settings.setSansSerifFontFamily(options.sansSerifFontFamily); settings.setSansSerifFontFamily(options.sansSerifFontFamily);
settings.setSerifFontFamily(options.serifFontFamily); settings.setSerifFontFamily(options.serifFontFamily);
settings.setStandardFontFamily(options.standardFontFamily); settings.setStandardFontFamily(options.standardFontFamily);
if (options.preferredContentMode != null) {
switch (fromValue(options.preferredContentMode)) {
case DESKTOP:
setDesktopMode(true);
break;
case MOBILE:
setDesktopMode(false);
break;
case RECOMMENDED:
break;
}
}
contentBlockerHandler.getRuleList().clear(); contentBlockerHandler.getRuleList().clear();
for (Map<String, Map<String, Object>> contentBlocker : options.contentBlockers) { for (Map<String, Map<String, Object>> contentBlocker : options.contentBlockers) {
@ -701,6 +714,24 @@ final public class InAppWebView extends InputAwareWebView {
} }
} }
public void setDesktopMode(final boolean enabled) {
final WebSettings webSettings = getSettings();
final String newUserAgent;
if (enabled) {
newUserAgent = webSettings.getUserAgentString().replace("Mobile", "eliboM").replace("Android", "diordnA");
}
else {
newUserAgent = webSettings.getUserAgentString().replace("eliboM", "Mobile").replace("diordnA", "Android");
}
webSettings.setUserAgentString(newUserAgent);
webSettings.setUseWideViewPort(enabled);
webSettings.setLoadWithOverviewMode(enabled);
webSettings.setSupportZoom(enabled);
webSettings.setBuiltInZoomControls(enabled);
}
@Override @Override
public void destroy() { public void destroy() {
super.destroy(); super.destroy();

View File

@ -1,11 +1,12 @@
package com.pichillilorenzo.flutter_inappbrowser.InAppWebView; package com.pichillilorenzo.flutter_inappbrowser.InAppWebView;
import android.content.Intent; import android.content.Intent;
import android.content.res.AssetManager;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.net.Uri; import android.net.Uri;
import android.net.http.SslCertificate;
import android.net.http.SslError; import android.net.http.SslError;
import android.os.Build; import android.os.Build;
import android.os.Bundle;
import android.util.Base64; import android.util.Base64;
import android.util.Log; import android.util.Log;
import android.webkit.ClientCertRequest; import android.webkit.ClientCertRequest;
@ -30,18 +31,14 @@ import com.pichillilorenzo.flutter_inappbrowser.InAppBrowserFlutterPlugin;
import com.pichillilorenzo.flutter_inappbrowser.JavaScriptBridgeInterface; import com.pichillilorenzo.flutter_inappbrowser.JavaScriptBridgeInterface;
import com.pichillilorenzo.flutter_inappbrowser.Util; import com.pichillilorenzo.flutter_inappbrowser.Util;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
import java.security.Key;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.cert.Certificate; import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.util.Enumeration;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -50,10 +47,6 @@ import io.flutter.plugin.common.MethodChannel;
public class InAppWebViewClient extends WebViewClient { public class InAppWebViewClient extends WebViewClient {
private X509Certificate[] mCertificates;
private PrivateKey mPrivateKey;
protected static final String LOG_TAG = "IABWebViewClient"; protected static final String LOG_TAG = "IABWebViewClient";
private FlutterWebView flutterWebView; private FlutterWebView flutterWebView;
private InAppBrowserActivity inAppBrowserActivity; private InAppBrowserActivity inAppBrowserActivity;
@ -280,44 +273,6 @@ public class InAppWebViewClient extends WebViewClient {
getChannel().invokeMethod("onLoadError", obj); getChannel().invokeMethod("onLoadError", obj);
} }
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
previousAuthRequestFailureCount = 0;
credentialsProposed = null;
Map<String, Object> obj = new HashMap<>();
if (inAppBrowserActivity != null)
obj.put("uuid", inAppBrowserActivity.uuid);
obj.put("url", error.getUrl());
obj.put("code", error.getPrimaryError());
String message;
switch (error.getPrimaryError()) {
case SslError.SSL_DATE_INVALID:
message = "The date of the certificate is invalid";
break;
case SslError.SSL_EXPIRED:
message = "The certificate has expired";
break;
case SslError.SSL_IDMISMATCH:
message = "Hostname mismatch";
break;
default:
case SslError.SSL_INVALID:
message = "A generic error occurred";
break;
case SslError.SSL_NOTYETVALID:
message = "The certificate is not yet valid";
break;
case SslError.SSL_UNTRUSTED:
message = "The certificate authority is not trusted";
break;
}
obj.put("message", "SslError: " + message);
getChannel().invokeMethod("onLoadError", obj);
handler.cancel();
//handler.proceed();
}
/** /**
* On received http auth request. * On received http auth request.
*/ */
@ -360,11 +315,6 @@ public class InAppWebViewClient extends WebViewClient {
Integer action = (Integer) responseMap.get("action"); Integer action = (Integer) responseMap.get("action");
if (action != null) { if (action != null) {
switch (action) { switch (action) {
case 0:
credentialsProposed = null;
previousAuthRequestFailureCount = 0;
handler.cancel();
return;
case 1: case 1:
String username = (String) responseMap.get("username"); String username = (String) responseMap.get("username");
String password = (String) responseMap.get("password"); String password = (String) responseMap.get("password");
@ -385,6 +335,12 @@ public class InAppWebViewClient extends WebViewClient {
} }
//handler.useHttpAuthUsernamePassword(); //handler.useHttpAuthUsernamePassword();
return; return;
case 0:
default:
credentialsProposed = null;
previousAuthRequestFailureCount = 0;
handler.cancel();
return;
} }
} }
} }
@ -404,49 +360,199 @@ public class InAppWebViewClient extends WebViewClient {
}); });
} }
// private void loadCertificateAndPrivateKey(InAppWebView webView) { /**
// * SslCertificate class does not has a public getter for the underlying
// try { * X509Certificate, we can only do this by hack. This only works for andorid 4.0+
// String key1 = webView.registrar.lookupKeyForAsset("assets/certificate.pfx"); * https://groups.google.com/forum/#!topic/android-developers/eAPJ6b7mrmg
// AssetManager mg = webView.registrar.activeContext().getResources().getAssets(); */
// InputStream certificateFileStream = mg.open(key1);//getClass().getResourceAsStream(); public static X509Certificate getX509CertFromSslCertHack(SslCertificate sslCert) {
// X509Certificate x509Certificate = null;
// KeyStore keyStore = KeyStore.getInstance("PKCS12");
// String password = ""; Bundle bundle = SslCertificate.saveState(sslCert);
// keyStore.load(certificateFileStream, password != null ? password.toCharArray() : null); byte[] bytes = bundle.getByteArray("x509-certificate");
//
// Enumeration<String> aliases = keyStore.aliases(); if (bytes == null) {
// String alias = aliases.nextElement(); x509Certificate = null;
// } else {
// Key key = keyStore.getKey(alias, password.toCharArray()); try {
// if (key instanceof PrivateKey) { CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
// mPrivateKey = (PrivateKey)key; Certificate cert = certFactory.generateCertificate(new ByteArrayInputStream(bytes));
// Certificate cert = keyStore.getCertificate(alias); x509Certificate = (X509Certificate) cert;
// mCertificates = new X509Certificate[1]; } catch (CertificateException e) {
// mCertificates[0] = (X509Certificate)cert; x509Certificate = null;
// } }
// }
// certificateFileStream.close();
// return x509Certificate;
// } catch (Exception e) { }
// e.printStackTrace();
// Log.e(LOG_TAG, e.getMessage()); @Override
// } public void onReceivedSslError(final WebView view, final SslErrorHandler handler, final SslError error) {
// } URL url;
// try {
// @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) url = new URL(error.getUrl());
// @Override } catch (MalformedURLException e) {
// public void onReceivedClientCertRequest(WebView view, ClientCertRequest request) { e.printStackTrace();
// Log.d(LOG_TAG, request.getHost()); Log.e(LOG_TAG, e.getMessage());
// Log.d(LOG_TAG, request.getKeyTypes().toString()); handler.cancel();
// Log.d(LOG_TAG, request.getPort() + ""); return;
// Log.d(LOG_TAG, request.getPrincipals().toString()); }
//
// if (mCertificates == null || mPrivateKey == null) { final String host = url.getHost();
// loadCertificateAndPrivateKey((InAppWebView) view); final String protocol = url.getProtocol();
// } final String realm = null;
// request.proceed(mPrivateKey, mCertificates); final int port = url.getPort();
// }
Map<String, Object> obj = new HashMap<>();
if (inAppBrowserActivity != null)
obj.put("uuid", inAppBrowserActivity.uuid);
obj.put("host", host);
obj.put("protocol", protocol);
obj.put("realm", realm);
obj.put("port", port);
obj.put("error", error.getPrimaryError());
obj.put("serverCertificate", null);
try {
X509Certificate certificate;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
certificate = error.getCertificate().getX509Certificate();
} else {
certificate = getX509CertFromSslCertHack(error.getCertificate());
}
obj.put("serverCertificate", certificate.getEncoded());
} catch (CertificateEncodingException e) {
e.printStackTrace();
Log.e(LOG_TAG,e.getLocalizedMessage());
}
String message;
switch (error.getPrimaryError()) {
case SslError.SSL_DATE_INVALID:
message = "The date of the certificate is invalid";
break;
case SslError.SSL_EXPIRED:
message = "The certificate has expired";
break;
case SslError.SSL_IDMISMATCH:
message = "Hostname mismatch";
break;
default:
case SslError.SSL_INVALID:
message = "A generic error occurred";
break;
case SslError.SSL_NOTYETVALID:
message = "The certificate is not yet valid";
break;
case SslError.SSL_UNTRUSTED:
message = "The certificate authority is not trusted";
break;
}
obj.put("message", message);
Log.d(LOG_TAG, obj.toString());
getChannel().invokeMethod("onReceivedServerTrustAuthRequest", obj, new MethodChannel.Result() {
@Override
public void success(Object response) {
if (response != null) {
Map<String, Object> responseMap = (Map<String, Object>) response;
Integer action = (Integer) responseMap.get("action");
if (action != null) {
switch (action) {
case 1:
handler.proceed();
return;
case 0:
default:
handler.cancel();
return;
}
}
}
handler.cancel();
}
@Override
public void error(String s, String s1, Object o) {
Log.e(LOG_TAG, s + ", " + s1);
}
@Override
public void notImplemented() {
handler.cancel();
}
});
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
public void onReceivedClientCertRequest(final WebView view, final ClientCertRequest request) {
URL url;
try {
url = new URL(view.getUrl());
} catch (MalformedURLException e) {
e.printStackTrace();
Log.e(LOG_TAG, e.getMessage());
request.cancel();
return;
}
final String protocol = url.getProtocol();
final String realm = null;
Map<String, Object> obj = new HashMap<>();
if (inAppBrowserActivity != null)
obj.put("uuid", inAppBrowserActivity.uuid);
obj.put("host", request.getHost());
obj.put("protocol", protocol);
obj.put("realm", realm);
obj.put("port", request.getPort());
getChannel().invokeMethod("onReceivedClientCertRequest", obj, new MethodChannel.Result() {
@Override
public void success(Object response) {
if (response != null) {
Map<String, Object> responseMap = (Map<String, Object>) response;
Integer action = (Integer) responseMap.get("action");
if (action != null) {
switch (action) {
case 1:
{
InAppWebView webView = (InAppWebView) view;
String certificatePath = (String) responseMap.get("certificatePath");
String certificatePassword = (String) responseMap.get("certificatePassword");
String androidKeyStoreType = (String) responseMap.get("androidKeyStoreType");
Util.PrivateKeyAndCertificates privateKeyAndCertificates = Util.loadPrivateKeyAndCertificate(webView.registrar, certificatePath, certificatePassword, androidKeyStoreType);
request.proceed(privateKeyAndCertificates.privateKey, privateKeyAndCertificates.certificates);
}
return;
case 2:
request.ignore();
return;
case 0:
default:
request.cancel();
return;
}
}
}
request.cancel();
}
@Override
public void error(String s, String s1, Object o) {
Log.e(LOG_TAG, s + ", " + s1);
}
@Override
public void notImplemented() {
request.cancel();
}
});
}
@Override @Override
public void onScaleChanged(WebView view, float oldScale, float newScale) { public void onScaleChanged(WebView view, float oldScale, float newScale) {
@ -482,6 +588,7 @@ public class InAppWebViewClient extends WebViewClient {
callback.proceed(report); callback.proceed(report);
return; return;
case 2: case 2:
default:
callback.showInterstitial(report); callback.showInterstitial(report);
return; return;
} }

View File

@ -23,11 +23,12 @@ public class InAppWebViewOptions extends Options {
public boolean javaScriptCanOpenWindowsAutomatically = false; public boolean javaScriptCanOpenWindowsAutomatically = false;
public boolean mediaPlaybackRequiresUserGesture = true; public boolean mediaPlaybackRequiresUserGesture = true;
public Integer textZoom = 100; public Integer textZoom = 100;
public Integer minimumFontSize = 8;
public boolean verticalScrollBarEnabled = true; public boolean verticalScrollBarEnabled = true;
public boolean horizontalScrollBarEnabled = true; public boolean horizontalScrollBarEnabled = true;
public List<String> resourceCustomSchemes = new ArrayList<>(); public List<String> resourceCustomSchemes = new ArrayList<>();
public List<Map<String, Map<String, Object>>> contentBlockers = new ArrayList<>(); public List<Map<String, Map<String, Object>>> contentBlockers = new ArrayList<>();
public Integer minimumFontSize = 8; public Integer preferredContentMode = PreferredContentModeOptionType.RECOMMENDED.toValue();
public boolean clearSessionCache = false; public boolean clearSessionCache = false;
public boolean builtInZoomControls = false; public boolean builtInZoomControls = false;

View File

@ -0,0 +1,31 @@
package com.pichillilorenzo.flutter_inappbrowser.InAppWebView;
import com.pichillilorenzo.flutter_inappbrowser.ContentBlocker.ContentBlockerActionType;
public enum PreferredContentModeOptionType {
RECOMMENDED (0),
MOBILE (1),
DESKTOP (2);
private final int value;
private PreferredContentModeOptionType(int value) {
this.value = value;
}
public boolean equalsValue(int otherValue) {
return value == otherValue;
}
public static PreferredContentModeOptionType fromValue(int value) {
for( PreferredContentModeOptionType type : PreferredContentModeOptionType.values()) {
if(value == type.toValue())
return type;
}
throw new IllegalArgumentException("No enum constant: " + value);
}
public int toValue() {
return this.value;
}
}

View File

@ -5,8 +5,16 @@ import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import android.util.Log; import android.util.Log;
import com.pichillilorenzo.flutter_inappbrowser.InAppWebView.InAppWebView;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.security.Key;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.Enumeration;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
@ -23,12 +31,11 @@ public class Util {
public static String getUrlAsset(PluginRegistry.Registrar registrar, String assetFilePath) throws IOException { public static String getUrlAsset(PluginRegistry.Registrar registrar, String assetFilePath) throws IOException {
String key = registrar.lookupKeyForAsset(assetFilePath); String key = registrar.lookupKeyForAsset(assetFilePath);
AssetManager mg = registrar.activeContext().getResources().getAssets();
InputStream is = null; InputStream is = null;
IOException e = null; IOException e = null;
try { try {
is = mg.open(key); is = getFileAsset(registrar, assetFilePath);
} catch (IOException ex) { } catch (IOException ex) {
e = ex; e = ex;
} finally { } finally {
@ -47,51 +54,98 @@ public class Util {
return ANDROID_ASSET_URL + key; return ANDROID_ASSET_URL + key;
} }
public static WaitFlutterResult invokeMethodAndWait(final MethodChannel channel, final String method, final Object arguments) throws InterruptedException { public static InputStream getFileAsset(PluginRegistry.Registrar registrar, String assetFilePath) throws IOException {
final CountDownLatch latch = new CountDownLatch(1); String key = registrar.lookupKeyForAsset(assetFilePath);
AssetManager mg = registrar.activeContext().getResources().getAssets();
return mg.open(key);
}
final Map<String, Object> flutterResultMap = new HashMap<>(); public static WaitFlutterResult invokeMethodAndWait(final MethodChannel channel, final String method, final Object arguments) throws InterruptedException {
flutterResultMap.put("result", null); final CountDownLatch latch = new CountDownLatch(1);
flutterResultMap.put("error", null);
Handler handler = new Handler(Looper.getMainLooper()); final Map<String, Object> flutterResultMap = new HashMap<>();
handler.post(new Runnable() { flutterResultMap.put("result", null);
@Override flutterResultMap.put("error", null);
public void run() {
channel.invokeMethod(method, arguments, new MethodChannel.Result() {
@Override
public void success(Object result) {
flutterResultMap.put("result", result);
latch.countDown();
}
@Override Handler handler = new Handler(Looper.getMainLooper());
public void error(String s, String s1, Object o) { handler.post(new Runnable() {
flutterResultMap.put("error", "ERROR: " + s + " " + s1); @Override
flutterResultMap.put("result", o); public void run() {
latch.countDown(); channel.invokeMethod(method, arguments, new MethodChannel.Result() {
} @Override
public void success(Object result) {
flutterResultMap.put("result", result);
latch.countDown();
}
@Override @Override
public void notImplemented() { public void error(String s, String s1, Object o) {
latch.countDown(); flutterResultMap.put("error", "ERROR: " + s + " " + s1);
} flutterResultMap.put("result", o);
}); latch.countDown();
} }
});
latch.await(); @Override
public void notImplemented() {
return new WaitFlutterResult(flutterResultMap.get("result"), (String) flutterResultMap.get("error")); latch.countDown();
} }
});
public static class WaitFlutterResult {
public Object result;
public String error;
public WaitFlutterResult(Object r, String e) {
result = r;
error = e;
} }
});
latch.await();
return new WaitFlutterResult(flutterResultMap.get("result"), (String) flutterResultMap.get("error"));
}
public static class WaitFlutterResult {
public Object result;
public String error;
public WaitFlutterResult(Object r, String e) {
result = r;
error = e;
} }
}
public static PrivateKeyAndCertificates loadPrivateKeyAndCertificate(PluginRegistry.Registrar registrar, String certificatePath, String certificatePassword, String keyStoreType) {
PrivateKeyAndCertificates privateKeyAndCertificates = null;
try {
InputStream certificateFileStream = getFileAsset(registrar, certificatePath);
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(certificateFileStream, certificatePassword != null ? certificatePassword.toCharArray() : null);
Enumeration<String> aliases = keyStore.aliases();
String alias = aliases.nextElement();
Key key = keyStore.getKey(alias, certificatePassword.toCharArray());
if (key instanceof PrivateKey) {
PrivateKey privateKey = (PrivateKey)key;
Certificate cert = keyStore.getCertificate(alias);
X509Certificate[] certificates = new X509Certificate[1];
certificates[0] = (X509Certificate)cert;
privateKeyAndCertificates = new PrivateKeyAndCertificates(privateKey, certificates);
}
certificateFileStream.close();
} catch (Exception e) {
e.printStackTrace();
Log.e(LOG_TAG, e.getMessage());
}
return privateKeyAndCertificates;
}
public static class PrivateKeyAndCertificates {
public X509Certificate[] certificates;
public PrivateKey privateKey;
public PrivateKeyAndCertificates(PrivateKey privateKey, X509Certificate[] certificates) {
this.privateKey = privateKey;
this.certificates = certificates;
}
}
} }

Binary file not shown.

View File

@ -82,11 +82,11 @@ class _InlineExampleScreenState extends State<InlineExampleScreen> {
BoxDecoration(border: Border.all(color: Colors.blueAccent)), BoxDecoration(border: Border.all(color: Colors.blueAccent)),
child: InAppWebView( child: InAppWebView(
//initialUrl: "https://www.youtube.com/embed/M7lc1UVf-VE?playsinline=1", //initialUrl: "https://www.youtube.com/embed/M7lc1UVf-VE?playsinline=1",
//initialUrl: "https://flutter.dev/", initialUrl: "https://flutter.dev/",
//initialUrl: "chrome://safe-browsing/match?type=malware", //initialUrl: "chrome://safe-browsing/match?type=malware",
//initialUrl: "http://192.168.1.20:8081/", //initialUrl: "http://192.168.1.20:8081/",
//initialUrl: "https://192.168.1.20:4433/authenticate", //initialUrl: "https://192.168.1.20:4433/",
initialFile: "assets/index.html", //initialFile: "assets/index.html",
initialHeaders: {}, initialHeaders: {},
initialOptions: [ initialOptions: [
InAppWebViewOptions( InAppWebViewOptions(
@ -95,15 +95,16 @@ class _InlineExampleScreenState extends State<InlineExampleScreen> {
useOnTargetBlank: true, useOnTargetBlank: true,
//useOnLoadResource: true, //useOnLoadResource: true,
useOnDownloadStart: true, useOnDownloadStart: true,
preferredContentMode: InAppWebViewUserPreferredContentMode.DESKTOP,
resourceCustomSchemes: ["my-special-custom-scheme"], resourceCustomSchemes: ["my-special-custom-scheme"],
contentBlockers: [ /*contentBlockers: [
ContentBlocker( ContentBlocker(
ContentBlockerTrigger(".*", ContentBlockerTrigger(".*",
resourceType: [ContentBlockerTriggerResourceType.IMAGE, ContentBlockerTriggerResourceType.STYLE_SHEET], resourceType: [ContentBlockerTriggerResourceType.IMAGE, ContentBlockerTriggerResourceType.STYLE_SHEET],
ifTopUrl: ["https://getbootstrap.com/"]), ifTopUrl: ["https://getbootstrap.com/"]),
ContentBlockerAction(ContentBlockerActionType.BLOCK) ContentBlockerAction(ContentBlockerActionType.BLOCK)
) )
] ]*/
), ),
AndroidInAppWebViewOptions( AndroidInAppWebViewOptions(
databaseEnabled: true, databaseEnabled: true,
@ -113,9 +114,6 @@ class _InlineExampleScreenState extends State<InlineExampleScreen> {
safeBrowsingEnabled: true, safeBrowsingEnabled: true,
//blockNetworkImage: true, //blockNetworkImage: true,
), ),
iOSInAppWebViewOptions(
preferredContentMode: iOSInAppWebViewUserPreferredContentMode.DESKTOP
)
], ],
onWebViewCreated: (InAppWebViewController controller) { onWebViewCreated: (InAppWebViewController controller) {
webView = controller; webView = controller;
@ -140,6 +138,13 @@ class _InlineExampleScreenState extends State<InlineExampleScreen> {
}, },
onLoadStop: (InAppWebViewController controller, String url) async { onLoadStop: (InAppWebViewController controller, String url) async {
print("stopped $url"); print("stopped $url");
if (Platform.isAndroid) {
controller.clearSslPreferences();
controller.clearClientCertPreferences();
}
// controller.injectScriptCode("""
// document.getElementById("SEARCH_WORD" + 5).scrollIntoView();
// """);
}, },
onLoadError: (InAppWebViewController controller, String url, int code, String message) async { onLoadError: (InAppWebViewController controller, String url, int code, String message) async {
print("error $url: $code, $message"); print("error $url: $code, $message");
@ -241,11 +246,20 @@ class _InlineExampleScreenState extends State<InlineExampleScreen> {
return new SafeBrowsingResponse(report: true, action: action); return new SafeBrowsingResponse(report: true, action: action);
}, },
onReceivedHttpAuthRequest: (InAppWebViewController controller, HttpAuthChallenge challenge) async { onReceivedHttpAuthRequest: (InAppWebViewController controller, HttpAuthChallenge challenge) async {
print("HTTP AUTH REQUEST: " + challenge.protectionSpace.host + ", realm: " + challenge.protectionSpace.realm + print("HTTP AUTH REQUEST: ${challenge.protectionSpace.host}, realm: ${challenge.protectionSpace.realm}, previous failure count: ${challenge.previousFailureCount.toString()}");
", previous failure count: " + challenge.previousFailureCount.toString());
return new HttpAuthResponse(username: "USERNAME", password: "PASSWORD", action: HttpAuthResponseAction.USE_SAVED_HTTP_AUTH_CREDENTIALS, permanentPersistence: true); return new HttpAuthResponse(username: "USERNAME", password: "PASSWORD", action: HttpAuthResponseAction.USE_SAVED_HTTP_AUTH_CREDENTIALS, permanentPersistence: true);
}, },
onReceivedServerTrustAuthRequest: (InAppWebViewController controller, ServerTrustChallenge challenge) async {
print("SERVER TRUST AUTH REQUEST: ${challenge.protectionSpace.host}, SSL ERROR CODE: ${challenge.error.toString()}, MESSAGE: ${challenge.message}");
return new ServerTrustAuthResponse(action: ServerTrustAuthResponseAction.PROCEED);
},
onReceivedClientCertRequest: (InAppWebViewController controller, ClientCertChallenge challenge) async {
print("CLIENT CERT REQUEST: ${challenge.protectionSpace.host}");
return new ClientCertResponse(certificatePath: "assets/certificate.pfx", certificatePassword: "", androidKeyStoreType: "PKCS12", action: ClientCertResponseAction.PROCEED);
},
), ),
), ),
), ),

View File

@ -42,6 +42,7 @@ flutter:
uses-material-design: true uses-material-design: true
assets: assets:
- assets/certificate.pfx
- assets/index.html - assets/index.html
- assets/page-1.html - assets/page-1.html
- assets/page-2.html - assets/page-2.html

View File

@ -38,22 +38,25 @@ class CredentialDatabase: NSObject, FlutterPlugin {
"realm": protectionSpace.realm, "realm": protectionSpace.realm,
"port": protectionSpace.port "port": protectionSpace.port
] as [String : Any?] ] as [String : Any?]
var crendentials: [[String: String?]] = [] var crendentials: [[String: String?]] = []
for c in credentials { for c in credentials {
let credential: [String: String?] = [ if let username = c.value.user, let password = c.value.password {
"username": c.value.user, let credential: [String: String] = [
"password": c.value.password, "username": username,
] "password": password,
crendentials.append(credential) ]
crendentials.append(credential)
}
} }
let dict = [ if crendentials.count > 0 {
"protectionSpace": protectionSpaceDict, let dict = [
"credentials": crendentials "protectionSpace": protectionSpaceDict,
] as [String : Any] "credentials": crendentials
allCredentials.append(dict) ] as [String : Any]
} allCredentials.append(dict)
} }
result(allCredentials) result(allCredentials)
break break
case "getHttpAuthCredentials": case "getHttpAuthCredentials":
@ -70,11 +73,13 @@ class CredentialDatabase: NSObject, FlutterPlugin {
if protectionSpace.host == host && protectionSpace.realm == realm && if protectionSpace.host == host && protectionSpace.realm == realm &&
protectionSpace.protocol == urlProtocol && protectionSpace.port == urlPort { protectionSpace.protocol == urlProtocol && protectionSpace.port == urlPort {
for c in credentials { for c in credentials {
let credential: [String: String?] = [ if let username = c.value.user, let password = c.value.password {
"username": c.value.user, let credential: [String: String] = [
"password": c.value.password, "username": username,
] "password": password,
crendentials.append(credential) ]
crendentials.append(credential)
}
} }
break break
} }
@ -148,8 +153,11 @@ class CredentialDatabase: NSObject, FlutterPlugin {
protectionSpace.protocol == urlProtocol && protectionSpace.port == urlPort { protectionSpace.protocol == urlProtocol && protectionSpace.port == urlPort {
protectionSpaceCredential = protectionSpace protectionSpaceCredential = protectionSpace
for c in credentials { for c in credentials {
credentialsToRemove.append(c.value) if let _ = c.value.user, let _ = c.value.password {
credentialsToRemove.append(c.value)
}
} }
break
} }
} }

View File

@ -82,6 +82,125 @@ window.\(JAVASCRIPT_BRIDGE_NAME).callHandler = function() {
let platformReadyJS = "window.dispatchEvent(new Event('flutterInAppBrowserPlatformReady'));"; let platformReadyJS = "window.dispatchEvent(new Event('flutterInAppBrowserPlatformReady'));";
let searchJavascript = """
var uiWebview_SearchResultCount = 0;
/*!
@method uiWebview_HighlightAllOccurencesOfStringForElement
@abstract // helper function, recursively searches in elements and their child nodes
@discussion // helper function, recursively searches in elements and their child nodes
element - HTML elements
keyword - string to search
*/
function uiWebview_HighlightAllOccurencesOfStringForElement(element,keyword) {
if (element) {
if (element.nodeType == 3) { // Text node
var count = 0;
var elementTmp = element;
while (true) {
var value = elementTmp.nodeValue; // Search for keyword in text node
var idx = value.toLowerCase().indexOf(keyword);
if (idx < 0) break;
count++;
elementTmp = document.createTextNode(value.substr(idx+keyword.length));
}
uiWebview_SearchResultCount += count;
var index = uiWebview_SearchResultCount;
while (true) {
var value = element.nodeValue; // Search for keyword in text node
var idx = value.toLowerCase().indexOf(keyword);
if (idx < 0) break; // not found, abort
//we create a SPAN element for every parts of matched keywords
var span = document.createElement("span");
var text = document.createTextNode(value.substr(idx,keyword.length));
span.appendChild(text);
span.setAttribute("class","uiWebviewHighlight");
span.style.backgroundColor="yellow";
span.style.color="black";
index--;
span.setAttribute("id", "SEARCH_WORD"+(index));
//span.setAttribute("id", "SEARCH_WORD"+uiWebview_SearchResultCount);
//element.parentNode.setAttribute("id", "SEARCH_WORD"+uiWebview_SearchResultCount);
//uiWebview_SearchResultCount++; // update the counter
text = document.createTextNode(value.substr(idx+keyword.length));
element.deleteData(idx, value.length - idx);
var next = element.nextSibling;
//alert(element.parentNode);
element.parentNode.insertBefore(span, next);
element.parentNode.insertBefore(text, next);
element = text;
}
} else if (element.nodeType == 1) { // Element node
if (element.style.display != "none" && element.nodeName.toLowerCase() != 'select') {
for (var i=element.childNodes.length-1; i>=0; i--) {
uiWebview_HighlightAllOccurencesOfStringForElement(element.childNodes[i],keyword);
}
}
}
}
}
// the main entry point to start the search
function uiWebview_HighlightAllOccurencesOfString(keyword) {
uiWebview_RemoveAllHighlights();
uiWebview_HighlightAllOccurencesOfStringForElement(document.body, keyword.toLowerCase());
}
// helper function, recursively removes the highlights in elements and their childs
function uiWebview_RemoveAllHighlightsForElement(element) {
if (element) {
if (element.nodeType == 1) {
if (element.getAttribute("class") == "uiWebviewHighlight") {
var text = element.removeChild(element.firstChild);
element.parentNode.insertBefore(text,element);
element.parentNode.removeChild(element);
return true;
} else {
var normalize = false;
for (var i=element.childNodes.length-1; i>=0; i--) {
if (uiWebview_RemoveAllHighlightsForElement(element.childNodes[i])) {
normalize = true;
}
}
if (normalize) {
element.normalize();
}
}
}
}
return false;
}
// the main entry point to remove the highlights
function uiWebview_RemoveAllHighlights() {
uiWebview_SearchResultCount = 0;
uiWebview_RemoveAllHighlightsForElement(document.body);
}
function uiWebview_ScrollTo(idx) {
var scrollTo = document.getElementById("SEARCH_WORD" + idx);
if (scrollTo) scrollTo.scrollIntoView();
}
"""
public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavigationDelegate, WKScriptMessageHandler { public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavigationDelegate, WKScriptMessageHandler {
var IABController: InAppBrowserWebViewController? var IABController: InAppBrowserWebViewController?
@ -196,7 +315,9 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
if #available(iOS 13.0, *) { if #available(iOS 13.0, *) {
configuration.preferences.isFraudulentWebsiteWarningEnabled = (options?.isFraudulentWebsiteWarningEnabled)! configuration.preferences.isFraudulentWebsiteWarningEnabled = (options?.isFraudulentWebsiteWarningEnabled)!
configuration.defaultWebpagePreferences.preferredContentMode = WKWebpagePreferences.ContentMode(rawValue: (options?.preferredContentMode)!)! if options?.preferredContentMode != nil {
configuration.defaultWebpagePreferences.preferredContentMode = WKWebpagePreferences.ContentMode(rawValue: (options?.preferredContentMode)!)!
}
} else { } else {
// Fallback on earlier versions // Fallback on earlier versions
} }
@ -209,6 +330,8 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
if (options?.clearCache)! { if (options?.clearCache)! {
clearCache() clearCache()
} }
evaluateJavaScript(searchJavascript, completionHandler: nil)
} }
@available(iOS 10.0, *) @available(iOS 10.0, *)
@ -552,7 +675,7 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
if error != nil { if error != nil {
let userInfo = (error! as NSError).userInfo let userInfo = (error! as NSError).userInfo
self.onConsoleMessage(sourceURL: (userInfo["WKJavaScriptExceptionSourceURL"] as? URL)?.absoluteString ?? "", lineNumber: userInfo["WKJavaScriptExceptionLineNumber"] as! Int, message: userInfo["WKJavaScriptExceptionMessage"] as! String, messageLevel: "ERROR") self.onConsoleMessage(sourceURL: (userInfo["WKJavaScriptExceptionSourceURL"] as? URL)?.absoluteString ?? "", lineNumber: userInfo["WKJavaScriptExceptionLineNumber"] as! Int, message: userInfo["WKJavaScriptExceptionMessage"] as! String, messageLevel: 3)
} }
if value == nil { if value == nil {
@ -731,6 +854,15 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
IABController!.forwardButton.isEnabled = canGoForward IABController!.forwardButton.isEnabled = canGoForward
IABController!.spinner.stopAnimating() IABController!.spinner.stopAnimating()
} }
// findAllAsync("Flutter", completionHandler: {(value, error) in
// if error != nil {
// print(error)
// } else if let foundOccurences: Int = value as! Int {
// print(foundOccurences)
// //self.findNext(to: foundOccurences - 4, completionHandler: nil)
// }
// })
} }
public func webView(_ view: WKWebView, public func webView(_ view: WKWebView,
@ -753,8 +885,6 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
public func webView(_ webView: WKWebView, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) { public func webView(_ webView: WKWebView, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
print (challenge.protectionSpace.authenticationMethod)
if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodHTTPBasic || if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodHTTPBasic ||
challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodDefault || challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodDefault ||
challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodHTTPDigest { challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodHTTPDigest {
@ -812,6 +942,7 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
} }
break break
default: default:
InAppWebView.credentialsProposed = []
completionHandler(.performDefaultHandling, nil) completionHandler(.performDefaultHandling, nil)
} }
return; return;
@ -821,34 +952,143 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
}) })
} }
else if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust { else if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
/// TODO: correspond to onSslError event of Android
completionHandler(.performDefaultHandling, nil) guard let serverTrust = challenge.protectionSpace.serverTrust else {
// guard let serverTrust = challenge.protectionSpace.serverTrust else { completionHandler(.performDefaultHandling, nil)
// completionHandler(.performDefaultHandling, nil) return
// return }
// }
// //if checkValidity(of: serverTrust) { onReceivedServerTrustAuthRequest(challenge: challenge, result: {(result) -> Void in
// if true { if result is FlutterError {
// let exceptions = SecTrustCopyExceptions(serverTrust) print((result as! FlutterError).message)
// SecTrustSetExceptions(serverTrust, exceptions) }
// let credential = URLCredential(trust: serverTrust) else if (result as? NSObject) == FlutterMethodNotImplemented {
// completionHandler(.useCredential, credential) completionHandler(.performDefaultHandling, nil)
// } else { }
// // Show a UI here warning the user the server credentials are else {
// // invalid, and cancel the load. var response: [String: Any]
// completionHandler(.cancelAuthenticationChallenge, nil) if let r = result {
// } response = r as! [String: Any]
var action = response["action"] as? Int
action = action != nil ? action : 0;
switch action {
case 0:
InAppWebView.credentialsProposed = []
completionHandler(.cancelAuthenticationChallenge, nil)
break
case 1:
let exceptions = SecTrustCopyExceptions(serverTrust)
SecTrustSetExceptions(serverTrust, exceptions)
let credential = URLCredential(trust: serverTrust)
completionHandler(.useCredential, credential)
break
default:
InAppWebView.credentialsProposed = []
completionHandler(.performDefaultHandling, nil)
}
return;
}
completionHandler(.performDefaultHandling, nil)
}
})
} }
else if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodClientCertificate { else if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodClientCertificate {
/// TODO: load certificates onReceivedClientCertRequest(challenge: challenge, result: {(result) -> Void in
completionHandler(.performDefaultHandling, nil) if result is FlutterError {
print((result as! FlutterError).message)
}
else if (result as? NSObject) == FlutterMethodNotImplemented {
completionHandler(.performDefaultHandling, nil)
}
else {
var response: [String: Any]
if let r = result {
response = r as! [String: Any]
var action = response["action"] as? Int
action = action != nil ? action : 0;
switch action {
case 0:
completionHandler(.cancelAuthenticationChallenge, nil)
break
case 1:
let certificatePath = response["certificatePath"] as! String;
let certificatePassword = response["certificatePassword"] as? String ?? "";
let key = SwiftFlutterPlugin.instance!.registrar!.lookupKey(forAsset: certificatePath)
let path = Bundle.main.path(forResource: key, ofType: nil)!
let PKCS12Data = NSData(contentsOfFile: path)!
if let identityAndTrust: IdentityAndTrust = self.extractIdentity(PKCS12Data: PKCS12Data, password: certificatePassword) {
let urlCredential: URLCredential = URLCredential(
identity: identityAndTrust.identityRef,
certificates: identityAndTrust.certArray as? [AnyObject],
persistence: URLCredential.Persistence.forSession);
completionHandler(.useCredential, urlCredential)
} else {
completionHandler(.performDefaultHandling, nil)
}
break
case 2:
completionHandler(.cancelAuthenticationChallenge, nil)
break
default:
completionHandler(.performDefaultHandling, nil)
}
return;
}
completionHandler(.performDefaultHandling, nil)
}
})
} }
else { else {
completionHandler(.performDefaultHandling, nil) completionHandler(.performDefaultHandling, nil)
} }
} }
fileprivate func createAlertDialog(message: String?, responseMessage: String?, confirmButtonTitle: String?, completionHandler: @escaping () -> Void) { struct IdentityAndTrust {
var identityRef:SecIdentity
var trust:SecTrust
var certArray:AnyObject
}
func extractIdentity(PKCS12Data:NSData, password: String) -> IdentityAndTrust? {
var identityAndTrust:IdentityAndTrust?
var securityError:OSStatus = errSecSuccess
var importResult: CFArray? = nil
securityError = SecPKCS12Import(
PKCS12Data as NSData,
[kSecImportExportPassphrase as String: password] as NSDictionary,
&importResult
)
if securityError == errSecSuccess {
let certItems:CFArray = importResult! as CFArray;
let certItemsArray:Array = certItems as Array
let dict:AnyObject? = certItemsArray.first;
if let certEntry:Dictionary = dict as? Dictionary<String, AnyObject> {
// grab the identity
let identityPointer:AnyObject? = certEntry["identity"];
let secIdentityRef:SecIdentity = (identityPointer as! SecIdentity?)!;
// grab the trust
let trustPointer:AnyObject? = certEntry["trust"];
let trustRef:SecTrust = trustPointer as! SecTrust;
// grab the cert
let chainPointer:AnyObject? = certEntry["chain"];
identityAndTrust = IdentityAndTrust(identityRef: secIdentityRef, trust: trustRef, certArray: chainPointer!);
}
} else {
print("Security Error: " + securityError.description)
if #available(iOS 11.3, *) {
print(SecCopyErrorMessageString(securityError,nil))
}
}
return identityAndTrust;
}
func createAlertDialog(message: String?, responseMessage: String?, confirmButtonTitle: String?, completionHandler: @escaping () -> Void) {
let title = responseMessage != nil && !responseMessage!.isEmpty ? responseMessage : message let title = responseMessage != nil && !responseMessage!.isEmpty ? responseMessage : message
let okButton = confirmButtonTitle != nil && !confirmButtonTitle!.isEmpty ? confirmButtonTitle : NSLocalizedString("Ok", comment: "") let okButton = confirmButtonTitle != nil && !confirmButtonTitle!.isEmpty ? confirmButtonTitle : NSLocalizedString("Ok", comment: "")
let alertController = UIAlertController(title: title, message: nil, let alertController = UIAlertController(title: title, message: nil,
@ -901,7 +1141,7 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
}) })
} }
fileprivate func createConfirmDialog(message: String?, responseMessage: String?, confirmButtonTitle: String?, cancelButtonTitle: String?, completionHandler: @escaping (Bool) -> Void) { func createConfirmDialog(message: String?, responseMessage: String?, confirmButtonTitle: String?, cancelButtonTitle: String?, completionHandler: @escaping (Bool) -> Void) {
let dialogMessage = responseMessage != nil && !responseMessage!.isEmpty ? responseMessage : message let dialogMessage = responseMessage != nil && !responseMessage!.isEmpty ? responseMessage : message
let okButton = confirmButtonTitle != nil && !confirmButtonTitle!.isEmpty ? confirmButtonTitle : NSLocalizedString("Ok", comment: "") let okButton = confirmButtonTitle != nil && !confirmButtonTitle!.isEmpty ? confirmButtonTitle : NSLocalizedString("Ok", comment: "")
let cancelButton = cancelButtonTitle != nil && !cancelButtonTitle!.isEmpty ? cancelButtonTitle : NSLocalizedString("Cancel", comment: "") let cancelButton = cancelButtonTitle != nil && !cancelButtonTitle!.isEmpty ? cancelButtonTitle : NSLocalizedString("Cancel", comment: "")
@ -963,8 +1203,7 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
}) })
} }
func createPromptDialog(message: String, defaultValue: String?, responseMessage: String?, confirmButtonTitle: String?, cancelButtonTitle: String?, value: String?, completionHandler: @escaping (String?) -> Void) {
fileprivate func createPromptDialog(message: String, defaultValue: String?, responseMessage: String?, confirmButtonTitle: String?, cancelButtonTitle: String?, value: String?, completionHandler: @escaping (String?) -> Void) {
let dialogMessage = responseMessage != nil && !responseMessage!.isEmpty ? responseMessage : message let dialogMessage = responseMessage != nil && !responseMessage!.isEmpty ? responseMessage : message
let okButton = confirmButtonTitle != nil && !confirmButtonTitle!.isEmpty ? confirmButtonTitle : NSLocalizedString("Ok", comment: "") let okButton = confirmButtonTitle != nil && !confirmButtonTitle!.isEmpty ? confirmButtonTitle : NSLocalizedString("Ok", comment: "")
let cancelButton = cancelButtonTitle != nil && !cancelButtonTitle!.isEmpty ? cancelButtonTitle : NSLocalizedString("Cancel", comment: "") let cancelButton = cancelButtonTitle != nil && !cancelButtonTitle!.isEmpty ? cancelButtonTitle : NSLocalizedString("Cancel", comment: "")
@ -1169,6 +1408,49 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
} }
} }
public func onReceivedServerTrustAuthRequest(challenge: URLAuthenticationChallenge, result: FlutterResult?) {
var serverCertificateData: NSData?
let serverTrust = challenge.protectionSpace.serverTrust!
if let serverCertificate = SecTrustGetCertificateAtIndex(serverTrust, 0) {
let serverCertificateCFData = SecCertificateCopyData(serverCertificate)
let data = CFDataGetBytePtr(serverCertificateCFData)
let size = CFDataGetLength(serverCertificateCFData)
serverCertificateData = NSData(bytes: data, length: size)
}
var arguments: [String: Any?] = [
"host": challenge.protectionSpace.host,
"protocol": challenge.protectionSpace.protocol,
"realm": challenge.protectionSpace.realm,
"port": challenge.protectionSpace.port,
"previousFailureCount": challenge.previousFailureCount,
"serverCertificate": serverCertificateData,
"error": -1,
"message": "",
]
if IABController != nil {
arguments["uuid"] = IABController!.uuid
}
if let channel = getChannel() {
channel.invokeMethod("onReceivedServerTrustAuthRequest", arguments: arguments, result: result)
}
}
public func onReceivedClientCertRequest(challenge: URLAuthenticationChallenge, result: FlutterResult?) {
var arguments: [String: Any?] = [
"host": challenge.protectionSpace.host,
"protocol": challenge.protectionSpace.protocol,
"realm": challenge.protectionSpace.realm,
"port": challenge.protectionSpace.port
]
if IABController != nil {
arguments["uuid"] = IABController!.uuid
}
if let channel = getChannel() {
channel.invokeMethod("onReceivedClientCertRequest", arguments: arguments, result: result)
}
}
public func onJsAlert(message: String, result: FlutterResult?) { public func onJsAlert(message: String, result: FlutterResult?) {
var arguments: [String: Any] = ["message": message] var arguments: [String: Any] = ["message": message]
if IABController != nil { if IABController != nil {
@ -1199,7 +1481,7 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
} }
} }
public func onConsoleMessage(sourceURL: String, lineNumber: Int, message: String, messageLevel: String) { public func onConsoleMessage(sourceURL: String, lineNumber: Int, message: String, messageLevel: Int) {
var arguments: [String: Any] = ["sourceURL": sourceURL, "lineNumber": lineNumber, "message": message, "messageLevel": messageLevel] var arguments: [String: Any] = ["sourceURL": sourceURL, "lineNumber": lineNumber, "message": message, "messageLevel": messageLevel]
if IABController != nil { if IABController != nil {
arguments["uuid"] = IABController!.uuid arguments["uuid"] = IABController!.uuid
@ -1234,27 +1516,27 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
public func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { public func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
if message.name.starts(with: "console") { if message.name.starts(with: "console") {
var messageLevel = "LOG" var messageLevel = 1
switch (message.name) { switch (message.name) {
case "consoleLog": case "consoleLog":
messageLevel = "LOG" messageLevel = 1
break; break;
case "consoleDebug": case "consoleDebug":
// on Android, console.debug is TIP // on Android, console.debug is TIP
messageLevel = "TIP" messageLevel = 0
break; break;
case "consoleError": case "consoleError":
messageLevel = "ERROR" messageLevel = 3
break; break;
case "consoleInfo": case "consoleInfo":
// on Android, console.info is LOG // on Android, console.info is LOG
messageLevel = "LOG" messageLevel = 1
break; break;
case "consoleWarn": case "consoleWarn":
messageLevel = "WARNING" messageLevel = 2
break; break;
default: default:
messageLevel = "LOG" messageLevel = 1
break; break;
} }
onConsoleMessage(sourceURL: "", lineNumber: 1, message: message.body as! String, messageLevel: messageLevel) onConsoleMessage(sourceURL: "", lineNumber: 1, message: message.body as! String, messageLevel: messageLevel)
@ -1287,4 +1569,17 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
private func getChannel() -> FlutterMethodChannel? { private func getChannel() -> FlutterMethodChannel? {
return (IABController != nil) ? SwiftFlutterPlugin.instance!.channel! : ((IAWController != nil) ? IAWController!.channel! : nil); return (IABController != nil) ? SwiftFlutterPlugin.instance!.channel! : ((IAWController != nil) ? IAWController!.channel! : nil);
} }
func findAllAsync(_ str: String?, completionHandler: ((Any?, Error?) -> Void)?) {
let startSearch = "uiWebview_HighlightAllOccurencesOfString('\(str ?? "")'); uiWebview_SearchResultCount"
evaluateJavaScript(startSearch, completionHandler: completionHandler)
}
func findNext(to index: Int, completionHandler: ((Any?, Error?) -> Void)?) {
evaluateJavaScript("uiWebview_ScrollTo('\(index)')", completionHandler: completionHandler)
}
func clearMatches(completionHandler: ((Any?, Error?) -> Void)?) {
evaluateJavaScript("uiWebview_RemoveAllHighlights()", completionHandler: completionHandler)
}
} }

View File

@ -21,4 +21,3 @@ A new Flutter plugin.
s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'VALID_ARCHS[sdk=iphonesimulator*]' => 'x86_64' } s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'VALID_ARCHS[sdk=iphonesimulator*]' => 'x86_64' }
s.swift_version = '5.0' s.swift_version = '5.0'
end end

View File

@ -410,7 +410,17 @@ class InAppBrowser {
///Event fires when a WebView received an HTTP authentication request. The default behavior is to cancel the request. ///Event fires when a WebView received an HTTP authentication request. The default behavior is to cancel the request.
/// ///
///[challenge] contains data about host, port, protocol, realm, etc. as specified in the auth challenge. ///[challenge] contains data about host, port, protocol, realm, etc. as specified in the auth challenge.
Future<HttpAuthResponse> onReceivedHttpAuthRequest(String url, HttpAuthChallenge challenge) { Future<HttpAuthResponse> onReceivedHttpAuthRequest(HttpAuthChallenge challenge) {
}
///
Future<ServerTrustAuthResponse> onReceivedServerTrustAuthRequest(ServerTrustChallenge challenge) {
}
///
Future<ClientCertResponse> onReceivedClientCertRequest(ClientCertChallenge challenge) {
} }

View File

@ -10,14 +10,13 @@ import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:flutter/gestures.dart'; import 'package:flutter/gestures.dart';
import 'http_auth_credentials_database.dart';
import 'types.dart'; import 'types.dart';
import 'in_app_browser.dart'; import 'in_app_browser.dart';
import 'channel_manager.dart'; import 'channel_manager.dart';
import 'webview_options.dart'; import 'webview_options.dart';
/* /*
* TODO: injectFileFromAssets, injectJavaScriptBeforeLoad * TODO: injectJavaScriptBeforeLoad
*/ */
///Initial [data] as a content for an [InAppWebView] instance, using [baseUrl] as the base URL for it. ///Initial [data] as a content for an [InAppWebView] instance, using [baseUrl] as the base URL for it.
@ -182,6 +181,12 @@ class InAppWebView extends StatefulWidget {
///[challenge] contains data about host, port, protocol, realm, etc. as specified in the auth challenge. ///[challenge] contains data about host, port, protocol, realm, etc. as specified in the auth challenge.
final onReceivedHttpAuthRequestCallback onReceivedHttpAuthRequest; final onReceivedHttpAuthRequestCallback onReceivedHttpAuthRequest;
///
final onReceivedServerTrustAuthRequestCallback onReceivedServerTrustAuthRequest;
///
final onReceivedClientCertRequestCallback onReceivedClientCertRequest;
///Initial url that will be loaded. ///Initial url that will be loaded.
final String initialUrl; final String initialUrl;
///Initial asset file that will be loaded. See [InAppWebView.loadFile()] for explanation. ///Initial asset file that will be loaded. See [InAppWebView.loadFile()] for explanation.
@ -226,6 +231,8 @@ class InAppWebView extends StatefulWidget {
this.onJsPrompt, this.onJsPrompt,
this.onSafeBrowsingHit, this.onSafeBrowsingHit,
this.onReceivedHttpAuthRequest, this.onReceivedHttpAuthRequest,
this.onReceivedServerTrustAuthRequest,
this.onReceivedClientCertRequest,
this.gestureRecognizers, this.gestureRecognizers,
}) : super(key: key); }) : super(key: key);
@ -487,7 +494,34 @@ class InAppWebViewController {
if (_widget != null && _widget.onReceivedHttpAuthRequest != null) if (_widget != null && _widget.onReceivedHttpAuthRequest != null)
return (await _widget.onReceivedHttpAuthRequest(this, challenge))?.toMap(); return (await _widget.onReceivedHttpAuthRequest(this, challenge))?.toMap();
else if (_inAppBrowser != null) else if (_inAppBrowser != null)
return (await _inAppBrowser.onReceivedHttpAuthRequest(host, challenge))?.toMap(); return (await _inAppBrowser.onReceivedHttpAuthRequest(challenge))?.toMap();
break;
case "onReceivedServerTrustAuthRequest":
String host = call.arguments["host"];
String protocol = call.arguments["protocol"];
String realm = call.arguments["realm"];
int port = call.arguments["port"];
int error = call.arguments["error"];
String message = call.arguments["message"];
Uint8List serverCertificate = call.arguments["serverCertificate"];
var protectionSpace = ProtectionSpace(host: host, protocol: protocol, realm: realm, port: port);
var challenge = ServerTrustChallenge(protectionSpace: protectionSpace, error: error, message: message, serverCertificate: serverCertificate);
if (_widget != null && _widget.onReceivedServerTrustAuthRequest != null)
return (await _widget.onReceivedServerTrustAuthRequest(this, challenge))?.toMap();
else if (_inAppBrowser != null)
return (await _inAppBrowser.onReceivedServerTrustAuthRequest(challenge))?.toMap();
break;
case "onReceivedClientCertRequest":
String host = call.arguments["host"];
String protocol = call.arguments["protocol"];
String realm = call.arguments["realm"];
int port = call.arguments["port"];
var protectionSpace = ProtectionSpace(host: host, protocol: protocol, realm: realm, port: port);
var challenge = ClientCertChallenge(protectionSpace: protectionSpace);
if (_widget != null && _widget.onReceivedClientCertRequest != null)
return (await _widget.onReceivedClientCertRequest(this, challenge))?.toMap();
else if (_inAppBrowser != null)
return (await _inAppBrowser.onReceivedClientCertRequest(challenge))?.toMap();
break; break;
case "onCallJsHandler": case "onCallJsHandler":
String handlerName = call.arguments["handlerName"]; String handlerName = call.arguments["handlerName"];
@ -847,7 +881,7 @@ class InAppWebViewController {
} }
///Takes a screenshot (in PNG format) of the WebView's visible viewport and returns a `Uint8List`. Returns `null` if it wasn't be able to take it. ///Takes a screenshot (in PNG format) of the WebView's visible viewport and returns a `Uint8List`. Returns `null` if it wasn't be able to take it.
///__safeBrowsingEnabled__ ///
///**NOTE for iOS**: available from iOS 11.0+. ///**NOTE for iOS**: available from iOS 11.0+.
Future<Uint8List> takeScreenshot() async { Future<Uint8List> takeScreenshot() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
@ -965,7 +999,7 @@ class InAppWebViewController {
return await _channel.invokeMethod('getSafeBrowsingPrivacyPolicyUrl', args); return await _channel.invokeMethod('getSafeBrowsingPrivacyPolicyUrl', args);
} }
///Clear all the webview's cache ///Clears all the webview's cache
Future<void> clearCache() async { Future<void> clearCache() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
if (_inAppBrowserUuid != null && _inAppBrowser != null) { if (_inAppBrowserUuid != null && _inAppBrowser != null) {
@ -975,9 +1009,36 @@ class InAppWebViewController {
await _channel.invokeMethod('clearCache', args); await _channel.invokeMethod('clearCache', args);
} }
///Clears the SSL preferences table stored in response to proceeding with SSL certificate errors.
///
///**NOTE**: available only for Android.
Future<void> clearSslPreferences() async {
Map<String, dynamic> args = <String, dynamic>{};
if (_inAppBrowserUuid != null && _inAppBrowser != null) {
_inAppBrowser.throwIsNotOpened();
args.putIfAbsent('uuid', () => _inAppBrowserUuid);
}
await _channel.invokeMethod('clearSslPreferences', args);
}
///Clears the client certificate preferences stored in response to proceeding/cancelling client cert requests.
///Note that WebView automatically clears these preferences when the system keychain is updated.
///The preferences are shared by all the WebViews that are created by the embedder application.
///
///**NOTE**: On iOS certificate-based credentials are never stored permanently.
///
///**NOTE**: available only for Android.
Future<void> clearClientCertPreferences() async {
Map<String, dynamic> args = <String, dynamic>{};
if (_inAppBrowserUuid != null && _inAppBrowser != null) {
_inAppBrowser.throwIsNotOpened();
args.putIfAbsent('uuid', () => _inAppBrowserUuid);
}
await _channel.invokeMethod('clearClientCertPreferences', args);
}
///Dispose/Destroy the WebView. ///Dispose/Destroy the WebView.
Future<void> _dispose() async { Future<void> _dispose() async {
await _channel.invokeMethod('dispose'); await _channel.invokeMethod('dispose');
} }
} }

View File

@ -1,3 +1,5 @@
import 'dart:typed_data';
import 'package:uuid/uuid.dart'; import 'package:uuid/uuid.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
@ -336,6 +338,79 @@ class HttpAuthCredential {
HttpAuthCredential({@required this.username, @required this.password}): assert(username != null && password != null); HttpAuthCredential({@required this.username, @required this.password}): assert(username != null && password != null);
} }
///
class ServerTrustAuthResponseAction {
final int _value;
const ServerTrustAuthResponseAction._internal(this._value);
toValue() => _value;
static const CANCEL = const ServerTrustAuthResponseAction._internal(0);
static const PROCEED = const ServerTrustAuthResponseAction._internal(1);
}
///
class ServerTrustAuthResponse {
ServerTrustAuthResponseAction action;
ServerTrustAuthResponse({this.action = ServerTrustAuthResponseAction.CANCEL});
Map<String, dynamic> toMap() {
return {
"action": action?.toValue()
};
}
}
///
class ServerTrustChallenge {
ProtectionSpace protectionSpace;
int error;
String message;
Uint8List serverCertificate;
ServerTrustChallenge({@required this.protectionSpace, @required this.error, this.message, this.serverCertificate}): assert(protectionSpace != null && error != null);
}
///
class ClientCertResponseAction {
final int _value;
const ClientCertResponseAction._internal(this._value);
toValue() => _value;
static const CANCEL = const ClientCertResponseAction._internal(0);
static const PROCEED = const ClientCertResponseAction._internal(1);
static const IGNORE = const ClientCertResponseAction._internal(2);
}
///
class ClientCertResponse {
String certificatePath;
String certificatePassword;
String androidKeyStoreType;
ClientCertResponseAction action;
ClientCertResponse({this.certificatePath, this.certificatePassword = "", this.androidKeyStoreType = "PKCS12", this.action = ClientCertResponseAction.CANCEL}) {
if (this.action == ClientCertResponseAction.PROCEED)
assert(certificatePath != null && certificatePath.isNotEmpty);
}
Map<String, dynamic> toMap() {
return {
"certificatePath": certificatePath,
"certificatePassword": certificatePassword,
"androidKeyStoreType": androidKeyStoreType,
"action": action?.toValue()
};
}
}
///
class ClientCertChallenge {
ProtectionSpace protectionSpace;
ClientCertChallenge({@required this.protectionSpace}): assert(protectionSpace != null);
}
/// ///
class AndroidInAppWebViewCacheMode { class AndroidInAppWebViewCacheMode {
final int _value; final int _value;
@ -421,14 +496,14 @@ class iOSInAppWebViewDataDetectorTypes {
} }
/// ///
class iOSInAppWebViewUserPreferredContentMode { class InAppWebViewUserPreferredContentMode {
final int _value; final int _value;
const iOSInAppWebViewUserPreferredContentMode._internal(this._value); const InAppWebViewUserPreferredContentMode._internal(this._value);
toValue() => _value; toValue() => _value;
static const RECOMMENDED = const iOSInAppWebViewUserPreferredContentMode._internal(0); static const RECOMMENDED = const InAppWebViewUserPreferredContentMode._internal(0);
static const MOBILE = const iOSInAppWebViewUserPreferredContentMode._internal(1); static const MOBILE = const InAppWebViewUserPreferredContentMode._internal(1);
static const DESKTOP = const iOSInAppWebViewUserPreferredContentMode._internal(2); static const DESKTOP = const InAppWebViewUserPreferredContentMode._internal(2);
} }
/// ///
@ -489,4 +564,6 @@ typedef onJsAlertCallback = Future<JsAlertResponse> Function(InAppWebViewControl
typedef onJsConfirmCallback = Future<JsConfirmResponse> Function(InAppWebViewController controller, String message); typedef onJsConfirmCallback = Future<JsConfirmResponse> Function(InAppWebViewController controller, String message);
typedef onJsPromptCallback = Future<JsPromptResponse> Function(InAppWebViewController controller, String message, String defaultValue); typedef onJsPromptCallback = Future<JsPromptResponse> Function(InAppWebViewController controller, String message, String defaultValue);
typedef onSafeBrowsingHitCallback = Future<SafeBrowsingResponse> Function(InAppWebViewController controller, String url, SafeBrowsingThreat threatType); typedef onSafeBrowsingHitCallback = Future<SafeBrowsingResponse> Function(InAppWebViewController controller, String url, SafeBrowsingThreat threatType);
typedef onReceivedHttpAuthRequestCallback = Future<HttpAuthResponse> Function(InAppWebViewController controller, HttpAuthChallenge challenge); typedef onReceivedHttpAuthRequestCallback = Future<HttpAuthResponse> Function(InAppWebViewController controller, HttpAuthChallenge challenge);
typedef onReceivedServerTrustAuthRequestCallback = Future<ServerTrustAuthResponse> Function(InAppWebViewController controller, ServerTrustChallenge challenge);
typedef onReceivedClientCertRequestCallback = Future<ClientCertResponse> Function(InAppWebViewController controller, ClientCertChallenge challenge);

View File

@ -35,11 +35,12 @@ class InAppWebViewOptions implements WebViewOptions, BrowserOptions, AndroidOpti
bool horizontalScrollBarEnabled; bool horizontalScrollBarEnabled;
List<String> resourceCustomSchemes; List<String> resourceCustomSchemes;
List<ContentBlocker> contentBlockers; List<ContentBlocker> contentBlockers;
InAppWebViewUserPreferredContentMode preferredContentMode;
InAppWebViewOptions({this.useShouldOverrideUrlLoading = false, this.useOnLoadResource = false, this.useOnDownloadStart = false, this.useOnTargetBlank = false, InAppWebViewOptions({this.useShouldOverrideUrlLoading = false, this.useOnLoadResource = false, this.useOnDownloadStart = false, this.useOnTargetBlank = false,
this.clearCache = false, this.userAgent = "", this.javaScriptEnabled = true, this.debuggingEnabled = false, this.javaScriptCanOpenWindowsAutomatically = false, this.clearCache = false, this.userAgent = "", this.javaScriptEnabled = true, this.debuggingEnabled = false, this.javaScriptCanOpenWindowsAutomatically = false,
this.mediaPlaybackRequiresUserGesture = true, this.textZoom = 100, this.minimumFontSize, this.verticalScrollBarEnabled = true, this.horizontalScrollBarEnabled = true, this.mediaPlaybackRequiresUserGesture = true, this.textZoom = 100, this.minimumFontSize, this.verticalScrollBarEnabled = true, this.horizontalScrollBarEnabled = true,
this.resourceCustomSchemes = const [], this.contentBlockers = const []}) { this.resourceCustomSchemes = const [], this.contentBlockers = const [], this.preferredContentMode = InAppWebViewUserPreferredContentMode.RECOMMENDED}) {
if (this.minimumFontSize == null) if (this.minimumFontSize == null)
this.minimumFontSize = Platform.isAndroid ? 8 : 0; this.minimumFontSize = Platform.isAndroid ? 8 : 0;
} }
@ -66,6 +67,7 @@ class InAppWebViewOptions implements WebViewOptions, BrowserOptions, AndroidOpti
"horizontalScrollBarEnabled": horizontalScrollBarEnabled, "horizontalScrollBarEnabled": horizontalScrollBarEnabled,
"resourceCustomSchemes": resourceCustomSchemes, "resourceCustomSchemes": resourceCustomSchemes,
"contentBlockers": contentBlockersMapList, "contentBlockers": contentBlockersMapList,
"preferredContentMode": preferredContentMode?.toValue()
}; };
} }
} }
@ -179,13 +181,11 @@ class iOSInAppWebViewOptions implements WebViewOptions, BrowserOptions, iOSOptio
bool isFraudulentWebsiteWarningEnabled; bool isFraudulentWebsiteWarningEnabled;
iOSInAppWebViewSelectionGranularity selectionGranularity; iOSInAppWebViewSelectionGranularity selectionGranularity;
List<iOSInAppWebViewDataDetectorTypes> dataDetectorTypes; List<iOSInAppWebViewDataDetectorTypes> dataDetectorTypes;
iOSInAppWebViewUserPreferredContentMode preferredContentMode;
iOSInAppWebViewOptions({this.disallowOverScroll = false, this.enableViewportScale = false, this.suppressesIncrementalRendering = false, this.allowsAirPlayForMediaPlayback = true, iOSInAppWebViewOptions({this.disallowOverScroll = false, this.enableViewportScale = false, this.suppressesIncrementalRendering = false, this.allowsAirPlayForMediaPlayback = true,
this.allowsBackForwardNavigationGestures = true, this.allowsLinkPreview = true, this.ignoresViewportScaleLimits = false, this.allowsInlineMediaPlayback = false, this.allowsBackForwardNavigationGestures = true, this.allowsLinkPreview = true, this.ignoresViewportScaleLimits = false, this.allowsInlineMediaPlayback = false,
this.allowsPictureInPictureMediaPlayback = true, this.transparentBackground = false, this.applicationNameForUserAgent = "", this.isFraudulentWebsiteWarningEnabled = true, this.allowsPictureInPictureMediaPlayback = true, this.transparentBackground = false, this.applicationNameForUserAgent = "", this.isFraudulentWebsiteWarningEnabled = true,
this.selectionGranularity = iOSInAppWebViewSelectionGranularity.DYNAMIC, this.dataDetectorTypes = const [iOSInAppWebViewDataDetectorTypes.NONE], this.selectionGranularity = iOSInAppWebViewSelectionGranularity.DYNAMIC, this.dataDetectorTypes = const [iOSInAppWebViewDataDetectorTypes.NONE]
this.preferredContentMode = iOSInAppWebViewUserPreferredContentMode.RECOMMENDED
}); });
@override @override
@ -209,8 +209,7 @@ class iOSInAppWebViewOptions implements WebViewOptions, BrowserOptions, iOSOptio
"applicationNameForUserAgent": applicationNameForUserAgent, "applicationNameForUserAgent": applicationNameForUserAgent,
"isFraudulentWebsiteWarningEnabled": isFraudulentWebsiteWarningEnabled, "isFraudulentWebsiteWarningEnabled": isFraudulentWebsiteWarningEnabled,
"selectionGranularity": selectionGranularity.toValue(), "selectionGranularity": selectionGranularity.toValue(),
"dataDetectorTypes": dataDetectorTypesList, "dataDetectorTypes": dataDetectorTypesList
"preferredContentMode": preferredContentMode.toValue(),
}; };
} }
} }

View File

@ -0,0 +1,105 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.history/
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# TypeScript v1 declaration files
typings/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
.env.test
# parcel-bundler cache (https://parceljs.org/)
.cache
# Next.js build output
.next
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and *not* Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port

View File

@ -0,0 +1,32 @@
-----BEGIN CERTIFICATE-----
MIIFgDCCA2gCCQD+lncxaKtzfDANBgkqhkiG9w0BAQsFADCBgTELMAkGA1UEBhMC
VVMxCzAJBgNVBAgMAk1BMQ8wDQYDVQQHDAZCb3N0b24xEzARBgNVBAoMCkV4YW1w
bGUgQ28xEDAOBgNVBAsMB3RlY2hvcHMxCzAJBgNVBAMMAmNhMSAwHgYJKoZIhvcN
AQkBFhFjZXJ0c0BleGFtcGxlLmNvbTAeFw0xOTEwMzAyMzMzMTVaFw00NzAzMTYy
MzMzMTVaMIGBMQswCQYDVQQGEwJVUzELMAkGA1UECAwCTUExDzANBgNVBAcMBkJv
c3RvbjETMBEGA1UECgwKRXhhbXBsZSBDbzEQMA4GA1UECwwHdGVjaG9wczELMAkG
A1UEAwwCY2ExIDAeBgkqhkiG9w0BCQEWEWNlcnRzQGV4YW1wbGUuY29tMIICIjAN
BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0K1Bul8vSlC90KFus9gW3V3b+6qz
Ji0fw5R1S0jeAPMA6IK+b+hBhdCpRKcq0PIZggwcBbk3zYKxAn5rLlSGj5eSQGZz
kpGUo0rwnFZF0L7bID0bmw0ELCHt3Z/cP5rx3UvM5a2V65j+ieiP8Fn5X5IKW+vz
Rehi1G0imTYjXM8so/Te8HkUjfWhvPofd2mss6fLlMViUIL0xJf6BML6Ds4nL5+8
M3zpolhPMXWSaFbQSssF6VuUFi8shIdEUNg3UAbTwUV6DVqYcYBWyrKjm1VM2Bk6
f5zPZVuwX52lP9ko1pgIFuZA18ErGv8Y5tPQQuzXVXxTpi7ToTZwa/W32tuyfvyo
bShw4ejRlrxj62nQTQcCYMDXjn/Kt/JPSlY6Lnd0x1iky5kJb0cSiWv/RPDlS5FF
0NSIpz6ZPS0mP7o6eAMwDW+6TqCZvzH+gT3ilFkZLMrIeNTjmi/lu46JigB6Brtg
nIZzI5DpGMxp2WkCA+BwYz9M8MykST+rCQ4Ikw8OhxBlxQtuEk6JEkozbztvhuqv
MJJeMf/rz1Y9bWl40dSDtvISo7FACVhlMSBqgkBkrYuJL8n0hCLqO65EkQrlMEKw
ON4XQ8eXLjv0XclLw7frCGGWxprc+nUZ8aiXki7pz3m3CC09GLVTHL2NnBdzMVkz
C/NdgQW16Hnn1xcCAwEAATANBgkqhkiG9w0BAQsFAAOCAgEAr0vvKqfBz3+GbFvm
IcMwNW+qVADXucP5Z0L9w93rIWU3957cXce/6mqYlUGPu9Rq+TMLn8tLX05Vhr9A
bp6QCCGhK+hRJ+BGmebTBFGj198VgQj9Nsvg8T1Px10oandzGD8dnGSgAB5GNTuV
c0pq4B1aW+NA9N5h/bGyrn0luYaXdsbV2jyr++waSAKcWtetwfrj5CErnQIAxUPo
LrEg75HMmdXZsCYhpPgeyHmPtmJN/czEVIQbQk/cjWzaEh7dKEpa9inBHf7DMBnS
M5xqnABVhgxCmC6wJ8OTfzY5OR2zHg2Xdra4YNQo88vmyzdvMpD+p1rxxI2ZDpds
9Eke2VtuZqi4wcEakHlSjTFMs8fF/x46+oGBNoWTcPzlDXyUiRMIGZO6zzNrc0Ez
sqgazhDQUH3Lg0dDHqPvSLw/zkyXPjh89fksspv00RP7yHUpyKl1Q+pCxQfflTes
KOkoj5OUzOWCbm6AvumVfQk302HbSqVjz9AQfoPVk8UfXoJ0FCF5LaynmBLJz+6l
2E5Up3gdDDh6qpNS5TLH6b2sKErQweEBfRAdo1fpp2RUEUOqV8uivFXVpQSc+TUV
RU1khy+tTiCnnjCKr+Xlkv/bvHZTv9hSx1TK3xsZkjHr5zzj7mltzmvQdpHVwUbD
lrzYwz1UU//M2zwtN5nHjE+bnks=
-----END CERTIFICATE-----

View File

@ -0,0 +1 @@
B83E5BFA72399E6B

View File

@ -0,0 +1,54 @@
-----BEGIN ENCRYPTED PRIVATE KEY-----
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
TDsQ2Rq7lfN7Gx+ugLnDlrnk1Q==
-----END ENCRYPTED PRIVATE KEY-----

View File

@ -0,0 +1,31 @@
[ ca ]
default_ca = CA_default
[ CA_default ]
serial = ca-serial
crl = ca-crl.pem
database = ca-database.txt
name_opt = CA_default
cert_opt = CA_default
default_crl_days = 9999
default_md = md5
[ req ]
default_bits = 4096
days = 9999
distinguished_name = req_distinguished_name
attributes = req_attributes
prompt = no
output_password = password
[ req_distinguished_name ]
C = US
ST = MA
L = Boston
O = Example Co
OU = techops
CN = ca
emailAddress = certs@example.com
[ req_attributes ]
challengePassword = test

Binary file not shown.

View File

@ -0,0 +1,32 @@
-----BEGIN CERTIFICATE-----
MIIFijCCA3KgAwIBAgIJALg+W/pyOZ5qMA0GCSqGSIb3DQEBBQUAMIGBMQswCQYD
VQQGEwJVUzELMAkGA1UECAwCTUExDzANBgNVBAcMBkJvc3RvbjETMBEGA1UECgwK
RXhhbXBsZSBDbzEQMA4GA1UECwwHdGVjaG9wczELMAkGA1UEAwwCY2ExIDAeBgkq
hkiG9w0BCQEWEWNlcnRzQGV4YW1wbGUuY29tMB4XDTE5MTAzMDIzMzcwOVoXDTIy
MDcyNTIzMzcwOVowgYYxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJNQTEPMA0GA1UE
BwwGQm9zdG9uMRMwEQYDVQQKDApFeGFtcGxlIENvMRAwDgYDVQQLDAd0ZWNob3Bz
MRAwDgYDVQQDDAdjbGllbnQxMSAwHgYJKoZIhvcNAQkBFhFjZXJ0c0BleGFtcGxl
LmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAPWhU8of86y3Lols
0cU40+cbCcAhOEsdDiouVKX9cpnYoP5IIsTOiQHjAcMYekTMNXxLcGGa8FWO1bDW
6WjvzNi0s2xqYMThP5h7m5XK4PR6NjLnE8I0kyfYjvx4vN/AXNWJXgNDJ+dkm3O0
ceXzMzswuLqrhEv7UpkJm37znimOBtP7JSFmoz5mT+/s3Aojzop1Le1VSPVANIbu
PgdsIdoNMR8oJrKai0PourJKuvOV5qiNm0123+lq9x4R+zoY2AIB34o9RJfcuUZs
+IqDt+SyL3MHwuP8OMmYyU6yerfWLZ2Ywsg01uDGQMaHfPH1S8d8Hiq4Vwfi9RWE
kvifb4eQDBzVN3x1Hn/1cEghaLneDiRRwRiFZFs/WKvafxMOY4M30gpuSStHzIt/
wE+N8vC8KNW2TyyPAkCq6L8BjOlJ7EX0eilbSroMz/SeTO8+t4+N9vJ5/4e4dSSQ
zo3Sdw3K8sdK7zoBToWGW26yCSEvnSBjNyWvNKLWsHM1wKxbN85sDYdeyn7pvjHe
K1g0eQF/WGbEyHCTws3tDCo/wfpbZNtkW1w8zlBAmpbZdNZDs4769pZ/tb9wctUk
TasbXlXedMmmw2eOZ3d/hj0REFpbl/34ClQSTnlVQJKv/7CwqzJWDUd5uWzaHPtr
hzUOJDKoDBEvfL+yAEJNMM8maGcpAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBAGhm
496UQQKPqXUXl52lPUhchjCxecfukVrLf2GR3H8r9W6s46pu60MGZBip7TQowXGN
v4uZKABxOkI7VoRUhndsO8KiR39DlmE/SocSrEyfe6cblMaMr7c6oTojcYiXHLbi
Wy52O5TX1iqMBNyrvPAfDjOubkaUF0iuyh1Vzn5OuHvWfmC5uOONHIUCukeyg+hg
wxYH5vYduhYiqmdAPNHrqrGRqOfbwG6KTMaqH6xihupJsgcxwVpzbdWWxPU37tZE
sI1RVS26Z6XuA2j1uQ4BzRUbGlXqvESYV9jSkKYvNfMqVDL+g2h9xumlPtTIiP6b
GZIN4zel7WhaOdftnz5w4AdaPt3sYXxTda95973TNe072tgR/lJsvobW1szSo0k8
m2AbWeepzeKftYanCydRz5MnlOX8mf3HaL0QqreQwxaiF40mOUVcMPJfY2pK25qU
pVK9E3oA1ufc7+8Hnx4KsE9DO/1VyCrpBq0LwUKjmhYTLChsuH0Gpj+/pqioWNKB
c6Mt+APzGG8jqWrwMlsixK2U6G22zASpFBWuFhQIShkL819zgpRB67XPQUgKkbQT
4wF9cR4xwHvz8NsFHyXGFkI3U9yGGOVXWMOQ+ZhLyWGkwg096mVro/AtGveAs0sr
QWfJbuZM/gWlaojLGqtatDEc+tfRZ7g+nxU+PhQq
-----END CERTIFICATE-----

View File

@ -0,0 +1,29 @@
-----BEGIN CERTIFICATE REQUEST-----
MIIE5TCCAs0CAQAwgYYxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJNQTEPMA0GA1UE
BwwGQm9zdG9uMRMwEQYDVQQKDApFeGFtcGxlIENvMRAwDgYDVQQLDAd0ZWNob3Bz
MRAwDgYDVQQDDAdjbGllbnQxMSAwHgYJKoZIhvcNAQkBFhFjZXJ0c0BleGFtcGxl
LmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAPWhU8of86y3Lols
0cU40+cbCcAhOEsdDiouVKX9cpnYoP5IIsTOiQHjAcMYekTMNXxLcGGa8FWO1bDW
6WjvzNi0s2xqYMThP5h7m5XK4PR6NjLnE8I0kyfYjvx4vN/AXNWJXgNDJ+dkm3O0
ceXzMzswuLqrhEv7UpkJm37znimOBtP7JSFmoz5mT+/s3Aojzop1Le1VSPVANIbu
PgdsIdoNMR8oJrKai0PourJKuvOV5qiNm0123+lq9x4R+zoY2AIB34o9RJfcuUZs
+IqDt+SyL3MHwuP8OMmYyU6yerfWLZ2Ywsg01uDGQMaHfPH1S8d8Hiq4Vwfi9RWE
kvifb4eQDBzVN3x1Hn/1cEghaLneDiRRwRiFZFs/WKvafxMOY4M30gpuSStHzIt/
wE+N8vC8KNW2TyyPAkCq6L8BjOlJ7EX0eilbSroMz/SeTO8+t4+N9vJ5/4e4dSSQ
zo3Sdw3K8sdK7zoBToWGW26yCSEvnSBjNyWvNKLWsHM1wKxbN85sDYdeyn7pvjHe
K1g0eQF/WGbEyHCTws3tDCo/wfpbZNtkW1w8zlBAmpbZdNZDs4769pZ/tb9wctUk
TasbXlXedMmmw2eOZ3d/hj0REFpbl/34ClQSTnlVQJKv/7CwqzJWDUd5uWzaHPtr
hzUOJDKoDBEvfL+yAEJNMM8maGcpAgMBAAGgGTAXBgkqhkiG9w0BCQcxCgwIcGFz
c3dvcmQwDQYJKoZIhvcNAQELBQADggIBAJal3mvhA+zM72cXUY1IYIVVS9QuSzqC
GkUT8O174bD1acc7KEm0uGS628sR0mFCKArfH9YTjUOX1+jORPBhVCXeg9xp/PPg
9mjTHyKPJu2vnq6GaS1WXqEOQJkppV5fBqciIDRQOu1goR2I21qjEUnVnb82jRXv
DpZ9YkgyMdf2Li9taj2snJSfXBwjhNH78lXmRz/HYTQSHyih4dfpdLpeIK8g+0uR
8bkJzga5hc2dDEyQQ+xQ+jVBdiAT6R/zV/V7ghJDHeJ4HuLuIVh9zIbAz0jPvG7u
pumHW3Lbr5ISwxmhOp1RczxWeRktP+8aVP0Uk5D/5EGxs8F+lbU4lh0RTm0ae6W1
UMExBn056C46ypvp7H0vLiijyhyVjo8kfz8HA392TlCOYxaJB2EaUoJA3yu5lb8w
nSQrlGCtA8ba9ceMFhjzIxE6I7qf93GZlK9m7HcqEdFNkm/byo2gQ5/SMSpOiL4C
IuK9p/bTBwpJL88ibAOq/V8IZBPRcyWnJcL7KzQKQGOVpl2egrf4Iqn5/FZwXpmL
zIVDHSRP1NwTqkAX1JxoHn7bjDDkhtB6V4qy2/uqscTBPA2m/Z+rJofusjsHblrN
nrQnzcwsiVU/gPgNUPTZi2wmxAkc7zFvnR3F1NCjLEr3G5rmF+M9aT3/5w0U3zOW
JN5HS6NqtcFI
-----END CERTIFICATE REQUEST-----

View File

@ -0,0 +1,51 @@
-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,26 @@
[ req ]
default_bits = 4096
days = 9999
distinguished_name = req_distinguished_name
attributes = req_attributes
prompt = no
x509_extensions = v3_ca
[ req_distinguished_name ]
C = US
ST = MA
L = Boston
O = Example Co
OU = techops
CN = client1
emailAddress = certs@example.com
[ req_attributes ]
challengePassword = password
[ v3_ca ]
authorityInfoAccess = @issuer_info
[ issuer_info ]
OCSP;URI.0 = http://ocsp.example.com/
caIssuers;URI.0 = http://example.com/ca.cert

View File

@ -0,0 +1,32 @@
-----BEGIN CERTIFICATE-----
MIIFijCCA3KgAwIBAgIJALg+W/pyOZ5rMA0GCSqGSIb3DQEBBQUAMIGBMQswCQYD
VQQGEwJVUzELMAkGA1UECAwCTUExDzANBgNVBAcMBkJvc3RvbjETMBEGA1UECgwK
RXhhbXBsZSBDbzEQMA4GA1UECwwHdGVjaG9wczELMAkGA1UEAwwCY2ExIDAeBgkq
hkiG9w0BCQEWEWNlcnRzQGV4YW1wbGUuY29tMB4XDTE5MTAzMDIzMzcxNFoXDTIy
MDcyNTIzMzcxNFowgYYxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJNQTEPMA0GA1UE
BwwGQm9zdG9uMRMwEQYDVQQKDApFeGFtcGxlIENvMRAwDgYDVQQLDAd0ZWNob3Bz
MRAwDgYDVQQDDAdjbGllbnQyMSAwHgYJKoZIhvcNAQkBFhFjZXJ0c0BleGFtcGxl
LmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAO2ARFzN+hQgmnEj
OXHcOVjCPH6g/UNXwUNjtEUBg+EDTGliOC8If6ObFjW4tfIjwgGdhKt++92LRxDe
BauM8al7QK8yZhw7I7waqm0C4tIw4MdAlcRxZ0gEG7l5i9jU55yPpUcxZ1VE0VuH
ADgAosZtjltsBv03tq2Cf11jTEcaly4ze/8vDaAyl+M4u9FrflPnYoPmCiMBTz1j
1mdVtGsg2nGk+MA5RX9bMwWZq5bT9j2RG7PDMoc139SieQx4/5aVqIH628K7X0xE
/YydLh7vARM15Fn8tJy4yj/fgkU+3AWN8nWoOwceLNEB/yorBs1tjjSWEt3NIP7d
kcyHYmZ7XyUMSzlfdSVY+OoM1z63MoSCTwZJzZ5fI4ca+NtoxYny4unyM1q7QKwA
CisynJvGD5aI5bRgpjujlpw5IEuGqBZRjkLB0heOrPUlGEKkvaH2r9w/rGo8w4Is
ebz3OHBeVpB2AqbifMa+3cwZ9/IpRWUKUdnoqye5ySx1RHWmz3fjwerTDPzHWxJ8
YH8HORvuwUm+j/hXFvB3D8S754X+OEA8bgu/7dfIE1C330WDvVAP6EngCZKAI8W2
6u2SYXKPviqXciap5B4K4QHyUsiVruBIvymwTnz1+sharbRBOll7ZZKbBK1UNaCn
XO0Vt7NM5EbNEhjBlrArPGxIT0bFAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBAD7W
3rD2n/PRT6U1TyInliTZ11+leG40+P+fhlXoajN8vwooCnoDZWQNXRYODekzC6t3
9jJWvbQyZnSL/jE81ixjN/CrHnrv1AmH63B3wgAaSYFja7UdfBE3rpb+ZsEZyjdu
54C1zsizwR85ZWUWoj22v3lzvmf0+FuqW9FxVREtZytePncfw40wFrQO9NX5QXo8
6ljZ18T7tNoeCv+C188SSbGNUDbC7VwTRCGACErbjAwX3ooQ5I2p3U3ipvTrXsnT
IYGeXwFqx7UjMtCYVJj8el+HlU1PZeyKZH/6j4HdseEZOvz8M/PSN8XjpStnhKQt
+dFmclEo68wU1KRnzUu38VynnKjSjeAZpws4eRqBr8rUeT8e52KbN07MLZ9q6GV7
V9MBERSIKNu7+PHIllvJCt3C3K0DGcq6ItNs5wOgtkXNI2RqovvtU2g6z1KdKthl
bVap4eh8WdVfYgFjTdiMFbLkFgZubNucyNXkKMPo79cnT1Wp1I3ZjNHuv7vtpXQD
WxfGZSsMM/ekgnR36wZFEqqt40bgie4RQJliIu1fWCVB6Y9kMb00dJ7otc8bIcVF
EZdTCbgdGvn91T9yN7yMXxoPmHq7WX6TG1hWVKLUAaiFAlJqFOg23fFj+6XlBD/G
/rGliGxn+1rvUywSv7oKVgTpbmwR65Fky2hDZWh5
-----END CERTIFICATE-----

View File

@ -0,0 +1,29 @@
-----BEGIN CERTIFICATE REQUEST-----
MIIE5TCCAs0CAQAwgYYxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJNQTEPMA0GA1UE
BwwGQm9zdG9uMRMwEQYDVQQKDApFeGFtcGxlIENvMRAwDgYDVQQLDAd0ZWNob3Bz
MRAwDgYDVQQDDAdjbGllbnQyMSAwHgYJKoZIhvcNAQkBFhFjZXJ0c0BleGFtcGxl
LmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAO2ARFzN+hQgmnEj
OXHcOVjCPH6g/UNXwUNjtEUBg+EDTGliOC8If6ObFjW4tfIjwgGdhKt++92LRxDe
BauM8al7QK8yZhw7I7waqm0C4tIw4MdAlcRxZ0gEG7l5i9jU55yPpUcxZ1VE0VuH
ADgAosZtjltsBv03tq2Cf11jTEcaly4ze/8vDaAyl+M4u9FrflPnYoPmCiMBTz1j
1mdVtGsg2nGk+MA5RX9bMwWZq5bT9j2RG7PDMoc139SieQx4/5aVqIH628K7X0xE
/YydLh7vARM15Fn8tJy4yj/fgkU+3AWN8nWoOwceLNEB/yorBs1tjjSWEt3NIP7d
kcyHYmZ7XyUMSzlfdSVY+OoM1z63MoSCTwZJzZ5fI4ca+NtoxYny4unyM1q7QKwA
CisynJvGD5aI5bRgpjujlpw5IEuGqBZRjkLB0heOrPUlGEKkvaH2r9w/rGo8w4Is
ebz3OHBeVpB2AqbifMa+3cwZ9/IpRWUKUdnoqye5ySx1RHWmz3fjwerTDPzHWxJ8
YH8HORvuwUm+j/hXFvB3D8S754X+OEA8bgu/7dfIE1C330WDvVAP6EngCZKAI8W2
6u2SYXKPviqXciap5B4K4QHyUsiVruBIvymwTnz1+sharbRBOll7ZZKbBK1UNaCn
XO0Vt7NM5EbNEhjBlrArPGxIT0bFAgMBAAGgGTAXBgkqhkiG9w0BCQcxCgwIcGFz
c3dvcmQwDQYJKoZIhvcNAQELBQADggIBAGDn6DOovGaSpbe2PGetJrvQht2T6de8
cbnGCusQSjdqssUtjnKqV8hE4K8RsbrwWK6Gz6VR3s+ayVvm6JMNSf129UJEoOO5
f1KKWOB1ZuvIaxxdpWWSEyT2NWsnxPb4PVgRv+SjAkFlpfQdPqG+BDyd7PbjffTi
Yut3+AejTU6pkD9A82SyVUYP0hcSdnusclxeecBpvuBC1r1+ZmWMhgVHN9C13Bin
KPanEcwSP9GER9E5z38+mM6outSBTQ48+sSrfYXJgJe1kJ5U5swkTv0msoysOG7y
A9mn+upeL2+BI/9IkLswJygfDOUEQDdiafT9PAhJczKm6uLzNFSi5d+W+2VGMBoM
K3Ur5tg1Hom/yvkPBLteldNPySswJ0QTHtuhyyKsIx7pzy68YJtCIdaFCoOPMiEy
ME95l2Tp/G8g9GQbRNnRtBaNAirrzRTEpOxfIX0JZk/vouz7vDvnXLBZOwzaAWM3
0/f5uQD4YrY5qQ2Xl+Wq5ZySYi6afxhh0w9X72W3AKOgXuVKN0VUFOW0ani+tKuw
PLPwMRx12vgPUhNGfosTzTSOnTyI5yBqG4BtSHnR5k1DYLwTjdlGCr8jcA3u8sLP
sJX0u9+/JSaemlb2E6/T4pHwIot+l52DBnbe66ekwVoDf47smefapiYE7Gb7KRxA
0j8l/ryckQZk
-----END CERTIFICATE REQUEST-----

View File

@ -0,0 +1,51 @@
-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,26 @@
[ req ]
default_bits = 4096
days = 9999
distinguished_name = req_distinguished_name
attributes = req_attributes
prompt = no
x509_extensions = v3_ca
[ req_distinguished_name ]
C = US
ST = MA
L = Boston
O = Example Co
OU = techops
CN = client2
emailAddress = certs@example.com
[ req_attributes ]
challengePassword = password
[ v3_ca ]
authorityInfoAccess = @issuer_info
[ issuer_info ]
OCSP;URI.0 = http://ocsp.example.com/
caIssuers;URI.0 = http://example.com/ca.cert

View File

@ -0,0 +1,67 @@
// Example of the server https is taken from here: https://engineering.circle.com/https-authorized-certs-with-node-js-315e548354a2
// Conversion of client1-crt.pem to certificate.pfx: https://stackoverflow.com/a/38408666/4637638
const express = require('express')
var https = require('https')
const auth = require('basic-auth')
const appHttps = express()
const appAuthBasic = express()
const fs = require('fs')
var options = {
key: fs.readFileSync('server-key.pem'),
cert: fs.readFileSync('server-crt.pem'),
ca: fs.readFileSync('ca-crt.pem'),
requestCert: true,
rejectUnauthorized: false
};
appHttps.get('/', (req, res) => {
const cert = req.connection.getPeerCertificate()
// The `req.client.authorized` flag will be true if the certificate is valid and was issued by a CA we white-listed
// earlier in `opts.ca`. We display the name of our user (CN = Common Name) and the name of the issuer, which is
// `localhost`.
if (req.client.authorized) {
res.send(`Hello ${cert.subject.CN}, your certificate was issued by ${cert.issuer.CN}!`)
// They can still provide a certificate which is not accepted by us. Unfortunately, the `cert` object will be an empty
// object instead of `null` if there is no certificate at all, so we have to check for a known field rather than
// truthiness.
} else if (cert.subject) {
res.status(403).send(`Sorry ${cert.subject.CN}, certificates from ${cert.issuer.CN} are not welcome here.`)
// And last, they can come to us with no certificate at all:
} else {
res.status(401).send(`Sorry, but you need to provide a client certificate to continue.`)
}
})
// Let's create our HTTPS server and we're ready to go.
https.createServer(options, appHttps).listen(4433)
// Ensure this is before any other middleware or routes
appAuthBasic.use((req, res, next) => {
let user = auth(req)
if (user === undefined || user['name'] !== 'user 1' || user['pass'] !== 'password 1') {
res.statusCode = 401
res.setHeader('WWW-Authenticate', 'Basic realm="Node"')
res.end('Unauthorized')
} else {
next()
}
})
appAuthBasic.get("/", (req, res) => {
res.send(`
<html>
<head>
</head>
<body>
<p>HELLO</p>
</body>
</html>
`);
})
appAuthBasic.listen(8081)

View File

@ -0,0 +1,387 @@
{
"name": "node_auth_basic",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"accepts": {
"version": "1.3.7",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
"integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
"requires": {
"mime-types": "~2.1.24",
"negotiator": "0.6.2"
}
},
"array-flatten": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
},
"basic-auth": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz",
"integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==",
"requires": {
"safe-buffer": "5.1.2"
}
},
"body-parser": {
"version": "1.19.0",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
"integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==",
"requires": {
"bytes": "3.1.0",
"content-type": "~1.0.4",
"debug": "2.6.9",
"depd": "~1.1.2",
"http-errors": "1.7.2",
"iconv-lite": "0.4.24",
"on-finished": "~2.3.0",
"qs": "6.7.0",
"raw-body": "2.4.0",
"type-is": "~1.6.17"
}
},
"bytes": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
"integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg=="
},
"content-disposition": {
"version": "0.5.3",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz",
"integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==",
"requires": {
"safe-buffer": "5.1.2"
}
},
"content-type": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
"integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
},
"cookie": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
"integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg=="
},
"cookie-signature": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
"integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
},
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"requires": {
"ms": "2.0.0"
}
},
"depd": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
"integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
},
"destroy": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
"integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
},
"ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
},
"encodeurl": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
"integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
},
"escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
"integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
},
"etag": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
"integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
},
"express": {
"version": "4.17.1",
"resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz",
"integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==",
"requires": {
"accepts": "~1.3.7",
"array-flatten": "1.1.1",
"body-parser": "1.19.0",
"content-disposition": "0.5.3",
"content-type": "~1.0.4",
"cookie": "0.4.0",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "~1.1.2",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"finalhandler": "~1.1.2",
"fresh": "0.5.2",
"merge-descriptors": "1.0.1",
"methods": "~1.1.2",
"on-finished": "~2.3.0",
"parseurl": "~1.3.3",
"path-to-regexp": "0.1.7",
"proxy-addr": "~2.0.5",
"qs": "6.7.0",
"range-parser": "~1.2.1",
"safe-buffer": "5.1.2",
"send": "0.17.1",
"serve-static": "1.14.1",
"setprototypeof": "1.1.1",
"statuses": "~1.5.0",
"type-is": "~1.6.18",
"utils-merge": "1.0.1",
"vary": "~1.1.2"
}
},
"finalhandler": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
"integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
"requires": {
"debug": "2.6.9",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"on-finished": "~2.3.0",
"parseurl": "~1.3.3",
"statuses": "~1.5.0",
"unpipe": "~1.0.0"
}
},
"forwarded": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
"integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
},
"fresh": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
"integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
},
"http-errors": {
"version": "1.7.2",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
"integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==",
"requires": {
"depd": "~1.1.2",
"inherits": "2.0.3",
"setprototypeof": "1.1.1",
"statuses": ">= 1.5.0 < 2",
"toidentifier": "1.0.0"
}
},
"https": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/https/-/https-1.0.0.tgz",
"integrity": "sha1-PDfHrhqO65ZpBKKtHpdaGUt+06Q="
},
"iconv-lite": {
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
"requires": {
"safer-buffer": ">= 2.1.2 < 3"
}
},
"inherits": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
},
"ipaddr.js": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz",
"integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA=="
},
"media-typer": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
"integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
},
"merge-descriptors": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
"integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
},
"methods": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
"integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
},
"mime": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
},
"mime-db": {
"version": "1.40.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz",
"integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA=="
},
"mime-types": {
"version": "2.1.24",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz",
"integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==",
"requires": {
"mime-db": "1.40.0"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
},
"negotiator": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
"integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
},
"on-finished": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
"integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
"requires": {
"ee-first": "1.1.1"
}
},
"parseurl": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
},
"path-to-regexp": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
"integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
},
"proxy-addr": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz",
"integrity": "sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==",
"requires": {
"forwarded": "~0.1.2",
"ipaddr.js": "1.9.0"
}
},
"qs": {
"version": "6.7.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
"integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ=="
},
"range-parser": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
},
"raw-body": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz",
"integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==",
"requires": {
"bytes": "3.1.0",
"http-errors": "1.7.2",
"iconv-lite": "0.4.24",
"unpipe": "1.0.0"
}
},
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"send": {
"version": "0.17.1",
"resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz",
"integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==",
"requires": {
"debug": "2.6.9",
"depd": "~1.1.2",
"destroy": "~1.0.4",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"fresh": "0.5.2",
"http-errors": "~1.7.2",
"mime": "1.6.0",
"ms": "2.1.1",
"on-finished": "~2.3.0",
"range-parser": "~1.2.1",
"statuses": "~1.5.0"
},
"dependencies": {
"ms": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
"integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
}
}
},
"serve-static": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz",
"integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==",
"requires": {
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"parseurl": "~1.3.3",
"send": "0.17.1"
}
},
"setprototypeof": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
"integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw=="
},
"statuses": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
"integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
},
"toidentifier": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
"integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="
},
"type-is": {
"version": "1.6.18",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
"requires": {
"media-typer": "0.3.0",
"mime-types": "~2.1.24"
}
},
"unpipe": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
"integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
},
"utils-merge": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
"integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
},
"vary": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
"integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
}
}
}

View File

@ -0,0 +1,16 @@
{
"name": "node_auth_basic",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"basic-auth": "latest",
"express": "latest",
"https": "latest"
}
}

View File

@ -0,0 +1,32 @@
-----BEGIN CERTIFICATE-----
MIIFjDCCA3SgAwIBAgIJALg+W/pyOZ5pMA0GCSqGSIb3DQEBBQUAMIGBMQswCQYD
VQQGEwJVUzELMAkGA1UECAwCTUExDzANBgNVBAcMBkJvc3RvbjETMBEGA1UECgwK
RXhhbXBsZSBDbzEQMA4GA1UECwwHdGVjaG9wczELMAkGA1UEAwwCY2ExIDAeBgkq
hkiG9w0BCQEWEWNlcnRzQGV4YW1wbGUuY29tMB4XDTE5MTAzMDIzMzQxOVoXDTIy
MDcyNTIzMzQxOVowgYgxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJNQTEPMA0GA1UE
BwwGQm9zdG9uMRMwEQYDVQQKDApFeGFtcGxlIENvMRAwDgYDVQQLDAd0ZWNob3Bz
MRIwEAYDVQQDDAlsb2NhbGhvc3QxIDAeBgkqhkiG9w0BCQEWEWNlcnRzQGV4YW1w
bGUuY29tMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA6+4m+blS6NcS
ZcftDArQhd7ekeeZzfiedonAXybjJ+GkqtigKW2hUtHldWRswx56braUAJYUsj2I
Xh9y9/vWCGUGF9ZsiPTI28bZgbfwvyGaO0K4mLPGdvOvtXgbbw3uC6hU+sbiFF88
BgZdwnsjoP7wPHqyzJ8PQ7BM7jdflLFn1uULS+9fe/7gPRSXxvS26WC1IYK8gVqQ
JBXH2tO3ZSh8LygePjKUWQF4xsN8mttRYlahdhNIMb1ZgBnG4rFq8JOc5fUbrYIz
s+LLMRm8zn3GIZgp4BiK900RJGxDXtVnx6ce4fdj+4OsL8NW3JzBNB66unsQ1gkz
WZbAbaXN7HeUpnktuGfA+TdWrzMd8XXaMDHXj9Uh8NtjToBJ5hHkStSkHRxBnlyo
MvWXGubn+EfUC5nVBGnG/Yfb9caKlpgIHh164ScGKlFlV4W8VpoA1W0BwE/R/E/I
lo5wiX5DP2uxh8bC7J1UK7guDnGDZASWsqNxtCBuCmEx9L6ePlnQbuc/AA+QLdyS
z4wb0IGefS+et0BOJFj2/u28/A5N21swarTCxPm0rczXZfRhooM8/X6Z3Pw6E5ze
D1S/49V8qLwqwcwkJhnIJoqSj/cWNGVXRY2Tqu5536NvK3e5BvOPeULf0IKYDhUs
LxwgpQ07gntS7UHBSejmmmjs92IiQiECAwEAATANBgkqhkiG9w0BAQUFAAOCAgEA
JxbqWQynzigBoaR7DP6AtXzYC2RcVBQuwyBSy348ruiIeQy3Niwt5cuhFA/LVyZP
QI6KQTj2Qd0elAkeSp55nG/ZWmcszAF8aWrFRNUc0U85eUcZSyKP1eRblFgf2sMg
HrJY0w0Ust43jk+tWNCftSHL6+uxaKc0z18vFmpHIBtpecCHYaA2mEUTTS19mzSu
K816jEqDvwxUWRbC8aTLOKDS3w1OLBtwVQ8bBvKCbJv7I3AmWtGkmDdetBwEe9u4
lpC/7Rmm57/UG5P8Vy7lVGyOnxQaTE1FxxNWwq02Iy4gc1UAovYYBtgsg+XfnpL6
TUo/9gtjl5K6OMxS/8JJjCi7U8RqiZICGTvjUvl0ahUtjs9CHtruWPbQ7tpA+IWQ
k9U708R3TAILHtq1Po1cWTtezYqUokLVwQlvwWJOezuPWnz0v5VKuLX64Ot0HAjI
rB/VggCRRFbxseM22q3xYKGpERhvAnSjD6bAYP11MlCj/4FBflHDWgDjTvmwRW05
GW7rsTT+4omu/yQGRXSqFpaD1pYuoi1j0Lhtk/LCwQLE9bxPBUf0t8Ut+q5s0s0X
yXB1git1Aq09HZ1yJRfL//g/PWOu5lFhc8Xe4grrB3peRl2yHNiBNXQ1m1DmxCpL
oeM+rfdX4oDVWH5oFmyFGAUOdsZ1JgvaasFq2JRKryA=
-----END CERTIFICATE-----

View File

@ -0,0 +1,29 @@
-----BEGIN CERTIFICATE REQUEST-----
MIIE5zCCAs8CAQAwgYgxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJNQTEPMA0GA1UE
BwwGQm9zdG9uMRMwEQYDVQQKDApFeGFtcGxlIENvMRAwDgYDVQQLDAd0ZWNob3Bz
MRIwEAYDVQQDDAlsb2NhbGhvc3QxIDAeBgkqhkiG9w0BCQEWEWNlcnRzQGV4YW1w
bGUuY29tMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA6+4m+blS6NcS
ZcftDArQhd7ekeeZzfiedonAXybjJ+GkqtigKW2hUtHldWRswx56braUAJYUsj2I
Xh9y9/vWCGUGF9ZsiPTI28bZgbfwvyGaO0K4mLPGdvOvtXgbbw3uC6hU+sbiFF88
BgZdwnsjoP7wPHqyzJ8PQ7BM7jdflLFn1uULS+9fe/7gPRSXxvS26WC1IYK8gVqQ
JBXH2tO3ZSh8LygePjKUWQF4xsN8mttRYlahdhNIMb1ZgBnG4rFq8JOc5fUbrYIz
s+LLMRm8zn3GIZgp4BiK900RJGxDXtVnx6ce4fdj+4OsL8NW3JzBNB66unsQ1gkz
WZbAbaXN7HeUpnktuGfA+TdWrzMd8XXaMDHXj9Uh8NtjToBJ5hHkStSkHRxBnlyo
MvWXGubn+EfUC5nVBGnG/Yfb9caKlpgIHh164ScGKlFlV4W8VpoA1W0BwE/R/E/I
lo5wiX5DP2uxh8bC7J1UK7guDnGDZASWsqNxtCBuCmEx9L6ePlnQbuc/AA+QLdyS
z4wb0IGefS+et0BOJFj2/u28/A5N21swarTCxPm0rczXZfRhooM8/X6Z3Pw6E5ze
D1S/49V8qLwqwcwkJhnIJoqSj/cWNGVXRY2Tqu5536NvK3e5BvOPeULf0IKYDhUs
LxwgpQ07gntS7UHBSejmmmjs92IiQiECAwEAAaAZMBcGCSqGSIb3DQEJBzEKDAhw
YXNzd29yZDANBgkqhkiG9w0BAQsFAAOCAgEANu/fvD8Bq5ISbTQX42hTyHfe3caL
G7BDJdwTaBhzHcJFL0xbHDpDKvEhWgjzfP253x4EEx2tLHOCntj7CNBCYBq/oyWs
60ef7RH6/Lg+zo3wPO8iSV4hvjJrddwb7nlmZOFK0rGnpdTEQyvr+885/pyeP6a9
iMtZZL3/QIz2RN79nuOhh3J5G9SsrQhxw7Dk7zXIaSXdC/+f+lJnJZav6Q+9w8WD
a5sgx8jMTjL9TjA9jCh02P04MM4MD4YjwY9K13I9qWs3bhR4n43omlDFbMMQ/Es6
t+8hN1yxsokSi0iZhCCCeUZxfbCyN5z23DmHYcbkTwhKbqW4QJnJ3Xw9eYmPNC9Q
FxxZmj6A7N2+V07jXQjyhdNWCeTLoPEyoAAwtNi8SASG6olvgIygX0rhRwU8HvZt
u9gKh5vTg4Si4rNZ6DatHY/qlJUcaaqLpG2nI8bctkefH65if8k1iKhfPTxawtHk
ytrsyU+oQ2d6maBcEqfY6ui2j5O3U4yUVM+8RjppM/qrzqg9c3++tRfRMg0ZArQu
lXjA5KnQuNAeSDJmDpfOVoHAXM/uPte8pP9ZVO7MLBqhjkdXfF0fJxV6qX0/l0nt
gurXEbIv19qz91QkzI2MeM59c+IYTkxYZmkbFqf4rB2OJT5yUcsJ88R7zipc8zFa
CR1YgTG5mrlPW2c=
-----END CERTIFICATE REQUEST-----

View File

@ -0,0 +1,51 @@
-----BEGIN RSA PRIVATE KEY-----
MIIJKAIBAAKCAgEA6+4m+blS6NcSZcftDArQhd7ekeeZzfiedonAXybjJ+Gkqtig
KW2hUtHldWRswx56braUAJYUsj2IXh9y9/vWCGUGF9ZsiPTI28bZgbfwvyGaO0K4
mLPGdvOvtXgbbw3uC6hU+sbiFF88BgZdwnsjoP7wPHqyzJ8PQ7BM7jdflLFn1uUL
S+9fe/7gPRSXxvS26WC1IYK8gVqQJBXH2tO3ZSh8LygePjKUWQF4xsN8mttRYlah
dhNIMb1ZgBnG4rFq8JOc5fUbrYIzs+LLMRm8zn3GIZgp4BiK900RJGxDXtVnx6ce
4fdj+4OsL8NW3JzBNB66unsQ1gkzWZbAbaXN7HeUpnktuGfA+TdWrzMd8XXaMDHX
j9Uh8NtjToBJ5hHkStSkHRxBnlyoMvWXGubn+EfUC5nVBGnG/Yfb9caKlpgIHh16
4ScGKlFlV4W8VpoA1W0BwE/R/E/Ilo5wiX5DP2uxh8bC7J1UK7guDnGDZASWsqNx
tCBuCmEx9L6ePlnQbuc/AA+QLdySz4wb0IGefS+et0BOJFj2/u28/A5N21swarTC
xPm0rczXZfRhooM8/X6Z3Pw6E5zeD1S/49V8qLwqwcwkJhnIJoqSj/cWNGVXRY2T
qu5536NvK3e5BvOPeULf0IKYDhUsLxwgpQ07gntS7UHBSejmmmjs92IiQiECAwEA
AQKCAgBMz++elEi7B/K0cn/p0MeNHj+Gb0ny9kPZQgYAtK92N4+eFAMpGclRJoc3
Tife6thfnU/YVQSN91yjkEelFzfa6dEvB5+0g0W10CrWgeV+xNjq/FEPXlB6P+Jw
CZec84MSBo8MSp4mlc78AyyZee/ipb3fYu89kXetyQNy91JGHfA51b50zY1f2F53
ZHH504iNuO+f5mmrD6ooJXM4+n8WDzVHQR9/joYf6EvDIrg5zrfTdXMB90sk7onq
G/pVqf+wLc8CS2fkeKSOp4MSDFgdPnO4Uksn67WqyEuReTIB+SbdqLp0RVAm/fqc
XlO0ZdC9QFf5CMI1fS9btqoVyMqe10+0T7mSRgzptsAoElZxt9/OPNW+7M0lGHJL
GWToqFTI7NZAppqiOw4YTGYF4krT/tBEv51NS83EytYJXKgE32z1n3HbxZVEQpJk
QZ7SqiLai+0MhWFiQhAu3GL1XqBqNiEOtPK2KZQaA/0FgOUU8fgrFdkuCZCow75D
9wKFUzXylH4qqgyyiH+/EU2aOnP0RthdK0mPdMO4QSZltaphxpzWmgwqfSPLqBUx
W1Lyw+FgM3Ux+40yFEuJgzAJzsmx3/C/+JG2UD6JXv7JYi1Dsats9I5fNBbTRTnb
JVbs5CbFTmYT3R2Eoj5f4VX1G5I4R4tRByYtuemLGC7euu7lAQKCAQEA+a2TkTNl
Nn3z0BBDgbMm5MM3bf/xEelrB4e+h1Chi6u0DmIVfnFXZDHZ/1xeLpTytGxj3tQ4
AC0Pkj26bjgz0oOqxO+WnuEL4+6NXAYTgcce5lPeWNZlDl1baoQwTNZT5Cumqyyy
Gs/ksUiB3zXxNbYDhYrbuFpV0Od2cfYusZCjHYVvsesteVRVTCjwH5qbo3o6IwHd
8T5L4FyvuzeDKnR+jcHgyPio5xg9lrChV2tVVC6DCStW1qvjZTaOVt9B/jkDfrXk
U78Hom2P2qv5b5iJy0aQ+d2hqPH3nW+x14rp1Ejac/FTVEKZLl4bgZRT/gbsepOQ
lBw/tC4IA14q8QKCAQEA8ed2Whd0uzAXWCjDj1xefEqAZN3l4ToPSvfxZkls3tgx
x8R9TlLmmEhtWjdl+68mhsSVoILVTTzFOBzbDaFNSjdyfTpf2927pofH5Sqy2x0d
HHE9XzXC3NjMDnwuv9TiVRq6S1uLmbSfPBVFYHGvJSdKj9fNIa09LehUaU0+qAes
FYr5W269Zpyz2e8pqmWC8jOIslbuqQxTJ3/2Xt4/s+BMMn0A6/iKByy915MnEyAi
aijdkeJWY9FHUXsj9Qpb0PQbp5MzMlSwOC5OaPQ7bbwoi2UKR9oG9I+BxuWdZWa4
z5OQBkKZBKopvy+UyxH6HwvCW/NX7k0DBDJVDhiqMQKCAQEAsnWBo2hD8LYIHfBx
//OIWjy6ktI8gdwAF0rMQTW9sfJRaXJsAIuMtl33flGMM+lcrN9IZw3y1RVgKtwa
NAvR5qcb2VWSqA8dC5cHzP2+1aT4jhLmO0NKygCqhbDkQRd0sQMORw1UZFuyD5FO
2rdYqFZnn/0HXXacHgxDGeOnrl0+aaxlkJsTTsSbjys3rq84jZmgkkMUWwl2/G/j
X4KnKyJHDpXEUIGDYGJwKxQmzjA2tkvkdxpWIGyNxVtAVBGRvt8Lg3cJM164Wz3K
c7c2O7Cg1P2kRCRJF1OT9CDQ0cpFpelI01Rrhi9wA7daG71kZ46VWmu9DFBArE22
hom5UQKCAQAXNLwu0lc5adijdW2lQBg0VjVQ99SIsbgr2fNHe7289wJ3hor6y4sh
irkq0WuwJFJO1WcBOevad/z3cBU3joTKnN0X1FTD09afmCD+BAWabYWQZ/CaRJ8H
7qhMr28Qqgx4W5MJjaOrwB8SdXvtT5qcttM5T1t3YpYe5E3MuENNfUbt3iIjulDj
Gh9Kxd26YfiY4Ya4jQxiHTOQ836gBTQZPmgw47sF6Bbdr1Ya6xvxgsCqbHcs+oiN
FNOBpo2BgVfjiLdqcP2VU3iAOzj8aomU8lr1XGELGYhXR7kMapSZ2KvcBezoHNVv
FB/jHl5oyLW06rzfc025J5wld+qe6pDRAoIBAHz4RLJ2WITZbCdDGn6nLnbI6jGd
RrYt2ZzMiz/QqAZV3WJP9vS+FDYDiU4IT7uCMuielbqNyB/3jd3ctxVD2GX3KbOh
WMdER9aHerafdFtxbOJXS/vkgjf8QBF54z1HhL+wnmSE+LaXI1R/WMJt7jBDbvcH
Ibfeyy+p76KO+qbEh07mHYjpCvhxaSeKaFX1vkJvXL4NiiZPNMemZWJkmrvr2k6E
WlmD61tDdYvEtMGZobPop/wJYVPE8yxivPtGpvdl7U8JJM++M8WTrMpWhxptelPL
6+egMoYXA/N1gTJNQ0w9eQEtNqXtQY/LQtWuUlhCanuT1eG/FyDGrpid8Bg=
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,26 @@
[ req ]
default_bits = 4096
days = 9999
distinguished_name = req_distinguished_name
attributes = req_attributes
prompt = no
x509_extensions = v3_ca
[ req_distinguished_name ]
C = US
ST = MA
L = Boston
O = Example Co
OU = techops
CN = localhost
emailAddress = certs@example.com
[ req_attributes ]
challengePassword = password
[ v3_ca ]
authorityInfoAccess = @issuer_info
[ issuer_info ]
OCSP;URI.0 = http://ocsp.example.com/
caIssuers;URI.0 = http://example.com/ca.cert