Cut long lines

This commit is contained in:
Guillaume Ayoub 2016-05-18 22:41:05 +02:00
parent 36ad6bd021
commit a8fda1aedf
3 changed files with 34 additions and 26 deletions

View File

@ -482,7 +482,8 @@ class Application:
"DAV": "1, 2, 3, calendar-access, addressbook, extended-mkcol", "DAV": "1, 2, 3, calendar-access, addressbook, extended-mkcol",
"Content-Type": "text/xml"} "Content-Type": "text/xml"}
answer = xmlutils.propfind( answer = xmlutils.propfind(
environ["PATH_INFO"], content, read_collections, write_collections, user) environ["PATH_INFO"], content, read_collections, write_collections,
user)
return client.MULTI_STATUS, headers, answer return client.MULTI_STATUS, headers, answer
def do_PROPPATCH(self, environ, read_collections, write_collections, def do_PROPPATCH(self, environ, read_collections, write_collections,

View File

@ -21,12 +21,13 @@ Authentication management.
Default is htpasswd authentication. Default is htpasswd authentication.
Apache's htpasswd command (httpd.apache.org/docs/programs/htpasswd.html) manages Apache's htpasswd command (httpd.apache.org/docs/programs/htpasswd.html)
a file for storing user credentials. It can encrypt passwords using different manages a file for storing user credentials. It can encrypt passwords using
methods, e.g. BCRYPT, MD5-APR1 (a version of MD5 modified for Apache), SHA1, or different methods, e.g. BCRYPT, MD5-APR1 (a version of MD5 modified for
by using the system's CRYPT routine. The CRYPT and SHA1 encryption methods Apache), SHA1, or by using the system's CRYPT routine. The CRYPT and SHA1
implemented by htpasswd are considered as insecure. MD5-APR1 provides medium encryption methods implemented by htpasswd are considered as insecure. MD5-APR1
security as of 2015. Only BCRYPT can be considered secure by current standards. provides medium security as of 2015. Only BCRYPT can be considered secure by
current standards.
MD5-APR1-encrypted credentials can be written by all versions of htpasswd (it MD5-APR1-encrypted credentials can be written by all versions of htpasswd (it
is the default, in fact), whereas BCRYPT requires htpasswd 2.4.x or newer. is the default, in fact), whereas BCRYPT requires htpasswd 2.4.x or newer.
@ -80,9 +81,9 @@ class BaseAuth:
def is_authenticated(self, user, password): def is_authenticated(self, user, password):
"""Validate credentials. """Validate credentials.
Iterate through htpasswd credential file until user matches, extract hash Iterate through htpasswd credential file until user matches, extract
(encrypted password) and check hash against user-given password, using the hash (encrypted password) and check hash against user-given password,
method specified in the Radicale config. using the method specified in the Radicale config.
""" """
raise NotImplementedError raise NotImplementedError
@ -135,30 +136,33 @@ class Auth(BaseAuth):
"supported." % self.encryption) "supported." % self.encryption)
def _plain(self, hash_value, password): def _plain(self, hash_value, password):
"""Check if ``hash_value`` and ``password`` match, using plain method.""" """Check if ``hash_value`` and ``password`` match, plain method."""
return hash_value == password return hash_value == password
def _crypt(self, crypt, hash_value, password): def _crypt(self, crypt, hash_value, password):
"""Check if ``hash_value`` and ``password`` match, using crypt method.""" """Check if ``hash_value`` and ``password`` match, crypt method."""
return crypt.crypt(password, hash_value) == hash_value return crypt.crypt(password, hash_value) == hash_value
def _sha1(self, hash_value, password): def _sha1(self, hash_value, password):
"""Check if ``hash_value`` and ``password`` match, using sha1 method.""" """Check if ``hash_value`` and ``password`` match, sha1 method."""
hash_value = hash_value.replace("{SHA}", "").encode("ascii") hash_value = hash_value.replace("{SHA}", "").encode("ascii")
password = password.encode(self.configuration.get("encoding", "stock")) password = password.encode(self.configuration.get("encoding", "stock"))
sha1 = hashlib.sha1() # pylint: disable=E1101 sha1 = hashlib.sha1() # pylint: disable=E1101
sha1.update(password) sha1.update(password)
return sha1.digest() == base64.b64decode(hash_value) return sha1.digest() == base64.b64decode(hash_value)
def _ssha(self, hash_salt_value, password): def _ssha(self, hash_value, password):
"""Check if ``hash_salt_value`` and ``password`` match, using salted sha1 """Check if ``hash_value`` and ``password`` match, salted sha1 method.
method. This method is not directly supported by htpasswd, but it can be
written with e.g. openssl, and nginx can parse it.""" This method is not directly supported by htpasswd, but it can be
hash_salt_value = hash_salt_value.replace( written with e.g. openssl, and nginx can parse it.
"""
hash_value = hash_value.replace(
"{SSHA}", "").encode("ascii").decode('base64') "{SSHA}", "").encode("ascii").decode('base64')
password = password.encode(self.configuration.get("encoding", "stock")) password = password.encode(self.configuration.get("encoding", "stock"))
hash_value = hash_salt_value[:20] hash_value = hash_value[:20]
salt_value = hash_salt_value[20:] salt_value = hash_value[20:]
sha1 = hashlib.sha1() # pylint: disable=E1101 sha1 = hashlib.sha1() # pylint: disable=E1101
sha1.update(password) sha1.update(password)
sha1.update(salt_value) sha1.update(salt_value)

View File

@ -181,9 +181,9 @@ def _prop_match(item, filter_):
filter_.remove(filter_[0]) filter_.remove(filter_[0])
elif filter_[0].tag == _tag("C", "text-match"): elif filter_[0].tag == _tag("C", "text-match"):
# Point #4 of rfc4791-9.7.2 # Point #4 of rfc4791-9.7.2
# TODO: collations are not supported, but the default ones needed for DAV # TODO: collations are not supported, but the default ones needed
# servers are actually pretty useless. Texts are lowered to be # for DAV servers are actually pretty useless. Texts are lowered to
# case-insensitive, almost as the "i;ascii-casemap" value. # be case-insensitive, almost as the "i;ascii-casemap" value.
match = next(filter_[0].itertext()).lower() match = next(filter_[0].itertext()).lower()
value = vobject_item.getChildValue(filter_.get("name").lower()) value = vobject_item.getChildValue(filter_.get("name").lower())
if value is None: if value is None:
@ -296,7 +296,8 @@ def delete(path, collection):
return _pretty_xml(multistatus) return _pretty_xml(multistatus)
def propfind(path, xml_request, read_collections, write_collections, user=None): def propfind(path, xml_request, read_collections, write_collections,
user=None):
"""Read and answer PROPFIND requests. """Read and answer PROPFIND requests.
Read rfc4918-9.1 for info. Read rfc4918-9.1 for info.
@ -324,12 +325,14 @@ def propfind(path, xml_request, read_collections, write_collections, user=None):
collections = [] collections = []
for collection in write_collections: for collection in write_collections:
collections.append(collection) collections.append(collection)
response = _propfind_response(path, collection, props, user, write=True) response = _propfind_response(
path, collection, props, user, write=True)
multistatus.append(response) multistatus.append(response)
for collection in read_collections: for collection in read_collections:
if collection in collections: if collection in collections:
continue continue
response = _propfind_response(path, collection, props, user, write=False) response = _propfind_response(
path, collection, props, user, write=False)
multistatus.append(response) multistatus.append(response)
return _pretty_xml(multistatus) return _pretty_xml(multistatus)