Dear All,

I am running a production server based on invenio 1.0.1, and a demo
server based on 1.1 maintenance or master (switching via git branch).


I have been following all of the LDAP related mail exchange and the
wiki, but I still have a problem in all my setups concerning the
external group import.

My (manually created local) users can now log in using their domain
password, and show up as external users in the corresponding table in
invenio master. All the AD groups they belong to are shown correctly
in the memberOf section in their account settings.


My goal would to generate fireroles for the external users ultimately
based on their AD group membership.

Problems:
If I use the "simplistic configuration" the "groups page" link only
shows the previously created local groups.

If I use the original fetch_user_groups_membership/ _get_groups
supplied in external_authentication_ldap.py I get an exception telling
me indices need to be integers, not strings:

Hello:

It is the first time this exception has been seen.


* 2013-11-06 17:23:46 -> TypeError: list indices must be integers, not
str
(external_authentication_ldap_mps_groupsusedexample.py:241:_get_groups)

** User details
                                     agent: Mozilla/5.0 (X11; Ubuntu;
Linux x86_64; rv:25.0) Gecko/20100101 Firefox/25.0
                                     email: guest
                                     group: []
                                     guest: 1
                                  nickname:
            precached_canseehiddenmarctags: False
precached_permitted_restricted_collections: []
                    precached_sendcomments: False
                        precached_useadmin: False
                       precached_usealerts: False
                      precached_useapprove: False
                      precached_usebaskets: False
                       precached_usegroups: False
                        precached_useloans: False
                     precached_usemessages: False
             precached_usepaperattribution: False
                   precached_usepaperclaim: False
                        precached_usestats: False
                   precached_viewclaimlink: False
                 precached_viewsubmissions: False
                                   referer:
<https://buettner.demo2/youraccount/login?ln=en&referer=http%3A//buettner.demo2/%3F>
                               remote_host:
                                 remote_ip: 134.76.235.30
                                   session:
9b69f922a61bb6bca8783573c5655082
                                       uid: 0
                                       uri: </youraccount/login?>

** Traceback details

Traceback (most recent call last):
  File "/usr/local/lib/python2.7/dist-packages/invenio/webuser.py",
line 695, in loginUser
    groups =
CFG_EXTERNAL_AUTHENTICATION[login_method].fetch_user_groups_membership(p_email,
p_pw, req)
  File
"/usr/local/lib/python2.7/dist-packages/invenio/external_authentication_ldap_mps_groupsusedexample.py",
line 246, in fetch_user_groups_membership
    return self._ldap_try(_get_groups)
  File
"/usr/local/lib/python2.7/dist-packages/invenio/external_authentication_ldap_mps_groupsusedexample.py",
line 126, in _ldap_try
    return command(connection)
  File
"/usr/local/lib/python2.7/dist-packages/invenio/external_authentication_ldap_mps_groupsusedexample.py",
line 241, in _get_groups
    group_name = group_infos[CFG_EXTERNAL_AUTH_LDAP_GROUP_NAME][0]
TypeError: list indices must be integers, not str

** Stack frame details

Frame _get_groups in
/usr/local/lib/python2.7/dist-packages/invenio/external_authentication_ldap_mps_groupsusedexample.py
at line 241
-------------------------------------------------------------------------------
       238                                                  query_group)
       239                 if len(ldap_group):
       240                     group_dn, group_infos = ldap_group[0]
---->  241                     group_name =
group_infos[CFG_EXTERNAL_AUTH_LDAP_GROUP_NAME][0]
       242                     if group_name in
CFG_EXTERNAL_AUTH_LDAP_HIDDEN_GROUPS:
       243                         continue
       244                     groups[group_id] = group_name
-------------------------------------------------------------------------------
                 group_infos =
"['ldap://DomainDnsZones.pc.linmpi.mpg.de/DC=DomainDnsZones,DC=pc,DC=linmpi,DC=mpg,DC=de']"
                   group_ids =
