--- a/__pkginfo__.py Thu Mar 29 14:20:41 2012 +0200
+++ b/__pkginfo__.py Tue Apr 10 17:03:19 2012 +0200
@@ -54,6 +54,7 @@
# server dependencies
'logilab-database': '>= 1.8.2',
'pysqlite': '>= 2.5.5', # XXX install pysqlite2
+ 'passlib': '',
}
__recommends__ = {
--- a/bin/clone_deps.py Thu Mar 29 14:20:41 2012 +0200
+++ b/bin/clone_deps.py Tue Apr 10 17:03:19 2012 +0200
@@ -1,24 +1,24 @@
#!/usr/bin/python
-import os
import sys
-from subprocess import call, Popen, PIPE
-try:
- from mercurial.dispatch import dispatch as hg_call
-except ImportError:
+
+from subprocess import call as sbp_call, Popen, PIPE
+from urllib import urlopen
+from os import path as osp, pardir, chdir
+
+
+def find_mercurial():
+ print "trying to find mercurial from the command line ..."
print '-' * 20
- print "mercurial module is not reachable from this Python interpreter"
- print "trying from command line ..."
- tryhg = os.system('hg --version')
+ tryhg = sbp_call(['hg', '--version'])
if tryhg:
- print 'mercurial seems to unavailable, please install it'
+ print 'mercurial seems to be unavailable, please install it'
raise
- print 'found it, ok'
print '-' * 20
def hg_call(args):
- call(['hg'] + args)
-from urllib import urlopen
-from os import path as osp, pardir
-from os.path import normpath, join, dirname
+ return sbp_call(['hg'] + args)
+
+ return hg_call
+
BASE_URL = 'http://www.logilab.org/hg/'
@@ -27,7 +27,7 @@
'logilab/devtools', 'logilab/mtconverter',
'cubes/blog', 'cubes/calendar', 'cubes/card', 'cubes/comment',
'cubes/datafeed', 'cubes/email', 'cubes/file', 'cubes/folder',
- 'cubes/forgotpwd', 'cubes/keyword', 'cubes/link',
+ 'cubes/forgotpwd', 'cubes/keyword', 'cubes/link', 'cubes/localperms',
'cubes/mailinglist', 'cubes/nosylist', 'cubes/person',
'cubes/preview', 'cubes/registration', 'cubes/rememberme',
'cubes/tag', 'cubes/vcsfile', 'cubes/zone']
@@ -65,9 +65,10 @@
else:
sys.stderr.write('usage %s [base_url]\n' % sys.argv[0])
sys.exit(1)
+ hg_call = find_mercurial()
print len(to_clone), 'repositories will be cloned'
- base_dir = normpath(join(dirname(__file__), pardir, pardir))
- os.chdir(base_dir)
+ base_dir = osp.normpath(osp.join(osp.dirname(__file__), pardir, pardir))
+ chdir(base_dir)
not_updated = []
for repo in to_clone:
url = base_url + repo
@@ -78,7 +79,7 @@
directory, repo = repo.split('/')
if not osp.isdir(directory):
os.mkdir(directory)
- open(join(directory, '__init__.py'), 'w').close()
+ open(osp.join(directory, '__init__.py'), 'w').close()
target_path = osp.join(directory, repo)
if osp.exists(target_path):
print target_path, 'seems already cloned. Skipping it.'
--- a/debian/control Thu Mar 29 14:20:41 2012 +0200
+++ b/debian/control Tue Apr 10 17:03:19 2012 +0200
@@ -35,7 +35,7 @@
Conflicts: cubicweb-multisources
Replaces: cubicweb-multisources
Provides: cubicweb-multisources
-Depends: ${misc:Depends}, ${python:Depends}, cubicweb-common (= ${source:Version}), cubicweb-ctl (= ${source:Version}), python-logilab-database (>= 1.8.2), cubicweb-postgresql-support | cubicweb-mysql-support | python-pysqlite2
+Depends: ${misc:Depends}, ${python:Depends}, cubicweb-common (= ${source:Version}), cubicweb-ctl (= ${source:Version}), python-logilab-database (>= 1.8.2), cubicweb-postgresql-support | cubicweb-mysql-support | python-pysqlite2, python-passlib
Recommends: pyro (<< 4.0.0), cubicweb-documentation (= ${source:Version})
Suggests: python-zmq
Description: server part of the CubicWeb framework
--- a/devtools/data/xvfb-run.sh Thu Mar 29 14:20:41 2012 +0200
+++ b/devtools/data/xvfb-run.sh Tue Apr 10 17:03:19 2012 +0200
@@ -1,13 +1,11 @@
#!/bin/sh
-# $Id: xvfb-run 2027 2004-11-16 14:54:16Z branden $
-
# This script starts an instance of Xvfb, the "fake" X server, runs a command
# with that server available, and kills the X server when done. The return
# value of the command becomes the return value of this script.
#
# If anyone is using this to build a Debian package, make sure the package
-# Build-Depends on xvfb, xbase-clients, and xfonts-base.
+# Build-Depends on xvfb and xauth.
set -e
@@ -15,7 +13,6 @@
SERVERNUM=99
AUTHFILE=
ERRORFILE=/dev/null
-STARTWAIT=3
XVFBARGS="-screen 0 640x480x8"
LISTENTCP="-nolisten tcp"
XAUTHPROTO=.
@@ -62,8 +59,6 @@
-s ARGS --server-args=ARGS arguments (other than server number and
"-nolisten tcp") to pass to the Xvfb server
(default: "$XVFBARGS")
--w DELAY --wait=DELAY delay in seconds to wait for Xvfb to start
- before running COMMAND (default: $STARTWAIT)
EOF
}
@@ -93,7 +88,7 @@
fi
fi
if [ -n "$XVFBPID" ]; then
- kill $XVFBPID
+ kill "$XVFBPID"
fi
}
@@ -120,7 +115,7 @@
-l|--listen-tcp) LISTENTCP="" ;;
-p|--xauth-protocol) XAUTHPROTO="$2"; shift ;;
-s|--server-args) XVFBARGS="$2"; shift ;;
- -w|--wait) STARTWAIT="$2"; shift ;;
+ -w|--wait) shift ;;
--) shift; break ;;
*) error "internal error; getopt permitted \"$1\" unexpectedly"
exit 6
@@ -163,10 +158,13 @@
XAUTHORITY=$AUTHFILE xauth source - << EOF >>"$ERRORFILE" 2>&1
add :$SERVERNUM $XAUTHPROTO $MCOOKIE
EOF
- XAUTHORITY=$AUTHFILE Xvfb ":$SERVERNUM" $XVFBARGS $LISTENTCP >>"$ERRORFILE" 2>&1 &
+ # handle SIGUSR1 so Xvfb knows to send a signal when it's ready to accept
+ # connections
+ trap : USR1
+ (trap '' USR1; XAUTHORITY=$AUTHFILE exec Xvfb ":$SERVERNUM" $XVFBARGS $LISTENTCP >>"$ERRORFILE" 2>&1) &
XVFBPID=$!
- sleep "$STARTWAIT"
+ wait || :
if kill -0 $XVFBPID 2>/dev/null; then
break
elif [ -n "$AUTONUM" ]; then
@@ -176,6 +174,7 @@
continue
fi
error "Xvfb failed to start" >&2
+ XVFBPID=
exit 1
done
--- a/doc/book/en/admin/config.rst Thu Mar 29 14:20:41 2012 +0200
+++ b/doc/book/en/admin/config.rst Tue Apr 10 17:03:19 2012 +0200
@@ -70,53 +70,53 @@
install the `postgresql-client` package on the |cubicweb| host, and others on the
database host.
-.. Note::
+Database cluster
+++++++++++++++++
- If you already have an existing cluster and PostgreSQL server running, you do
- not need to execute the initilization step of your PostgreSQL database unless
- you want a specific cluster for |cubicweb| databases or if your existing
- cluster doesn't use the UTF8 encoding (see note below).
+If you already have an existing cluster and PostgreSQL server running, you do
+not need to execute the initilization step of your PostgreSQL database unless
+you want a specific cluster for |cubicweb| databases or if your existing
+cluster doesn't use the UTF8 encoding (see note below).
-* First, initialize a PostgreSQL cluster with the command ``initdb``::
+To initialize a PostgreSQL cluster, use the command ``initdb``::
$ initdb -E UTF8 -D /path/to/pgsql
- Notice the encoding specification. This is necessary since |cubicweb| usually
- want UTF8 encoded database. If you use a cluster with the wrong encoding, you'll
- get error like::
+Notice the encoding specification. This is necessary since |cubicweb| usually
+want UTF8 encoded database. If you use a cluster with the wrong encoding, you'll
+get error like::
- new encoding (UTF8) is incompatible with the encoding of the template database (SQL_ASCII)
- HINT: Use the same encoding as in the template database, or use template0 as template.
-
+ new encoding (UTF8) is incompatible with the encoding of the template database (SQL_ASCII)
+ HINT: Use the same encoding as in the template database, or use template0 as template.
- Once initialized, start the database server PostgreSQL with the command::
+Once initialized, start the database server PostgreSQL with the command::
- $ postgres -D /path/to/psql
+ $ postgres -D /path/to/psql
- If you cannot execute this command due to permission issues, please make sure
- that your username has write access on the database. ::
+If you cannot execute this command due to permission issues, please make sure
+that your username has write access on the database. ::
- $ chown username /path/to/pgsql
+ $ chown username /path/to/pgsql
-* The database authentication can be either set to `ident sameuser` or `md5`. If
- set to `md5`, make sure to use an existing user of your database. If set to
- `ident sameuser`, make sure that your client's operating system user name has a
- matching user in the database. If not, please do as follow to create a user::
+Database authentication
++++++++++++++++++++++++
- $ su
- $ su - postgres
- $ createuser -s -P username
+The database authentication is configured in `pg_hba.conf`. It can be either set
+to `ident sameuser` or `md5`. If set to `md5`, make sure to use an existing
+user of your database. If set to `ident sameuser`, make sure that your client's
+operating system user name has a matching user in the database. If not, please
+do as follow to create a user::
- The option `-P` (for password prompt), will encrypt the password with the
- method set in the configuration file :file:`pg_hba.conf`. If you do not use this
- option `-P`, then the default value will be null and you will need to set it
- with::
+ $ su
+ $ su - postgres
+ $ createuser -s -P username
- $ su postgres -c "echo ALTER USER username WITH PASSWORD 'userpasswd' | psql"
+The option `-P` (for password prompt), will encrypt the password with the
+method set in the configuration file :file:`pg_hba.conf`. If you do not use this
+option `-P`, then the default value will be null and you will need to set it
+with::
-.. Note::
- The authentication method can be configured in file:`pg_hba.conf`.
-
+ $ su postgres -c "echo ALTER USER username WITH PASSWORD 'userpasswd' | psql"
The above login/password will be requested when you will create an instance with
`cubicweb-ctl create` to initialize the database of your instance.
@@ -149,7 +149,6 @@
cat /usr/share/postgresql/8.X/contrib/tsearch2.sql | psql -U username template1
-
.. _MySqlConfiguration:
MySql
@@ -196,12 +195,12 @@
The ALTER DATABASE command above requires some permissions that your
user may not have. In that case you will have to ask your local DBA to
-run the query for you.
+run the query for you.
You can check that the setting is correct by running the following
query which must return '1'::
- SELECT is_read_committed_snapshot_on
+ SELECT is_read_committed_snapshot_on
FROM sys.databases WHERE name='<databasename>';
@@ -210,6 +209,7 @@
SQLite
~~~~~~
+
SQLite has the great advantage of requiring almost no configuration. Simply
use 'sqlite' as db-driver, and set path to the dabase as db-name. Don't specify
anything for db-user and db-password, they will be ignore anyway.
@@ -226,6 +226,7 @@
Pyro name server
~~~~~~~~~~~~~~~~
+
If you want to use Pyro to access your instance remotely, or to have multi-source
or distributed configuration, it is required to have a Pyro name server running
on your network. By default it is detected by a broadcast request, but you can
--- a/doc/book/en/admin/setup.rst Thu Mar 29 14:20:41 2012 +0200
+++ b/doc/book/en/admin/setup.rst Tue Apr 10 17:03:19 2012 +0200
@@ -63,9 +63,9 @@
deb http://download.logilab.org/production/ lucid/
- Note that for Ubuntu Maverick and newer, you shall use the `lucid`
- repository and install the ``libgecode19`` package from `lucid
- universe <http://packages.ubuntu.com/lucid/libgecode19>`_.
+Note that for Ubuntu Maverick and newer, you shall use the `lucid`
+repository and install the ``libgecode19`` package from `lucid
+universe <http://packages.ubuntu.com/lucid/libgecode19>`_.
The repositories are signed with the `Logilab's gnupg key`_. You can download
and register the key to avoid warnings::
--- a/etwist/server.py Thu Mar 29 14:20:41 2012 +0200
+++ b/etwist/server.py Tue Apr 10 17:03:19 2012 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
--- a/md5crypt.py Thu Mar 29 14:20:41 2012 +0200
+++ b/md5crypt.py Tue Apr 10 17:03:19 2012 +0200
@@ -51,18 +51,16 @@
v = v >> 6
return ret
-def crypt(pw, salt, magic=None):
+def crypt(pw, salt):
if isinstance(pw, unicode):
pw = pw.encode('utf-8')
- if magic is None:
- magic = MAGIC
# Take care of the magic string if present
- if salt[:len(magic)] == magic:
- salt = salt[len(magic):]
+ if salt.startswith(MAGIC):
+ salt = salt[len(MAGIC):]
# salt can have up to 8 characters:
salt = salt.split('$', 1)[0]
salt = salt[:8]
- ctx = pw + magic + salt
+ ctx = pw + MAGIC + salt
final = md5(pw + salt + pw).digest()
for pl in xrange(len(pw), 0, -16):
if pl > 16:
@@ -114,4 +112,4 @@
|(int(ord(final[10])) << 8)
|(int(ord(final[5]))), 4)
passwd = passwd + to64((int(ord(final[11]))), 2)
- return salt + '$' + passwd
+ return passwd
--- a/misc/migration/3.14.3_Any.py Thu Mar 29 14:20:41 2012 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,2 +0,0 @@
-# keep the same behavior on existing instance but use the new one on new instance.
-config['https-deny-anonymous'] = True
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/migration/3.14.7_Any.py Tue Apr 10 17:03:19 2012 +0200
@@ -0,0 +1,4 @@
+# migrate default format for TriInfo `comment_format` attribute
+sync_schema_props_perms('TrInfo')
+
+commit()
--- a/schemas/workflow.py Thu Mar 29 14:20:41 2012 +0200
+++ b/schemas/workflow.py Tue Apr 10 17:03:19 2012 +0200
@@ -185,7 +185,7 @@
# make by_transition optional because we want to allow managers to set
# entity into an arbitrary state without having to respect wf transition
by_transition = SubjectRelation('BaseTransition', cardinality='?*')
- comment = RichString(fulltextindexed=True)
+ comment = RichString(fulltextindexed=True, default_format='text/plain')
tr_count = Int(description='autocomputed attribute used to ensure transition coherency')
# get actor and date time using owned_by and creation_date
--- a/server/querier.py Thu Mar 29 14:20:41 2012 +0200
+++ b/server/querier.py Tue Apr 10 17:03:19 2012 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -417,7 +417,7 @@
if rqlexpr.check(session, eid):
break
else:
- raise Unauthorized()
+ raise Unauthorized('No read acces on %r with eid %i.' % (var, eid))
restricted_vars.update(localcheck)
localchecks.setdefault(tuple(localcheck.iteritems()), []).append(solution)
# raise Unautorized exception if the user can't access to any solution
@@ -723,7 +723,7 @@
rqlst = rqlst.copy()
self._annotate(rqlst)
if args:
- # different SQL generated when some argument is None or not (IS
+ # different SQL generated when some argument is None or not (IS
# NULL). This should be considered when computing sql cache key
cachekey += tuple(sorted([k for k,v in args.iteritems()
if v is None]))
--- a/server/rqlannotation.py Thu Mar 29 14:20:41 2012 +0200
+++ b/server/rqlannotation.py Tue Apr 10 17:03:19 2012 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -28,12 +28,13 @@
from rql.utils import common_parent
def _annotate_select(annotator, rqlst):
+ has_text_query = False
for subquery in rqlst.with_:
- annotator._annotate_union(subquery.query)
+ if annotator._annotate_union(subquery.query):
+ has_text_query = True
#if server.DEBUG:
# print '-------- sql annotate', repr(rqlst)
getrschema = annotator.schema.rschema
- has_text_query = False
need_distinct = rqlst.distinct
for rel in rqlst.iget_nodes(Relation):
if getrschema(rel.r_type).symmetric and not isinstance(rel.parent, Exists):
@@ -154,6 +155,11 @@
sstinfo['scope'] = common_parent(sstinfo['scope'], stinfo['scope']).scope
except CantSelectPrincipal:
stinfo['invariant'] = False
+ # see unittest_rqlannotation. test_has_text_security_cache_bug
+ # XXX probably more to do, but yet that work without more...
+ for col_alias in rqlst.aliases.itervalues():
+ if col_alias.stinfo.get('ftirels'):
+ has_text_query = True
rqlst.need_distinct = need_distinct
return has_text_query
@@ -272,8 +278,7 @@
def _annotate_union(self, union):
has_text_query = False
for select in union.children:
- htq = _annotate_select(self, select)
- if htq:
+ if _annotate_select(self, select):
has_text_query = True
return has_text_query
--- a/server/sources/native.py Thu Mar 29 14:20:41 2012 +0200
+++ b/server/sources/native.py Tue Apr 10 17:03:19 2012 +0200
@@ -1626,7 +1626,7 @@
# if pwd is None but a password is provided, something is wrong
raise AuthenticationError('bad password')
# passwords are stored using the Bytes type, so we get a StringIO
- args['pwd'] = Binary(crypt_password(password, pwd.getvalue()[:2]))
+ args['pwd'] = Binary(crypt_password(password, pwd.getvalue()))
# get eid from login and (crypted) password
rset = self.source.syntax_tree_search(session, self._auth_rqlst, args)
try:
--- a/server/test/unittest_postgres.py Thu Mar 29 14:20:41 2012 +0200
+++ b/server/test/unittest_postgres.py Tue Apr 10 17:03:19 2012 +0200
@@ -1,20 +1,20 @@
# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
-# This file is part of Logilab-common.
+# This file is part of CubicWeb.
#
-# Logilab-common is free software: you can redistribute it and/or modify it
-# under the terms of the GNU Lesser General Public License as published by the
-# Free Software Foundation, either version 2.1 of the License, or (at your
-# option) any later version.
+# CubicWeb is free software: you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation, either version 2.1 of the License, or (at your option)
+# any later version.
#
-# Logilab-common is distributed in the hope that it will be useful, but WITHOUT
+# CubicWeb 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 Lesser General Public License for more
# details.
#
# You should have received a copy of the GNU Lesser General Public License along
-# with Logilab-common. If not, see <http://www.gnu.org/licenses/>.
+# with CubicWeb. If not, see <http://www.gnu.org/licenses/>.
from __future__ import with_statement
--- a/server/test/unittest_querier.py Thu Mar 29 14:20:41 2012 +0200
+++ b/server/test/unittest_querier.py Tue Apr 10 17:03:19 2012 +0200
@@ -1,5 +1,5 @@
# -*- coding: iso-8859-1 -*-
-# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -18,6 +18,8 @@
# with CubicWeb. If not, see <http://www.gnu.org/licenses/>.
"""unit tests for modules cubicweb.server.querier and cubicweb.server.ssplanner
"""
+from __future__ import with_statement
+
from datetime import date, datetime, timedelta, tzinfo
from logilab.common.testlib import TestCase, unittest_main
@@ -28,7 +30,7 @@
from cubicweb.server.utils import crypt_password
from cubicweb.server.sources.native import make_schema
from cubicweb.devtools import get_test_db_handler, TestServerConfiguration
-
+from cubicweb.devtools.testlib import CubicWebTC
from cubicweb.devtools.repotest import tuplify, BaseQuerierTC
from unittest_session import Variable
@@ -70,14 +72,15 @@
('C0 text,C1 integer', {'A': 'table0.C0', 'B': 'table0.C1'}))
-def setUpModule(*args):
+def setUpClass(cls, *args):
global repo, cnx
config = TestServerConfiguration(apphome=UtilsTC.datadir)
handler = get_test_db_handler(config)
handler.build_db_cache()
repo, cnx = handler.get_repo_and_cnx()
+ cls.repo = repo
-def tearDownModule(*args):
+def tearDownClass(cls, *args):
global repo, cnx
cnx.close()
repo.shutdown()
@@ -85,9 +88,8 @@
class UtilsTC(BaseQuerierTC):
- def setUp(self):
- self.__class__.repo = repo
- super(UtilsTC, self).setUp()
+ setUpClass = classmethod(setUpClass)
+ tearDownClass = classmethod(tearDownClass)
def get_max_eid(self):
# no need for cleanup here
@@ -242,9 +244,8 @@
class QuerierTC(BaseQuerierTC):
- def setUp(self):
- self.__class__.repo = repo
- super(QuerierTC, self).setUp()
+ setUpClass = classmethod(setUpClass)
+ tearDownClass = classmethod(tearDownClass)
def test_encoding_pb(self):
self.assertRaises(RQLSyntaxError, self.execute,
@@ -1259,7 +1260,7 @@
cursor.execute("SELECT %supassword from %sCWUser WHERE %slogin='bob'"
% (SQL_PREFIX, SQL_PREFIX, SQL_PREFIX))
passwd = str(cursor.fetchone()[0])
- self.assertEqual(passwd, crypt_password('toto', passwd[:2]))
+ self.assertEqual(passwd, crypt_password('toto', passwd))
rset = self.execute("Any X WHERE X is CWUser, X login 'bob', X upassword %(pwd)s",
{'pwd': Binary(passwd)})
self.assertEqual(len(rset.rows), 1)
@@ -1274,7 +1275,7 @@
cursor.execute("SELECT %supassword from %sCWUser WHERE %slogin='bob'"
% (SQL_PREFIX, SQL_PREFIX, SQL_PREFIX))
passwd = str(cursor.fetchone()[0])
- self.assertEqual(passwd, crypt_password('tutu', passwd[:2]))
+ self.assertEqual(passwd, crypt_password('tutu', passwd))
rset = self.execute("Any X WHERE X is CWUser, X login 'bob', X upassword %(pwd)s",
{'pwd': Binary(passwd)})
self.assertEqual(len(rset.rows), 1)
@@ -1501,5 +1502,20 @@
self.assertFalse(self.execute('Any X WHERE X is CWEType, X name %(name)s', {'name': None}))
self.assertTrue(self.execute('Any X WHERE X is CWEType, X name %(name)s', {'name': 'CWEType'}))
+
+class NonRegressionTC(CubicWebTC):
+
+ def test_has_text_security_cache_bug(self):
+ req = self.request()
+ self.create_user(req, 'user', ('users',))
+ aff1 = req.create_entity('Societe', nom=u'aff1')
+ aff2 = req.create_entity('Societe', nom=u'aff2')
+ self.commit()
+ with self.login('user', password='user'):
+ res = self.execute('Any X WHERE X has_text %(text)s', {'text': 'aff1'})
+ self.assertEqual(res.rows, [[aff1.eid]])
+ res = self.execute('Any X WHERE X has_text %(text)s', {'text': 'aff2'})
+ self.assertEqual(res.rows, [[aff2.eid]])
+
if __name__ == '__main__':
unittest_main()
--- a/server/test/unittest_rqlannotation.py Thu Mar 29 14:20:41 2012 +0200
+++ b/server/test/unittest_rqlannotation.py Tue Apr 10 17:03:19 2012 +0200
@@ -1,5 +1,5 @@
# -*- coding: iso-8859-1 -*-
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -350,6 +350,12 @@
self.assertEqual(rqlst.defined_vars['X']._q_invariant, False)
self.assertEqual(rqlst.defined_vars['Y']._q_invariant, True)
+
+ def test_has_text_security_cache_bug(self):
+ rqlst = self._prepare('Any X WHERE X has_text "toto" WITH X BEING '
+ '(Any C WHERE C is Societe, C nom CS)')
+ self.assertTrue(rqlst.parent.has_text_query)
+
if __name__ == '__main__':
from logilab.common.testlib import unittest_main
unittest_main()
--- a/server/utils.py Thu Mar 29 14:20:41 2012 +0200
+++ b/server/utils.py Tue Apr 10 17:03:19 2012 +0200
@@ -28,27 +28,49 @@
from cubicweb.server import SOURCE_TYPES
-try:
- from crypt import crypt
-except ImportError:
- # crypt is not available (eg windows)
- from cubicweb.md5crypt import crypt
+from passlib.utils import handlers as uh, to_hash_str
+from passlib.context import CryptContext
+
+from cubicweb.md5crypt import crypt as md5crypt
-def getsalt(chars=string.letters + string.digits):
- """generate a random 2-character 'salt'"""
- return choice(chars) + choice(chars)
+class CustomMD5Crypt(uh.HasSalt, uh.GenericHandler):
+ name = 'cubicweb-md5crypt'
+ setting_kwds = ("salt",)
+ min_salt_size = 0
+ max_salt_size = 8
+ salt_chars = uh.H64_CHARS
+ @classmethod
+ def from_string(cls, hash):
+ if hash is None:
+ raise ValueError("no hash specified")
+ if hash.count('$') != 1:
+ raise ValueError("invalid cubicweb-md5 hash")
+ salt = hash.split('$', 1)[0]
+ chk = hash.split('$', 1)[1]
+ return cls(salt=salt, checksum=chk, strict=True)
+
+ def to_string(self):
+ return to_hash_str(u'%s$%s' % (self.salt, self.checksum or u''))
+
+ def calc_checksum(self, secret):
+ return md5crypt(secret, self.salt.encode('ascii'))
+
+myctx = CryptContext(['sha512_crypt', CustomMD5Crypt, 'des_crypt'])
def crypt_password(passwd, salt=None):
"""return the encrypted password using the given salt or a generated one
"""
- if passwd is None:
- return None
if salt is None:
- salt = getsalt()
- return crypt(passwd, salt)
-
+ return myctx.encrypt(passwd)
+ # empty hash, accept any password for backwards compat
+ if salt == '':
+ return salt
+ if myctx.verify(passwd, salt):
+ return salt
+ # wrong password
+ return ''
def cartesian_product(seqin):
"""returns a generator which returns the cartesian product of `seqin`
--- a/test/unittest_rqlrewrite.py Thu Mar 29 14:20:41 2012 +0200
+++ b/test/unittest_rqlrewrite.py Tue Apr 10 17:03:19 2012 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
--- a/test/unittest_schema.py Thu Mar 29 14:20:41 2012 +0200
+++ b/test/unittest_schema.py Tue Apr 10 17:03:19 2012 +0200
@@ -348,6 +348,10 @@
self.assertEqual(cstr.repo_check(self.session, 1, self.session.user.eid),
None) # no validation error, constraint checked
+class WorkflowShemaTC(CubicWebTC):
+ def test_trinfo_default_format(self):
+ tr = self.session.user.cw_adapt_to('IWorkflowable').fire_transition('deactivate')
+ self.assertEqual(tr.comment_format, 'text/plain')
if __name__ == '__main__':
unittest_main()
--- a/web/data/cubicweb.ajax.js Thu Mar 29 14:20:41 2012 +0200
+++ b/web/data/cubicweb.ajax.js Tue Apr 10 17:03:19 2012 +0200
@@ -372,7 +372,7 @@
/**
* .. function:: loadRemote(url, form, reqtype='GET', sync=false)
*
- * Asynchronously (unless `async` argument is set to false) load an url or path
+ * Asynchronously (unless `sync` argument is set to true) load an url or path
* and return a deferred whose callbacks args are decoded according to the
* Content-Type response header. `form` should be additional form params
* dictionary, `reqtype` the HTTP request type (get 'GET' or 'POST').
--- a/web/data/cubicweb.css Thu Mar 29 14:20:41 2012 +0200
+++ b/web/data/cubicweb.css Tue Apr 10 17:03:19 2012 +0200
@@ -39,6 +39,12 @@
padding: %(h3Padding)s;
}
+
+h4 {
+ font-size: %(h4FontSize)s;
+}
+
+
div.tabbedprimary + h1,
h1.plain {
border-bottom: none;
--- a/web/data/cubicweb.edition.js Thu Mar 29 14:20:41 2012 +0200
+++ b/web/data/cubicweb.edition.js Tue Apr 10 17:03:19 2012 +0200
@@ -435,11 +435,15 @@
}
}
if (globalerrors.length) {
- if (globalerrors.length == 1) {
- var innernode = SPAN(null, globalerrors[0]);
- } else {
- var innernode = UL(null, $.map(globalerrors, partial(LI, null)));
- }
+ if (globalerrors.length == 1) {
+ var innernode = SPAN(null, globalerrors[0]);
+ } else {
+ var linodes =[];
+ for(var i=0; i<globalerrors.length; i++){
+ linodes.push(LI(null, globalerrors[i]));
+ }
+ var innernode = UL(null, linodes);
+ }
// insert DIV and innernode before the form
var div = DIV({
'class': "errorMessage",
--- a/web/data/cubicweb.old.css Thu Mar 29 14:20:41 2012 +0200
+++ b/web/data/cubicweb.old.css Tue Apr 10 17:03:19 2012 +0200
@@ -43,7 +43,7 @@
}
h4 {
- font-size: 120%;
+ font-size: %(h4FontSize)s;
margin: 0.2em 0px;
}
--- a/web/data/uiprops.py Thu Mar 29 14:20:41 2012 +0200
+++ b/web/data/uiprops.py Tue Apr 10 17:03:19 2012 +0200
@@ -89,20 +89,22 @@
# h3 { font-size:1.30769em; }
# h
-h1FontSize = '1.5em' # 18px
+h1FontSize = '2.3em' # 25.3833px
h1Padding = '0 0 0.14em 0 '
h1Margin = '0.8em 0 0.5em'
h1Color = '#000'
h1BorderBottomStyle = lazystr('0.06em solid %(h1Color)s')
-h2FontSize = '1.33333em'
-h2Padding = '0.4em 0 0.35em 0'
+h2FontSize = '2em' #
+h2Padding = '0.4em 0 0.35em 0' # 22.0667px
h2Margin = '0'
-h3FontSize = '1.16667em'
+h3FontSize = '1.7em' #18.75px
h3Padding = '0.5em 0 0.57em 0'
h3Margin = '0'
+h4FontSize = '1.4em' # 15.45px
+
# links
aColor = '#e6820e'
--- a/web/form.py Thu Mar 29 14:20:41 2012 +0200
+++ b/web/form.py Tue Apr 10 17:03:19 2012 +0200
@@ -91,7 +91,9 @@
super(Form, self).__init__(req, rset=rset, row=row, col=col)
self.fields = list(self.__class__._fields_)
if mainform:
- self.add_hidden(u'__form_id', kwargs.pop('formvid', self.__regid__))
+ formid = kwargs.pop('formvid', self.__regid__)
+ self.add_hidden(u'__form_id', formid)
+ self._posting = self._cw.form.get('__form_id') == formid
for key, val in kwargs.iteritems():
if key in controller.NAV_FORM_PARAMETERS:
self.add_hidden(key, val)
@@ -145,6 +147,16 @@
return getattr(self, '_form_previous_values', {})
return self.parent_form.form_previous_values
+ @property
+ def posting(self):
+ """return True if the form is being posted, False if it is being
+ generated.
+ """
+ # XXX check behaviour on regeneration after error
+ if self.parent_form is None:
+ return self._posting
+ return self.parent_form.posting
+
@iclassmethod
def _fieldsattr(cls_or_self):
if isinstance(cls_or_self, type):
--- a/web/views/startup.py Thu Mar 29 14:20:41 2012 +0200
+++ b/web/views/startup.py Tue Apr 10 17:03:19 2012 +0200
@@ -53,6 +53,7 @@
add_etype_links = ()
skip_startup_views = set( ('index', 'manage', 'schema', 'owl', 'changelog',
'systempropertiesform', 'propertiesform',
+ 'loggedout', 'login',
'cw.users-and-groups-management', 'cw.groups-management',
'cw.users-management', 'cw.sources-management',
'siteinfo', 'info', 'registry', 'gc',
--- a/web/views/tableview.py Thu Mar 29 14:20:41 2012 +0200
+++ b/web/views/tableview.py Tue Apr 10 17:03:19 2012 +0200
@@ -883,7 +883,7 @@
default_column_renderer_class = EntityTableColRenderer
columns = None # to be defined in concret class
- def call(self, columns=None):
+ def call(self, columns=None, **kwargs):
if columns is not None:
self.columns = columns
self.layout_render(self.w)
--- a/web/webconfig.py Thu Mar 29 14:20:41 2012 +0200
+++ b/web/webconfig.py Tue Apr 10 17:03:19 2012 +0200
@@ -21,7 +21,7 @@
_ = unicode
import os
-from os.path import join, exists, split
+from os.path import join, exists, split, isdir
from warnings import warn
from logilab.common.decorators import cached
@@ -408,7 +408,8 @@
rdir, filename = split(rpath)
if rdir:
staticdir = join(staticdir, rdir)
- os.makedirs(staticdir)
+ if not isdir(staticdir) and 'w' in mode:
+ os.makedirs(staticdir)
return file(join(staticdir, filename), mode)
def static_file_add(self, rpath, data):