Configuration

This document describes the configuration options of a WsgiDAV server.

The WsgiDAVApp object is configured by passing a Python dict with distinct options, that define

  • Server options (hostname, port, SSL cert, …)
  • List of share-name / WebDAV provider mappings
  • Optional list of users for authentication
  • Optional custom DAV providers (i.e. other than FilesystemProvider)
  • Optional custom lock manager, property manager and domain controller
  • Advanced debugging options
  • (and more)

This section shows the available options and defaults:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# -*- coding: utf-8 -*-
# (c) 2009-2021 Martin Wendt and contributors; see WsgiDAV https://github.com/mar10/wsgidav
# Original PyFileServer (c) 2005 Ho Chun Wei.
# Licensed under the MIT license:
# http://www.opensource.org/licenses/mit-license.php
r"""
::

     _      __         _ ___  ___ _   __
    | | /| / /__ ___  (_) _ \/ _ | | / /
    | |/ |/ (_-</ _ `/ / // / __ | |/ /
    |__/|__/___/\_, /_/____/_/ |_|___/
               /___/

Default confguration.
"""
from wsgidav.debug_filter import WsgiDavDebugFilter
from wsgidav.dir_browser import WsgiDavDirBrowser
from wsgidav.error_printer import ErrorPrinter
from wsgidav.http_authenticator import HTTPAuthenticator
from wsgidav.request_resolver import RequestResolver

__docformat__ = "reStructuredText"

# Use these settings, if config file does not define them (or is totally missing)
DEFAULT_VERBOSE = 3

DEFAULT_CONFIG = {
    "server": "cheroot",
    "server_args": {},
    "host": "localhost",
    "port": 8080,
    "mount_path": None,  # Application root, e.g. <mount_path>/<share_name>/<res_path>
    "provider_mapping": {},
    "add_header_MS_Author_Via": True,
    "hotfixes": {
        "emulate_win32_lastmod": False,  # True: support Win32LastModifiedTime
        "re_encode_path_info": None,  # (See issue #73) None: activate on Python 3
        "unquote_path_info": False,  # See issue #8
        "win_accept_anonymous_options": False,
        "winxp_accept_root_share_login": False,  # Was True in v2.4
    },
    "property_manager": None,  # True: use property_manager.PropertyManager
    "mutable_live_props": [],
    "lock_manager": True,  # True: use lock_manager.LockManager
    "middleware_stack": [
        WsgiDavDebugFilter,
        ErrorPrinter,
        HTTPAuthenticator,
        WsgiDavDirBrowser,  # configured under dir_browser option (see below)
        RequestResolver,  # this must be the last middleware item
    ],
    # HTTP Authentication Options
    "http_authenticator": {
        # None: dc.simple_dc.SimpleDomainController(user_mapping)
        "domain_controller": None,
        "accept_basic": True,  # Allow basic authentication, True or False
        "accept_digest": True,  # Allow digest authentication, True or False
        "default_to_digest": True,  # True (default digest) or False (default basic)
        # Name of a header field that will be accepted as authorized user
        "trusted_auth_header": None,
    },
    #: Used by SimpleDomainController only
    "simple_dc": {"user_mapping": {}},  # NO anonymous access by default
    # Verbose Output
    # 0 - no output
    # 1 - no output (excepting application exceptions)
    # 2 - show warnings
    # 3 - show single line request summaries (for HTTP logging)
    # 4 - show additional events
    # 5 - show full request/response header info (HTTP Logging)
    #     request body and GET response bodies not shown
    "verbose": DEFAULT_VERBOSE,
    # Error printer options
    "error_printer": {"catch_all": True},  # False,
    "enable_loggers": [],
    "dir_browser": {
        "enable": True,  # Render HTML listing for GET requests on collections
        # List of fnmatch patterns:
        "ignore": [
            ".DS_Store",  # macOS folder meta data
            "._*",  # macOS hidden data files
            "Thumbs.db",  # Windows image previews
        ],
        "icon": True,
        "response_trailer": True,  # Raw HTML code, appended as footer (True: use a default)
        "show_user": True,  # Show authenticated user an realm
        # Send <dm:mount> response if request URL contains '?davmount'
        "davmount": False,
        # Add an 'open as webfolder' link (requires Windows clients):
        "ms_mount": False,
        "ms_sharepoint_support": True,  # Invoke MS Offce documents for editing using WebDAV
        # "ms_sharepoint_plugin": False,  # Invoke MS Offce documents for editing using WebDAV
        # "ms_sharepoint_urls": False,  # Prepend 'ms-word:ofe|u|' to URL for MS Offce documents
        # The path to the directory that contains template.html and associated assets.
        # The default is the htdocs directory within the dir_browser directory.
        "htdocs_path": None,
    },
}

