Code

svn repository setup
[roundup.git] / templates / classic / detectors / userauditor.py
1 # Copyright (c) 2003 Richard Jones (richard@mechanicalcat.net)
2 #
3 # Permission is hereby granted, free of charge, to any person obtaining a copy
4 # of this software and associated documentation files (the "Software"), to deal
5 # in the Software without restriction, including without limitation the rights
6 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 # copies of the Software, and to permit persons to whom the Software is
8 # furnished to do so, subject to the following conditions:
9 #
10 #   The above copyright notice and this permission notice shall be included in
11 #   all copies or substantial portions of the Software.
12 #
13 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 # SOFTWARE.
20 #
21 #$Id: userauditor.py,v 1.9 2007-09-12 21:11:13 jpend Exp $
23 import re
25 # regular expression thanks to: http://www.regular-expressions.info/email.html
26 # this is the "99.99% solution for syntax only".
27 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]))")
28 email_rfc = re.compile('^' + email_regexp[0] + '@' + email_regexp[1] + '$', re.IGNORECASE)
29 email_local = re.compile('^' + email_regexp[0] + '$', re.IGNORECASE)
31 def valid_address(address):
32     ''' If we see an @-symbol in the address then check against the full
33         RFC syntax. Otherwise it is a local-only address so only check
34         the local part of the RFC syntax.
35     '''
36     if '@' in address:
37         return email_rfc.match(address)
38     else:
39         return email_local.match(address)
41 def get_addresses(user):
42     ''' iterate over all known addresses in a newvalues dict
43         this takes of the address/alterate_addresses handling
44     '''
45     if user.has_key('address'):
46         yield user['address']
47     if user.get('alternate_addresses', None):
48         for address in user['alternate_addresses'].split('\n'):
49             yield address
51 def audit_user_fields(db, cl, nodeid, newvalues):
52     ''' Make sure user properties are valid.
54         - email address is syntactically valid
55         - email address is unique
56         - roles specified exist
57         - timezone is valid
58     '''
60     for address in get_addresses(newvalues):
61         if not valid_address(address):
62             raise ValueError, 'Email address syntax is invalid'
64         check_main = db.user.stringFind(address=address)
65         # make sure none of the alts are owned by anyone other than us (x!=nodeid)
66         check_alts = [x for x in db.user.filter(None, {'alternate_addresses' : address}) if x != nodeid]
67         if check_main or check_alts:
68             raise ValueError, 'Email address %s already in use' % address
70     for rolename in [r.lower().strip() for r in newvalues.get('roles', '').split(',')]:
71             if rolename and not db.security.role.has_key(rolename):
72                 raise ValueError, 'Role "%s" does not exist'%rolename
74     tz = newvalues.get('timezone', None)
75     if tz:
76         # if they set a new timezone validate the timezone by attempting to
77         # use it before we store it to the db.
78         import roundup.date
79         import datetime
80         try:
81             TZ = roundup.date.get_timezone(tz)
82             dt = datetime.datetime.now()
83             local = TZ.localize(dt).utctimetuple()
84         except IOError:
85             raise ValueError, 'Timezone "%s" does not exist' % tz
86         except ValueError:
87             raise ValueError, 'Timezone "%s" exceeds valid range [-23...23]' % tz
89 def init(db):
90     # fire before changes are made
91     db.user.audit('set', audit_user_fields)
92     db.user.audit('create', audit_user_fields)
94 # vim: sts=4 sw=4 et si