annotate website/issues/detectors/userauditor.py @ 5525:bb7865241f8a

Make CSV import/export compatible across Python versions (also RDBMS journals) (issue 2550976, issue 2550975). The roundup-admin export and import commands are used for migrating between different database backends. It is desirable that they should be usable also for migrations between Python 2 and Python 3, and in some cases (e.g. with the anydbm backend) this may be required. To be usable for such migrations, the format of the generated CSV files needs to be stable, meaning the same as currently used with Python 2. The export process uses repr() to produce the fields in the CSV files and eval() to convert them back to Python data structures. repr() of strings with non-ASCII characters produces different results for Python 2 and Python 3. This patch adds repr_export and eval_import functions to roundup/anypy/strings.py which provide the required operations that are just repr() and eval() in Python 2, but are more complicated in Python 3 to use data representations compatible with Python 2. These functions are then used in the required places for export and import. repr() and eval() are also used in storing the dict of changed values in the journal for the RDBMS backends. It is similarly desirable that the database be compatible between Python 2 and Python 3, so that export and import do not need to be used for a migration between Python versions for non-anydbm back ends. Thus, this patch changes rdbms_common.py in the places involved in storing journals in the database, not just in those involved in import/export. Given this patch, import/export with non-ASCII characters appear based on some limited testing to work across Python versions, and an instance using the sqlite backend appears to be compatible between Python versions without needing import/export, *if* the sessions/otks databases (which use anydbm) are deleted when changing Python version.
author Joseph Myers <jsm@polyomino.org.uk>
date Sun, 02 Sep 2018 23:48:04 +0000
parents 0942fe89e82e
children
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
4632
4f25640b5521 Unified all copies of detectors/userauditor.py
Thomas Arendsen Hein <thomas@intevation.de>
parents: 4024
diff changeset
1 # Copyright (c) 2003 Richard Jones (richard@mechanicalcat.net)
4f25640b5521 Unified all copies of detectors/userauditor.py
Thomas Arendsen Hein <thomas@intevation.de>
parents: 4024
diff changeset
2 #
4f25640b5521 Unified all copies of detectors/userauditor.py
Thomas Arendsen Hein <thomas@intevation.de>
parents: 4024
diff changeset
3 # Permission is hereby granted, free of charge, to any person obtaining a copy
4f25640b5521 Unified all copies of detectors/userauditor.py
Thomas Arendsen Hein <thomas@intevation.de>
parents: 4024
diff changeset
4 # of this software and associated documentation files (the "Software"), to deal
4f25640b5521 Unified all copies of detectors/userauditor.py
Thomas Arendsen Hein <thomas@intevation.de>
parents: 4024
diff changeset
5 # in the Software without restriction, including without limitation the rights
4f25640b5521 Unified all copies of detectors/userauditor.py
Thomas Arendsen Hein <thomas@intevation.de>
parents: 4024
diff changeset
6 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
4f25640b5521 Unified all copies of detectors/userauditor.py
Thomas Arendsen Hein <thomas@intevation.de>
parents: 4024
diff changeset
7 # copies of the Software, and to permit persons to whom the Software is
4f25640b5521 Unified all copies of detectors/userauditor.py
Thomas Arendsen Hein <thomas@intevation.de>
parents: 4024
diff changeset
8 # furnished to do so, subject to the following conditions:
4f25640b5521 Unified all copies of detectors/userauditor.py
Thomas Arendsen Hein <thomas@intevation.de>
parents: 4024
diff changeset
9 #
4f25640b5521 Unified all copies of detectors/userauditor.py
Thomas Arendsen Hein <thomas@intevation.de>
parents: 4024
diff changeset
10 # The above copyright notice and this permission notice shall be included in
4f25640b5521 Unified all copies of detectors/userauditor.py
Thomas Arendsen Hein <thomas@intevation.de>
parents: 4024
diff changeset
11 # all copies or substantial portions of the Software.
4f25640b5521 Unified all copies of detectors/userauditor.py
Thomas Arendsen Hein <thomas@intevation.de>
parents: 4024
diff changeset
12 #
4f25640b5521 Unified all copies of detectors/userauditor.py
Thomas Arendsen Hein <thomas@intevation.de>
parents: 4024
diff changeset
13 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
4f25640b5521 Unified all copies of detectors/userauditor.py
Thomas Arendsen Hein <thomas@intevation.de>
parents: 4024
diff changeset
14 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
4f25640b5521 Unified all copies of detectors/userauditor.py
Thomas Arendsen Hein <thomas@intevation.de>
parents: 4024
diff changeset
15 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
4f25640b5521 Unified all copies of detectors/userauditor.py
Thomas Arendsen Hein <thomas@intevation.de>
parents: 4024
diff changeset
16 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
4f25640b5521 Unified all copies of detectors/userauditor.py
Thomas Arendsen Hein <thomas@intevation.de>
parents: 4024
diff changeset
17 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
4f25640b5521 Unified all copies of detectors/userauditor.py
Thomas Arendsen Hein <thomas@intevation.de>
parents: 4024
diff changeset
18 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
4f25640b5521 Unified all copies of detectors/userauditor.py
Thomas Arendsen Hein <thomas@intevation.de>
parents: 4024
diff changeset
19 # SOFTWARE.
4f25640b5521 Unified all copies of detectors/userauditor.py
Thomas Arendsen Hein <thomas@intevation.de>
parents: 4024
diff changeset
20 #
4f25640b5521 Unified all copies of detectors/userauditor.py
Thomas Arendsen Hein <thomas@intevation.de>
parents: 4024
diff changeset
21
4024
c2d0d3e9099d svn repository setup
Stefan Seefeld <stefan@users.sourceforge.net>
parents:
diff changeset
22 import re
c2d0d3e9099d svn repository setup
Stefan Seefeld <stefan@users.sourceforge.net>
parents:
diff changeset
23
c2d0d3e9099d svn repository setup
Stefan Seefeld <stefan@users.sourceforge.net>
parents:
diff changeset
24 # regular expression thanks to: http://www.regular-expressions.info/email.html
c2d0d3e9099d svn repository setup
Stefan Seefeld <stefan@users.sourceforge.net>
parents:
diff changeset
25 # this is the "99.99% solution for syntax only".
c2d0d3e9099d svn repository setup
Stefan Seefeld <stefan@users.sourceforge.net>
parents:
diff changeset
26 email_regexp = (r"[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*", r"(localhost|(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9]))")
c2d0d3e9099d svn repository setup
Stefan Seefeld <stefan@users.sourceforge.net>
parents:
diff changeset
27 email_rfc = re.compile('^' + email_regexp[0] + '@' + email_regexp[1] + '$', re.IGNORECASE)
c2d0d3e9099d svn repository setup
Stefan Seefeld <stefan@users.sourceforge.net>
parents:
diff changeset
28 email_local = re.compile('^' + email_regexp[0] + '$', re.IGNORECASE)
c2d0d3e9099d svn repository setup
Stefan Seefeld <stefan@users.sourceforge.net>
parents:
diff changeset
29
c2d0d3e9099d svn repository setup
Stefan Seefeld <stefan@users.sourceforge.net>
parents:
diff changeset
30 def valid_address(address):
c2d0d3e9099d svn repository setup
Stefan Seefeld <stefan@users.sourceforge.net>
parents:
diff changeset
31 ''' If we see an @-symbol in the address then check against the full
c2d0d3e9099d svn repository setup
Stefan Seefeld <stefan@users.sourceforge.net>
parents:
diff changeset
32 RFC syntax. Otherwise it is a local-only address so only check
c2d0d3e9099d svn repository setup
Stefan Seefeld <stefan@users.sourceforge.net>
parents:
diff changeset
33 the local part of the RFC syntax.
c2d0d3e9099d svn repository setup
Stefan Seefeld <stefan@users.sourceforge.net>
parents:
diff changeset
34 '''
c2d0d3e9099d svn repository setup
Stefan Seefeld <stefan@users.sourceforge.net>
parents:
diff changeset
35 if '@' in address:
c2d0d3e9099d svn repository setup
Stefan Seefeld <stefan@users.sourceforge.net>
parents:
diff changeset
36 return email_rfc.match(address)
c2d0d3e9099d svn repository setup
Stefan Seefeld <stefan@users.sourceforge.net>
parents:
diff changeset
37 else:
c2d0d3e9099d svn repository setup
Stefan Seefeld <stefan@users.sourceforge.net>
parents:
diff changeset
38 return email_local.match(address)
c2d0d3e9099d svn repository setup
Stefan Seefeld <stefan@users.sourceforge.net>
parents:
diff changeset
39
c2d0d3e9099d svn repository setup
Stefan Seefeld <stefan@users.sourceforge.net>
parents:
diff changeset
40 def get_addresses(user):
c2d0d3e9099d svn repository setup
Stefan Seefeld <stefan@users.sourceforge.net>
parents:
diff changeset
41 ''' iterate over all known addresses in a newvalues dict
c2d0d3e9099d svn repository setup
Stefan Seefeld <stefan@users.sourceforge.net>
parents:
diff changeset
42 this takes of the address/alterate_addresses handling
c2d0d3e9099d svn repository setup
Stefan Seefeld <stefan@users.sourceforge.net>
parents:
diff changeset
43 '''
5381
0942fe89e82e Python 3 preparation: change "x.has_key(y)" to "y in x".
Joseph Myers <jsm@polyomino.org.uk>
parents: 5378
diff changeset
44 if 'address' in user:
4024
c2d0d3e9099d svn repository setup
Stefan Seefeld <stefan@users.sourceforge.net>
parents:
diff changeset
45 yield user['address']
c2d0d3e9099d svn repository setup
Stefan Seefeld <stefan@users.sourceforge.net>
parents:
diff changeset
46 if user.get('alternate_addresses', None):
c2d0d3e9099d svn repository setup
Stefan Seefeld <stefan@users.sourceforge.net>
parents:
diff changeset
47 for address in user['alternate_addresses'].split('\n'):
c2d0d3e9099d svn repository setup
Stefan Seefeld <stefan@users.sourceforge.net>
parents:
diff changeset
48 yield address
c2d0d3e9099d svn repository setup
Stefan Seefeld <stefan@users.sourceforge.net>
parents:
diff changeset
49
c2d0d3e9099d svn repository setup
Stefan Seefeld <stefan@users.sourceforge.net>
parents:
diff changeset
50 def audit_user_fields(db, cl, nodeid, newvalues):
c2d0d3e9099d svn repository setup
Stefan Seefeld <stefan@users.sourceforge.net>
parents:
diff changeset
51 ''' Make sure user properties are valid.
c2d0d3e9099d svn repository setup
Stefan Seefeld <stefan@users.sourceforge.net>
parents:
diff changeset
52
c2d0d3e9099d svn repository setup
Stefan Seefeld <stefan@users.sourceforge.net>
parents:
diff changeset
53 - email address is syntactically valid
c2d0d3e9099d svn repository setup
Stefan Seefeld <stefan@users.sourceforge.net>
parents:
diff changeset
54 - email address is unique
c2d0d3e9099d svn repository setup
Stefan Seefeld <stefan@users.sourceforge.net>
parents:
diff changeset
55 - roles specified exist
c2d0d3e9099d svn repository setup
Stefan Seefeld <stefan@users.sourceforge.net>
parents:
diff changeset
56 - timezone is valid
c2d0d3e9099d svn repository setup
Stefan Seefeld <stefan@users.sourceforge.net>
parents:
diff changeset
57 '''
c2d0d3e9099d svn repository setup
Stefan Seefeld <stefan@users.sourceforge.net>
parents:
diff changeset
58
c2d0d3e9099d svn repository setup
Stefan Seefeld <stefan@users.sourceforge.net>
parents:
diff changeset
59 for address in get_addresses(newvalues):
c2d0d3e9099d svn repository setup
Stefan Seefeld <stefan@users.sourceforge.net>
parents:
diff changeset
60 if not valid_address(address):
5378
35ea9b1efc14 Python 3 preparation: "raise" syntax.
Joseph Myers <jsm@polyomino.org.uk>
parents: 4633
diff changeset
61 raise ValueError('Email address syntax is invalid "%s"'%address)
4024
c2d0d3e9099d svn repository setup
Stefan Seefeld <stefan@users.sourceforge.net>
parents:
diff changeset
62
c2d0d3e9099d svn repository setup
Stefan Seefeld <stefan@users.sourceforge.net>
parents:
diff changeset
63 check_main = db.user.stringFind(address=address)
c2d0d3e9099d svn repository setup
Stefan Seefeld <stefan@users.sourceforge.net>
parents:
diff changeset
64 # make sure none of the alts are owned by anyone other than us (x!=nodeid)
c2d0d3e9099d svn repository setup
Stefan Seefeld <stefan@users.sourceforge.net>
parents:
diff changeset
65 check_alts = [x for x in db.user.filter(None, {'alternate_addresses' : address}) if x != nodeid]
c2d0d3e9099d svn repository setup
Stefan Seefeld <stefan@users.sourceforge.net>
parents:
diff changeset
66 if check_main or check_alts:
5378
35ea9b1efc14 Python 3 preparation: "raise" syntax.
Joseph Myers <jsm@polyomino.org.uk>
parents: 4633
diff changeset
67 raise ValueError('Email address %s already in use' % address)
4024
c2d0d3e9099d svn repository setup
Stefan Seefeld <stefan@users.sourceforge.net>
parents:
diff changeset
68
4633
ad1a337cb5b7 Prevent AttributeError when removing all roles of a user
Thomas Arendsen Hein <thomas@intevation.de>
parents: 4632
diff changeset
69 newroles = newvalues.get('roles')
ad1a337cb5b7 Prevent AttributeError when removing all roles of a user
Thomas Arendsen Hein <thomas@intevation.de>
parents: 4632
diff changeset
70 if newroles:
ad1a337cb5b7 Prevent AttributeError when removing all roles of a user
Thomas Arendsen Hein <thomas@intevation.de>
parents: 4632
diff changeset
71 for rolename in [r.lower().strip() for r in newroles.split(',')]:
5381
0942fe89e82e Python 3 preparation: change "x.has_key(y)" to "y in x".
Joseph Myers <jsm@polyomino.org.uk>
parents: 5378
diff changeset
72 if rolename and rolename not in db.security.role:
5378
35ea9b1efc14 Python 3 preparation: "raise" syntax.
Joseph Myers <jsm@polyomino.org.uk>
parents: 4633
diff changeset
73 raise ValueError('Role "%s" does not exist'%rolename)
4024
c2d0d3e9099d svn repository setup
Stefan Seefeld <stefan@users.sourceforge.net>
parents:
diff changeset
74
c2d0d3e9099d svn repository setup
Stefan Seefeld <stefan@users.sourceforge.net>
parents:
diff changeset
75 tz = newvalues.get('timezone', None)
c2d0d3e9099d svn repository setup
Stefan Seefeld <stefan@users.sourceforge.net>
parents:
diff changeset
76 if tz:
c2d0d3e9099d svn repository setup
Stefan Seefeld <stefan@users.sourceforge.net>
parents:
diff changeset
77 # if they set a new timezone validate the timezone by attempting to
c2d0d3e9099d svn repository setup
Stefan Seefeld <stefan@users.sourceforge.net>
parents:
diff changeset
78 # use it before we store it to the db.
c2d0d3e9099d svn repository setup
Stefan Seefeld <stefan@users.sourceforge.net>
parents:
diff changeset
79 import roundup.date
c2d0d3e9099d svn repository setup
Stefan Seefeld <stefan@users.sourceforge.net>
parents:
diff changeset
80 import datetime
c2d0d3e9099d svn repository setup
Stefan Seefeld <stefan@users.sourceforge.net>
parents:
diff changeset
81 try:
c2d0d3e9099d svn repository setup
Stefan Seefeld <stefan@users.sourceforge.net>
parents:
diff changeset
82 TZ = roundup.date.get_timezone(tz)
c2d0d3e9099d svn repository setup
Stefan Seefeld <stefan@users.sourceforge.net>
parents:
diff changeset
83 dt = datetime.datetime.now()
c2d0d3e9099d svn repository setup
Stefan Seefeld <stefan@users.sourceforge.net>
parents:
diff changeset
84 local = TZ.localize(dt).utctimetuple()
c2d0d3e9099d svn repository setup
Stefan Seefeld <stefan@users.sourceforge.net>
parents:
diff changeset
85 except IOError:
5378
35ea9b1efc14 Python 3 preparation: "raise" syntax.
Joseph Myers <jsm@polyomino.org.uk>
parents: 4633
diff changeset
86 raise ValueError('Timezone "%s" does not exist' % tz)
4024
c2d0d3e9099d svn repository setup
Stefan Seefeld <stefan@users.sourceforge.net>
parents:
diff changeset
87 except ValueError:
5378
35ea9b1efc14 Python 3 preparation: "raise" syntax.
Joseph Myers <jsm@polyomino.org.uk>
parents: 4633
diff changeset
88 raise ValueError('Timezone "%s" exceeds valid range [-23...23]' % tz)
4024
c2d0d3e9099d svn repository setup
Stefan Seefeld <stefan@users.sourceforge.net>
parents:
diff changeset
89
c2d0d3e9099d svn repository setup
Stefan Seefeld <stefan@users.sourceforge.net>
parents:
diff changeset
90 def init(db):
c2d0d3e9099d svn repository setup
Stefan Seefeld <stefan@users.sourceforge.net>
parents:
diff changeset
91 # fire before changes are made
c2d0d3e9099d svn repository setup
Stefan Seefeld <stefan@users.sourceforge.net>
parents:
diff changeset
92 db.user.audit('set', audit_user_fields)
c2d0d3e9099d svn repository setup
Stefan Seefeld <stefan@users.sourceforge.net>
parents:
diff changeset
93 db.user.audit('create', audit_user_fields)
4632
4f25640b5521 Unified all copies of detectors/userauditor.py
Thomas Arendsen Hein <thomas@intevation.de>
parents: 4024
diff changeset
94
4f25640b5521 Unified all copies of detectors/userauditor.py
Thomas Arendsen Hein <thomas@intevation.de>
parents: 4024
diff changeset
95 # vim: sts=4 sw=4 et si

Roundup Issue Tracker: http://roundup-tracker.org/