Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/mar10/wsgidav
Browse files Browse the repository at this point in the history
  • Loading branch information
mar10 committed Jan 17, 2022
2 parents 5d9027c + 45d6b49 commit d22ada5
Show file tree
Hide file tree
Showing 17 changed files with 68 additions and 41 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
## 4.0.2 / Unreleased

## 4.0.1 / 2022-01-11

- #241: Add SSL libs to MSI installer

## 4.0.0 / 2022-01-09

**Breaking Changes**
Expand Down
13 changes: 6 additions & 7 deletions sample_wsgidav.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,12 @@ server: cheroot

#: Server specific arguments, passed to the server. For example cheroot:
#: https://cheroot.cherrypy.dev/en/latest/pkg/cheroot.wsgi.html#cheroot.wsgi.Server
server_args:
# max: -1
# numthreads: 10
# request_queue_size: 5
# shutdown_timeout: 5
# timeout: 10
verbose: 0
# server_args:
# max: -1
# numthreads: 10
# request_queue_size: 5
# shutdown_timeout: 5
# timeout: 10

# Server hostname (default: localhost, use --host on command line)
host: 0.0.0.0
Expand Down
10 changes: 0 additions & 10 deletions setup_bdist_msi.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,22 +65,12 @@

# These dependencies are for plain WsgiDAV:
install_requires = [
# "defusedxml",
# "jinja2", # NOTE: we must use lower-case name, otherwise import will fail
# "json5",
# "yaml", # NOTE: must import 'yaml' (but dependency is named 'PyYAML')
# Used by wsgidav.dc.nt_dc:
# "win32net",
# "win32netcon",
# "win32security",
]
# ... The Windows MSI Setup should include lxml and CherryPy
install_requires.extend(
[
"cheroot",
# "cheroot.ssl.builtin",
"lxml",
# "wsgidav.dc.nt_dc",
]
)
setup_requires = install_requires
Expand Down
8 changes: 7 additions & 1 deletion tests/test_lock_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@

from wsgidav.dav_error import DAVError
from wsgidav.lock_man import lock_manager, lock_storage
from wsgidav.lock_man.lock_storage_redis import LockStorageRedis

try:
from wsgidav.lock_man.lock_storage_redis import LockStorageRedis
except ImportError:
LockStorageRedis = None

# ========================================================================
# BasicTest
Expand Down Expand Up @@ -397,6 +401,8 @@ class RedisTest(BasicTest):
_redis_connect_failed = None

def setUp(self):
if LockStorageRedis is None:
raise unittest.SkipTest("Test redis installed")
if RedisTest._redis_connect_failed:
raise unittest.SkipTest("Test requires a running redis instance (again)")

Expand Down
11 changes: 10 additions & 1 deletion tests/test_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,9 @@ def testBasics(self):

self.assertRaises(ValueError, fix_path, "a/b", "/root/x")
if sys.platform == "win32":
assert fix_path("a/b", "/root/x", must_exist=False) == r"C:\root\x\a\b"
assert (
fix_path("a/b", "/root/x", must_exist=False).lower() == r"c:\root\x\a\b"
)
else:
assert fix_path("a/b", "/root/x", must_exist=False) == "/root/x/a/b"
assert fix_path("/a/b", "/root/x", must_exist=False) == "/a/b"
Expand Down Expand Up @@ -173,6 +175,13 @@ def testBasics(self):
self.assertRaises(IndexError, get_dict_value, d, "d.t.[2]")
self.assertRaises(KeyError, get_dict_value, d, "d.q")

d = {"a": None, "b": {}, "c": False}
assert get_dict_value(d, "a", as_dict=True) == {}
assert get_dict_value(d, "b", as_dict=True) == {}
assert get_dict_value(d, "c", as_dict=True) is False
assert get_dict_value(d, "x", as_dict=True) == {}
self.assertRaises(KeyError, get_dict_value, d, "x", as_dict=False)


class LoggerTest(unittest.TestCase):
"""Test configurable logging."""
Expand Down
5 changes: 3 additions & 2 deletions wsgidav/dav_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -771,7 +771,7 @@ def set_property_value(self, name, value, *, dry_run=False):

