--- a/misc/migration/bootstrapmigration_repository.py Wed Aug 05 12:15:48 2009 +0200
+++ b/misc/migration/bootstrapmigration_repository.py Wed Aug 05 17:33:40 2009 +0200
@@ -21,11 +21,10 @@
# use an internal session since some entity might forbid modifications to admin
isession = repo.internal_session()
for eid, in rql('Any X', ask_confirm=False):
- try:
+ type, source, extid = session.describe(eid)
+ if source == 'system':
isession.execute('SET X cwuri %(u)s WHERE X eid %(x)s',
{'x': eid, 'u': base_url + u'eid/%s' % eid})
- except RepositoryError:
- print 'unable to set cwuri for', eid, session.describe(eid)
isession.commit()
repo.hm.register_hook(uniquecstrcheck_before_modification, 'before_add_entity', '')
repo.hm.register_hook(uniquecstrcheck_before_modification, 'before_update_entity', '')
--- a/server/hooks.py Wed Aug 05 12:15:48 2009 +0200
+++ b/server/hooks.py Wed Aug 05 17:33:40 2009 +0200
@@ -536,6 +536,9 @@
def after_update_eproperty(session, entity):
+ if not ('pkey' in entity.edited_attributes or
+ 'value' in entity.edited_attributes):
+ return
key, value = entity.pkey, entity.value
try:
value = session.vreg.typed_value(key, value)
--- a/server/migractions.py Wed Aug 05 12:15:48 2009 +0200
+++ b/server/migractions.py Wed Aug 05 17:33:40 2009 +0200
@@ -694,6 +694,22 @@
self.commit()
self.rqlexecall(ss.rdef2rql(rschema),
ask_confirm=self.verbosity>=2)
+ if rtype in META_RTYPES:
+ # if the relation is in META_RTYPES, ensure we're adding it for
+ # all entity types *in the persistent schema*, not only those in
+ # the fs schema
+ for etype in self.repo.schema.entities():
+ if not etype in self.fs_schema:
+ # get sample object type and rproperties
+ objtypes = rschema.objects()
+ assert len(objtypes) == 1
+ objtype = objtypes[0]
+ props = rschema.rproperties(
+ rschema.subjects(objtype)[0], objtype)
+ assert props
+ self.rqlexecall(ss.rdef2rql(rschema, etype, objtype, props),
+ ask_confirm=self.verbosity>=2)
+
if commit:
self.commit()
--- a/server/schemahooks.py Wed Aug 05 12:15:48 2009 +0200
+++ b/server/schemahooks.py Wed Aug 05 17:33:40 2009 +0200
@@ -446,26 +446,26 @@
return
subjtype, rtype, objtype = session.schema.schema_by_eid(rdef.eid)
cstrtype = self.entity.type
- cstr = rtype.constraint_by_type(subjtype, objtype, cstrtype)
- prevcstr = CONSTRAINTS[cstrtype].deserialize(self.entity.value)
+ oldcstr = rtype.constraint_by_type(subjtype, objtype, cstrtype)
+ newcstr = CONSTRAINTS[cstrtype].deserialize(self.entity.value)
table = SQL_PREFIX + str(subjtype)
column = SQL_PREFIX + str(rtype)
# alter the physical schema on size constraint changes
- if prevcstr.type() == 'SizeConstraint' and (
- cstr is None or cstr.max != prevcstr.max):
+ if newcstr.type() == 'SizeConstraint' and (
+ oldcstr is None or oldcstr.max != newcstr.max):
adbh = self.session.pool.source('system').dbhelper
card = rtype.rproperty(subjtype, objtype, 'cardinality')
- coltype = type_from_constraints(adbh, objtype, [prevcstr],
+ coltype = type_from_constraints(adbh, objtype, [newcstr],
creating=False)
sql = adbh.sql_change_col_type(table, column, coltype, card != '1')
try:
session.system_sql(sql, rollback_on_failure=False)
self.info('altered column %s of table %s: now VARCHAR(%s)',
- column, table, prevcstr.max)
+ column, table, newcstr.max)
except Exception, ex:
# not supported by sqlite for instance
self.error('error while altering table %s: %s', table, ex)
- elif cstrtype == 'UniqueConstraint':
+ elif cstrtype == 'UniqueConstraint' and oldcstr is None:
session.pool.source('system').create_index(
self.session, table, column, unique=True)
@@ -598,8 +598,8 @@
self.prepare_constraints(subjtype, rtype, objtype)
cstrtype = self.entity.type
self.cstr = rtype.constraint_by_type(subjtype, objtype, cstrtype)
- self.prevcstr = CONSTRAINTS[cstrtype].deserialize(self.entity.value)
- self.prevcstr.eid = self.entity.eid
+ self.newcstr = CONSTRAINTS[cstrtype].deserialize(self.entity.value)
+ self.newcstr.eid = self.entity.eid
def commit_event(self):
if self.cancelled:
@@ -607,7 +607,7 @@
# in-place modification
if not self.cstr is None:
self.constraints.remove(self.cstr)
- self.constraints.append(self.prevcstr)
+ self.constraints.append(self.newcstr)
class MemSchemaCWConstraintDel(MemSchemaOperation):
--- a/server/sources/ldapuser.py Wed Aug 05 12:15:48 2009 +0200
+++ b/server/sources/ldapuser.py Wed Aug 05 17:33:40 2009 +0200
@@ -41,35 +41,61 @@
ONELEVEL = ldap.SCOPE_ONELEVEL
SUBTREE = ldap.SCOPE_SUBTREE
-# XXX only for edition ??
-## password encryption possibilities
-#ENCRYPTIONS = ('SHA', 'CRYPT', 'MD5', 'CLEAR') # , 'SSHA'
-
-# mode identifier : (port, protocol)
-MODES = {
- 0: (389, 'ldap'),
- 1: (636, 'ldaps'),
- 2: (0, 'ldapi'),
- }
+# map ldap protocol to their standard port
+PROTO_PORT = {'ldap': 389,
+ 'ldaps': 636,
+ 'ldapi': None,
+ }
class LDAPUserSource(AbstractSource):
"""LDAP read-only CWUser source"""
support_entities = {'CWUser': False}
- port = None
-
- cnx_mode = 0
- cnx_dn = ''
- cnx_pwd = ''
-
options = (
('host',
{'type' : 'string',
'default': 'ldap',
- 'help': 'ldap host',
+ 'help': 'ldap host. It may contains port information using \
+<host>:<port> notation.',
+ 'group': 'ldap-source', 'inputlevel': 1,
+ }),
+ ('protocol',
+ {'type' : 'choice',
+ 'default': 'ldap',
+ 'choices': ('ldap', 'ldaps', 'ldapi'),
+ 'help': 'ldap protocol',
+ 'group': 'ldap-source', 'inputlevel': 1,
+ }),
+
+ ('auth-mode',
+ {'type' : 'choice',
+ 'default': 'simple',
+ 'choices': ('simple', 'cram_md5', 'digest_md5', 'gssapi'),
+ 'help': 'authentication mode used to authenticate user to the ldap.',
'group': 'ldap-source', 'inputlevel': 1,
}),
+ ('auth-realm',
+ {'type' : 'string',
+ 'default': None,
+ 'help': 'realm to use when using gssapp/kerberos authentication.',
+ 'group': 'ldap-source', 'inputlevel': 1,
+ }),
+
+ ('data-cnx-dn',
+ {'type' : 'string',
+ 'default': '',
+ 'help': 'user dn to use to open data connection to the ldap (eg used \
+to respond to rql queries).',
+ 'group': 'ldap-source', 'inputlevel': 1,
+ }),
+ ('data-cnx-password',
+ {'type' : 'string',
+ 'default': '',
+ 'help': 'password to use to open data connection to the ldap (eg used to respond to rql queries).',
+ 'group': 'ldap-source', 'inputlevel': 1,
+ }),
+
('user-base-dn',
{'type' : 'string',
'default': 'ou=People,dc=logilab,dc=fr',
@@ -129,6 +155,12 @@
AbstractSource.__init__(self, repo, appschema, source_config,
*args, **kwargs)
self.host = source_config['host']
+ self.protocol = source_config.get('protocol', 'ldap')
+ self.authmode = source_config.get('auth-mode', 'simple')
+ self._authenticate = getattr(self, '_auth_%s' % self.authmode)
+ self.cnx_dn = source_config.get('data-cnx-dn') or ''
+ self.cnx_pwd = source_config.get('data-cnx-password') or ''
+ self.user_base_scope = globals()[source_config['user-scope']]
self.user_base_dn = source_config['user-base-dn']
self.user_base_scope = globals()[source_config['user-scope']]
self.user_classes = splitstrip(source_config['user-classes'])
@@ -225,8 +257,9 @@
raise AuthenticationError()
# check password by establishing a (unused) connection
try:
- self._connect(user['dn'], password)
- except:
+ self._connect(user, password)
+ except Exception, ex:
+ self.info('while trying to authenticate %s: %s', user, ex)
# Something went wrong, most likely bad credentials
raise AuthenticationError()
return self.extid2eid(user['dn'], 'CWUser', session)
@@ -368,15 +401,16 @@
return result
- def _connect(self, userdn=None, userpwd=None):
- port, protocol = MODES[self.cnx_mode]
- if protocol == 'ldapi':
+ def _connect(self, user=None, userpwd=None):
+ if self.protocol == 'ldapi':
hostport = self.host
+ elif not ':' in self.host:
+ hostport = '%s:%s' % (self.host, PROTO_PORT[self.protocol])
else:
- hostport = '%s:%s' % (self.host, self.port or port)
- self.info('connecting %s://%s as %s', protocol, hostport,
- userdn or 'anonymous')
- url = LDAPUrl(urlscheme=protocol, hostport=hostport)
+ hostport = self.host
+ self.info('connecting %s://%s as %s', self.protocol, hostport,
+ user and user['dn'] or 'anonymous')
+ url = LDAPUrl(urlscheme=self.protocol, hostport=hostport)
conn = ReconnectLDAPObject(url.initializeUrl())
# Set the protocol version - version 3 is preferred
try:
@@ -391,14 +425,42 @@
#conn.set_option(ldap.OPT_NETWORK_TIMEOUT, conn_timeout)
#conn.timeout = op_timeout
# Now bind with the credentials given. Let exceptions propagate out.
- if userdn is None:
+ if user is None:
+ # no user specified, we want to initialize the 'data' connection,
assert self._conn is None
self._conn = conn
- userdn = self.cnx_dn
- userpwd = self.cnx_pwd
- conn.simple_bind_s(userdn, userpwd)
+ # XXX always use simple bind for data connection
+ if not self.cnx_dn:
+ conn.simple_bind_s(self.cnx_dn, self.cnx_pwd)
+ else:
+ self._authenticate(conn, {'dn': self.cnx_dn}, self.cnx_pwd)
+ else:
+ # user specified, we want to check user/password, no need to return
+ # the connection which will be thrown out
+ self._authenticate(conn, user, userpwd)
return conn
+ def _auth_simple(self, conn, user, userpwd):
+ conn.simple_bind_s(user['dn'], userpwd)
+
+ def _auth_cram_md5(self, conn, user, userpwd):
+ from ldap import sasl
+ auth_token = sasl.cram_md5(user['dn'], userpwd)
+ conn.sasl_interactive_bind_s("", auth_tokens)
+
+ def _auth_digest_md5(self, conn, user, userpwd):
+ from ldap import sasl
+ auth_token = sasl.digest_md5(user['dn'], userpwd)
+ conn.sasl_interactive_bind_s("", auth_tokens)
+
+ def _auth_gssapi(self, conn, user, userpwd):
+ # print XXX not proper sasl/gssapi
+ from ldap import sasl
+ import kerberos
+ if not kerberos.checkPassword(user[self.user_login_attr], userpwd):
+ raise Exception('BAD login / mdp')
+ #conn.sasl_interactive_bind_s("", auth_tokens)
+
def _search(self, session, base, scope,
searchstr='(objectClass=*)', attrs=()):
"""make an ldap query"""
--- a/web/action.py Wed Aug 05 12:15:48 2009 +0200
+++ b/web/action.py Wed Aug 05 17:33:40 2009 +0200
@@ -6,6 +6,7 @@
:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
"""
__docformat__ = "restructuredtext en"
+_ = unicode
from cubicweb import target
from cubicweb.selectors import (partial_relation_possible, match_search_state,
@@ -13,8 +14,6 @@
accepts_compat, condition_compat, deprecate)
from cubicweb.appobject import AppObject
-_ = unicode
-
class Action(AppObject):
"""abstract action. Handle the .search_states attribute to match