diff --git a/CHANGES.rst b/CHANGES.rst
index 715e307..e3e2713 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -25,10 +25,6 @@ History
- Remove five.globalrequest dependency.
[cillianderoiste]
-
-1.9.0 (unreleased)
-------------------
-
- Added Spanish translation #132
[macagua]
@@ -38,6 +34,19 @@ History
- Drop support for Plone 5/ Python < 3.9
[jensens]
+- Default user roles configuration implementation.
+ [sauzher]
+
+
+1.8.3 (2024-11-13)
+------------------
+
+- Add uninstall profile
+ [dumitval]
+- Fix: use exact_match for searchUsers/searchGroups in getRolesForPrincipal/getPropertiesForUser
+ to avoid unexpected results
+ [mamico]
+
1.8.2 (2022-10-31)
------------------
diff --git a/src/pas/plugins/ldap/configure.zcml b/src/pas/plugins/ldap/configure.zcml
index 6792929..eaab756 100644
--- a/src/pas/plugins/ldap/configure.zcml
+++ b/src/pas/plugins/ldap/configure.zcml
@@ -8,12 +8,7 @@
i18n_domain="pas.plugins.ldap"
>
-
-
-
+
diff --git a/src/pas/plugins/ldap/defaults.py b/src/pas/plugins/ldap/defaults.py
index 425fe48..a069785 100644
--- a/src/pas/plugins/ldap/defaults.py
+++ b/src/pas/plugins/ldap/defaults.py
@@ -7,7 +7,12 @@
"server.password": "secret",
"server.ignore_cert": False,
"server.start_tls": False,
+ "server.tls_cacertfile": None,
+ "server.tls_cacertdir": None,
+ "server.tls_clcertfile": None,
+ "server.tls_clkeyfile": None,
"server.page_size": 1000,
+ "server.roles": ["Member"],
"server.conn_timeout": 5,
"server.op_timeout": 600,
"cache.cache": False,
diff --git a/src/pas/plugins/ldap/plonecontrolpanel/configure.zcml b/src/pas/plugins/ldap/plonecontrolpanel/configure.zcml
index 31b6f13..f4dba7a 100644
--- a/src/pas/plugins/ldap/plonecontrolpanel/configure.zcml
+++ b/src/pas/plugins/ldap/plonecontrolpanel/configure.zcml
@@ -18,6 +18,14 @@
directory="profiles/default"
/>
+
+
+
diff --git a/src/pas/plugins/ldap/plonecontrolpanel/profiles/uninstall/registry.xml b/src/pas/plugins/ldap/plonecontrolpanel/profiles/uninstall/registry.xml
new file mode 100644
index 0000000..7e43441
--- /dev/null
+++ b/src/pas/plugins/ldap/plonecontrolpanel/profiles/uninstall/registry.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/pas/plugins/ldap/plonecontrolpanel/setuphandlers.py b/src/pas/plugins/ldap/plonecontrolpanel/setuphandlers.py
new file mode 100644
index 0000000..75c6996
--- /dev/null
+++ b/src/pas/plugins/ldap/plonecontrolpanel/setuphandlers.py
@@ -0,0 +1,39 @@
+# -*- coding: utf-8 -*-
+from zope.component.hooks import getSite
+from pas.plugins.ldap.plugin import LDAPPlugin
+import logging
+
+
+logger = logging.getLogger(__name__)
+
+
+TITLE = "LDAP plugin (pas.plugins.ldap)"
+
+
+def _removePlugin(pas, PLUGIN_ID="pasldap"):
+ installed = pas.objectIds()
+ if PLUGIN_ID not in installed:
+ return TITLE + " already uninstalled."
+ plugin = getattr(pas, PLUGIN_ID)
+ if not isinstance(plugin, LDAPPlugin):
+ logger.warning(
+ "Uninstall aborted. PAS plugin %s is not an LDAPPlugin.",
+ PLUGIN_ID)
+ for info in pas.plugins.listPluginTypeInfo():
+ interface = info["interface"]
+ if not interface.providedBy(plugin):
+ continue
+ try:
+ pas.plugins.deactivatePlugin(interface, plugin.getId())
+ except KeyError:
+ # the plugin was not active
+ pass
+ pas._delObject(PLUGIN_ID)
+ logger.info("Removed LDAPPlugin %s from acl_users.", PLUGIN_ID)
+
+
+def uninstall(context):
+ site = getSite()
+ pas = site.acl_users
+ _removePlugin(pas)
+
\ No newline at end of file
diff --git a/src/pas/plugins/ldap/plugin.py b/src/pas/plugins/ldap/plugin.py
index bc4488b..8385f9c 100644
--- a/src/pas/plugins/ldap/plugin.py
+++ b/src/pas/plugins/ldap/plugin.py
@@ -224,7 +224,7 @@ def authenticateCredentials(self, credentials):
pw = credentials.get("password")
if not (login and pw):
return default
- logger.debug("credentials: %s" % credentials)
+ logger.debug("login: %s" % login)
users = self.users
if not users:
return default
@@ -442,8 +442,8 @@ def getRolesForPrincipal(self, principal, request=None):
users = self.users
if not users:
return default
- if self.enumerateUsers(id=principal.getId()):
- return ("Member",)
+ if self.enumerateUsers(id=principal.getId(), exact_match=True):
+ return tuple(set(self._ldap_props.roles))
return default
@security.private
@@ -564,7 +564,9 @@ def getPropertiesForUser(self, user_or_group, request=None):
if not isinstance(ugid, str):
ugid = ugid.decode("utf-8")
try:
- if self.enumerateUsers(id=ugid) or self.enumerateGroups(id=ugid):
+ if self.enumerateUsers(id=ugid, exact_match=True) or self.enumerateGroups(
+ id=ugid, exact_match=True
+ ):
return LDAPUserPropertySheet(user_or_group, self)
except KeyError:
pass
@@ -690,7 +692,6 @@ def getGroupById(self, group_id):
for propfinder_id, propfinder in plugins.listPlugins(
pas_interfaces.IPropertiesPlugin
):
-
data = propfinder.getPropertiesForUser(group, None)
if not data:
continue
@@ -699,7 +700,6 @@ def getGroupById(self, group_id):
group._addGroups(pas._getGroupsForPrincipal(group, None, plugins=plugins))
# add roles
for rolemaker_id, rolemaker in plugins.listPlugins(pas_interfaces.IRolesPlugin):
-
roles = rolemaker.getRolesForPrincipal(group, None)
if not roles:
continue
diff --git a/src/pas/plugins/ldap/properties.py b/src/pas/plugins/ldap/properties.py
index 4e3cfb3..b50b2ab 100644
--- a/src/pas/plugins/ldap/properties.py
+++ b/src/pas/plugins/ldap/properties.py
@@ -102,6 +102,7 @@ def fetch(name, default=UNSET):
return val
props.uri = fetch("server.uri")
+
if not fetch("server.anonymous"):
props.user = fetch("server.user")
password = fetch("server.password")
@@ -111,12 +112,12 @@ def fetch(name, default=UNSET):
props.user = ""
props.password = ""
props.ignore_cert = fetch("server.ignore_cert")
+ props.start_tls = fetch('server.start_tls')
+ props.tls_cacertfile = fetch('server.tls_cacertfile')
+ props.tls_cacertdir = fetch('server.tls_cacertdir')
+ props.tls_clcertfile = fetch('server.tls_clcertfile')
+ props.tls_clkeyfile = fetch('server.tls_clkeyfile')
# TODO: later
- # props.start_tls = fetch('server.start_tls')
- # props.tls_cacertfile = fetch('server.tls_cacertfile')
- # props.tls_cacertdir = fetch('server.tls_cacertdir')
- # props.tls_clcertfile = fetch('server.tls_clcertfile')
- # props.tls_clkeyfile = fetch('server.tls_clkeyfile')
# props.retry_max = fetch(at('server.retry_max')
# props.retry_delay = fetch('server.retry_delay')
props.page_size = fetch("server.page_size")
@@ -125,8 +126,11 @@ def fetch(name, default=UNSET):
props.cache = fetch("cache.cache")
props.memcached = fetch("cache.memcached")
props.timeout = fetch("cache.timeout")
+
+ props.roles = fetch("users.roles") # a server wide variable, but related to user
+
users.baseDN = fetch("users.dn")
- # build attrmap from static keys and dynamic keys inputs
+ # build attrmap from static keys and dynamic keys inputs
users.attrmap = odict()
users.attrmap.update(fetch("users.aliases_attrmap"))
users_propsheet_attrmap = fetch("users.propsheet_attrmap")
@@ -242,18 +246,21 @@ def __init__(self, plugin):
self.plugin = plugin
# XXX: Later
- tls_cacertfile = ""
- tls_cacertdir = ""
- tls_clcertfile = ""
- tls_clkeyfile = ""
retry_max = 3
retry_delay = 5
uri = propproxy("server.uri")
user = propproxy("server.user")
+ roles = propproxy("server.roles")
password = propproxy("server.password")
start_tls = propproxy("server.start_tls")
ignore_cert = propproxy("server.ignore_cert")
+ start_tls = propproxy("server.start_tls")
+ tls_cacertfile = propproxy("server.tls_cacertfile")
+ tls_cacertdir = propproxy("server.tls_cacertdir")
+ tls_clcertfile = propproxy("server.tls_clcertfile")
+ tls_clkeyfile = propproxy("server.tls_clkeyfile")
+
page_size = propproxy("server.page_size")
conn_timeout = propproxy("server.conn_timeout")
op_timeout = propproxy("server.op_timeout")
diff --git a/src/pas/plugins/ldap/properties.yaml b/src/pas/plugins/ldap/properties.yaml
index 4ed979d..131297f 100644
--- a/src/pas/plugins/ldap/properties.yaml
+++ b/src/pas/plugins/ldap/properties.yaml
@@ -54,6 +54,36 @@ widgets:
props:
label: i18n:lbl_ignore_certificate_check:Ignore certificate check?
help: i18n:help_ignore_certificate_check:If set on authenticate a failing certificate chain check including CA is ignored.
+ - start_tls:
+ factory: '#field:checkbox'
+ value: expr:context.props.start_tls
+ props:
+ label: Use TLS connection
+ help: If set, the connection is upgraded to TLS.
+ - tls_cacertfile:
+ factory: '#field:text'
+ value: expr:context.props.tls_cacertfile
+ props:
+ label: Path to CA certificate file for TLS communication (OPT_X_TLS_CACERTFILE)
+ help: If set, the LDAP server certificate is checked against the CA certificate file.
+ - tls_cacertdir:
+ factory: '#field:text'
+ value: expr:context.props.tls_cacertdir
+ props:
+ label: Path to folder with CA certificate files for TLS communication (OPT_X_TLS_CACERTDIR)
+ help: If set, the LDAP server certificate is checked against the CA certificates in folder.
+ - tls_clcertfile:
+ factory: '#field:text'
+ value: expr:context.props.tls_clcertfile
+ props:
+ label: Path to client certificate file for TLS communication (OPT_X_TLS_CERTFILE). Requires tls_clkeyfile
+ help: If set, the client certificate is sent to the server.
+ - tls_clkeyfile:
+ factory: '#field:text'
+ value: expr:context.props.tls_clkeyfile
+ props:
+ label: Path to client certificate key for TLS communication (OPT_X_TLS_KEYFILE). Requires tls_clcertfile
+ help: If set, the client certificate is sent to the server.
- page_size:
factory: '#field:number'
value: expr:context.props.page_size
@@ -100,6 +130,15 @@ widgets:
value: expr:context.users.memberOfSupport
props:
label: i18n:lbl_memberOf_attribute_supported:memberOf attribute supported?
+ - roles:
+ factory: '#array'
+ value: expr:context.props.roles
+ props:
+ label: Roles aquired
+ array.label: Roles acquired
+ widgets:
+ - roles:
+ factory: field:text
- recursiveGroups:
factory: '#field:checkbox'
value: expr:context.users.recursiveGroups
diff --git a/tests/testing.py b/tests/testing.py
index f7ea0a8..d0f92ca 100644
--- a/tests/testing.py
+++ b/tests/testing.py
@@ -34,6 +34,7 @@ def ldapprops(context):
props.uri = ldaptesting.props.uri
props.user = ldaptesting.props.user
+ props.roles = ldaptesting.props.roles
props.password = ldaptesting.props.password
props.cache = ldaptesting.props.cache
props.page_size = ldaptesting.props.page_size