# Live property
config = self.environ["wsgidav.config"]
# hotfixes = config.get("hotfixes", {})
# hotfixes = util.get_dict_value(config, "hotfixes", as_dict=True)
mutableLiveProps = config.get("mutable_live_props", [])
# Accept custom live property updates on resources if configured.
if (
Expand Down Expand Up @@ -802,7 +802,8 @@ def set_property_value(self, name, value, *, dry_run=False):
# with this so we ignore older clients. Others pre-Win10 should be tested.
if name.startswith("{urn:schemas-microsoft-com:}"):
agent = self.environ.get("HTTP_USER_AGENT", "None")
win32_emu = config.get("hotfixes", {}).get("emulate_win32_lastmod", False)
hotfixes = util.get_dict_value(config, "hotfixes", as_dict=True)
win32_emu = hotfixes.get("emulate_win32_lastmod", False)
if win32_emu and "MiniRedir/6.1" not in agent:
if "Win32LastModifiedTime" in name:
return self.set_last_modified(self.path, value.text, dry_run)
Expand Down
4 changes: 2 additions & 2 deletions wsgidav/dc/nt_dc.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,8 @@
class NTDomainController(BaseDomainController):
def __init__(self, wsgidav_app, config):
super().__init__(wsgidav_app, config)
# auth_conf = config["http_authenticator"]
dc_conf = config.get("nt_dc", {})

dc_conf = util.get_dict_value(config, "nt_dc", as_dict=True)

self.preset_domain = dc_conf.get("preset_domain")
self.preset_server = dc_conf.get("preset_server")
Expand Down
3 changes: 1 addition & 2 deletions wsgidav/dc/pam_dc.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,7 @@ def __init__(self, wsgidav_app, config):

self.pam = pam.pam()

# auth_conf = config["http_authenticator"]
dc_conf = config.get("pam_dc", {})
dc_conf = util.get_dict_value(config, "pam_dc", as_dict=True)

self.pam_service = dc_conf.get("service", "login")
self.pam_encoding = dc_conf.get("encoding", "utf-8")
Expand Down
3 changes: 2 additions & 1 deletion wsgidav/dc/simple_dc.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@
class SimpleDomainController(BaseDomainController):
def __init__(self, wsgidav_app, config):
super().__init__(wsgidav_app, config)
dc_conf = config.get("simple_dc", {})

dc_conf = util.get_dict_value(config, "simple_dc", as_dict=True)

self.user_map = dc_conf.get("user_mapping")
if self.user_map is None:
Expand Down
3 changes: 2 additions & 1 deletion wsgidav/dir_browser/_dir_browser.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ class WsgiDavDirBrowser(BaseMiddleware):

def __init__(self, wsgidav_app, next_app, config):
super().__init__(wsgidav_app, next_app, config)
self.dir_config = config.get("dir_browser", {})

self.dir_config = util.get_dict_value(config, "dir_browser", as_dict=True)

htdocs_path = self.dir_config.get("htdocs_path")
if htdocs_path:
Expand Down
2 changes: 1 addition & 1 deletion wsgidav/error_printer.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
class ErrorPrinter(BaseMiddleware):
def __init__(self, wsgidav_app, next_app, config):
super().__init__(wsgidav_app, next_app, config)
self.err_config = config.get("error_printer", {})
self.err_config = util.get_dict_value(config, "error_printer", as_dict=True)

def is_disabled(self):
return self.err_config.get("enable") is False
Expand Down
7 changes: 4 additions & 3 deletions wsgidav/http_authenticator.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,8 @@


def make_domain_controller(wsgidav_app, config):
dc = config.get("http_authenticator", {}).get("domain_controller")
auth_conf = util.get_dict_value(config, "http_authenticator", as_dict=True)
dc = auth_conf.get("domain_controller")
org_dc = dc
if dc is True or not dc:
# True or null:
Expand Down Expand Up @@ -142,7 +143,7 @@ def __init__(self, wsgidav_app, next_app, config):
dc = make_domain_controller(wsgidav_app, config)
self.domain_controller = dc

hotfixes = config.get("hotfixes", {})
hotfixes = util.get_dict_value(config, "hotfixes", as_dict=True)
# HOT FIX for Windows XP (Microsoft-WebDAV-MiniRedir/5.1.2600):
# When accessing a share '/dav/', XP sometimes sends digests for '/'.
# With this fix turned on, we allow '/' digests, when a matching '/dav' account
Expand All @@ -158,7 +159,7 @@ def __init__(self, wsgidav_app, next_app, config):
"win_accept_anonymous_options", False
)

auth_conf = config.get("http_authenticator", {})
auth_conf = util.get_dict_value(config, "http_authenticator", as_dict=True)

self.accept_basic = auth_conf.get("accept_basic", True)
self.accept_digest = auth_conf.get("accept_digest", True)
Expand Down
2 changes: 1 addition & 1 deletion wsgidav/request_resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ def __call__(self, environ, start_response):

provider = environ["wsgidav.provider"]
config = environ["wsgidav.config"]
hotfixes = config.get("hotfixes", {})
hotfixes = util.get_dict_value(config, "hotfixes", as_dict=True)