When a Python dict is passed to the WsgiDAVApp constructor, its values will override the defaults from above:

root_path = gettempdir()
provider = FilesystemProvider(root_path)

config = {
    "host": "0.0.0.0",
    "port": 8080,
    "provider_mapping": {"/": provider},
    "verbose": 1,
    }
app = WsgiDAVApp(config)

Use a Configuration File

When running from the CLI (command line interface), some settings may be passed as arguments, e.g.:

$ wsgidav --host=0.0.0.0 --port=8080 --root=/tmp --auth=anonymous

Serving on http://0.0.0.0:8080 ...

Much more options are available when a configuration file is used. By default wsgidav.yaml, wsgidav.json, and wsgidav.conf are searched in the local directory. An alternative file name can be specified like so:

$ wsgidav --config=my_config.yaml

To prevent the use of of a local default configuration file, use this option:

$ wsgidav --no-config

The options described below can be defined for the CLI either

  • in YAML syntax inside a wsgidav.yaml file,
  • in JSON syntax inside a wsgidav.json file, or
  • in Python syntax inside a wsgidav.conf file.

Note

The three supported file formats are just different ways for the CLI to generate a Python dict that is then passed to the WsgiDAVApp constructor.

The YAML format is recommended.

For a start, copy YAML Sample Configuration and edit it to your needs. (Alternatively use JSON Sample Configuration or Python Sample Configuration.)

Verbosity Level

The verbosity level can have a value from 0 to 5 (default: 3):

Verbosity Option Log level Remarks
0 -qqq CRITICAL quiet
1 -qq ERROR no output (excepting application exceptions)
2 -q WARN warnings and errors only
3   INFO show single line request summaries (for HTTP logging)
4 -v DEBUG show additional events
5 -vv DEBUG show full request/response header info (HTTP Logging) request body and GET response bodies not shown

Middleware Stack

WsgiDAV is built as WSGI application (WsgiDAVApp) that is extended by a list of middleware components which implement additional functionality.

This stack is defined as a list of WSGI compliant application instances, e.g.:

from wsgidav.debug_filter import WsgiDavDebugFilter

debug_filter = WsgiDavDebugFilter(wsgidav_app, next_app, config)

conf = {
    ...
    "middleware_stack": [
        debug_filter,
        ...
        ],
    ...
    }

If the middleware class constructor has a common signature, it is sufficient to pass the class instead of the instantiated object. The built-in middleware derives from BaseMiddleware, so we can simplify as:

from wsgidav.dir_browser import WsgiDavDirBrowser
from wsgidav.debug_filter import WsgiDavDebugFilter
from wsgidav.error_printer import ErrorPrinter
from wsgidav.http_authenticator import HTTPAuthenticator
from wsgidav.request_resolver import RequestResolver

conf = {
    ...
    "middleware_stack": [
        WsgiDavDebugFilter,
        ErrorPrinter,
        HTTPAuthenticator,
        WsgiDavDirBrowser,
        RequestResolver,  # this must be the last middleware item
        ],
    ...
    }

The middleware stack can be configured and extended. The following example removes the directory browser, and adds a third-party debugging tool:

import dozer

# from wsgidav.dir_browser import WsgiDavDirBrowser
from wsgidav.debug_filter import WsgiDavDebugFilter
from wsgidav.error_printer import ErrorPrinter
from wsgidav.http_authenticator import HTTPAuthenticator
from wsgidav.request_resolver import RequestResolver

# Enable online profiling and GC inspection. See https://github.com/mgedmin/dozer
# (Requires `pip install Dozer`):
dozer_app = dozer.Dozer(wsgidav_app)
dozer_profiler = dozer.Profiler(dozer_app, None, "/tmp")

conf = {
    ...
    "middleware_stack": [
        dozer_app,
        dozer_profiler,
        WsgiDavDebugFilter,
        ErrorPrinter,
        HTTPAuthenticator,
        # WsgiDavDirBrowser,
        RequestResolver,  # this must be the last middleware item
        ],
    ...
    }

The stack can also be defined in text files, for example YAML. Again, we can pass an import path for a WSGI compliant class if the signature is known. For third-party middleware however, the constructor’s positional arguments should be explicitly listed:

...
middleware_stack:
    - dozer.Dozer:
        - "${application}"
    - dozer.Profiler:
        - "${application}"
        - null  # global_conf
        - /tmp  # profile_path
    - wsgidav.debug_filter.WsgiDavDebugFilter
    - wsgidav.error_printer.ErrorPrinter
    - wsgidav.http_authenticator.HTTPAuthenticator
    - wsgidav.dir_browser.WsgiDavDirBrowser
    - wsgidav.request_resolver.RequestResolver

