Litmus is running from docker

This commit is contained in:
Martin Wendt 2018-09-09 15:21:37 +02:00
parent 11e86ac997
commit 5f79c3a55f
8 changed files with 156 additions and 451 deletions

View file

@ -84,7 +84,12 @@ except ImportError:
try:
try:
from cherrypy import __version__ as cp_version
# from cherrypy import __version__ as cp_version
from cheroot import wsgi
cp_version = wsgi.Server.version
except ImportError:
# Bundled CherryPy wsgiserver in WsgDAV 1.x
server_folder = os.path.abspath(

View file

@ -405,11 +405,7 @@ class DAVClient(object):
root = ElementTree.Element("{DAV:}lockinfo")
object_to_etree(
root,
{
"locktype": lock_type,
"lockscope": lock_scope,
"owner": {"href": owner},
},
{"locktype": lock_type, "lockscope": lock_scope, "owner": {"href": owner}},
namespace="DAV:",
)
tree = ElementTree.ElementTree(root)

BIN
tests/litmus.log Normal file

Binary file not shown.

View file

@ -48,7 +48,12 @@ class BasicTest(unittest.TestCase):
"""Property manager should be lazy opening on first access."""
pm = self.pm
assert not pm._loaded, "PM must be closed until first access"
pm.get_properties(self.respath)
print(pm, self.respath)
try:
pm.get_properties(self.respath)
except Exception:
print("NOTE: if this fails, try to delete the temp files: {}".format(pm))
raise
assert pm._loaded, "PM must be opened after first access"
def testValidation(self):

View file

@ -1,219 +0,0 @@
# Note: This file is in Python syntax and format
################################################################################
# WsgiDAV configuration file used for client test plan
#
# Version Date Info
# 1.0 2013-12-14 Created
#
################################################################################
# INITIALIZATION - Do not modify this section
provider_mapping = {}
user_mapping = {}
def addShare(shareName, davProvider):
provider_mapping[shareName] = davProvider
def addUser(realmName, user, password, description, roles=[]):
realmName = "/" + realmName.strip(r"\/")
userDict = user_mapping.setdefault(realmName, {}).setdefault(user, {})
userDict["password"] = password
userDict["description"] = description
userDict["roles"] = roles
################################################################################
# SERVER OPTIONS
#===============================================================================
server = "cheroot"
#===============================================================================
# Additional Options
#
# Add the MS-Author-Via Response Header to OPTIONS command
add_header_MS_Author_Via = True
#===============================================================================
# Debugging
verbose = 1 # 0 - no output (excepting application exceptions)
# 1 - show single line request summaries (HTTP logging)
# 2 - show additional events
# 3 - show full request/response header info (HTTP Logging)
# request body and GET response bodies not shown
debug_methods = [
# "PROPFIND",
# "PROPPATCH",
# "MKCOL",
# "POST",
# "DELETE",
# "PUT",
# "COPY",
# "MOVE",
# "LOCK",
# "UNLOCK",
# "OPTIONS",
# "GET",
# "HEAD",
]
debug_litmus = [
# "lock_excl",
# "http: 1",
# "mkcol_with_body",
# "notowner_modify",
# "cond_put_corrupt_token",
# "copy_coll",
# "fail_cond_put_unlocked",
# "fail_complex_cond_put",
# "basic: 9",
# "props: 16",
# "locks: 9",
# "http: 2",
]
# Enable specific module loggers
# E.g. ["lock_manager", "property_manager", "http_authenticator", ...]
enable_loggers = [
# "ext_wsgiutils_server",
# "wsgidav.ext_wsgiutils_server",
# "http_authenticator",
# "lock_manager",
# "lock_storage",
# "request_server",
# "dav_provider",
# "fs_dav_provider",
# "mysql_dav_provider",
]
################################################################################
# WsgiDavDirBrowser
dir_browser = {
"enable": True, # Render HTML listing for GET requests on collections
"response_trailer": "", # Raw HTML code, appended as footer
"davmount": True, # Send <dm:mount> response if request URL contains '?davmount'
"msmount": True, # Add an 'open as webfolder' link (requires Windows)
# "msSharepointUrls": True, # Prepend 'ms-word:ofe|u|' to URL for MS Offce documents
"msSharepointHelper": True, # Use
}
################################################################################
# DAV Provider
#===============================================================================
# Property Manager
#
# Uncomment this lines to specify your own property manager.
# Default: wsgidav.prop_man.property_manager.PropertyManager
# Also available: wsgidav.prop_man.property_manager.ShelvePropertyManager
#
# Example: Use in-memory property manager (this is also the default)
from wsgidav.prop_man.property_manager import PropertyManager
property_manager = PropertyManager()
# Example: Use PERSISTENT shelve based property manager
#from wsgidav.prop_man.property_manager import ShelvePropertyManager
#property_manager = ShelvePropertyManager("wsgidav-props.shelve")
#===============================================================================
# Lock Manager
#
# Uncomment this lines to specify your own locks manager.
# Default: wsgidav.lock_storage.LockStorageDict
# Also available: wsgidav.lock_storage.LockStorageShelve
#
# Check the documentation on how to develop custom lock managers.
# Note that the default LockStorageDict works in-memory, and thus is NOT
# persistent.
# Example: Use in-memory lock manager
# (this is the same as passing 'lock_manager = True', which is default)
#from wsgidav.lock_storage import LockStorageDict
#lock_manager = LockStorageDict()
# Example: Use PERSISTENT shelve based lock manager
#from wsgidav.lock_storage import LockStorageShelve
#lock_manager = LockStorageShelve("wsgidav-locks.shelve")
#===============================================================================
# SHARES
#
# If you would like to publish files in the location '/v_root' through a
# WsgiDAV share 'files', so that it can be accessed by this URL:
# http://server:port/files
# insert the following line:
# addShare("files", "/v_root")
# or, on a Windows box:
# addShare("files", "c:\\v_root")
#
# To access the same directory using a root level share
# http://server:port/
# insert this line:
# addShare("", "/v_root")
#
# The above examples use wsgidav.fs_dav_provider.FilesystemProvider, which is
# the default provider implementation.
#
# If you wish to use a custom provider, an object must be passed as second
# parameter. See the examples below.
#addShare("", r"C:\temp\davshare")
#addShare("dav", "~/wsgidav_test")
addShare("", "~/wsgidav_test")
### Add a read-only file share:
#from wsgidav.fs_dav_provider import FilesystemProvider
#addShare("dav_ro", FilesystemProvider("/temp/dav_ro", readonly=True))
################################################################################
# AUTHENTICATION
#===============================================================================
# HTTP Authentication Options
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)
trusted_auth_header = None
#===============================================================================
# USERS
#
# This section is ONLY used by the DEFAULT Domain Controller.
#
# Users are defined per realm:
# addUser(<realm>, <user>, <password>, <description>)
#
# Note that the default Domain Controller uses the share name as realm name.
#
# If no users are specified for a realm, no authentication is required.
# Thus granting read-write access to anonymous!
#
# Note: If you wish to use Windows WebDAV support (such as Windows XP's My
# Network Places), you need to include the domain of the user as part of the
# username (note the DOUBLE slash), such as:
# addUser("v_root", "domain\\user", "password", "description")
addUser("", "tester", "secret", "")
addUser("", "tester2", "secret2", "")
#addUser("dav", "tester", "secret", "")
#addUser("dav", "tester2", "secret2", "")