is_asterisk_options = environ["REQUEST_METHOD"] == "OPTIONS" and path == "*"
if path == "/":
Expand Down
2 changes: 1 addition & 1 deletion wsgidav/request_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -1459,7 +1459,7 @@ def do_OPTIONS(self, environ, start_response):
path = environ["PATH_INFO"]
provider = self._davProvider
config = environ["wsgidav.config"]
hotfixes = config.get("hotfixes", {})
hotfixes = util.get_dict_value(config, "hotfixes", as_dict=True)

res = provider.get_resource_inst(path, environ)

Expand Down
14 changes: 9 additions & 5 deletions wsgidav/server/server_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -363,7 +363,7 @@ def _init_config():

# Quick-configuration of DomainController
auth = cli_opts.get("auth")
auth_conf = config.get("http_authenticator", {})
auth_conf = util.get_dict_value(config, "http_authenticator", as_dict=True)
if auth and auth_conf.get("domain_controller"):
parser.error(
"--auth option can only be used when no domain_controller is configured"
Expand Down Expand Up @@ -462,7 +462,8 @@ def _run_cheroot(app, config, _server):
"numthreads": 50, # TODO: still required?
}
# Override or add custom args
server_args.update(config.get("server_args", {}))
custom_args = util.get_dict_value(config, "server_args", as_dict=True)
server_args.update(custom_args)

class PatchedServer(wsgi.Server):
STARTUP_NOTIFICATION_DELAY = 0.5
Expand Down Expand Up @@ -566,7 +567,8 @@ def _run_gevent(app, config, server):
"wsgi_app": app,
"bind_addr": (config["host"], config["port"]),
}
server_args.update(config.get("server_args", {}))
custom_args = util.get_dict_value(config, "server_args", as_dict=True)
server_args.update(custom_args)

if info["use_ssl"]:
dav_server = WSGIServer(
Expand Down Expand Up @@ -649,7 +651,8 @@ def load(self):
}
)
# Override or add custom args
server_args.update(config.get("server_args", {}))
custom_args = util.get_dict_value(config, "server_args", as_dict=True)
server_args.update(custom_args)

version = f"gunicorn/{gunicorn.__version__}"
version = f"WsgiDAV/{__version__} {version} Python {util.PYTHON_VERSION}"
Expand Down Expand Up @@ -745,7 +748,8 @@ def _run_uvicorn(app, config, server):
}
)
# Override or add custom args
server_args.update(config.get("server_args", {}))
custom_args = util.get_dict_value(config, "server_args", as_dict=True)
server_args.update(custom_args)

version = f"uvicorn/{uvicorn.__version__}"
version = f"WsgiDAV/{__version__} {version} Python {util.PYTHON_VERSION}"
Expand Down
16 changes: 15 additions & 1 deletion wsgidav/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,12 +105,19 @@ def to_set(val, *, or_none=False, raise_error=False) -> set:
return res


def get_dict_value(d, key_path, default=NO_DEFAULT):
def get_dict_value(d, key_path, default=NO_DEFAULT, *, as_dict=False):
"""Return the value of a nested dict using dot-notation path.
Args:
d (dict):
key_path (str):
default (any):
as_dict (bool):
Assume default is `{}` and also return `{}` if the key exists with
a value of `None`. This covers the case where suboptions are
supposed to be dicts, but are defined in a YAML file as entry
without a value.
Raises:
KeyError:
ValueError:
Expand All @@ -123,6 +130,13 @@ def get_dict_value(d, key_path, default=NO_DEFAULT):
Todo:
* k[1] instead of k.[1]
"""
if as_dict:
try:
res = get_dict_value(d, key_path, default={})
return res if res is not None else {}
except (AttributeError, KeyError, ValueError, IndexError):
return {}

if default is not NO_DEFAULT:
try:
return get_dict_value(d, key_path)
Expand Down
4 changes: 2 additions & 2 deletions wsgidav/wsgidav_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ def __init__(self, config):

self.verbose = config.get("verbose", 3)

hotfixes = config.get("hotfixes", {})
hotfixes = util.get_dict_value(config, "hotfixes", as_dict=True)

self.re_encode_path_info = hotfixes.get("re_encode_path_info", True)
if type(self.re_encode_path_info) is not bool:
Expand Down Expand Up @@ -185,7 +185,7 @@ def __init__(self, config):
self.prop_manager = prop_manager

self.mount_path = config.get("mount_path")
auth_conf = config.get("http_authenticator", {})
auth_conf = util.get_dict_value(config, "http_authenticator", as_dict=True)

# Instantiate DAV resource provider objects for every share.
# provider_mapping may contain the args that are passed to a
Expand Down

0 comments on commit d22ada5

Please sign in to comment.