|
18 | 18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
19 | 19 | # SOFTWARE. |
20 | 20 | # |
21 | | -#$Id: userauditor.py,v 1.8 2007-09-11 21:28:29 jpend Exp $ |
| 21 | +#$Id: userauditor.py,v 1.9 2007-09-12 21:11:13 jpend Exp $ |
| 22 | + |
| 23 | +import re |
| 24 | + |
| 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) |
| 30 | + |
| 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) |
| 40 | + |
| 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 |
22 | 50 |
|
23 | 51 | def audit_user_fields(db, cl, nodeid, newvalues): |
24 | 52 | ''' Make sure user properties are valid. |
25 | 53 |
|
26 | | - - email address has no spaces in it |
| 54 | + - email address is syntactically valid |
27 | 55 | - email address is unique |
28 | 56 | - roles specified exist |
29 | 57 | - timezone is valid |
30 | 58 | ''' |
31 | | - if newvalues.has_key('address'): |
32 | | - address = newvalues['address'] |
33 | | - if address: |
34 | | - if ' ' in address: |
35 | | - raise ValueError, 'Email address must not contain spaces' |
36 | | - user = db.user.stringFind(address=address) |
37 | | - if len(user): |
38 | | - raise ValueError, 'Email address already in use' |
| 59 | + |
| 60 | + for address in get_addresses(newvalues): |
| 61 | + if not valid_address(address): |
| 62 | + raise ValueError, 'Email address syntax is invalid' |
| 63 | + |
| 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 |
39 | 69 |
|
40 | 70 | for rolename in [r.lower().strip() for r in newvalues.get('roles', '').split(',')]: |
41 | 71 | if rolename and not db.security.role.has_key(rolename): |
42 | 72 | raise ValueError, 'Role "%s" does not exist'%rolename |
43 | 73 |
|
44 | | - if newvalues.has_key('timezone'): |
45 | | - tz = newvalues['timezone'] |
46 | | - if tz: |
47 | | - # if they set a new timezone validate the timezone by attempting to |
48 | | - # use it before we store it to the db. |
49 | | - import roundup.date |
50 | | - import datetime |
51 | | - try: |
52 | | - TZ = roundup.date.get_timezone(tz) |
53 | | - dt = datetime.datetime.now() |
54 | | - local = TZ.localize(dt).utctimetuple() |
55 | | - except IOError: |
56 | | - raise ValueError, 'Timezone "%s" does not exist' % tz |
57 | | - except ValueError: |
58 | | - raise ValueError, 'Timezone "%s" exceeds valid range [-23...23]' % tz |
| 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 |
59 | 88 |
|
60 | 89 | def init(db): |
61 | 90 | # fire before changes are made |
|
0 commit comments