Merge branch 'jb_link_user_auth'

This commit is contained in:
Lukas Matena 2024-08-09 09:55:04 +02:00
commit 109b36ab5e
12 changed files with 52446 additions and 38 deletions

15
deps/+wxWidgets/webview.patch vendored Normal file
View File

@ -0,0 +1,15 @@
diff -ur build.orig/cmake/lib/webview/CMakeLists.txt build/cmake/lib/webview/CMakeLists.txt
--- wxWidgets.orig/build/cmake/lib/webview/CMakeLists.txt 2024-06-14 19:02:36.000000000 +0200
+++ wxWidgets/build/cmake/lib/webview/CMakeLists.txt 2024-07-01 16:21:52.922737200 +0200
@@ -46,9 +46,9 @@
elseif(WXMSW)
if(wxUSE_WEBVIEW_EDGE)
# Update the following variables if updating WebView2 SDK
- set(WEBVIEW2_VERSION "1.0.705.50")
+ set(WEBVIEW2_VERSION "1.0.2592.51")
set(WEBVIEW2_URL "https://www.nuget.org/api/v2/package/Microsoft.Web.WebView2/${WEBVIEW2_VERSION}")
- set(WEBVIEW2_SHA256 "6a34bb553e18cfac7297b4031f3eac2558e439f8d16a45945c22945ac404105d")
+ set(WEBVIEW2_SHA256 "805c79e05184fab18c9fe7b8ba820c598399b97adc1fbf5b0ea490efad91d5b8")
set(WEBVIEW2_DEFAULT_PACKAGE_DIR "${CMAKE_BINARY_DIR}/packages/Microsoft.Web.WebView2.${WEBVIEW2_VERSION}")

View File

@ -30,6 +30,7 @@ endif ()
add_cmake_project(wxWidgets
URL https://github.com/prusa3d/wxWidgets/archive/323a465e577e03f330e2e6a4c78e564d125340cb.zip
URL_HASH SHA256=B538E4AD3CC93117932F4DED70C476D6650F9C70A9D4055A08F3693864C47465
PATCH_COMMAND COMMAND ${PATCH_CMD} ${CMAKE_CURRENT_LIST_DIR}/webview.patch
CMAKE_ARGS
"-DCMAKE_DEBUG_POSTFIX:STRING="
-DwxBUILD_PRECOMP=ON

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})
@ -426,7 +437,7 @@ target_link_libraries(
if (MSVC)
target_link_libraries(libslic3r_gui PUBLIC Setupapi.lib)
elseif (CMAKE_SYSTEM_NAME STREQUAL "Linux")
target_link_libraries(libslic3r_gui PUBLIC ${DBUS_LIBRARIES})
target_link_libraries(libslic3r_gui PUBLIC ${DBUS_LIBRARIES})
elseif (APPLE)
target_link_libraries(libslic3r_gui PUBLIC ${DISKARBITRATION_LIBRARY} ${COREWLAN_LIBRARY})
endif()
@ -446,8 +457,12 @@ endif ()
# link these libraries.
if (UNIX AND NOT APPLE)
find_package(GTK${SLIC3R_GTK} REQUIRED)
target_include_directories(libslic3r_gui PRIVATE ${GTK${SLIC3R_GTK}_INCLUDE_DIRS})
target_link_libraries(libslic3r_gui PUBLIC ${GTK${SLIC3R_GTK}_LIBRARIES} fontconfig)
find_package(PkgConfig REQUIRED)
pkg_check_modules(WEBKIT2GTK REQUIRED webkit2gtk-4.0)
target_include_directories(libslic3r_gui PRIVATE ${GTK${SLIC3R_GTK}_INCLUDE_DIRS} ${WEBKIT2GTK_INCLUDE_DIRS})
target_link_libraries(libslic3r_gui PUBLIC ${GTK${SLIC3R_GTK}_LIBRARIES} fontconfig ${WEBKIT2GTK_LIBS})
endif ()
# Add a definition so that we can tell we are compiling slic3r.

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

@ -8,6 +8,7 @@
#include <stddef.h>
#include <vector>
#include <set>
#include <cstddef>
namespace DoubleSlider {

52080
src/slic3r/GUI/WebView2.h Normal file

File diff suppressed because it is too large Load Diff

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"
@ -753,6 +755,10 @@ PrinterWebViewPanel::PrinterWebViewPanel(wxWindow* parent, const wxString& defau
return;
m_browser->Bind(wxEVT_WEBVIEW_LOADED, &PrinterWebViewPanel::on_loaded, this);
#ifndef NDEBUG
m_browser->EnableAccessToDevTools();
m_browser->EnableContextMenu();
#endif
}
void PrinterWebViewPanel::on_loaded(wxWebViewEvent& evt)
@ -774,14 +780,18 @@ void PrinterWebViewPanel::send_api_key()
wxString key = from_u8(m_api_key);
wxString script = wxString::Format(R"(
// Check if window.fetch exists before overriding
if (window.fetch) {
const originalFetch = window.fetch;
if (window.originalFetch === undefined) {
console.log('Patching fetch with API key');
window.originalFetch = window.fetch;
window.fetch = function(input, init = {}) {
init.headers = init.headers || {};
init.headers['X-API-Key'] = '%s';
return originalFetch(input, init);
init.headers['X-Api-Key'] = sessionStorage.getItem('apiKey');
console.log('Patched fetch', input, init);
return window.originalFetch(input, init);
};
}
sessionStorage.setItem('authType', 'ApiKey');
sessionStorage.setItem('apiKey', '%s');
)",
key);
@ -789,34 +799,18 @@ void PrinterWebViewPanel::send_api_key()
BOOST_LOG_TRIVIAL(debug) << "RunScript " << script << "\n";
m_browser->AddUserScript(script);
m_browser->Reload();
remove_webview_credentials(m_browser);
}
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->AddUserScript("sessionStorage.removeItem('authType'); sessionStorage.removeItem('apiKey'); console.log('Session Storage cleared');");
m_browser->Reload();
m_api_key_sent = true;
setup_webview_with_credentials(m_browser, m_usr, m_psk);
}
void PrinterWebViewPanel::sys_color_changed()
@ -1006,7 +1000,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 +1313,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 +1325,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 +1344,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