"['CN=.osiweb-assistants,OU=OD-Groups,OU=OsirisDAWN,DC=pc,DC=linmpi,DC=mpg,DC=de',
'CN=admin_CVS_write,OU=OD-Groups,OU=OsirisDAWN,DC=pc,DC=linmpi,DC=mpg,DC=de',
'CN=.osiweb-admin,OU=OD-Groups,OU=OsirisDAWN,DC=pc,DC=linmpi,DC=mpg,DC=de',
'CN=dods-user,OU=OsirisDAWN,DC=pc,DC=linmpi,DC=mpg,DC=de',
'CN=.osi-wiki,OU=OD-Groups,OU=OsirisDAWN,DC=pc,DC=linmpi,DC=mpg,DC=de',
'CN=.EJSM-HRC.Read,OU=MPAe-PC,DC=pc,DC=linmpi,DC=mpg,DC=de',
'CN=.DAWN-Archive-Write,OU=OD-Groups,OU=OsirisDAWN,DC=pc,DC=linmpi,DC=m
[...]
                      groups =  '{}'
                     user_dn =  "'CN=Buettner\\\\,
Irene,OU=MPAe-PC,DC=pc,DC=linmpi,DC=mpg,DC=de'"
                query_person =
"'(|([email protected])([email protected])([email protected])([email protected]))'"
                   user_info =  "{'comment': ['MPAeler
/SCRIPTPATH:logon.bat'], 'mailNickname': ['buettner'],
'primaryGroupID': ['513'], 'logonCount': ['5281'], 'cn': ['Buettner,
Irene'], 'countryCode': <*****>, 'dSCorePropagationData':
['16010101000000.0Z'], 'objectClass': ['top', 'person',
'organizationalPerson', 'user'], 'userPrincipalName':
['[email protected]'], 'lastLogonTimestamp':
['130274159143120716'], 'msExchRecipientDisplayType': ['1073741824'],
'homeMDB': ['CN=MBX1-DB3,CN=Databases,CN=Exchange Administrative [...]
                  connection =  '<ldap.ldapobject.SimpleLDAPObject instance
at 0x7f5775b359e0>'
                 query_group =
"'(uidNumber=CN=.osiweb-assistants,OU=OD-Groups,OU=OsirisDAWN,DC=pc,DC=linmpi,DC=mpg,DC=de)'"
                    group_dn =  'None'
                    group_id =
"'CN=.osiweb-assistants,OU=OD-Groups,OU=OsirisDAWN,DC=pc,DC=linmpi,DC=mpg,DC=de'"
                  ldap_group =  "[(None,
['ldap://DomainDnsZones.pc.linmpi.mpg.de/DC=DomainDnsZones,DC=pc,DC=linmpi,DC=mpg,DC=de']),
(None,
['ldap://ForestDnsZones.pc.linmpi.mpg.de/DC=ForestDnsZones,DC=pc,DC=linmpi,DC=mpg,DC=de']),
(None,
['ldap://pc.linmpi.mpg.de/CN=Configuration,DC=pc,DC=linmpi,DC=mpg,DC=de'])]"
                       users =  "[('CN=Buettner\\\\,
