1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/themes/mercurialroundup/detectors/userauditor.py Mon Jun 07 01:14:02 2010 +0200
1.3 @@ -0,0 +1,94 @@
1.4 +# Copyright (c) 2003 Richard Jones (richard@mechanicalcat.net)
1.5 +#
1.6 +# Permission is hereby granted, free of charge, to any person obtaining a copy
1.7 +# of this software and associated documentation files (the "Software"), to deal
1.8 +# in the Software without restriction, including without limitation the rights
1.9 +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1.10 +# copies of the Software, and to permit persons to whom the Software is
1.11 +# furnished to do so, subject to the following conditions:
1.12 +#
1.13 +# The above copyright notice and this permission notice shall be included in
1.14 +# all copies or substantial portions of the Software.
1.15 +#
1.16 +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1.17 +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1.18 +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1.19 +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1.20 +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
1.21 +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
1.22 +# SOFTWARE.
1.23 +#
1.24 +#$Id: userauditor.py,v 1.9 2007-09-12 21:11:13 jpend Exp $
1.25 +
1.26 +import re
1.27 +
1.28 +# regular expression thanks to: http://www.regular-expressions.info/email.html
1.29 +# this is the "99.99% solution for syntax only".
1.30 +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]))")
1.31 +email_rfc = re.compile('^' + email_regexp[0] + '@' + email_regexp[1] + '$', re.IGNORECASE)
1.32 +email_local = re.compile('^' + email_regexp[0] + '$', re.IGNORECASE)
1.33 +
1.34 +def valid_address(address):
1.35 + ''' If we see an @-symbol in the address then check against the full
1.36 + RFC syntax. Otherwise it is a local-only address so only check
1.37 + the local part of the RFC syntax.
1.38 + '''
1.39 + if '@' in address:
1.40 + return email_rfc.match(address)
1.41 + else:
1.42 + return email_local.match(address)
1.43 +
1.44 +def get_addresses(user):
1.45 + ''' iterate over all known addresses in a newvalues dict
1.46 + this takes of the address/alterate_addresses handling
1.47 + '''
1.48 + if user.has_key('address'):
1.49 + yield user['address']
1.50 + if user.get('alternate_addresses', None):
1.51 + for address in user['alternate_addresses'].split('\n'):
1.52 + yield address
1.53 +
1.54 +def audit_user_fields(db, cl, nodeid, newvalues):
1.55 + ''' Make sure user properties are valid.
1.56 +
1.57 + - email address is syntactically valid
1.58 + - email address is unique
1.59 + - roles specified exist
1.60 + - timezone is valid
1.61 + '''
1.62 +
1.63 + for address in get_addresses(newvalues):
1.64 + if not valid_address(address):
1.65 + raise ValueError, 'Email address syntax is invalid'
1.66 +
1.67 + check_main = db.user.stringFind(address=address)
1.68 + # make sure none of the alts are owned by anyone other than us (x!=nodeid)
1.69 + check_alts = [x for x in db.user.filter(None, {'alternate_addresses' : address}) if x != nodeid]
1.70 + if check_main or check_alts:
1.71 + raise ValueError, 'Email address %s already in use' % address
1.72 +
1.73 + for rolename in [r.lower().strip() for r in newvalues.get('roles', '').split(',')]:
1.74 + if rolename and not db.security.role.has_key(rolename):
1.75 + raise ValueError, 'Role "%s" does not exist'%rolename
1.76 +
1.77 + tz = newvalues.get('timezone', None)
1.78 + if tz:
1.79 + # if they set a new timezone validate the timezone by attempting to
1.80 + # use it before we store it to the db.
1.81 + import roundup.date
1.82 + import datetime
1.83 + try:
1.84 + TZ = roundup.date.get_timezone(tz)
1.85 + dt = datetime.datetime.now()
1.86 + local = TZ.localize(dt).utctimetuple()
1.87 + except IOError:
1.88 + raise ValueError, 'Timezone "%s" does not exist' % tz
1.89 + except ValueError:
1.90 + raise ValueError, 'Timezone "%s" exceeds valid range [-23...23]' % tz
1.91 +
1.92 +def init(db):
1.93 + # fire before changes are made
1.94 + db.user.audit('set', audit_user_fields)
1.95 + db.user.audit('create', audit_user_fields)
1.96 +
1.97 +# vim: sts=4 sw=4 et si