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:
parent
1bbbab245f
commit
2bf3931f59
@ -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"):
|
||||||
|
|||||||
@ -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"]
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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):
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user