Trusted Domains: Allow trusting a domain regardless of HTTP basic auth credentials (#437)

* Trusted Domains: don't consider HTTP basic auth credentials part of the domain name

* Trusted Domains: hide "add as trusted domain" menu item if entry does not contain domain

* Trusted Domains: strip HTTP basic auth credentials also when adding as trusted domain via context menu
This commit is contained in:
Tremolo4 2021-10-17 14:44:07 +02:00 committed by GitHub
parent 1bbbab245f
commit 2bf3931f59
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 50 additions and 25 deletions

View File

@ -9,9 +9,9 @@ import re
import sys import sys
import threading import threading
import time import time
from fnmatch import fnmatch
from copy import deepcopy from copy import deepcopy
from functools import wraps from functools import wraps
from urllib.parse import urlparse
from twisted.application.internet import ClientService from twisted.application.internet import ClientService
from twisted.internet.endpoints import HostnameEndpoint from twisted.internet.endpoints import HostnameEndpoint
@ -546,27 +546,47 @@ class SyncplayClient(object):
if oldRoomList != newRoomList: if oldRoomList != newRoomList:
self._config['roomList'] = newRoomList self._config['roomList'] = newRoomList
def _isURITrustableAndTrusted(self, URIToTest):
"""Returns a tuple of booleans: (trustable, trusted).
A given URI is "trustable" if it uses HTTP or HTTPS (constants.TRUSTABLE_WEB_PROTOCOLS).
A given URI is "trusted" if it matches an entry in the trustedDomains config.
Such an entry is considered matching if the domain is the same and the path
is a prefix of the given URI's path.
A "trustable" URI is always "trusted" if the config onlySwitchToTrustedDomains is false.
"""
o = urlparse(URIToTest)
trustable = o.scheme in constants.TRUSTABLE_WEB_PROTOCOLS
if not trustable:
# untrustable URIs are never trusted, return early
return False, False
if not self._config['onlySwitchToTrustedDomains']:
# trust all trustable URIs in this case
return trustable, True
# check for matching trusted domains
if self._config['trustedDomains']:
for entry in self._config['trustedDomains']:
trustedDomain, _, path = entry.partition('/')
if o.hostname not in (trustedDomain, "www." + trustedDomain):
# domain does not match
continue
if path and not o.path.startswith('/' + path):
# trusted domain has a path component and it does not match
continue
# match found, trust this domain
return trustable, True
# no matches found, do not trust this domain
return trustable, False
def isUntrustedTrustableURI(self, URIToTest): def isUntrustedTrustableURI(self, URIToTest):
if utils.isURL(URIToTest): if utils.isURL(URIToTest):
for trustedProtocol in constants.TRUSTABLE_WEB_PROTOCOLS: trustable, trusted = self._isURITrustableAndTrusted(URIToTest)
if URIToTest.startswith(trustedProtocol) and not self.isURITrusted(URIToTest): return trustable and not trusted
return True
return False return False
def isURITrusted(self, URIToTest): def isURITrusted(self, URIToTest):
URIToTest = URIToTest+"/" trustable, trusted = self._isURITrustableAndTrusted(URIToTest)
for trustedProtocol in constants.TRUSTABLE_WEB_PROTOCOLS: return trustable and trusted
if URIToTest.startswith(trustedProtocol):
if self._config['onlySwitchToTrustedDomains']:
if self._config['trustedDomains']:
for trustedDomain in self._config['trustedDomains']:
trustableURI = ''.join([trustedProtocol, trustedDomain, "/*"])
if fnmatch(URIToTest, trustableURI):
return True
return False
else:
return True
return False
def openFile(self, filePath, resetPosition=False, fromUser=False): def openFile(self, filePath, resetPosition=False, fromUser=False):
if fromUser and filePath.endswith(".txt") or filePath.endswith(".m3u") or filePath.endswith(".m3u8"): if fromUser and filePath.endswith(".txt") or filePath.endswith(".m3u") or filePath.endswith(".m3u8"):

View File

@ -345,6 +345,6 @@ SYNCPLAY_DOWNLOAD_URL = "https://syncplay.pl/download/"
SYNCPLAY_PUBLIC_SERVER_LIST_URL = "https://syncplay.pl/listpublicservers?{}" # Params SYNCPLAY_PUBLIC_SERVER_LIST_URL = "https://syncplay.pl/listpublicservers?{}" # Params
DEFAULT_TRUSTED_DOMAINS = ["youtube.com", "youtu.be"] DEFAULT_TRUSTED_DOMAINS = ["youtube.com", "youtu.be"]
TRUSTABLE_WEB_PROTOCOLS = ["http://www.", "https://www.", "http://", "https://"] TRUSTABLE_WEB_PROTOCOLS = ["http", "https"]
PRIVATE_FILE_FIELDS = ["path"] PRIVATE_FILE_FIELDS = ["path"]

View File

@ -731,7 +731,8 @@ class MainWindow(QtWidgets.QMainWindow):
lambda: utils.open_system_file_browser(pathFound)) lambda: utils.open_system_file_browser(pathFound))
if self._syncplayClient.isUntrustedTrustableURI(firstFile): if self._syncplayClient.isUntrustedTrustableURI(firstFile):
domain = utils.getDomainFromURL(firstFile) domain = utils.getDomainFromURL(firstFile)
menu.addAction(QtGui.QPixmap(resourcespath + "shield_add.png"), getMessage("addtrusteddomain-menu-label").format(domain), lambda: self.addTrustedDomain(domain)) if domain:
menu.addAction(QtGui.QPixmap(resourcespath + "shield_add.png"), getMessage("addtrusteddomain-menu-label").format(domain), lambda: self.addTrustedDomain(domain))
menu.addAction(QtGui.QPixmap(resourcespath + "delete.png"), getMessage("removefromplaylist-menu-label"), lambda: self.deleteSelectedPlaylistItems()) menu.addAction(QtGui.QPixmap(resourcespath + "delete.png"), getMessage("removefromplaylist-menu-label"), lambda: self.deleteSelectedPlaylistItems())
menu.addSeparator() menu.addSeparator()
menu.addAction(QtGui.QPixmap(resourcespath + "arrow_switch.png"), getMessage("shuffleremainingplaylist-menu-label"), lambda: self.shuffleRemainingPlaylist()) menu.addAction(QtGui.QPixmap(resourcespath + "arrow_switch.png"), getMessage("shuffleremainingplaylist-menu-label"), lambda: self.shuffleRemainingPlaylist())
@ -794,7 +795,8 @@ class MainWindow(QtWidgets.QMainWindow):
menu.addAction(QtGui.QPixmap(resourcespath + "film_go.png"), getMessage("openusersfile-menu-label").format(shortUsername), lambda: self.openFile(pathFound, resetPosition=False, fromUser=True)) menu.addAction(QtGui.QPixmap(resourcespath + "film_go.png"), getMessage("openusersfile-menu-label").format(shortUsername), lambda: self.openFile(pathFound, resetPosition=False, fromUser=True))
if self._syncplayClient.isUntrustedTrustableURI(filename): if self._syncplayClient.isUntrustedTrustableURI(filename):
domain = utils.getDomainFromURL(filename) domain = utils.getDomainFromURL(filename)
menu.addAction(QtGui.QPixmap(resourcespath + "shield_add.png"), getMessage("addtrusteddomain-menu-label").format(domain), lambda: self.addTrustedDomain(domain)) if domain:
menu.addAction(QtGui.QPixmap(resourcespath + "shield_add.png"), getMessage("addtrusteddomain-menu-label").format(domain), lambda: self.addTrustedDomain(domain))
if not isURL(filename) and filename != getMessage("nofile-note"): if not isURL(filename) and filename != getMessage("nofile-note"):
path = self._syncplayClient.fileSwitch.findFilepath(filename) path = self._syncplayClient.fileSwitch.findFilepath(filename)

View File

@ -395,12 +395,15 @@ def playlistIsValid(files):
def getDomainFromURL(URL): def getDomainFromURL(URL):
try: try:
URL = URL.split("//")[-1].split("/")[0] o = urllib.parse.urlparse(URL)
if URL.startswith("www."): except ValueError:
URL = URL[4:] # not a URL
return URL
except:
return None return None
if o.hostname is not None and o.hostname.startswith("www."):
return o.hostname[4:]
else:
# may return None if URL does not have domain (invalid url)
return o.hostname
def open_system_file_browser(path): def open_system_file_browser(path):