78
tests/wsgidav-litmus.yaml Normal file
View file

@ -0,0 +1,78 @@
server: "cheroot"
server_args: {}
host: 192.168.178.44
# host: 0.0.0.0
port: 8080
ssl_certificate: "../wsgidav/server/sample_bogo_server.crt"
ssl_private_key: "../wsgidav/server/sample_bogo_server.key"
# ----------------------------------------------------------------------------
# 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
provider_mapping:
"/": "/temp"
# ==============================================================================
# AUTHENTICATION
http_authenticator:
#: Domain controller that is used to resolve realms and authorization.
#: Default null: wsgidav.domain_controller.SimpleDomainController,
#: which uses the `user_mapping` option below.
#: (See http://wsgidav.readthedocs.io/en/latest/user_guide_configure.html
#: for an example of NT domain controller, etc.)
domain_controller: null
# domain_controller: null
#: Allow basic authentication
accept_basic: true
#: Allow digest authentication
accept_digest: false
# accept_digest: true
#: true (default digest) or false (default basic)
default_to_digest: false
# default_to_digest: true
# Access control per share, used by the default SimpleDomainController.
# These routes must match the provider mapping.
# NOTE: Provider routes without a matching entry here, are open for
# anonymous access!
user_mapping:
"/":
"tester":
password: "secret"
verbose: 3
#logger_format: "%(asctime)s.%(msecs)03d - <%(thread)05d> %(name)-27s %(levelname)-8s: %(message)s"
#logger_date_format: "%H:%M:%S"
#catch_all: false
# 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: []
property_manager: true
# 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: true

