From c953dd9eea4f6240f825564b737b12ee26e5eb5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Ba=C5=99tip=C3=A1n?= Date: Fri, 21 Jun 2024 17:13:27 +0200 Subject: [PATCH] WebView: handling authentication challenge (passing user credentials) for OSX (working) and Linux (WIP) --- src/slic3r/CMakeLists.txt | 13 +- src/slic3r/GUI/MainFrame.cpp | 8 +- src/slic3r/GUI/WebViewDialog.cpp | 35 ++--- src/slic3r/GUI/WebViewPlatformUtils.hpp | 8 ++ src/slic3r/GUI/WebViewPlatformUtilsLinux.cpp | 32 +++++ src/slic3r/GUI/WebViewPlatformUtilsMac.mm | 134 +++++++++++++++++++ src/slic3r/GUI/WebViewPlatformUtilsWin32.cpp | 10 ++ 7 files changed, 210 insertions(+), 30 deletions(-) create mode 100644 src/slic3r/GUI/WebViewPlatformUtils.hpp create mode 100644 src/slic3r/GUI/WebViewPlatformUtilsLinux.cpp create mode 100644 src/slic3r/GUI/WebViewPlatformUtilsMac.mm create mode 100644 src/slic3r/GUI/WebViewPlatformUtilsWin32.cpp diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 997edc56c3..2b4581d839 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -29,6 +29,7 @@ set(SLIC3R_GUI_SOURCES GUI/WebViewDialog.hpp GUI/WebView.cpp GUI/WebView.hpp + GUI/WebViewPlatformUtils.hpp GUI/SysInfoDialog.cpp GUI/SysInfoDialog.hpp GUI/KBShortcutsDialog.cpp @@ -389,9 +390,19 @@ if (APPLE) GUI/Mouse3DHandlerMac.mm GUI/InstanceCheckMac.mm GUI/InstanceCheckMac.h - ) + GUI/WebViewPlatformUtilsMac.mm + + ) FIND_LIBRARY(DISKARBITRATION_LIBRARY DiskArbitration) FIND_LIBRARY(COREWLAN_LIBRARY CoreWLAN) +elseif (WIN32) + list(APPEND SLIC3R_GUI_SOURCES + GUI/WebViewPlatformUtilsWin32.cpp + ) +else() # Linux + list(APPEND SLIC3R_GUI_SOURCES + GUI/WebViewPlatformUtilsLinux.cpp + ) endif () add_library(libslic3r_gui STATIC ${SLIC3R_GUI_SOURCES}) diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index f29cdea76f..1b3c060bb9 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -855,11 +855,9 @@ void MainFrame::show_printer_webview_tab(DynamicPrintConfig* dpc) if (dynamic_cast*>(dpc->option("printhost_authorization_type"))->value == AuthorizationType::atKeyPassword) { set_printer_webview_api_key(dpc->opt_string("printhost_apikey")); } -#if 0 // The user password authentication is not working in prusa link as of now. else { - mset_printer_webview_credentials(dpc->opt_string("printhost_user"), dpc->opt_string("printhost_password")); + set_printer_webview_credentials(dpc->opt_string("printhost_user"), dpc->opt_string("printhost_password")); } -#endif // 0 // add printer or change url if (get_printer_webview_tab_added()) { set_printer_webview_tab_url(from_u8(url)); @@ -903,7 +901,9 @@ void MainFrame::set_printer_webview_tab_url(const wxString& url) add_printer_webview_tab(url); return; } - m_printer_webview->clear(); + // TODO: this will reset already filled credential when bundle loaded, + // what's the reason of clearing credentials here? + //m_printer_webview->clear(); m_printer_webview->set_default_url(url); if (m_tabpanel->GetSelection() == m_tabpanel->FindPage(m_printer_webview)) { diff --git a/src/slic3r/GUI/WebViewDialog.cpp b/src/slic3r/GUI/WebViewDialog.cpp index 02717f3c95..ead1795447 100644 --- a/src/slic3r/GUI/WebViewDialog.cpp +++ b/src/slic3r/GUI/WebViewDialog.cpp @@ -7,6 +7,8 @@ #include "slic3r/GUI/UserAccount.hpp" #include "slic3r/GUI/format.hpp" #include "slic3r/GUI/WebView.hpp" +#include "slic3r/GUI/WebViewPlatformUtils.hpp" + #include "slic3r/GUI/MsgDialog.hpp" #include "slic3r/GUI/Field.hpp" @@ -775,10 +777,12 @@ void PrinterWebViewPanel::send_api_key() wxString script = wxString::Format(R"( // Check if window.fetch exists before overriding if (window.fetch) { + console.log('Patching fetch with API key'); const originalFetch = window.fetch; window.fetch = function(input, init = {}) { init.headers = init.headers || {}; - init.headers['X-API-Key'] = '%s'; + init.headers['X-Api-Key'] = '%s'; + console.log('Patched fetch', input, init); return originalFetch(input, init); }; } @@ -797,26 +801,7 @@ void PrinterWebViewPanel::send_credentials() if (!m_browser || m_api_key_sent) return; m_api_key_sent = true; - wxString usr = from_u8(m_usr); - wxString psk = from_u8(m_psk); - wxString script = wxString::Format(R"( - // Check if window.fetch exists before overriding - if (window.fetch) { - const originalFetch = window.fetch; - window.fetch = function(input, init = {}) { - init.headers = init.headers || {}; - init.headers['X-API-Key'] = 'Basic ' + btoa(`%s:%s`); - return originalFetch(input, init); - }; - } -)", usr, psk); - - m_browser->RemoveAllUserScripts(); - BOOST_LOG_TRIVIAL(debug) << "RunScript " << script << "\n"; - m_browser->AddUserScript(script); - - m_browser->Reload(); - + setup_webview_with_credentials(m_browser, m_usr, m_psk); } void PrinterWebViewPanel::sys_color_changed() @@ -1006,7 +991,7 @@ void WebViewDialog::on_reload_button(wxCommandEvent& WXUNUSED(evt)) } -void WebViewDialog::on_navigation_request(wxWebViewEvent &evt) +void WebViewDialog::on_navigation_request(wxWebViewEvent &evt) { } @@ -1319,7 +1304,7 @@ void PrinterPickWebViewDialog::request_compatible_printers_SLA() wxString script = GUI::format_wxstr("window._prusaConnect_v1.requestCompatiblePrinter(%1%)", request); run_script(script); } -void PrinterPickWebViewDialog::on_dpi_changed(const wxRect &suggested_rect) +void PrinterPickWebViewDialog::on_dpi_changed(const wxRect &suggested_rect) { wxWindow *parent = GetParent(); const wxSize &size = wxSize( @@ -1331,7 +1316,7 @@ void PrinterPickWebViewDialog::on_dpi_changed(const wxRect &suggested_rect) Refresh(); } -LoginWebViewDialog::LoginWebViewDialog(wxWindow *parent, std::string &ret_val, const wxString& url) +LoginWebViewDialog::LoginWebViewDialog(wxWindow *parent, std::string &ret_val, const wxString& url) : WebViewDialog(parent , url , _L("Log in dialog"), @@ -1350,7 +1335,7 @@ void LoginWebViewDialog::on_navigation_request(wxWebViewEvent &evt) EndModal(wxID_OK); } } -void LoginWebViewDialog::on_dpi_changed(const wxRect &suggested_rect) +void LoginWebViewDialog::on_dpi_changed(const wxRect &suggested_rect) { const wxSize &size = wxSize(50 * wxGetApp().em_unit(), 80 * wxGetApp().em_unit()); SetMinSize(size); diff --git a/src/slic3r/GUI/WebViewPlatformUtils.hpp b/src/slic3r/GUI/WebViewPlatformUtils.hpp new file mode 100644 index 0000000000..5b876266dc --- /dev/null +++ b/src/slic3r/GUI/WebViewPlatformUtils.hpp @@ -0,0 +1,8 @@ +#pragma once + +#include +#include + +namespace Slic3r::GUI { + void setup_webview_with_credentials(wxWebView* web_view, const std::string& username, const std::string& password); +} diff --git a/src/slic3r/GUI/WebViewPlatformUtilsLinux.cpp b/src/slic3r/GUI/WebViewPlatformUtilsLinux.cpp new file mode 100644 index 0000000000..64ecb600dc --- /dev/null +++ b/src/slic3r/GUI/WebViewPlatformUtilsLinux.cpp @@ -0,0 +1,32 @@ +#include "WebViewPlatformUtils.hpp" +#include +#include +#include + + +namespace Slic3r::GUI { + +struct Credentials { + std::string username; + std::string password; +}; + +using WebViewCredentialsMap = std::unordered_map>; +WebViewCredentialsMap g_credentials; + +gboolean webkit_authorize_handler(WebKitWebView *web_view, WebKitAuthenticationRequest *request, gpointer user_data) +{ + const Credentials& creds = *static_cast(user_data); + webkit_authentication_request_authenticate(request, webkit_credential_new(creds.username.c_str(), creds.password.c_str())); +} + + void setup_webview_with_credentials(wxWebView* web_view, const std::string& username, const std::string& password) +{ + std::unique_ptr ptr(new Credentials{username, password}); + g_credentials[web_view->GetNativeBackend()] = std::move(ptr); + g_signal_connect(web_view, "authorize", + G_CALLBACK(webkit_authorize_handler), g_credentials[web_view].get()); + +} + +} diff --git a/src/slic3r/GUI/WebViewPlatformUtilsMac.mm b/src/slic3r/GUI/WebViewPlatformUtilsMac.mm new file mode 100644 index 0000000000..03c555d647 --- /dev/null +++ b/src/slic3r/GUI/WebViewPlatformUtilsMac.mm @@ -0,0 +1,134 @@ +#include "WebViewPlatformUtils.hpp" + +#import +#import +#import +#import + +@interface MyNavigationDelegate: NSObject { + id _delegate; + NSString* _username; + NSString* _passwd; +} +- (id) initWithOriginalDelegate: (id) delegate userName: (const char*) username password: (const char*) password; +@end + +@implementation MyNavigationDelegate +- (id) initWithOriginalDelegate: (id) delegate userName: (const char*) username password: (const char*) password { + if (self = [super init]) { + _delegate = delegate; + _username = [[NSString alloc] initWithFormat:@"%s", username]; + _passwd = [[NSString alloc] initWithFormat:@"%s", password]; + } + return self; +} + +- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler WK_SWIFT_ASYNC(3) { + if ([_delegate respondsToSelector:@selector(webView:decidePolicyForNavigationAction:decisionHandler:)]) { + [_delegate webView:webView decidePolicyForNavigationAction:navigationAction decisionHandler:decisionHandler]; + } else { + decisionHandler(WKNavigationActionPolicyAllow); + } +} + +- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction preferences:(WKWebpagePreferences *)preferences decisionHandler:(void (^)(WKNavigationActionPolicy, WKWebpagePreferences *))decisionHandler WK_SWIFT_ASYNC(4) API_AVAILABLE(macos(10.15), ios(13.0)) { + if ([_delegate respondsToSelector:@selector(webView:decidePolicyForNavigationAction:preferences:decisionHandler:)]) { + [_delegate webView:webView decidePolicyForNavigationAction:navigationAction preferences:preferences decisionHandler:decisionHandler]; + } else { + decisionHandler(WKNavigationActionPolicyAllow, preferences); + } +} + +- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler WK_SWIFT_ASYNC(3) { + if ([_delegate respondsToSelector:@selector(webView:decidePolicyForNavigationResponse:decisionHandler:)]) { + [_delegate webView:webView decidePolicyForNavigationResponse: navigationResponse decisionHandler: decisionHandler]; + } else { + decisionHandler(WKNavigationResponsePolicyAllow); + } +} + +- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation { + if ([_delegate respondsToSelector:@selector(webView:didStartProvisionalNavigation:)]) { + [_delegate webView:webView didStartProvisionalNavigation:navigation]; + } +} + +- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(null_unspecified WKNavigation *)navigation { + if ([_delegate respondsToSelector:@selector(webView:didReceiveServerRedirectForProvisionalNavigation:)]) { + [_delegate webView:webView didReceiveServerRedirectForProvisionalNavigation:navigation]; + } +} + +- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error { + if ([_delegate respondsToSelector:@selector(webView:didFailProvisionalNavigation:withError:)]) { + [_delegate webView:webView didFailProvisionalNavigation:navigation withError:error]; + } +} + +- (void)webView:(WKWebView *)webView didCommitNavigation:(null_unspecified WKNavigation *)navigation { + if ([_delegate respondsToSelector:@selector(webView:didCommitNavigation:)]) { + [_delegate webView:webView didCommitNavigation:navigation]; + } +} + +- (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation { + if ([_delegate respondsToSelector:@selector(webView:didFinishNavigation:)]) { + [_delegate webView:webView didFinishNavigation:navigation]; + } +} + +- (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error { + if ([_delegate respondsToSelector:@selector(webView:didFailNavigation:withError:)]) { + [_delegate webView:webView didFailNavigation:navigation withError:error]; + } +} + +- (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler WK_SWIFT_ASYNC_NAME(webView(_:respondTo:)) { + //challenge.protectionSpace.realm + completionHandler( + NSURLSessionAuthChallengeUseCredential, + [NSURLCredential credentialWithUser: _username password: _passwd persistence: NSURLCredentialPersistenceForSession] + ); +} + +- (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView API_AVAILABLE(macos(10.11), ios(9.0)) { + if ([_delegate respondsToSelector:@selector(webViewWebContentProcessDidTerminate:)]) { + [_delegate webViewWebContentProcessDidTerminate:webView]; + } +} + +- (void)webView:(WKWebView *)webView authenticationChallenge:(NSURLAuthenticationChallenge *)challenge shouldAllowDeprecatedTLS:(void (^)(BOOL))decisionHandler WK_SWIFT_ASYNC_NAME(webView(_:shouldAllowDeprecatedTLSFor:)) WK_SWIFT_ASYNC(3) API_AVAILABLE(macos(11.0), ios(14.0)) { + if ([_delegate respondsToSelector:@selector(webView:authenticationChallenge:shouldAllowDeprecatedTLS:)]) { + [_delegate webView:webView authenticationChallenge:challenge shouldAllowDeprecatedTLS:decisionHandler]; + } else { + decisionHandler(YES); + } +} + +- (void)webView:(WKWebView *)webView navigationAction:(WKNavigationAction *)navigationAction didBecomeDownload:(WKDownload *)download API_AVAILABLE(macos(11.3), ios(14.5)) { + if ([_delegate respondsToSelector:@selector(webView:navigationAction:didBecomeDownload:)]) { + [_delegate webView:webView navigationAction:navigationAction didBecomeDownload:download]; + } +} + +- (void)webView:(WKWebView *)webView navigationResponse:(WKNavigationResponse *)navigationResponse didBecomeDownload:(WKDownload *)download API_AVAILABLE(macos(11.3), ios(14.5)) { + if ([_delegate respondsToSelector:@selector(webView:navigationResponse:didBecomeDownload:)]) { + [_delegate webView:webView navigationResponse:navigationResponse didBecomeDownload:download]; + } +} +@end + + +namespace Slic3r::GUI { +void setup_webview_with_credentials(wxWebView* web_view, const std::string& username, const std::string& password) +{ + WKWebView* backend = static_cast(web_view->GetNativeBackend()); + if (![backend.navigationDelegate isKindOfClass:MyNavigationDelegate.class]) { + backend.navigationDelegate = [[MyNavigationDelegate alloc] + initWithOriginalDelegate:backend.navigationDelegate + userName:username.c_str() + password:password.c_str()]; + } +} +} + diff --git a/src/slic3r/GUI/WebViewPlatformUtilsWin32.cpp b/src/slic3r/GUI/WebViewPlatformUtilsWin32.cpp new file mode 100644 index 0000000000..1fcd5448d4 --- /dev/null +++ b/src/slic3r/GUI/WebViewPlatformUtilsWin32.cpp @@ -0,0 +1,10 @@ +#include "WebViewPlatformUtils.hpp" + +namespace Slic3r::GUI { + +void setup_webview_with_credentials(wxWebView* web_view, const std::string& username, const std::string& password) +{ + // TODO: fill IE/Edge implementation here +} + +}