|
|
@ -0,0 +1,141 @@ |
|
|
|
from radicale.auth import BaseAuth |
|
|
|
from radicale.log import logger |
|
|
|
|
|
|
|
import ldap3 |
|
|
|
|
|
|
|
PLUGIN_CONFIG_SCHEMA = {"auth": { |
|
|
|
"ldap_server_url": { |
|
|
|
"value": None, |
|
|
|
"type": str}, |
|
|
|
"ldap_binddn": { |
|
|
|
"value": None, |
|
|
|
"type": str}, |
|
|
|
"ldap_secret": { |
|
|
|
"value": None, |
|
|
|
"type": str}, |
|
|
|
"ldap_search_base": { |
|
|
|
"value": None, |
|
|
|
"type": str}, |
|
|
|
"ldap_user_filter": { |
|
|
|
"value": None, |
|
|
|
"type": str}, |
|
|
|
"ldap_user_attribute": { |
|
|
|
"value": None, |
|
|
|
"type": str}, |
|
|
|
"ldap_access_group_filter": { |
|
|
|
"value": "None", |
|
|
|
"type": str}, |
|
|
|
"ldap_access_group_attribute": { |
|
|
|
"value": "None", |
|
|
|
"type": str}}} |
|
|
|
|
|
|
|
class Auth(BaseAuth): |
|
|
|
def __init__(self, configuration): |
|
|
|
super().__init__(configuration.copy(PLUGIN_CONFIG_SCHEMA)) |
|
|
|
|
|
|
|
def login(self, user, password): |
|
|
|
"""Check if ``user``/``password`` couple is valid.""" |
|
|
|
serverUrl = self.configuration.get("auth", "ldap_server_url") |
|
|
|
binddn = self.configuration.get("auth", "ldap_binddn") |
|
|
|
secret = self.configuration.get("auth", "ldap_secret") |
|
|
|
searchBase = self.configuration.get("auth", "ldap_search_base") |
|
|
|
userFilter = self.configuration.get("auth", "ldap_user_filter") |
|
|
|
userAttribute = self.configuration.get("auth", "ldap_user_attribute") |
|
|
|
accessGroupFilter = self.configuration.get("auth", "ldap_access_group_filter") |
|
|
|
accessGroupAttribute = self.configuration.get("auth", "ldap_access_group_attribute") |
|
|
|
|
|
|
|
logger.info("LDAP: start connection") |
|
|
|
logger.debug("LDAP: server URL: %s" % serverUrl) |
|
|
|
logger.debug("LDAP: binddn: %s" % binddn) |
|
|
|
logger.debug("LDAP: secret: %s" % secret) |
|
|
|
## TODO: check for errors |
|
|
|
server = ldap3.Server(serverUrl) |
|
|
|
conn = ldap3.Connection(server, binddn, secret) |
|
|
|
conn.bind() |
|
|
|
result = conn.result |
|
|
|
|
|
|
|
logger.info("LDAP: connection successful") |
|
|
|
logger.debug("LDAP bind result: %s" % str(result)) |
|
|
|
|
|
|
|
if result['description'] == "invalidCredentials": |
|
|
|
logger.warning("LDAP: binddn credentials are invalid") |
|
|
|
return "" |
|
|
|
|
|
|
|
if (not accessGroupFilter == "None") and (not accessGroupAttribute == "None"): |
|
|
|
logger.debug("LDAP access group filter: %s" % accessGroupFilter) |
|
|
|
logger.debug("LDAP access group attribute: %s" % accessGroupAttribute) |
|
|
|
|
|
|
|
logger.debug("LDAP: search access group") |
|
|
|
conn.search(searchBase, accessGroupFilter) |
|
|
|
numberOfResponseEntries = len(conn.response) |
|
|
|
|
|
|
|
# ~ logger.debug("LDAP:") |
|
|
|
logger.debug("LDAP search result: %s" % str(conn.result)) |
|
|
|
logger.debug("LDAP number of response entries: %s" % str(numberOfResponseEntries)) |
|
|
|
|
|
|
|
if numberOfResponseEntries > 0: |
|
|
|
if numberOfResponseEntries == 1: |
|
|
|
accessGroupDn = conn.response[0]['dn'] |
|
|
|
logger.debug("LDAP access group DN: %s" % accessGroupDn) |
|
|
|
else: |
|
|
|
logger.warning("LDAP: more than 1 group found") |
|
|
|
return "" |
|
|
|
else: |
|
|
|
logger.warning("LDAP: no group found") |
|
|
|
return "" |
|
|
|
|
|
|
|
logger.debug("LDAP user filter: %s" % userFilter) |
|
|
|
logger.debug("LDAP user attribute: %s" % userAttribute) |
|
|
|
|
|
|
|
logger.debug("LDAP: search user %s" % user) |
|
|
|
if (not accessGroupFilter == "None") and (not accessGroupAttribute == "None"): |
|
|
|
conn.search(searchBase, userFilter.format(name=user), attributes=[userAttribute, accessGroupAttribute]) |
|
|
|
else: |
|
|
|
conn.search(searchBase, userFilter.format(name=user), attributes=[userAttribute]) |
|
|
|
numberOfResponseEntries = len(conn.response) |
|
|
|
|
|
|
|
logger.debug("LDAP search result: %s" % str(conn.result)) |
|
|
|
logger.debug("LDAP number of response entries: %s" % str(numberOfResponseEntries)) |
|
|
|
|
|
|
|
if numberOfResponseEntries > 0: |
|
|
|
if numberOfResponseEntries == 1: |
|
|
|
userDn = conn.response[0]['dn'] |
|
|
|
userAttributeValue = conn.response[0]['attributes'][userAttribute] |
|
|
|
logger.debug("LDAP user DN: %s" % userDn) |
|
|
|
|
|
|
|
if (not accessGroupFilter == "None") and (not accessGroupAttribute == "None"): |
|
|
|
userMemberOf = conn.response[0]['attributes'][accessGroupAttribute] |
|
|
|
|
|
|
|
userIsInAccessGroup = False |
|
|
|
for group in userMemberOf: |
|
|
|
if group == accessGroupDn: |
|
|
|
userIsInAccessGroup = True |
|
|
|
break |
|
|
|
|
|
|
|
if userIsInAccessGroup: |
|
|
|
logger.debug("LDAP: user is in access group") |
|
|
|
else: |
|
|
|
logger.debug("LDAP: user is NOT in access group") |
|
|
|
return "" |
|
|
|
else: |
|
|
|
logger.warning("LDAP: more than 1 user found") |
|
|
|
return "" |
|
|
|
else: |
|
|
|
logger.warning("LDAP: no user found") |
|
|
|
return "" |
|
|
|
|
|
|
|
logger.debug("LDAP: restart connection for user") |
|
|
|
## TODO: check for errors |
|
|
|
server = ldap3.Server(serverUrl) |
|
|
|
conn = ldap3.Connection(server, userDn, password) |
|
|
|
conn.bind() |
|
|
|
result = conn.result |
|
|
|
|
|
|
|
logger.debug("LDAP bind result: %s" % str(result)) |
|
|
|
|
|
|
|
if result['description'] == "invalidCredentials": |
|
|
|
logger.warning("LDAP: user credentials are invalid") |
|
|
|
return "" |
|
|
|
else: |
|
|
|
logger.info("LDAP: user successful verified") |
|
|
|
return userAttributeValue |