View file

@ -1,161 +0,0 @@
################################################################################
# WsgiDAV configuration file
#
# See
# http://wsgidav.readthedocs.io/en/latest/user_guide_configure.html
# for a complete, annotated configuration example.
#
################################################################################
# HELPERS - Do not modify this section
provider_mapping = {}
user_mapping = {}
def addShare(shareName, davProvider):
provider_mapping[shareName] = davProvider
def addUser(realmName, user, password, description, roles=[]):
realmName = "/" + realmName.strip(r"\/")
userDict = user_mapping.setdefault(realmName, {}).setdefault(user, {})
userDict["password"] = password
userDict["description"] = description
userDict["roles"] = roles
################################################################################
# SERVER OPTIONS
#===============================================================================
# server = "cheroot"
# server_args = {}
# host = "localhost"
# host = "192.168.178.44"
host = "0.0.0.0"
port = 8080
# Enable SSL support
ssl_certificate = "wsgidav/server/sample_bogo_server.crt"
ssl_private_key = "wsgidav/server/sample_bogo_server.key"
# ssl_certificate_chain = None
# Add the MS-Author-Via Response Header to OPTIONS command to allow editing
# with Microsoft Office (default: True)
add_header_MS_Author_Via = True
# Add custom headers
# Response headers must be a list of header-name / header-value
# response_headers = [("Access-Control-Allow-Origin", "http://example.org")]
#===============================================================================
# Debugging
# verbose = 3
# 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 = {
"response_trailer": "", # Raw HTML code, appended as footer
"davmount": True, # Send <dm:mount> response if request URL contains '?davmount'
"ms_mount": True, # Add an 'open as webfolder' link (requires Windows)
"ms_sharepoint_support": True, # Invoke MS Offce documents for editing using WebDAV
}
################################################################################
# DAV Provider
#===============================================================================
# Property Manager
# Example: Use PERSISTENT shelve based property manager
#from wsgidav.property_manager import ShelvePropertyManager
#propsmanager = ShelvePropertyManager("wsgidav-props.shelve")
### Use in-memory property manager (NOT persistent)
propsmanager = True
#===============================================================================
# Lock Manager
#
# Example: Use PERSISTENT shelve based lock manager
#from wsgidav.lock_storage import LockStorageShelve
#locksmanager = LockStorageShelve("wsgidav-locks.shelve")
#===============================================================================
# SHARES
addShare("", r"~/davshare")
################################################################################
# AUTHENTICATION
#===============================================================================
# HTTP Authentication Options
acceptbasic = True # Allow basic authentication, True or False
acceptdigest = True # Allow digest authentication, True or False
defaultdigest = True # True (default digest) or False (default basic)
# Enter the name of a header field that will be accepted as authorized user.
# Including quotes, for example: trusted_auth_header = "REMOTE_USER"
trusted_auth_header = None
#domaincontroller = # Uncomment this line to specify your own domain controller
# Default: wsgidav.domain_controller, which uses the USERS
# section below
# Example: use a domain controller that allows users to authenticate against
# a Windows NT domain or a local computer.
# Note: NTDomainController requires basic authentication:
# Set acceptbasic=True, acceptdigest=False, defaultdigest=False
# from wsgidav.addons.nt_domain_controller import NTDomainController
# domaincontroller = NTDomainController(presetdomain=None, presetserver=None)
# acceptbasic = True
# acceptdigest = False
# defaultdigest = False
#===============================================================================
# USERS
#
# This section is ONLY used by the DEFAULT Domain Controller.
#
# Users are defined per realm:
# addUser(<realm>, <user>, <password>, <description>)
#
# Note that the default Domain Controller uses the share name as realm name.
#
# If no users are specified for a realm, no authentication is required.
# Thus granting read-write access to anonymous!
#
# Note: If you wish to use Windows WebDAV support (such as Windows XP's My
# Network Places), you need to include the domain of the user as part of the
# username (note the DOUBLE slash), such as:
# addUser("v_root", "domain\\user", "password", "description")
addUser("", "tester", "secret", "")
#addUser("", "tester2", "secret2", "")

