From b941c9c0e4bee2d352144e7773ddb158ff774e01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mark=20H=C3=B6llmann?= Date: Sat, 27 Mar 2021 14:01:12 +0100 Subject: [PATCH] add authentication via url --- inkycal/modules/ical_parser.py | 83 +++++++++++++++++++++-------- inkycal/modules/inkycal_agenda.py | 2 +- inkycal/modules/inkycal_calendar.py | 2 +- 3 files changed, 62 insertions(+), 25 deletions(-) diff --git a/inkycal/modules/ical_parser.py b/inkycal/modules/ical_parser.py index 69bf6356..fef66074 100644 --- a/inkycal/modules/ical_parser.py +++ b/inkycal/modules/ical_parser.py @@ -14,7 +14,7 @@ """ import arrow -from urllib.request import urlopen +import urllib import logging import time import os @@ -48,37 +48,74 @@ def load_url(self, url, username=None, password=None): example: 'URL1' (single url) OR ['URL1', 'URL2'] (multiple URLs) add username and password to access protected files """ + ical = [] + #logger.debug(f"all urls: '{url}'") #may contain sensitive information + #make list if string + if type(url) == str: + url = [url] if type(url) == list: - if (username == None) and (password == None): - ical = [Calendar.from_ical(str(urlopen(_).read().decode())) - for _ in url] - else: - ical = [auth_ical(each_url, username, password) for each_url in url] - elif type(url) == str: - if (username == None) and (password == None): - ical = [Calendar.from_ical(str(urlopen(url).read().decode()))] - else: - ical = [auth_ical(url, username, password)] + #logger.debug(f"all urls (again): '{url}'") #may contain sensitive information + for u in url: + #remove leading whitespaces + u = u.lstrip() + #logger.debug(f"current url: '{u}'") #may contain sensitive information + + #parse the url + parsed_url = urllib.parse.urlparse(u) + + #override global username with calendar specific username + if parsed_url.username is not None: + #extract username + username_act = parsed_url.username + logger.debug("extracted the username from url") + else: + #use global username + username_act = username + logger.debug("using the global username") + #logger.debug(f"username: '{username_act}'") #may contain sensitive information + + #override global password with calendar specific password + if parsed_url.password is not None: + #extract password + password_act = parsed_url.password + logger.debug("extracted the password from url") + else: + #use global password + password_act = password + logger.debug("using the global password") + #logger.debug(f"password: '{password_act}'") #may contain sensitive information + + #get parsed result without username/password (netloc just hostname) + parsed_url_without= parsed_url._replace(netloc=parsed_url.hostname) + #unparse url to get the original url without user/password + u = urllib.parse.urlunparse(parsed_url_without) + logger.debug(f"parsed url: '{u}'") + + if (username_act == None) and (password_act == None): + #load unprotected calendar + ical.append(Calendar.from_ical(str(urllib.request.urlopen(u).read().decode()))) + else: + #load password protected calendar + ical.append(self.auth_ical(u, username_act, password_act)) else: raise Exception (f"Input: '{url}' is not a string or list!") - - def auth_ical(url, uname, passwd): - """Authorisation helper for protected ical files""" - - # Credit to Joshka - password_mgr = urllib.request.HTTPPasswordMgrWithDefaultRealm() - password_mgr.add_password(None, url, username, password) - handler = urllib.request.HTTPBasicAuthHandler(password_mgr) - opener = urllib.request.build_opener(handler) - ical = Calendar.from_ical(str(opener.open(url).read().decode())) - return ical - # Add the parsed icalendar/s to the self.icalendars list if ical: self.icalendars += ical logger.info('loaded iCalendars from URLs') + def auth_ical(self, url, uname, passwd): + """Authorisation helper for protected ical files""" + + # Credit to Joshka + password_mgr = urllib.request.HTTPPasswordMgrWithDefaultRealm() + password_mgr.add_password(None, url, uname, passwd) + handler = urllib.request.HTTPBasicAuthHandler(password_mgr) + opener = urllib.request.build_opener(handler) + ical = Calendar.from_ical(str(opener.open(url).read().decode())) + return ical + def load_from_file(self, filepath): """Input a string or list of strings containing valid iCalendar filepaths example: 'path1' (single file) OR ['path1', 'path2'] (multiple files) diff --git a/inkycal/modules/inkycal_agenda.py b/inkycal/modules/inkycal_agenda.py index 87fe6b8d..fde7bbe6 100644 --- a/inkycal/modules/inkycal_agenda.py +++ b/inkycal/modules/inkycal_agenda.py @@ -24,7 +24,7 @@ class Agenda(inkycal_module): requires = { "ical_urls" : { - "label":"iCalendar URL/s, separate multiple ones with a comma", + "label":"iCalendar URL/s, separate multiple ones with a comma, e.g. http://example.com/calendar or with authentication: http://username:password@example.com/calendar. Please do not enter username/password in the WebUI. Add them afterwards in the json file manually.", }, } diff --git a/inkycal/modules/inkycal_calendar.py b/inkycal/modules/inkycal_calendar.py index cbd7187e..838ac219 100644 --- a/inkycal/modules/inkycal_calendar.py +++ b/inkycal/modules/inkycal_calendar.py @@ -36,7 +36,7 @@ class Calendar(inkycal_module): }, "ical_urls" : { - "label":"iCalendar URL/s, separate multiple ones with a comma", + "label":"iCalendar URL/s, separate multiple ones with a comma, e.g. http://example.com/calendar or with authentication: http://username:password@example.com/calendar. Please do not enter username/password in the WebUI. Add them afterwards in the json file manually.", }, "ical_files" : {