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)