Author: adc
Date: Thu Sep 12 15:38:22 2013
New Revision: 1522628
URL: http://svn.apache.org/r1522628
Log:
Handy LDAP data
Added:
labs/panopticon/src/asf/data/ldap.py
labs/panopticon/tests/test_ldap.py
Modified:
labs/panopticon/requirements.txt
Modified: labs/panopticon/requirements.txt
URL:
http://svn.apache.org/viewvc/labs/panopticon/requirements.txt?rev=1522628&r1=1522627&r2=1522628&view=diff
==============================================================================
--- labs/panopticon/requirements.txt (original)
+++ labs/panopticon/requirements.txt Thu Sep 12 15:38:22 2013
@@ -3,4 +3,5 @@ brownie
keyring==1.6.1
mock
nose
+python-ldap
restkit
Added: labs/panopticon/src/asf/data/ldap.py
URL:
http://svn.apache.org/viewvc/labs/panopticon/src/asf/data/ldap.py?rev=1522628&view=auto
==============================================================================
--- labs/panopticon/src/asf/data/ldap.py (added)
+++ labs/panopticon/src/asf/data/ldap.py Thu Sep 12 15:38:22 2013
@@ -0,0 +1,172 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+"""
+ LDAP Access
+"""
+
+from __future__ import absolute_import
+from logging import getLogger
+import os
+
+import ldap
+
+
+LDAP_URL = 'ldaps://ldap-tunnel.apache.org:6636'
+LDAP_TLS_CACERTFILE = '/etc/openldap/asf-ldap-client.pem'
+LDAP_BASE = 'ou=people,dc=apache,dc=org'
+
+log = getLogger(__name__)
+
+
+class LDAP(object):
+ """
+ Read-only LDAP access to ASF's Active Directory Server.
+
+ Example::
+
+ ldap = asf.data.ldap.LDAP()
+
+ res = ldap.find_by_common_name('Alan Cabrera')
+ res = ldap.find_by_username('adc')
+
+ if ldap.is_valid_account('adc'):
+ # Ok!
+ ...
+ """
+
+ def __init__(self, LDAP_URL=None, TLS_CA_cert_file=None):
+ """
+ Connect to ASF Active Directory Server.
+
+ :param LDAP_URL: LDAP URL to use to connect to ASF Active Directory
Server
+ :param TLS_CA_cert_file: TLS CA certificate file to use to connect
to ASF Active Directory Server
+ """
+ self.base = LDAP_BASE
+ self.ldap = _ldap_cnx(LDAP_URL, TLS_CA_cert_file)
+ self.ldap.simple_bind_s()
+
+
+ def search(self, search_filter, attributes=None):
+ """
+ Perform a search against the LDAP server.
+
+ :param search_filter: An LDAP search filter.
+ :param attributes: LDAP attributes to return. Defaults to all ('*').
+
+ :return: Returns a list of dicts or None if no results were found.
+ """
+
+ if isinstance(attributes, str):
+ attributes = [attributes]
+
+ if attributes is None:
+ attributes = ['*']
+
+ # With a little help for my friends..
+ if not search_filter.startswith('(') and not
search_filter.endswith(')'):
+ search_filter = '(%s)' % search_filter
+
+ res = self.ldap.search_s(self.base, ldap.SCOPE_ONELEVEL,
search_filter, attributes)
+
+ if res == []:
+ return None
+
+ if res[0][0] is None:
+ return None
+
+ return res
+
+ def search_first(self, search_filter, attributes=None):
+ """
+ Perform a search against the LDAP server & return the first result
or None.
+
+ :param search_filter: An LDAP search filter.
+ :param attributes: LDAP attributes to return. Defaults to all ('*').
+ """
+
+ res = self.search(search_filter, attributes=attributes)
+
+ if res is None:
+ return None
+
+ if attributes and isinstance(attributes, str):
+ return res[0][1][attributes][0]
+
+ return res[0][1]
+
+ def find_by_common_name(self, name, attributes=None):
+ """
+ Find an LDAP entry by a user's "common" (full) name.
+
+ :param name: The user's full name.
+ :param attributes: LDAP attributes to return. Defaults to all ('*').
+ """
+
+ return self.search_first('(cn=%s)' % name, attributes)
+
+ def find_by_username(self, username, attributes=None):
+ """
+ Find an LDAP entry by a user's login name.
+
+ :param username: The user's full name.
+ :param attributes: LDAP attributes to return. Defaults to all ('*').
+ """
+
+ return self.search_first('(uid=%s)' % username, attributes)
+
+ def is_valid_account(self, username):
+ """ Check if the username is valid in the directory. Returns `True` or
`False`. """
+ res = self.search_first('(uid=%s)' % username, 'uid')
+
+ if res and res == username:
+ return True
+
+ return False
+
+
+def cacert_file():
+ """ Return the path to the CA Cert file to validate the TLS connection. """
+ tls_cacert_file = os.environ.get('PANOPTICON_LDAP_TLS_CACERTFILE',
LDAP_TLS_CACERTFILE)
+
+ log.debug('Obtained %s for LDAP TLS certificate file', tls_cacert_file)
+
+ return tls_cacert_file
+
+
+def ldap_url():
+ """ Return the path to the CA Cert file to validate the TLS connection. """
+ url = os.environ.get('PANOPTICON_LDAP_URL', LDAP_URL)
+
+ log.debug('Obtained %s for LDAP URL', url)
+
+ return url
+
+
+def _ldap_cnx(LDAP_URL=None, TLS_CA_cert_file=None):
+ """ Gets an LDAP connection and makes sure the cert file option is set. """
+ TLS_CA_cert_file = TLS_CA_cert_file or cacert_file()
+ ldap.set_option(ldap.OPT_X_TLS_CACERTFILE, TLS_CA_cert_file)
+
+ LDAP_URL = LDAP_URL or ldap_url()
+
+ cnx = ldap.initialize(LDAP_URL)
+ cnx.set_option(ldap.OPT_REFERRALS, 0)
+
+ return cnx
Added: labs/panopticon/tests/test_ldap.py
URL:
http://svn.apache.org/viewvc/labs/panopticon/tests/test_ldap.py?rev=1522628&view=auto
==============================================================================
--- labs/panopticon/tests/test_ldap.py (added)
+++ labs/panopticon/tests/test_ldap.py Thu Sep 12 15:38:22 2013
@@ -0,0 +1,56 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+from nose.tools import assert_equals, assert_raises
+
+from asf.data.ldap import LDAP
+
+
+def test_find_by_username():
+ ldap = LDAP()
+
+ result = ldap.find_by_username('adc')
+
+ for k, v in result.iteritems():
+ print k, v
+
+ assert_equals(['adc'], result['uid'])
+ assert_equals(['Alan Cabrera'], result['cn'])
+
+ assert ldap.is_valid_account('adc')
+
+
+def test_is_valid_account():
+ ldap = LDAP()
+
+ assert ldap.is_valid_account('adc')
+
+
+def test_find_by_common_name():
+ ldap = LDAP()
+
+ result = ldap.find_by_common_name('Alan Cabrera')
+
+ for k, v in result.iteritems():
+ print k, v
+
+ assert_equals(['adc'], result['uid'])
+ assert_equals(['Alan Cabrera'], result['cn'])
+
+ assert ldap.is_valid_account('adc')
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]