Note that the external middleware must be available, for example by calling pip install Doze, so this will not be possible if WsgiDAV is running from the MSI installer.

DAVProvider

A DAVProvider handles read and write requests for all URLs that start with a given share path.

WsgiDAV comes bundled with FilesystemProvider, a DAVProvider that serves DAV requests by reading and writing to the server’s file system.
However, custom DAVProviders may be implemented and used, that publish a database backend, cloud drive, or any virtual data structure.

The provider_mapping configuration routes share paths to specific DAVProvider instances.

By default a writable FilesystemProvider is assumed, but can be forced to read-only. Note that a DomainController may still restrict access completely or prevent editing depending on authentication.

Three syntax variants are supported:

  1. <mount_path>: <folder_path>
  2. <mount_path>: { "root": <folder_path>, "readonly": <bool> }
  3. <mount_path>: { "provider": <class_path>, "args:" ..., "kwargs": ... }

For example:

provider_mapping:
    "/": "/path/to/share1"
    "/home": "~"
    "/pub":
        root: "/path/to/share2"
        readonly: true
    "/share3":
        provider: path.to.CustomDAVProviderClass
        args: ["/path/to/share3", "second_arg"]
        kwargs: {"another_arg": 42}

Property Manager

Todo

TODO

Lock Manager

Todo

TODO

Domain Controller

The HTTP authentication middleware relies on a domain controller. Currently three variants are supported.

SimpleDomainController

Allows to authenticate against a plain mapping of shares and user names.

The pseudo-share "*" maps all URLs that are not explicitly listed.

A value of true can be used to enable anonymous access.

Example YAML configuration:

http_authenticator:
    domain_controller: null  # Same as wsgidav.dc.simple_dc.SimpleDomainController
    accept_basic: true  # Pass false to prevent sending clear text passwords
    accept_digest: true
    default_to_digest: true

simple_dc:
    user_mapping:
        "*":
            "user1":
                password: "abc123"
            "user2":
                password: "qwerty"
        "/pub": true

An optional roles list will be passed in environ[“wsgidav.auth.roles”] to downstream middleware. This is currently not used by the provided middleware, but may be handy for custom handlers:

simple_dc:
    user_mapping:
        "*":
            "user1":
                password: "abc123"
                roles: ["editor", "admin"]
            "user2":
                password: "abc123"
                roles: []

If no config file is used, anonymous authentication can be enabled on the command line like:

$ wsgidav ... --auth=anonymous

which simply defines this setting:

simple_dc:
    user_mapping:
        "*": true

NTDomainController

Allows users to authenticate against a Windows NT domain or a local computer.

The NTDomainController requires basic authentication and therefore should use SSL.

Example YAML configuration:

ssl_certificate: wsgidav/server/sample_bogo_server.crt
ssl_private_key: wsgidav/server/sample_bogo_server.key
ssl_certificate_chain: None

http_authenticator:
    domain_controller: wsgidav.dc.nt_dc.NTDomainController
    accept_basic: true
    accept_digest: false
    default_to_digest: false

nt_dc:
    preset_domain: null
    preset_server: null

If no config file is used, NT authentication can be enabled on the command line like:

$ wsgidav ... --auth=nt

PAMDomainController

Allows users to authenticate against a PAM (Pluggable Authentication Modules), that are at the core of user authentication in any modern linux distribution and macOS.

The PAMDomainController requires basic authentication and therefore should use SSL.

Example YAML configuration that authenticates users against the server’s known user accounts:

ssl_certificate: wsgidav/server/sample_bogo_server.crt
ssl_private_key: wsgidav/server/sample_bogo_server.key
ssl_certificate_chain: None

http_authenticator:
    domain_controller: wsgidav.dc.pam_dc.PAMDomainController
    accept_basic: true
    accept_digest: false
    default_to_digest: false

pam_dc:
    service: "login"

If no config file is used, PAM authentication can be enabled on the command line like:

$ wsgidav ... --auth=pam-login

Sample wsgidav.yaml

The YAML syntax is probably the most concise format to define configuration:

Download Sample Configuration.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
# WsgiDAV configuration file
#
# 1. Rename this file to `wsgidav.yaml`
# 2. Adjust settings as appropriate
# 3. Run `wsgidav` from the same directory or pass file path with `--config` option.
#
# See http://wsgidav.readthedocs.io/en/latest/user_guide_configure.html
#
# ============================================================================
# SERVER OPTIONS

server: "cheroot"

