class PropertyEditForm(LDAPEditForm): """An edit form for LDAP properties. """ form_fields = FormFields(ILDAPPropertyConfiguration) label = _(u"Edit Property") description = _(u"Edit a LDAP property.") form_name = _(u"Configure property") fieldset = "schema"
class ServerEditForm(LDAPEditForm): """An edit form for LDAP servers. """ form_fields = FormFields(ILDAPServerConfiguration) label = _(u"Edit Server") description = _(u"Edit a LDAP or ActiveDirectory server.") form_name = _(u"Configure server") fieldset = "servers"
class ILDAPPropertyConfiguration(Interface): description = TextLine(title=_(u"label_property_description", default=u"Property description"), required=False) ldap_name = ASCIILine( title=_(u"label_ldap_property", default=u"LDAP property name"), description=_(u"help_ldap_property", default=u"The name of the property as used in the " "LDAP directory."), required=True) plone_name = ASCIILine( title=_(u"label_plone_property", default=u"Plone property name"), description=_( u"help_plone_property", default=u"The name of the property as used in the Plone site. " u"If no name is specified, the property will not be " u"visible in Plone but may still be used as the login " u"name, user id, or rDN."), required=False) multi_valued = Bool( title=_(u"label_multi_valued", default=u"Multi-valued property"), description=_( u"help_multi_valued", default=u"Select if this property can have multiple values."), required=True) binary = Bool( title=_(u"label_binary", default=u"Binary property"), description=_( u"help_binary", default=u"Select if this property can have binary values."), required=True)
class PropertyAddForm(LDAPAddForm): """An add form for LDAP properties. """ form_fields = FormFields(ILDAPPropertyConfiguration) label = _(u"Add Property") description = _(u"Add a LDAP property to the schema.") form_name = _(u"Configure property") fieldset = "schema" def create(self, data): property = LDAPProperty() applyChanges(property, self.form_fields, data) return property
class ILDAPServerConfiguration(Interface): """Configuration of an LDAP server. """ enabled = Bool( title=_(u"label_ldap_enabled", default=u"Active"), description=_(u"help_ldap_enabled", default=u"Use this LDAP server"), default=False, required=True) server = ASCIILine( title=_(u"label_ldap_server", default=u"Server"), description=_(u"help_ldap_server", default="The address or hostname of the LDAP server."), default="localhost", required=True) connection_type = Choice( title=_(u"label_ldap_connection_type", default=u"Connection type"), description=_(u"help_ldap_connection_type", default=u""), vocabulary="plone.app.ldap.engine.LDAPConnectionTypes", default=0, required=True) connection_timeout = Int( title=_(u"label_connection_timeout", default=u"Connection timeout"), description=_(u"help_connection_timeout", default=u"The timeout in seconds to wait for a connection to " "the LDAP server to be established."), default=5, min=-1, max=300, required=True) operation_timeout = Int( title=_(u"label_operation_timeout", default=u"Operation timeout"), description=_(u"help_operation_timeout", default=u"The timeout in seconds to wait for an operation such " "as a search or update to complete. If no timeout should be " "used use -1 as value."), default=-1, min=-1, max=300, required=True)
class ServerAddForm(LDAPAddForm): """An add form for LDAP servers. """ form_fields = FormFields(ILDAPServerConfiguration) label = _(u"Add Server") description = _(u"Add an new LDAP or ActiveDirectory server.") form_name = _(u"Configure server") fieldset = "servers" def create(self, data): server = LDAPServer() applyChanges(server, self.form_fields, data) notify(ObjectCreatedEvent(server)) return server
def handle_edit_actions(self, action, data): # Filter out non-required fields that have no value so their # existing value is not overwritten. This protects us from # overwriting the bind password. data = dict([(key, value) for (key, value) in data.iteritems() if value is not None]) if applyChanges(self.context, self.form_fields, data, self.adapters): try: notify(ObjectModifiedEvent(self.storage)) except LDAPError, e: widget = self.widgets.get("bind_dn") widget.error = WidgetInputError("bind_dn", widget.label, LDAPBindFailure("value")) self.errors += (widget.error,) self.status = _("There were errors")
class ILDAPConfiguration(ILDAPBinding): """LDAP configuration utility""" servers = Container(title=u"LDAP servers", description=u"List of LDAP servers that can be used.", required=True) schema = Container(title=u"LDAP schema", description=u"The LDAP schema contains information on used LDAP properties", required=True) activated_plugins = List(title=_(u"Activated plugins"), value_type=ASCIILine( title=u"interface",))
def handle_edit_actions(self, action, data): # Filter out non-required fields that have no value so their # existing value is not overwritten. This protects us from # overwriting the bind password. data = dict([(key, value) for (key, value) in data.iteritems() if value is not None]) if applyChanges(self.context, self.form_fields, data, self.adapters): try: notify(ObjectModifiedEvent(self.storage)) except LDAPError: widget = self.widgets.get("bind_dn") widget.error = WidgetInputError("bind_dn", widget.label, LDAPBindFailure("value")) self.errors += (widget.error, ) self.status = _("There were errors") return self.request.response.redirect(self.nextURL())
# Filter out non-required fields that have no value so their # existing value is not overwritten. This protects us from # overwriting the bind password. data = dict([(key, value) for (key, value) in data.iteritems() if value is not None]) if applyChanges(self.context, self.form_fields, data, self.adapters): try: notify(ObjectModifiedEvent(self.storage)) except LDAPError, e: widget = self.widgets.get("bind_dn") widget.error = WidgetInputError("bind_dn", widget.label, LDAPBindFailure("value")) self.errors += (widget.error,) self.status = _("There were errors") return self.request.response.redirect(self.nextURL()) @action(_(u"label_enable", default=u"Enable"), name=u"EnableServer") def handle_enable_server(self, action, data): for id in self.request.form.get("serverId", []): if id in self.storage.servers: server = self.storage.servers[id] if server.enabled == False: server.enabled = True notify(ObjectModifiedEvent(server)) return self.request.response.redirect(self.nextURL()) @action(_(u"label_disable", default=u"Disable"), name=u"DisableServer") def handle_disable_server(self, action, data): for id in self.request.form.get("serverId", []): if id in self.storage.servers: server = self.storage.servers[id] if server.enabled == True:
class ILDAPBinding(Interface): ldap_type = Choice( title=_(u"label_ldap_type", default=u"LDAP server type"), description=_( u"help_ldap_server_type", default=u"Plone supports both Active Directory and standard " u"LDAP servers. For Active Directory the read-only " u"LDAP interface which is enabled for all Active " u"Directory servers can be used."), vocabulary="plone.app.ldap.engine.LDAPServerTypes", default=u"LDAP", required=True) rdn_attribute = Choice( title=_(u"label_ldap_dn_attribute", default=u"rDN attribute"), description=_(u"help_ldap_dn_attribute", default=u"This is attribute is used to build the " u"distinguished name (DN) for users that are being " u"created in your LDAP directory. This is commonly " u"either the users full name ('cn' property) or the " u"userid ('uid' property)."), default="uid", vocabulary="plone.app.ldap.engine.LDAPSingleValueAttributes", required=True) userid_attribute = Choice( title=_(u"label_ldap_userid_attribute", default=u"User id attribute"), description=_( u"help_ldap_userid_attribute", default=u"This attribute is used as the userid inside Plone " u"for LDAP users. It has to be unique for all users."), default="uid", vocabulary="plone.app.ldap.engine.LDAPSingleValueAttributes", required=True) login_attribute = Choice( title=_(u"label_ldap_login_attribute", default=u"Login name attribute"), description=_( u"help_ldap_login_attribute", default=u"The attribute is used as the login name for LDAP " u"users logging into your site. In most cases this " u"should be the same as the user id attribute."), default="uid", vocabulary="plone.app.ldap.engine.LDAPSingleValueAttributes", required=True) user_object_classes = ASCIILine( title=_(u"label_ldap_user_object_classes", default=u"LDAP object classes"), description=_( u"help_ldap_user_object_classes", default=u"Each object in the LDAP database has a structural " u"object class and optionally several supplemental " u"object classes. These classes define the required " u"and optional properties that can be present on an " u"object. Classes can be entered in a comma seperated " u"list."), default="pilotPerson", required=True) bind_dn = ASCIILine( title=_(u"label_ldap_bind_dn", default=u"Bind DN"), description=_( u"help_ldap_bind_dn", default=u"The DN of a manager account in the LDAP directory. " u"This must be allowed to access all user and group " u"information as well as be able to update and create " u"users and groups. Please note that Plone only " u"supports simple binds. SASL is not supported."), required=False) bind_password = Password( title=_(u"label_ldap_bind_password", default=u"Bind password"), description=_( u"help_ldap_bind_password", default=u"Password to use when binding to the LDAP server."), required=False) user_base = ASCIILine( title=_(u"label_ldap_user_base", default=u"Base DN for users"), description=_( u"help_ldap_user_base", default=u"This is the location in your LDAP directory where " u"all users are stored."), required=True) user_scope = Choice( title=_(u"label_ldap_user_scope", default=u"Search scope for users"), description=_( u"help_ldap_user_scope", default=u"The search scope determines where the LDAP server " u"will search for users. With \"one level\" it will " u"only look for users directly in the user base " u"location; \"subtree\" will allow the server to also " u"look in subfolders of the user base location."), default=SCOPE_SUBTREE, vocabulary="plone.app.ldap.engine.LDAPScopes", required=True) group_base = ASCIILine( title=_(u"label_ldap_group_base", default=u"Base DN for groups"), description=_( u"help_ldap_group_base", default=u"This is the location in your LDAP directory where " u"all groups are stored. There are several options for " u"object class and members possible: the groupOfNames, " u"accessGroup or group object classes can be used with " u"members given in the member property, or the " u"groupOfUniqueNames object class can be used with " u"uniqueMember property. In Active Directory systems " u"only the group object class is supported."), required=True) group_scope = Choice( title=_(u"label_ldap_group_scope", default=u"Search scope for groups"), description=_( u"help_ldap_group_scope", default=u"The search scope determines where the LDAP server " u"will search for groups. With \"one level\" it will " u"only look for groups directly in the group base " u"location; \"subtree\" will allow the server to also " u"look in subfolders of the group base location."), default=SCOPE_SUBTREE, vocabulary="plone.app.ldap.engine.LDAPScopes", required=True) password_encryption = Choice( title=_(u"label_ldap_user_password_encryption", default=u"User password encryption"), description=_( u"help_ldap_user_password_encryption", default=u"Method of encryption used for user passwords."), default="crypt", vocabulary="plone.app.ldap.engine.LDAPPasswordEncryption", ) default_user_roles = ASCIILine( title=_(u"label_ldap_default_user_roles", default=u"Default user roles"), description=_(u"help_ldap_default_user_roles", default=u"Default roles for new users."), default="Member", required=True, ) read_only = Bool( title=_(u"label_ldap_read_only", default=u"Read Only"), description=_( u"help_ldap_read_only", default=u"Control whether Plone should attempt to modify " u"objects and properties on the server."), default=False, )
# overwriting the bind password. data = dict([(key,value) for (key,value) in data.iteritems() if value is not None]) if applyChanges(self.context, self.form_fields, data, self.adapters): try: notify(ObjectModifiedEvent(self.storage)) except LDAPError, e: widget=self.widgets.get("bind_dn") widget.error=WidgetInputError("bind_dn", widget.label, LDAPBindFailure("value")) self.errors += (widget.error,) self.status= _("There were errors") return self.request.response.redirect(self.nextURL()) @action(_(u'label_enable', default=u'Enable'), name=u'EnableServer') def handle_enable_server(self, action, data): for id in self.request.form.get("serverId", []): if id in self.storage.servers: server = self.storage.servers[id] if server.enabled == False: server.enabled = True notify(ObjectModifiedEvent(server)) return self.request.response.redirect(self.nextURL()) @action(_(u'label_disable', default=u'Disable'), name=u'DisableServer') def handle_disable_server(self, action, data): for id in self.request.form.get("serverId", []): if id in self.storage.servers: server = self.storage.servers[id] if server.enabled == True:
class LDAPControlPanel(EditForm): template = ViewPageTemplateFile("controlpanel.pt") form_fields = FormFields(ILDAPBinding) label = u"LDAP Control Panel" description = u"In this control panel you can configure an LDAP connection. You can either use a standard LDAP server or a Microsoft Active Directory Server." form_name = u"LDAP Control Panel" @action(_("Apply"), condition=haveInputWidgets) def handle_edit_actions(self, action, data): # Filter out non-required fields that have no value so their # existing value is not overwritten. This protects us from # overwriting the bind password. data = dict([(key, value) for (key, value) in data.iteritems() if value is not None]) if applyChanges(self.context, self.form_fields, data, self.adapters): try: notify(ObjectModifiedEvent(self.storage)) except LDAPError: widget = self.widgets.get("bind_dn") widget.error = WidgetInputError("bind_dn", widget.label, LDAPBindFailure("value")) self.errors += (widget.error, ) self.status = _("There were errors") return self.request.response.redirect(self.nextURL()) @action(_(u'label_enable', default=u'Enable'), name=u'EnableServer') def handle_enable_server(self, action, data): for id in self.request.form.get("serverId", []): if id in self.storage.servers: server = self.storage.servers[id] if server.enabled is False: server.enabled = True notify(ObjectModifiedEvent(server)) return self.request.response.redirect(self.nextURL()) @action(_(u'label_disable', default=u'Disable'), name=u'DisableServer') def handle_disable_server(self, action, data): for id in self.request.form.get("serverId", []): if id in self.storage.servers: server = self.storage.servers[id] if server.enabled is True: server.enabled = False notify(ObjectModifiedEvent(server)) return self.request.response.redirect(self.nextURL()) @action(_(u'label_delete', default=u'Delete'), name=u'DeleteServer') def handle_delete_server(self, action, data): for id in self.request.form.get("serverId", []): if id in self.storage.servers: notify(ObjectRemovedEvent(self.storage.servers[id])) del self.storage.servers[id] return self.request.response.redirect(self.nextURL()) @action(_(u'label_delete_property', default=u'Delete'), name=u'DeleteProperty') def handle_delete_property(self, action, data): for id in self.request.form.get("propertyId", []): if id in self.storage.schema: notify(ObjectRemovedEvent(self.storage.schema[id])) del self.storage.schema[id] return self.request.response.redirect(self.nextURL()) @action(_(u'label_purge', default=u'Purge'), name=u'Purge') def handle_cache_purge(self, action, data): luf = getLDAPPlugin()._getLDAPUserFolder() luf.manage_reinit() self.status = 'User caches cleared' return self.request.response.redirect(self.nextURL()) @action(_(u'label_update_cache_timeouts', default=u'Update Cache Timeouts'), name=u'UpdateCacheTimeouts') def handle_update_cache_timeouts(self, action, data): luf = getLDAPPlugin()._getLDAPUserFolder() for cache_type, cache_value_name in [ ('authenticated', 'auth_cache_seconds'), ('anonymous', 'anon_cache_seconds'), ('negative', 'negative_cache_seconds'), ]: cache_value = self.request.form['form.' + cache_value_name] try: cache_value = int(cache_value) except ValueError: continue if cache_value != getattr(self, cache_value_name, None): luf.setCacheTimeout(cache_type=cache_type, timeout=cache_value) self.status = 'Cache timeout changed' return self.request.response.redirect(self.nextURL()) # cache properties delegate to the LDAPUserFolder instance def get_auth_cache_seconds(self): try: luf = getLDAPPlugin()._getLDAPUserFolder() except KeyError: return 600 return luf.getCacheTimeout('authenticated') def set_auth_cache_seconds(self, value): luf = getLDAPPlugin()._getLDAPUserFolder() luf.setCacheTimeout(cache_type='authenticated', timeout=value) auth_cache_seconds = property(get_auth_cache_seconds, set_auth_cache_seconds) def get_anon_cache_seconds(self): try: luf = getLDAPPlugin()._getLDAPUserFolder() except KeyError: return 600 return luf.getCacheTimeout('anonymous') def set_anon_cache_seconds(self, value): luf = getLDAPPlugin()._getLDAPUserFolder() luf.setCacheTimeout(cache_type='anonymous', timeout=value) anon_cache_seconds = property(get_anon_cache_seconds, set_anon_cache_seconds) def get_negative_cache_seconds(self): try: luf = getLDAPPlugin()._getLDAPUserFolder() except KeyError: return 600 return luf.getCacheTimeout('negative') def set_negative_cache_seconds(self, value): luf = getLDAPPlugin()._getLDAPUserFolder() luf.setCacheTimeout(cache_type='negative', timeout=value) negative_cache_seconds = property(get_negative_cache_seconds, set_negative_cache_seconds) def anon_cache(self): try: luf = getLDAPPlugin()._getLDAPUserFolder() except KeyError: return [] users = luf.getUsers(authenticated=0) for user in users: user.cache_type = 'anonymous' return users def auth_cache(self): try: luf = getLDAPPlugin()._getLDAPUserFolder() except KeyError: return [] users = luf.getUsers(authenticated=1) for user in users: user.cache_type = 'authenticated' return users def nextURL(self): url = str( getMultiAdapter((self.context, self.request), name=u"absolute_url")) return url + "/@@ldap-controlpanel#" + self.request.form.get( 'fieldset_id', '') @property @memoize def storage(self): return getUtility(ILDAPConfiguration) def servers(self): def contype(c): if c == 0: return "LDAP" elif c == 1: return "LDAP over SSL" else: return "LDAP over IPC" return [ dict(id=s.__name__, enabled=s.enabled, server=s.server, connection_type=contype(s.connection_type), connection_timeout=s.connection_timeout, operation_timeout=s.operation_timeout) for s in self.storage.servers.values() ] def schema(self): storage = self.storage def protected(attr): return attr.__name__ in (storage.rdn_attribute, storage.userid_attribute, storage.login_attribute) return [ dict(id=p.__name__, description=p.description, ldap_name=p.ldap_name, plone_name=p.plone_name, multi_valued=p.multi_valued, binary=p.binary, protected=protected(p)) for p in storage.schema.values() ]
class LDAPBindFailure(ValidationError): __doc__ = _(u"LDAP server refused your credentials")