From e02dcbe83be0e6d84b49bd0c137058a5e2e210b0 Mon Sep 17 00:00:00 2001 From: Roberto Metere Date: Sat, 14 Oct 2023 00:05:50 +0100 Subject: [PATCH 1/8] Read credentials stored in a file --- gp_saml_gui.py | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/gp_saml_gui.py b/gp_saml_gui.py index f540a3b..70c4e92 100755 --- a/gp_saml_gui.py +++ b/gp_saml_gui.py @@ -22,6 +22,8 @@ import xml.etree.ElementTree as ET import ssl import tempfile +import configparser +import itertools from operator import setitem from os import path, dup2, execvp @@ -45,7 +47,7 @@ def handle_comment(self, data: str) -> None: class SAMLLoginView: - def __init__(self, uri, html=None, verbose=False, cookies=None, verify=True, user_agent=None): + def __init__(self, uri, html=None, verbose=False, cookies=None, verify=True, user_agent=None, credentials=None): Gtk.init(None) window = Gtk.Window() @@ -71,6 +73,19 @@ def __init__(self, uri, html=None, verbose=False, cookies=None, verify=True, use settings.set_user_agent(user_agent) self.wview.set_settings(settings) + if credentials: + self.credentials = {} + config = configparser.ConfigParser() + try: + with open(credentials) as fp: + config.read_file(itertools.chain(["[gp-saml-gui]\n"], fp), source=credentials) + except: + print("Error opening '%s'" % credentials) + config = None + if config: + for x in ['username', 'password']: + self.credentials[x] = config['gp-saml-gui'][x] + window.resize(500, 500) window.add(self.wview) window.show_all() @@ -83,7 +98,7 @@ def __init__(self, uri, html=None, verbose=False, cookies=None, verify=True, use self.wview.load_html(html, uri) else: self.wview.load_uri(uri) - + def close(self, window, event): self.closed = True Gtk.main_quit() @@ -116,6 +131,10 @@ def log_resource_text(self, resource, result, content_type, charset=None, show_h if charset or content_type.startswith('text/'): print(data.decode(charset or 'utf-8'), file=stderr) + def setvalue_DOM_element(self, selector, value): + if self.wview: + self.wview.evaluate_javascript("document.getElementById('" + selector + "').value='" + value + "';", -1, None, None, None, None, None) + def on_load_changed(self, webview, event): if event != WebKit2.LoadEvent.FINISHED: return @@ -129,6 +148,10 @@ def on_load_changed(self, webview, event): if self.verbose: print('[PAGE ] Finished loading page %s' % uri, file=stderr) + # Set credentials + for x in self.credentials: + self.setvalue_DOM_element(x, self.credentials[x]) + # convert to normal dict d = {} h.foreach(lambda k, v: setitem(d, k.lower(), v)) @@ -253,6 +276,8 @@ def parse_args(args = None): x.add_argument('-S','--sudo-openconnect', action='store_const', dest='exec', const='sudo', help='Use sudo to exec openconnect') g.add_argument('-u','--uri', action='store_true', help='Treat server as the complete URI of the SAML entry point, rather than GlobalProtect server') g.add_argument('--clientos', choices=set(pf2clientos.values()), default=default_clientos, help="clientos value to send (default is %(default)s)") + p.add_argument('-l','--login', default='~/.gp-saml-gui-credentials', + help='Read login credentials in this file (instead of default %(default)s)') p.add_argument('-f','--field', dest='extra', action='append', default=[], help='Extra form field(s) to pass to include in the login query string (e.g. "-f magic-cookie-value=deadbeef01234567")') p.add_argument('--allow-insecure-crypto', dest='insecure', action='store_true', @@ -354,7 +379,7 @@ def main(args = None): # spawn WebKit view to do SAML interactive login if args.verbose: print("Got SAML %s, opening browser..." % sam, file=stderr) - slv = SAMLLoginView(uri, html, verbose=args.verbose, cookies=args.cookies, verify=args.verify, user_agent=args.user_agent) + slv = SAMLLoginView(uri, html, verbose=args.verbose, cookies=args.cookies, verify=args.verify, user_agent=args.user_agent, credentials=args.login) Gtk.main() if slv.closed: print("Login window closed by user.", file=stderr) From f2dd67ddb77a94b6a235ec60e4809c281801e028 Mon Sep 17 00:00:00 2001 From: Roberto Metere Date: Tue, 7 Jan 2025 09:55:04 +0000 Subject: [PATCH 2/8] rename to match argparse Namespace --- gp_saml_gui.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/gp_saml_gui.py b/gp_saml_gui.py index 4f21ce0..c461b27 100755 --- a/gp_saml_gui.py +++ b/gp_saml_gui.py @@ -84,18 +84,18 @@ def __init__(self, uri, html, args): settings.set_user_agent(args.user_agent) self.wview.set_settings(settings) - if args.credentials: - self.credentials = {} + if args.login: + self.login = {} config = configparser.ConfigParser() try: - with open(args.credentials) as fp: - config.read_file(itertools.chain(["[gp-saml-gui]\n"], fp), source=args.credentials) + with open(args.login) as fp: + config.read_file(itertools.chain(["[gp-saml-gui]\n"], fp), source=args.login) except: - print("Error opening '%s'" % args.credentials) + print("Error opening or reading '%s'" % args.login) config = None if config: for x in ['username', 'password']: - self.credentials[x] = config['gp-saml-gui'][x] + self.login[x] = config['gp-saml-gui'][x] window.resize(500, 500) window.add(self.wview) @@ -167,9 +167,9 @@ def on_load_changed(self, webview, event): if not rs or not h: return - # Set credentials - for x in self.credentials: - self.setvalue_DOM_element(x, self.credentials[x]) + # Set login credentials + for x in self.login: + self.setvalue_DOM_element(x, self.login[x]) # convert to normal dict d = {} @@ -313,7 +313,7 @@ def parse_args(args = None): g.add_argument('-u','--uri', action='store_true', help='Treat server as the complete URI of the SAML entry point, rather than GlobalProtect server') g.add_argument('--clientos', choices=set(pf2clientos.values()), default=default_clientos, help="clientos value to send (default is %(default)s)") p.add_argument('-l','--login', default='~/.gp-saml-gui-credentials', - help='Read login credentials in this file (instead of default %(default)s)') + help='Read login credentials from the file specified (instead of default %(default)s)') p.add_argument('-f','--field', dest='extra', action='append', default=[], help='Extra form field(s) to pass to include in the login query string (e.g. "-f magic-cookie-value=deadbeef01234567")') p.add_argument('--allow-insecure-crypto', dest='insecure', action='store_true', From 1444b86500463116c91548e5d2757cfef136984d Mon Sep 17 00:00:00 2001 From: Roberto Metere Date: Tue, 7 Jan 2025 09:56:16 +0000 Subject: [PATCH 3/8] expand ~ --- gp_saml_gui.py | 1 + 1 file changed, 1 insertion(+) diff --git a/gp_saml_gui.py b/gp_saml_gui.py index c461b27..af2f480 100755 --- a/gp_saml_gui.py +++ b/gp_saml_gui.py @@ -85,6 +85,7 @@ def __init__(self, uri, html, args): self.wview.set_settings(settings) if args.login: + args.login = path.expanduser(args.login) self.login = {} config = configparser.ConfigParser() try: From 2d25af4899827578078e3b923794571d48cfd5e8 Mon Sep 17 00:00:00 2001 From: Roberto Metere Date: Tue, 7 Jan 2025 10:02:09 +0000 Subject: [PATCH 4/8] read the ini file content --- gp_saml_gui.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/gp_saml_gui.py b/gp_saml_gui.py index af2f480..a8512c1 100755 --- a/gp_saml_gui.py +++ b/gp_saml_gui.py @@ -89,8 +89,7 @@ def __init__(self, uri, html, args): self.login = {} config = configparser.ConfigParser() try: - with open(args.login) as fp: - config.read_file(itertools.chain(["[gp-saml-gui]\n"], fp), source=args.login) + config.read(args.login) except: print("Error opening or reading '%s'" % args.login) config = None From 56d2d1b33a50d0c60705a6318475283eb79f4c0e Mon Sep 17 00:00:00 2001 From: Roberto Metere Date: Tue, 7 Jan 2025 10:03:12 +0000 Subject: [PATCH 5/8] use domain as section names --- gp_saml_gui.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gp_saml_gui.py b/gp_saml_gui.py index a8512c1..9e09d35 100755 --- a/gp_saml_gui.py +++ b/gp_saml_gui.py @@ -93,9 +93,9 @@ def __init__(self, uri, html, args): except: print("Error opening or reading '%s'" % args.login) config = None - if config: + if config and config.has_section(args.server): for x in ['username', 'password']: - self.login[x] = config['gp-saml-gui'][x] + self.login[x] = config[args.server][x] window.resize(500, 500) window.add(self.wview) From dabe8459f0c51839fcf5f881da717df4e093f470 Mon Sep 17 00:00:00 2001 From: Roberto Metere Date: Tue, 7 Jan 2025 10:04:12 +0000 Subject: [PATCH 6/8] fill custom fields --- gp_saml_gui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gp_saml_gui.py b/gp_saml_gui.py index 9e09d35..13dbe72 100755 --- a/gp_saml_gui.py +++ b/gp_saml_gui.py @@ -94,7 +94,7 @@ def __init__(self, uri, html, args): print("Error opening or reading '%s'" % args.login) config = None if config and config.has_section(args.server): - for x in ['username', 'password']: + for x in config[args.server]: self.login[x] = config[args.server][x] window.resize(500, 500) From dcb30631b143d1cca74c4c02938e22d4624b8032 Mon Sep 17 00:00:00 2001 From: Roberto Metere Date: Tue, 7 Jan 2025 10:08:47 +0000 Subject: [PATCH 7/8] fix issue dlenski on May 2, 2024: These need quoting. Using JSON should be sufficient, e.g. "document.getElementById(%s).value = %s;" % (json.dumps(selector), json.dumps(value)); --- gp_saml_gui.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/gp_saml_gui.py b/gp_saml_gui.py index 13dbe72..fa81319 100755 --- a/gp_saml_gui.py +++ b/gp_saml_gui.py @@ -31,6 +31,7 @@ import tempfile import configparser import itertools +import json from operator import setitem from os import path, dup2, execvp, environ @@ -145,7 +146,8 @@ def log_resource_text(self, resource, result, content_type, charset=None, show_h def setvalue_DOM_element(self, selector, value): if self.wview: - self.wview.evaluate_javascript("document.getElementById('" + selector + "').value='" + value + "';", -1, None, None, None, None, None) + # + self.wview.evaluate_javascript("document.getElementById(%s).value = %s;" % (json.dumps(selector), json.dumps(value)), -1, None, None, None, None, None) def on_load_changed(self, webview, event): if event != WebKit2.LoadEvent.FINISHED: From 8fcdec613bfdb8e10ca8091c13a0f0fbc8bb20e3 Mon Sep 17 00:00:00 2001 From: Roberto Metere Date: Tue, 7 Jan 2025 10:21:04 +0000 Subject: [PATCH 8/8] add "name" attribute completion --- gp_saml_gui.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/gp_saml_gui.py b/gp_saml_gui.py index fa81319..35aa729 100755 --- a/gp_saml_gui.py +++ b/gp_saml_gui.py @@ -146,7 +146,10 @@ def log_resource_text(self, resource, result, content_type, charset=None, show_h def setvalue_DOM_element(self, selector, value): if self.wview: - # + # name attibute + self.wview.evaluate_javascript("Array.from(document.getElementsByName(%s)).forEach(el => el.value = %s);" % (json.dumps(selector), json.dumps(value)), -1, None, None, None, None, None) + + # id attribute self.wview.evaluate_javascript("document.getElementById(%s).value = %s;" % (json.dumps(selector), json.dumps(value)), -1, None, None, None, None, None) def on_load_changed(self, webview, event):