@ -224,8 +224,21 @@ public:
void send_api_key();
void send_credentials();
void set_api_key(const std::string& key) { m_api_key = key; }
void set_credentials(const std::string& usr, const std::string& psk) { m_usr = usr; m_psk = psk; }
void set_api_key(const std::string &key)
{
if (m_api_key != key) {
clear();
m_api_key = key;
}
}
void set_credentials(const std::string &usr, const std::string &psk)
{
if (m_usr != usr || m_psk != psk) {
clear();
m_usr = usr;
m_psk = psk;
}
}
void clear() { m_api_key.clear(); m_usr.clear(); m_psk.clear(); m_api_key_sent = false; }
void sys_color_changed() override;
private:

View File

@ -0,0 +1,9 @@
#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);
void remove_webview_credentials(wxWebView* web_view);
}

View File

@ -0,0 +1,56 @@
#include "WebViewPlatformUtils.hpp"
#include <unordered_map>
#include <memory>
#include <webkit2/webkit2.h>
namespace Slic3r::GUI {
struct Credentials {
std::string username;
std::string password;
};
std::unordered_map<WebKitWebView*, gulong> g_webview_authorize_handlers;
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(), WEBKIT_CREDENTIAL_PERSISTENCE_PERMANENT));
return TRUE;
}
void free_credentials(gpointer user_data, GClosure* closure)
{
Credentials* creds = static_cast<Credentials*>(user_data);
delete creds;
}
void setup_webview_with_credentials(wxWebView* web_view, const std::string& username, const std::string& password)
{
WebKitWebView* native_backend = static_cast<WebKitWebView *>(web_view->GetNativeBackend());
Credentials* user_data = new Credentials{username, password};
remove_webview_credentials(web_view);
auto handler = g_signal_connect_data(
native_backend,
"authenticate",
G_CALLBACK(webkit_authorize_handler),
user_data,
&free_credentials,
static_cast<GConnectFlags>(0)
);
g_webview_authorize_handlers[native_backend] = handler;
}
void remove_webview_credentials(wxWebView* web_view)
{
WebKitWebView* native_backend = static_cast<WebKitWebView *>(web_view->GetNativeBackend());
if (auto it = g_webview_authorize_handlers.find(native_backend);
it != g_webview_authorize_handlers.end()) {
g_signal_handler_disconnect(native_backend, it->second);
g_webview_authorize_handlers.erase(it);
}
}
}