Irene,OU=MPAe-PC,DC=pc,DC=linmpi,DC=mpg,DC=de', {'comment': ['MPAeler
/SCRIPTPATH:logon.bat'], 'mailNickname': ['buettner'],
'primaryGroupID': ['513'], 'logonCount': ['5281'], 'cn': ['Buettner,
Irene'], 'countryCode': <*****>, 'dSCorePropagationData':
['16010101000000.0Z'], 'objectClass': ['top', 'person',
'organizationalPerson', 'user'], 'userPrincipalName':
['[email protected]'], 'lastLogonTimestamp':
['130274159143120716'], 'msExchRecipientDisplayType': ['1073741824 [...]

Frame _ldap_try in
/usr/local/lib/python2.7/dist-packages/invenio/external_authentication_ldap_mps_groupsusedexample.py
at line 128
-------------------------------------------------------------------------------
       125
       126                 return command(connection)
       127             except ldap.SERVER_DOWN, error_message:
---->  128                 continue
       129         raise InvenioWebAccessExternalAuthError
       130
       131
-------------------------------------------------------------------------------
                  connection =  '<ldap.ldapobject.SimpleLDAPObject instance
at 0x7f5775b359e0>'
                     command =  '<function _get_groups at 0x7f57779422a8>'
                        self =
'<invenio.external_authentication_ldap_mps_groupsusedexample.ExternalAuthLDAP
instance at 0x7f57759615f0>'
                      server =  "'pc.linmpi.mpg.de'"

Frame fetch_user_groups_membership in
/usr/local/lib/python2.7/dist-packages/invenio/external_authentication_ldap_mps_groupsusedexample.py
at line 246
-------------------------------------------------------------------------------
       243                         continue
       244                     groups[group_id] = group_name
       245             return groups
---->  246         return self._ldap_try(_get_groups)
       247
       248     def fetch_user_preferences(self, username,
password=None, req=None):
       249         """Given a username and a password, returns a
dictionary of keys and
-------------------------------------------------------------------------------
                    username =  "'[email protected]'"
                 _get_groups =  '<function _get_groups at 0x7f57779422a8>'
                        self =
'<invenio.external_authentication_ldap_mps_groupsusedexample.ExternalAuthLDAP
instance at 0x7f57759615f0>'
                         req =
'<invenio.webinterface_handler_wsgi.SimulatedModPythonRequest object
at 0x7f5777940950>'
                query_person =
"'(|([email protected])([email protected])([email protected])([email protected]))'"
                      attrib =  "'mail'"
                    password =  "'<*****>'"

Frame loginUser in
/usr/local/lib/python2.7/dist-packages/invenio/webuser.py at line 704
-------------------------------------------------------------------------------
       701             except (AttributeError, NotImplementedError):
       702                 pass
       703             except:
---->  704                 register_exception(req=req, alert_admin=True)
       705                 return (None, p_email, p_pw, 16)
       706             ### ib 2013 should not depend on nickname TBC
else: # Groups synchronization
       707             if groups:
-------------------------------------------------------------------------------
                        p_un =  "'buettner'"
                     id_user =  '9L'
                login_method =  "'LDAP'"
                        p_pw =  "'<*****>'"
                   old_email =  "'[email protected]'"
                     p_extid =  "'CN=Buettner\\\\,
Irene,OU=MPAe-PC,DC=pc,DC=linmpi,DC=mpg,DC=de'"
                         req =
'<invenio.webinterface_handler_wsgi.SimulatedModPythonRequest object
at 0x7f5777940950>'
                     p_email =  "'[email protected]'"
                      result =  "('[email protected]', 'CN=Buettner\\\\,
Irene,OU=MPAe-PC,DC=pc,DC=linmpi,DC=mpg,DC=de')"
                      regexp =  '<_sre.SRE_Pattern object at 0x7f5777912cd8>'
                query_result =  '((9L,),)'
                  p_nickname =  'None'


Example of my default setup in master branch is attached.
What am I missing?

Question:
assuming the problems can be solved, how would I automatically
register all members of some specified AD group?

Please advise.


Best regards

Irene Buettner


On 05/28/2013 06:41 PM, Tibor Simko wrote:
> On Tue, 28 May 2013, Alexander Wagner wrote:
>> Here you go. It's based on the EPFL sample that comes wiht 
>> Invenio. HTH :)
> 
> Thanks, converted into Trac Howto and posted here:
> 
> <http://invenio-software.org/wiki/HowTo/HowToConfigureLDAPLogin>
> 
> Best regards -- Tibor Simko
> 


-- 
Irene Buettner                           Tel 05556 979 143
MPI fuer Sonnensystemforschung           Fax 05556 979 240
Max-Planck-Strasse 2                     Room S126
D-37191 Katlenburg-Lindau                e-mail [email protected]
# -*- coding: utf-8 -*-
##
## This file is part of Invenio.
## Copyright (C) 2007, 2008, 2009, 2010, 2011 CERN.
##
## Invenio 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 2 of the
## License, or (at your option) any later version.
##
## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.

"""External user authentication for MPS's LDAP instance.

This LDAP/AD external authentication system relies on a collaborative LDAP
organized like this:

o=EPFL, c=CH
    |
    |
    +--ou=groups
    |      |
    |      |
    |      +--- cn=xxx
    |           displayName= name of the group
    |           uniqueIdentifier= some local id for groups
    |
    |
    |
    +--ou=users
    |     |
    |     |
    |     +---uid= some local id for users (ex: grfavre)
    |         uniqueIdentifier= another local id (ex: 128933)
    |         [email protected]
    |         memberOf= id of a group
    |         memberOf= id of another group
    |
    +



AD nomenclature
------------

ou organizational unit
dc
rdc
cn 
dn distinguished name

This example of an LDAP authentication should help you develop yours in your
specific installation.
"""

__revision__ = \
    "$Id$"

import ldap
from invenio.external_authentication import ExternalAuth, \
                                            InvenioWebAccessExternalAuthError


# only one server for now; we might add more for redundancy
CFG_EXTERNAL_AUTH_LDAP_SERVERS = ['pc.linmpi.mpg.de']
# we might want to reduce this so we get less info..?
CFG_EXTERNAL_AUTH_LDAP_CONTEXT = 'dc=pc, dc=linmpi, dc=mpg, dc=de'
CFG_EXTERNAL_AUTH_LDAP_USER_UID  = ["sAMAccountName", "uid", "uniqueIdentifier", "mail"]
CFG_EXTERNAL_AUTH_LDAP_MAIL_ENTRY = 'mail'

CFG_EXTERNAL_AUTH_LDAP_GROUP_MEMBERSHIP = 'memberOf'

### this results in type error because they are use as indices below 
#CFG_EXTERNAL_AUTH_LDAP_GROUP_UID = 'uniqueIdentifier'

# displayName does not work: type error
# uidNumber does not work: type error
#CFG_EXTERNAL_AUTH_LDAP_GROUP_NAME = 'uidNumber'

# If you like LDAP users to be added to a group when they first access the system, set the following parameter. I provide this just an example, as I do NOT want users to be added to groups.
#CFG_EXTERNAL_AUTH_LDAP_GROUP_MEMBERSHIP = 'gidNumber'
CFG_EXTERNAL_AUTH_LDAP_GROUP_UID = 'uidNumber'
CFG_EXTERNAL_AUTH_LDAP_GROUP_NAME = 'displayName'
 
# check later;
#CFG_EXTERNAL_AUTH_LDAP_HIDDEN_GROUPS = ['Users', 'Domain Users']

class ExternalAuthLDAP(ExternalAuth):
    """
    External authentication example for a custom LDAP-based
    authentication service.
    """
    def __init__(self):
        """Initialize stuff here"""
        ExternalAuth.__init__(self)
        self.enforce_external_nicknames = True

    def _ldap_try (self, command):
        """ Try to run the specified command on the first LDAP server that
        is not down."""
        for server in CFG_EXTERNAL_AUTH_LDAP_SERVERS:
            try:
                # works, but order TBC, see below referrals only for LDAP, not for AD, see mediawiki and mantisbt
                ldap.set_option(ldap.OPT_REFERRALS, 0);
                #works:
                connection = ldap.initialize('ldap://pc.linmpi.mpg.de')
                # generic:
                # connection = ldap.initialize(server)
                # next two missing in CERN example
                # connection.set_option(ldap.OPT_REFERRALS, 0)
                # works: set the protocol version
                connection.protocol_version = 3;

                #username1='PC\\'+username
                #connection.simple_bind_s(username1, password);

                # note this works with 2 backslashes!
                connection.simple_bind_s('domain\\username', 'topsecret');
                
                return command(connection)
            except ldap.SERVER_DOWN, error_message:
                continue
        raise InvenioWebAccessExternalAuthError


    def auth_user(self, username, password, req=None):
        """
        Check USERNAME and PASSWORD against the LDAP system.
        Return (None, None) if authentication failed, or the (email address, user_dn) of the
        person if the authentication was successful.
        Raise InvenioWebAccessExternalAuthError in case of external troubles.
        Note: for SSO the parameter are discarded and overloaded by Shibboleth
        variables
        """
        if not password:
            return None, None
        query = '(|' + ''.join (['(%s=%s)' % (attrib, username)
                                 for attrib in 
                                     CFG_EXTERNAL_AUTH_LDAP_USER_UID]) \
                + ')'
  
        def _check (connection):
            users = connection.search_s(CFG_EXTERNAL_AUTH_LDAP_CONTEXT,
                                        ldap.SCOPE_SUBTREE,
                                        query)

            # We pick the first result, as all the data we are interested
            # it should be the same in all the entries.
            if len(users):
                user_dn, user_info = users [0]
            else:
                return None, None
 
            try:
                username1='PC\\'+username
                # print username1
                connection.simple_bind_s(username1, password);

            except ldap.INVALID_CREDENTIALS:
                # It is enough to fail on one server to consider the credential
                # to be invalid
                return None, None
            return user_info[CFG_EXTERNAL_AUTH_LDAP_MAIL_ENTRY][0], user_dn
        return self._ldap_try(_check)

    def user_exists(self, email, req=None):
        """Check the external authentication system for existance of email.
        @return: True if the user exists, False otherwise
        """
        query = '(%s=%s)' % (CFG_EXTERNAL_AUTH_LDAP_MAIL_ENTRY, email)
        def _check (connection):
            users = connection.search_s(CFG_EXTERNAL_AUTH_LDAP_CONTEXT,
                                        ldap.SCOPE_SUBTREE,
                                        query)
            return len(users) != 0
        return self._ldap_try(_check)

    def fetch_user_nickname(self, username, password=None, req=None):
        """Given a username and a password, returns the right nickname belonging
        to that user (username could be an email).
        """
        query = '(|' + ''.join (['(%s=%s)' % (attrib, username)
                                 for attrib in
                                     CFG_EXTERNAL_AUTH_LDAP_USER_UID]) \
                 + ')'
        def _get_nickname(connection):
            users = connection.search_s(CFG_EXTERNAL_AUTH_LDAP_CONTEXT,
                                        ldap.SCOPE_SUBTREE,
                                        query)
            # We pick the first result, as all the data we are interested
            # in should be the same in all the entries.
            if len(users):
                user_dn, user_info = users[0]
            else:
                return None
            emails = user_info[CFG_EXTERNAL_AUTH_LDAP_MAIL_ENTRY]
            if len(emails):
                email = emails[0]
            else:
                return False
            (left_part, right_part) = email.split('@')
            nickname = left_part.replace('.', ' ').title()
            if right_part != 'mps.mpg.de':
                nickname += ' - ' + right_part
                return nickname
            return self._ldap_try(_get_nickname)

    def fetch_user_groups_membership(self, username, password=None, req=None):
        """Given a username and a password, returns a dictionary of groups
        and their description to which the user is subscribed.
        Raise InvenioWebAccessExternalAuthError in case of troubles.
        """
        query_person = '(|' + ''.join (['(%s=%s)' % (attrib, username)
                                 for attrib in
                                     CFG_EXTERNAL_AUTH_LDAP_USER_UID]) \
                        + ')'
        def _get_groups(connection):
            users = connection.search_s(CFG_EXTERNAL_AUTH_LDAP_CONTEXT,
                                        ldap.SCOPE_SUBTREE,
                                        query_person)
            if len(users):
                user_dn, user_info = users[0]
            else:
                return {}
            groups = {}
            group_ids = user_info[CFG_EXTERNAL_AUTH_LDAP_GROUP_MEMBERSHIP]
            for group_id in group_ids:
                query_group = '(%s=%s)' % (CFG_EXTERNAL_AUTH_LDAP_GROUP_UID,
                                           group_id)
                ldap_group = connection.search_s(CFG_EXTERNAL_AUTH_LDAP_CONTEXT,
                                                 ldap.SCOPE_SUBTREE,
                                                 query_group)
                if len(ldap_group):
                    group_dn, group_infos = ldap_group[0]
                    group_name = group_infos[CFG_EXTERNAL_AUTH_LDAP_GROUP_NAME][0]
                    if group_name in CFG_EXTERNAL_AUTH_LDAP_HIDDEN_GROUPS:
                        continue
                    groups[group_id] = group_name
            return groups
        return self._ldap_try(_get_groups)

    def fetch_user_preferences(self, username, password=None, req=None):
        """Given a username and a password, returns a dictionary of keys and
        values, corresponding to external infos and settings.

        userprefs = {"telephone": "2392489",
                     "address": "10th Downing Street"}

        (WEBUSER WILL erase all prefs that starts by EXTERNAL_ and will
        store: "EXTERNAL_telephone"; all internal preferences can use whatever
        name but starting with EXTERNAL). If a pref begins with HIDDEN_ it will
        be ignored.
        """
        query = '(|' + ''.join (['(%s=%s)' % (attrib, username)
                                 for attrib in
                                     CFG_EXTERNAL_AUTH_LDAP_USER_UID]) \
                 + ')'
        def _get_personal_infos(connection):
            users = connection.search_s(CFG_EXTERNAL_AUTH_LDAP_CONTEXT,
                                        ldap.SCOPE_SUBTREE,
                                        query)
            if len(users):
                user_dn, user_info = users [0]
                return user_info
            else:
                return {}
        return self._ldap_try(_get_personal_infos)


Reply via email to