WebView: handling authentication challenge (passing user credentials) for OSX (working) and Linux (WIP)

This commit is contained in:
Jan Bařtipán 2024-06-21 17:13:27 +02:00 committed by Lukas Matena
parent b4fadc102d
commit c953dd9eea
7 changed files with 210 additions and 30 deletions

View File

@ -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})

View File

@ -855,11 +855,9 @@ void MainFrame::show_printer_webview_tab(DynamicPrintConfig* dpc)
if (dynamic_cast<const ConfigOptionEnum<AuthorizationType>*>(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)) {

View File

@ -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);

View File

@ -0,0 +1,8 @@
#pragma once
#include <wx/webview.h>
#include <string>
namespace Slic3r::GUI {
void setup_webview_with_credentials(wxWebView* web_view, const std::string& username, const std::string& password);
}

View File

@ -0,0 +1,32 @@
#include "WebViewPlatformUtils.hpp"
#include <unordered_map>
#include <memory>
#include <webkit2/webkit2.h>
namespace Slic3r::GUI {
struct Credentials {
std::string username;
std::string password;
};
using WebViewCredentialsMap = std::unordered_map<void*, std::unique_ptr<Credentials>>;
WebViewCredentialsMap g_credentials;
gboolean webkit_authorize_handler(WebKitWebView *web_view, WebKitAuthenticationRequest *request, gpointer user_data)
{
const Credentials& creds = *static_cast<const Credentials*>(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<Credentials> 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());
}
}

View File

@ -0,0 +1,134 @@
#include "WebViewPlatformUtils.hpp"
#import <WebKit/WKWebView.h>
#import <WebKit/WKNavigationDelegate.h>
#import <Foundation/Foundation.h>
#import <Foundation/NSURLSession.h>
@interface MyNavigationDelegate: NSObject<WKNavigationDelegate> {
id<WKNavigationDelegate> _delegate;
NSString* _username;
NSString* _passwd;
}
- (id) initWithOriginalDelegate: (id<WKNavigationDelegate>) delegate userName: (const char*) username password: (const char*) password;
@end
@implementation MyNavigationDelegate
- (id) initWithOriginalDelegate: (id<WKNavigationDelegate>) 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<WKWebView*>(web_view->GetNativeBackend());
if (![backend.navigationDelegate isKindOfClass:MyNavigationDelegate.class]) {
backend.navigationDelegate = [[MyNavigationDelegate alloc]
initWithOriginalDelegate:backend.navigationDelegate
userName:username.c_str()
password:password.c_str()];
}
}
}

View File

@ -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
}
}