View File

@ -0,0 +1,141 @@
#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;
- (id<WKNavigationDelegate>) wrapped_delegate;
@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;
}
- (id<WKNavigationDelegate>) wrapped_delegate {
return _delegate;
}
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
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 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 {
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 {
//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 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);
}
}
@end
namespace Slic3r::GUI {
void setup_webview_with_credentials(wxWebView* web_view, const std::string& username, const std::string& password)
{
remove_webview_credentials(web_view);
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()];
}
}
void remove_webview_credentials(wxWebView* web_view)
{
WKWebView* backend = static_cast<WKWebView*>(web_view->GetNativeBackend());
if ([backend.navigationDelegate isKindOfClass:MyNavigationDelegate.class]) {
MyNavigationDelegate* my_delegate = backend.navigationDelegate;
backend.navigationDelegate = my_delegate.wrapped_delegate;
}
}
}

View File

@ -0,0 +1,83 @@
#include "WebViewPlatformUtils.hpp"
#ifdef __WIN32__
#include "WebView2.h"
#include <wrl.h>
#include <atlbase.h>
#include <boost/log/trivial.hpp>
#include <unordered_map>
#include "wx/msw/private/comptr.h"
#include "GUI_App.hpp"
#include "format.hpp"
#include "Mainframe.hpp"
namespace Slic3r::GUI {
std::unordered_map<ICoreWebView2*,EventRegistrationToken> g_basic_auth_handler_tokens;
void setup_webview_with_credentials(wxWebView* webview, const std::string& username, const std::string& password)
{
ICoreWebView2 *webView2 = static_cast<ICoreWebView2 *>(webview->GetNativeBackend());
wxCOMPtr<ICoreWebView2_10> wv2_10;
HRESULT hr = webView2->QueryInterface(IID_PPV_ARGS(&wv2_10));
if (FAILED(hr)) {
return;
}
remove_webview_credentials(webview);
// should it be stored?
EventRegistrationToken basicAuthenticationRequestedToken = {};
if (FAILED(wv2_10->add_BasicAuthenticationRequested(
Microsoft::WRL::Callback<ICoreWebView2BasicAuthenticationRequestedEventHandler>(
[username, password](ICoreWebView2 *sender, ICoreWebView2BasicAuthenticationRequestedEventArgs *args) {
wxCOMPtr<ICoreWebView2BasicAuthenticationResponse> basicAuthenticationResponse;
if (FAILED(args->get_Response(&basicAuthenticationResponse))) {
return -1;
}
if (FAILED(basicAuthenticationResponse->put_UserName(GUI::from_u8(username).c_str()))) {
return -1;
}
if (FAILED(basicAuthenticationResponse->put_Password(GUI::from_u8(password).c_str()))) {
return -1;
}
return 0;
}
).Get(),
&basicAuthenticationRequestedToken
))) {
BOOST_LOG_TRIVIAL(error) << "WebView: Cannot register authentication request handler";
} else {
g_basic_auth_handler_tokens[webView2] = basicAuthenticationRequestedToken;
}
}
void remove_webview_credentials(wxWebView* webview)
{
ICoreWebView2 *webView2 = static_cast<ICoreWebView2 *>(webview->GetNativeBackend());
wxCOMPtr<ICoreWebView2_10> wv2_10;
HRESULT hr = webView2->QueryInterface(IID_PPV_ARGS(&wv2_10));
if (FAILED(hr)) {
return;
}
if (auto it = g_basic_auth_handler_tokens.find(webView2);
it != g_basic_auth_handler_tokens.end()) {
if (FAILED(wv2_10->remove_BasicAuthenticationRequested(it->second))) {
BOOST_LOG_TRIVIAL(error) << "WebView: Unregistering authentication request handler failed";
} else {
g_basic_auth_handler_tokens.erase(it);
}
} else {
BOOST_LOG_TRIVIAL(error) << "WebView: Cannot unregister authentication request handler";
}
}
} // namespace Slic3r::GUI
#endif // WIN32