# Server specific arguments, for example
# cheroot: https://cheroot.cherrypy.org/en/latest/pkg/cheroot.wsgi.html#cheroot.wsgi.Server
# server_args:
#     numthreads: 50

host: 0.0.0.0
port: 8080

# Add custom response headers (list of header-name / header-value tuples):
#response_headers:
#    - ["Access-Control-Allow-Origin", "http://example.org"]

# Transfer block size in bytes
block_size: 8192

#: Add the MS-Author-Via Response Header to OPTIONS command to allow editing
#: with Microsoft Office (default: true)
add_header_MS_Author_Via: true

hotfixes:
    winxp_accept_root_share_login: false
    win_accept_anonymous_options: false
    #: See issue #8
    unquote_path_info: false
    #: (See issue #73) Set null to activate on Python 3 only
    re_encode_path_info: null
    # Handle Microsoft's Win32LastModifiedTime property.
    # This is useful only in the case when you copy files from a Windows
    # client into a WebDAV share. Windows sends the "last modified" time of
    # the file in a Microsoft extended property called "Win32LastModifiedTime"
    # instead of the standard WebDAV property "getlastmodified". So without
    # this config option set to "True", the "last modified" time of the copied
    # file will be "now" instead of its original value.
    # The proper solution for dealing with the Windows WebDAV client is to use
    # a persistent property manager. This setting is merely a work-around.
    # NOTE: Works with Win10, can't work with Win7. Other versions untested.
    emulate_win32_lastmod: true


# ----------------------------------------------------------------------------
# SSL Support

#: The certificate should match the servers hostname, so the bogus certs will
#: not work in all scenarios.
#: (Paths can be absolute or relative to this config file.)

# ssl_certificate: "wsgidav/server/sample_bogo_server.crt"
# ssl_private_key: "wsgidav/server/sample_bogo_server.key"
# ssl_certificate_chain: null

#: Cheroot server supports 'builtin' and 'pyopenssl' (default: 'builtin')
# ssl_adapter: "pyopenssl"

# ----------------------------------------------------------------------------

#: Modify to customize the WSGI application stack:
middleware_stack:
    - wsgidav.debug_filter.WsgiDavDebugFilter
    - wsgidav.error_printer.ErrorPrinter
    - wsgidav.http_authenticator.HTTPAuthenticator
    - wsgidav.dir_browser.WsgiDavDirBrowser
    - wsgidav.request_resolver.RequestResolver  # this must be the last middleware item

# ==============================================================================
# SHARES

#: Application root, applied before provider mapping shares,
#: e.g. <mount_path>/<share_name>/<res_path>
mount_path: null

#: Route share paths to DAVProvider instances
#: By default a writable `FilesystemProvider` is assumed, but can be forced
#: to read-only.
#: Note that a DomainController may still restrict access completely or prevent
#: editing depending on authentication.
#: The following syntax variants are supported:
#:     <mount_path>: <folder_path>
#: or
#:     <mount_path>: { "root": <folder_path>, "readonly": <bool> }
#: or
#:     <mount_path>: { "provider": <class_path>, "args": [<pos-args>, ...] }

provider_mapping:
    "/": "/path/to/share1"
    "/pub":
        root: "/path/to/share2"
        readonly: true
    "/share3":
        provider: path.to.CustomDAVProviderClass
        args: ["/path/to/share3", "second_arg"]
        kwargs: {"another_arg": 42}


# ==============================================================================
# AUTHENTICATION
http_authenticator:
    #: Allow basic authentication
    accept_basic: true
    #: Allow digest authentication
    accept_digest: true
    #: true (default digest) or false (default basic)
    default_to_digest: true
    #: Header field that will be accepted as authorized user.
    #: Including quotes, for example: trusted_auth_header = "REMOTE_USER"
    trusted_auth_header: null
    #: Domain controller that is used to resolve realms and authorization.
    #: Default null: which uses SimpleDomainController and the
    #: `simple_dc.user_mapping` option below.
    #: (See http://wsgidav.readthedocs.io/en/latest/user_guide_configure.html
    #: for details.)
    domain_controller: null
    # domain_controller: wsgidav.dc.simple_dc.SimpleDomainController
    # domain_controller: wsgidav.dc.pam_dc.PAMDomainController
    # domain_controller: wsgidav.dc.nt_dc.NTDomainController


# Additional options for SimpleDomainController only:
simple_dc:
    # Access control per share.
    # These routes must match the provider mapping.
    # NOTE: Provider routes without a matching entry here, are inaccessible.
    user_mapping:
        "*":  # default (used for all shares that are not explicitly listed)
            "user1":
                password: "abc123"
                # Optional: passed to downstream middleware as environ["wsgidav.auth.roles"]
                roles: ["editor"]
            "user2":
                password: "def456"
                password: "qwerty"
        "/pub": true  # Pass true to allow anonymous access

