Clean LDAP support
This commit is contained in:
parent
78e52d5cf4
commit
12a8e01492
35
config
35
config
@ -15,9 +15,9 @@ hosts = 0.0.0.0:5232
|
||||
daemon = False
|
||||
# SSL flag, enable HTTPS protocol
|
||||
ssl = False
|
||||
# SSL certificate path (if needed)
|
||||
# SSL certificate path
|
||||
certificate = /etc/apache2/ssl/server.crt
|
||||
# SSL private key (if needed)
|
||||
# SSL private key
|
||||
key = /etc/apache2/ssl/server.key
|
||||
|
||||
[encoding]
|
||||
@ -28,29 +28,24 @@ stock = utf-8
|
||||
|
||||
[acl]
|
||||
# Access method
|
||||
# Value: None | htpasswd
|
||||
# Value: None | htpasswd | LDAP
|
||||
type = None
|
||||
# Personal calendars only available for logged in users (if needed)
|
||||
# Personal calendars only available for logged in users
|
||||
personal = False
|
||||
# Htpasswd filename (if needed)
|
||||
filename = /etc/radicale/users
|
||||
# Htpasswd encryption method (if needed)
|
||||
# Htpasswd filename
|
||||
htpasswd_filename = /etc/radicale/users
|
||||
# Htpasswd encryption method
|
||||
# Value: plain | sha1 | crypt
|
||||
encryption = crypt
|
||||
|
||||
[authLdap]
|
||||
#LDAP Host
|
||||
LDAPServer = 127.0.0.1
|
||||
#Fields to create a LDAP bind
|
||||
#Value to add before the user name in a LDAP bind
|
||||
LDAPPrepend = uid=
|
||||
#Value to add after the user name in a LDAP bind
|
||||
LDAPAppend = ou=users,dc=exmaple,dc=dom
|
||||
#=> uid=corentin,ou=users,dc=exmaple,dc=dom
|
||||
htpasswd_encryption = crypt
|
||||
# LDAP server URL, with protocol and port
|
||||
ldap_url = ldap://localhost:389/
|
||||
# LDAP base path
|
||||
ldap_base = ou=users,dc=example,dc=com
|
||||
# LDAP login attribute
|
||||
ldap_attribute = uid
|
||||
|
||||
[storage]
|
||||
# Folder for storing local calendars,
|
||||
# created if not present
|
||||
# Folder for storing local calendars, created if not present
|
||||
folder = ~/.config/radicale/calendars
|
||||
|
||||
[logging]
|
||||
|
@ -60,26 +60,30 @@ def _check(request, function):
|
||||
if not request._calendar or not request.server.acl:
|
||||
return function(request)
|
||||
|
||||
log.LOGGER.info("Checking rights for %s" % request._calendar.owner)
|
||||
if request._calendar.owner is None and PERSONAL:
|
||||
# No owner and personal calendars, don't check rights
|
||||
return function(request)
|
||||
|
||||
log.LOGGER.info(
|
||||
"Checking rights for calendar owned by %s" % request._calendar.owner)
|
||||
|
||||
authorization = request.headers.get("Authorization", None)
|
||||
if authorization:
|
||||
challenge = authorization.lstrip("Basic").strip().encode("ascii")
|
||||
plain = request._decode(base64.b64decode(challenge))
|
||||
user, password = plain.split(":")
|
||||
user, password = request._decode(base64.b64decode(challenge)).split(":")
|
||||
else:
|
||||
user = password = None
|
||||
|
||||
if request.server.acl.has_right(request._calendar.owner, user, password):
|
||||
function(request)
|
||||
log.LOGGER.info("%s allowed" % request._calendar.owner)
|
||||
return function(request)
|
||||
else:
|
||||
log.LOGGER.info("%s refused" % request._calendar.owner)
|
||||
request.send_response(client.UNAUTHORIZED)
|
||||
request.send_header(
|
||||
"WWW-Authenticate",
|
||||
"Basic realm=\"Radicale Server - Password Required\"")
|
||||
request.end_headers()
|
||||
log.LOGGER.info("%s refused" % request._calendar.owner)
|
||||
|
||||
def _log_request_content(request, function):
|
||||
"""Log the content of the request and store it in the request object."""
|
||||
|
61
radicale/acl/LDAP.py
Normal file
61
radicale/acl/LDAP.py
Normal file
@ -0,0 +1,61 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Radicale Server - Calendar Server
|
||||
# Copyright © 2011 Corentin Le Bail
|
||||
# Copyright © 2011 Guillaume Ayoub
|
||||
#
|
||||
# This library is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Radicale. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""
|
||||
LDAP ACL.
|
||||
|
||||
Authentication based on the ``python-ldap`` module
|
||||
(http://www.python-ldap.org/).
|
||||
|
||||
"""
|
||||
|
||||
import ldap
|
||||
from radicale import config, log
|
||||
|
||||
|
||||
BASE = config.get("acl", "ldap_base")
|
||||
ATTRIBUTE = config.get("acl", "ldap_attribute")
|
||||
CONNEXION = ldap.initialize(config.get("acl", "ldap_url"))
|
||||
PERSONAL = config.getboolean("acl", "personal")
|
||||
|
||||
|
||||
def has_right(owner, user, password):
|
||||
"""Check if ``user``/``password`` couple is valid."""
|
||||
if (user != owner and PERSONAL) or not user:
|
||||
# User is not owner and personal calendars, or no user given, forbidden
|
||||
return False
|
||||
|
||||
dn = "%s=%s" % (ATTRIBUTE, ldap.dn.escape_dn_chars(user))
|
||||
log.LOGGER.debug("LDAP bind for %s in base %s" % (dn, BASE))
|
||||
|
||||
users = CONNEXION.search_s(BASE, ldap.SCOPE_ONELEVEL, dn)
|
||||
if users:
|
||||
log.LOGGER.debug("User %s found" % user)
|
||||
try:
|
||||
CONNEXION.simple_bind_s(users[0][0], password or "")
|
||||
except ldap.INVALID_CREDENTIALS:
|
||||
log.LOGGER.debug("Invalid credentials")
|
||||
else:
|
||||
log.LOGGER.debug("LDAP bind OK")
|
||||
return True
|
||||
else:
|
||||
log.LOGGER.debug("User %s not found" % user)
|
||||
|
||||
log.LOGGER.debug("LDAP bind failed")
|
||||
return False
|
@ -1,28 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import sys
|
||||
import ldap
|
||||
import radicale
|
||||
|
||||
LDAPSERVER = config.get("authLdap", "LDAPServer")
|
||||
LDAPPREPEND = config.get("authLdap", "LDAPPrepend")
|
||||
LDAPAPPEND = config.get("authLdap", "LDAPAppend")
|
||||
|
||||
def has_right(owner, user, password):
|
||||
if user == None:
|
||||
user=""
|
||||
if password == None:
|
||||
password=""
|
||||
if owner != user:
|
||||
return False
|
||||
try:
|
||||
radicale.log.LOGGER.info("Open LDAP server connexion")
|
||||
l=ldap.open(LDAPSERVER, 389)
|
||||
cn="%s%s,%s" % (LDAPPREPEND, user, LDAPAPPEND)
|
||||
radicale.log.LOGGER.info("LDAP bind with dn: %s" % (cn))
|
||||
l.simple_bind_s(cn, password);
|
||||
radicale.log.LOGGER.info("LDAP bind ok")
|
||||
return True
|
||||
except:
|
||||
radicale.log.LOGGER.info("Nu such credential")
|
||||
return False
|
@ -33,6 +33,11 @@ import hashlib
|
||||
from radicale import config
|
||||
|
||||
|
||||
FILENAME = config.get("acl", "htpasswd_filename")
|
||||
PERSONAL = config.getboolean("acl", "personal")
|
||||
ENCRYPTION = config.get("acl", "htpasswd_encryption")
|
||||
|
||||
|
||||
def _plain(hash_value, password):
|
||||
"""Check if ``hash_value`` and ``password`` match using plain method."""
|
||||
return hash_value == password
|
||||
@ -48,7 +53,7 @@ def _crypt(hash_value, password):
|
||||
def _sha1(hash_value, password):
|
||||
"""Check if ``hash_value`` and ``password`` match using sha1 method."""
|
||||
hash_value = hash_value.replace("{SHA}", "").encode("ascii")
|
||||
password = password.encode(config.get("encoding", "stock"))
|
||||
password = password.encode(config.get("htpasswd_encoding", "stock"))
|
||||
sha1 = hashlib.sha1() # pylint: disable=E1101
|
||||
sha1.update(password)
|
||||
return sha1.digest() == base64.b64decode(hash_value)
|
||||
@ -56,18 +61,9 @@ def _sha1(hash_value, password):
|
||||
|
||||
def has_right(owner, user, password):
|
||||
"""Check if ``user``/``password`` couple is valid."""
|
||||
if owner is None and PERSONAL:
|
||||
# No owner and personal calendars, everybody is allowed
|
||||
return True
|
||||
|
||||
for line in open(FILENAME).readlines():
|
||||
if line.strip():
|
||||
login, hash_value = line.strip().split(":")
|
||||
if login == user and (not PERSONAL or user == owner):
|
||||
return CHECK_PASSWORD(hash_value, password)
|
||||
return locals()["_%s" % ENCRYPTION](hash_value, password)
|
||||
return False
|
||||
|
||||
|
||||
FILENAME = config.get("acl", "filename")
|
||||
PERSONAL = config.getboolean("acl", "personal")
|
||||
CHECK_PASSWORD = locals()["_%s" % config.get("acl", "encryption")]
|
||||
|
@ -50,17 +50,16 @@ INITIAL_CONFIG = {
|
||||
"acl": {
|
||||
"type": "None",
|
||||
"personal": "False",
|
||||
"filename": "/etc/radicale/users",
|
||||
"encryption": "crypt"},
|
||||
"httpasswd_filename": "/etc/radicale/users",
|
||||
"httpasswd_encryption": "crypt",
|
||||
"ldap_url": "ldap://localhost:389/",
|
||||
"ldap_base": "ou=users,dc=example,dc=com",
|
||||
"ldap_attribute": "uid"},
|
||||
"storage": {
|
||||
"folder": os.path.expanduser("~/.config/radicale/calendars")},
|
||||
"logging": {
|
||||
"config": "/etc/radicale/logging",
|
||||
"debug": "False"},
|
||||
"authLdap": {
|
||||
"LDAPServer": "127.0.0.1",
|
||||
"LDAPPrepend": "uid=",
|
||||
"LDAPAppend": "ou=users,dc=example,dc=com"}}
|
||||
"debug": "False"}}
|
||||
|
||||
# Create a ConfigParser and configure it
|
||||
_CONFIG_PARSER = ConfigParser()
|
||||
|
Loading…
Reference in New Issue
Block a user