View file

@ -201,17 +201,17 @@ class HTTPAuthenticator(BaseMiddleware):
return self.next_app(environ, start_response)
if "HTTP_AUTHORIZATION" in environ:
authheader = environ["HTTP_AUTHORIZATION"]
authmatch = self._header_method.search(authheader)
authmethod = "None"
if authmatch:
authmethod = authmatch.group(1).lower()
auth_header = environ["HTTP_AUTHORIZATION"]
auth_match = self._header_method.search(auth_header)
auth_method = "None"
if auth_match:
auth_method = auth_match.group(1).lower()
if authmethod == "digest" and self._accept_digest:
if auth_method == "digest" and self._accept_digest:
return self.auth_digest_auth_request(environ, start_response)
elif authmethod == "digest" and self._accept_basic:
elif auth_method == "digest" and self._accept_basic:
return self.send_basic_auth_response(environ, start_response)
elif authmethod == "basic" and self._accept_basic:
elif auth_method == "basic" and self._accept_basic:
return self.auth_basic_auth_request(environ, start_response)
# The requested auth method is not supported.
@ -222,7 +222,7 @@ class HTTPAuthenticator(BaseMiddleware):
_logger.warn(
"HTTPAuthenticator: respond with 400 Bad request; Auth-Method: {}".format(
authmethod
auth_method
)
)
@ -259,10 +259,10 @@ class HTTPAuthenticator(BaseMiddleware):
realm_name = self.domain_controller.get_domain_realm(
environ["PATH_INFO"], environ
)
authheader = environ["HTTP_AUTHORIZATION"]
auth_header = environ["HTTP_AUTHORIZATION"]
authvalue = ""
try:
authvalue = authheader[len("Basic ") :].strip()
authvalue = auth_header[len("Basic ") :].strip()
except Exception:
authvalue = ""
# authvalue = authvalue.strip().decode("base64")
@ -318,39 +318,40 @@ class HTTPAuthenticator(BaseMiddleware):
environ["PATH_INFO"], environ
)
isinvalidreq = False
is_invalid_req = False
authheaderdict = dict([])
authheaders = environ["HTTP_AUTHORIZATION"] + ","
if not authheaders.lower().strip().startswith("digest"):
isinvalidreq = True
auth_header_dict = {}
auth_headers = environ["HTTP_AUTHORIZATION"] + ","
if not auth_headers.lower().strip().startswith("digest"):
is_invalid_req = True
# Hotfix for Windows file manager and OSX Finder:
# Some clients don't urlencode paths in auth header, so uri value may
# contain commas, which break the usual regex headerparser. Example:
# Digest user_name="user",realm="/",uri="a,b.txt",nc=00000001, ...
# -> [..., ('uri', '"a'), ('nc', '00000001'), ...]
# Override any such values with carefully extracted ones.
authheaderlist = self._header_parser.findall(authheaders)
authheaderfixlist = self._header_fix_parser.findall(authheaders)
if authheaderfixlist:
auth_header_list = self._header_parser.findall(auth_headers)
auth_header_fixlist = self._header_fix_parser.findall(auth_headers)
if auth_header_fixlist:
_logger.info(
"Fixing authheader comma-parsing: extend {} with {}".format(
authheaderlist, authheaderfixlist
"Fixing auth_header comma-parsing: extend {} with {}".format(
auth_header_list, auth_header_fixlist
)
)
authheaderlist += authheaderfixlist
for authheader in authheaderlist:
authheaderkey = authheader[0]
authheadervalue = authheader[1].strip().strip('"')
authheaderdict[authheaderkey] = authheadervalue
auth_header_list += auth_header_fixlist
for auth_header in auth_header_list:
authheaderkey = auth_header[0]
authheadervalue = auth_header[1].strip().strip('"')
auth_header_dict[authheaderkey] = authheadervalue
_logger.debug(
"auth_digest_auth_request: {}".format(environ["HTTP_AUTHORIZATION"])
)
_logger.debug(" -> {}".format(authheaderdict))
_logger.debug(" -> {}".format(auth_header_dict))
if "user_name" in authheaderdict:
req_username = authheaderdict["user_name"]
req_username = None
if "user_name" in auth_header_dict:
req_username = auth_header_dict["user_name"]
req_username_org = req_username
# Hotfix for Windows XP:
# net use W: http://127.0.0.1/dav /USER:DOMAIN\tester tester
@ -367,63 +368,63 @@ class HTTPAuthenticator(BaseMiddleware):
if not self.domain_controller.is_realm_user(
realm_name, req_username, environ
):
isinvalidreq = True
is_invalid_req = True
else:
isinvalidreq = True
is_invalid_req = True
# TODO: Chun added this comments, but code was commented out
# Do not do realm checking - a hotfix for WinXP using some other realm's
# auth details for this realm - if user/password match
if "realm" in authheaderdict:
if authheaderdict["realm"].upper() != realm_name.upper():
if "realm" in auth_header_dict:
if auth_header_dict["realm"].upper() != realm_name.upper():
if HOTFIX_WINXP_AcceptRootShareLogin:
# Hotfix: also accept '/'
if authheaderdict["realm"].upper() != "/":
isinvalidreq = True
if auth_header_dict["realm"].upper() != "/":
is_invalid_req = True
else:
isinvalidreq = True
is_invalid_req = True
if "algorithm" in authheaderdict:
if authheaderdict["algorithm"].upper() != "MD5":
isinvalidreq = True # only MD5 supported
if "algorithm" in auth_header_dict:
if auth_header_dict["algorithm"].upper() != "MD5":
is_invalid_req = True # only MD5 supported
if "uri" in authheaderdict:
req_uri = authheaderdict["uri"]
if "uri" in auth_header_dict:
req_uri = auth_header_dict["uri"]
if "nonce" in authheaderdict:
req_nonce = authheaderdict["nonce"]
if "nonce" in auth_header_dict:
req_nonce = auth_header_dict["nonce"]
else:
isinvalidreq = True
is_invalid_req = True
req_hasqop = False
if "qop" in authheaderdict:
req_hasqop = True
req_qop = authheaderdict["qop"]
req_has_qop = False
if "qop" in auth_header_dict:
req_has_qop = True
req_qop = auth_header_dict["qop"]
if req_qop.lower() != "auth":
isinvalidreq = True # only auth supported, auth-int not supported
is_invalid_req = True # only auth supported, auth-int not supported
else:
req_qop = None
if "cnonce" in authheaderdict:
req_cnonce = authheaderdict["cnonce"]
if "cnonce" in auth_header_dict:
req_cnonce = auth_header_dict["cnonce"]
else:
req_cnonce = None
if req_hasqop:
isinvalidreq = True
if req_has_qop:
is_invalid_req = True
if "nc" in authheaderdict: # is read but nonce-count checking not implemented
req_nc = authheaderdict["nc"]
if "nc" in auth_header_dict: # is read but nonce-count checking not implemented
req_nc = auth_header_dict["nc"]
else:
req_nc = None
if req_hasqop:
isinvalidreq = True
if req_has_qop:
is_invalid_req = True
if "response" in authheaderdict:
req_response = authheaderdict["response"]
if "response" in auth_header_dict:
req_response = auth_header_dict["response"]
else:
isinvalidreq = True
is_invalid_req = True
if not isinvalidreq:
if not is_invalid_req:
req_password = self.domain_controller.get_realm_user_password(
realm_name, req_username, environ
)
@ -468,15 +469,15 @@ class HTTPAuthenticator(BaseMiddleware):
)
)
else:
isinvalidreq = True
is_invalid_req = True
else:
isinvalidreq = True
is_invalid_req = True
else:
# _logger.debug("digest succeeded for realm '{}', user '{}'"
# .format(realm_name, req_username))
pass
if isinvalidreq:
if is_invalid_req:
_logger.warn(
"Authentication failed for user '{}', realm '{}'".format(
req_username, realm_name