# Additional options for NTDomainController only:
nt_dc:
    preset_domain: null
    preset_server: null

# Additional options for PAMDomainController only:
pam_dc:
    service: "login"
    encoding: "utf-8"
    resetcreds: true


# ==============================================================================
# DEBUGGING

#: Set verbosity level (but will be overridden by -v or -q arguments)
verbose: 3

#: Set logging output format
#: (see https://docs.python.org/3/library/logging.html#logging.Formatter)
logger_format: "%(asctime)s.%(msecs)03d - <%(thread)05d> %(name)-27s %(levelname)-8s: %(message)s"
#logger_format: "%(asctime)s.%(msecs)03d - %(levelname)-8s: %(message)s"
logger_date_format: "%H:%M:%S"

#: Let ErrorPrinter middleware catch all exceptions to return as 500 Internal Error
error_printer:
    catch_all: true

# Enable specific module loggers
# E.g. ["lock_manager", "property_manager", "http_authenticator", ...]
# enable_loggers: ["http_authenticator", ]

# Enable max. logging for certain http methods
# E.g. ["COPY", "DELETE", "GET", "HEAD", "LOCK", "MOVE", "OPTIONS", "PROPFIND", "PROPPATCH", "PUT", "UNLOCK"]
debug_methods: []

# Enable max. logging during  litmus suite tests that contain certain strings
# E.g. ["lock_excl", "notowner_modify", "fail_cond_put_unlocked", ...]
debug_litmus: []


# ----------------------------------------------------------------------------
# WsgiDavDirBrowser

dir_browser:
    enable: true
    #: List of fnmatch patterns that will be hidden in the directory listing
    ignore:
        - ".DS_Store"  # macOS folder meta data
        - "Thumbs.db"  # Windows image previews
        - "._*"  # macOS hidden data files
    #: Display WsgiDAV icon in header
    icon: true
    #: Raw HTML code, appended as footer (true: use a default trailer)
    response_trailer: true
    #: Display the name and realm of the authenticated user (or 'anomymous')
    show_user: true
    show_logout: true
    #: Send <dm:mount> response if request URL contains '?davmount'
    #: Also add a respective link at the top of the listing
    #: (See https://tools.ietf.org/html/rfc4709)
    davmount: false
    #: Add an 'open as webfolder' link (requires Windows IE <= 7!)
    ms_mount: false
    #: Invoke MS Office documents for editing using WebDAV by adding a JavaScript
    #: click handler.
    #: - For IE 11 and below invokes the SharePoint ActiveXObject("SharePoint.OpenDocuments")
    #: - If the custom legacy Firefox plugin is available, it will be used
    #:   https://docs.microsoft.com/en-us/previous-versions/office/developer/sharepoint-2010/ff407576(v%3Doffice.14)
    #: - Otherwise the Office URL prefix is used (e.g. 'ms-word:ofe|u|http://server/path/file.docx')
    ms_sharepoint_support: true
    #: The path to the directory that contains template.html and associated
    #: assets.
    #: The default is the htdocs directory within the dir_browser directory.
    htdocs_path: null


# ----------------------------------------------------------------------------
# Property Manager
# null: (default) Don't support dead properties
# true: Use in-memory property manager (NOT persistent)
property_manager: true

# Example:
# Use PERSISTENT shelve based property manager
# from wsgidav.prop_man.property_manager import ShelvePropertyManager
# property_manager: ShelvePropertyManager("wsgidav-props.shelve")

# Optional additional live property modification
mutable_live_props:
    # Enable to allow clients to use e.g. the touch or cp / rsync commands with the
    # preserve-timestamp flags in a mounted DAV share (may be RFC4918 incompliant)
    - "{DAV:}getlastmodified"

# ----------------------------------------------------------------------------
# Lock Manager

lock_manager: true

# Example:
# Use PERSISTENT shelve based lock manager
# from wsgidav.lock_storage import LockStorageShelve
# lock_manager = LockStorageShelve("wsgidav-locks.shelve")

Sample wsgidav.json

We can also use a JSON file for configuration. The structure is identical to the YAML format.

See the sample_wsgidav.json example. (Note that the parser allows JavaScript-style comments)

Sample wsgidav.conf

This format uses plain Python syntax, which allows us to use Python data structures, and even write helper functions, etc.

This is the most powerful and flexible format, that can be used in complex scenarios.

See the Sample wsgidav.conf example.