diff --git a/roundup/mailgw.py b/roundup/mailgw.py
index 53dc401d20dea4d9c4ba0613aa34621586ec9f67..edbfb89254cb66621090fc246985b0dd8fb93583 100644 (file)
--- a/roundup/mailgw.py
+++ b/roundup/mailgw.py
an exception, the original message is bounced back to the sender with the
explanatory message given in the exception.
-$Id: mailgw.py,v 1.74 2002-05-29 01:16:17 richard Exp $
+$Id: mailgw.py,v 1.81 2002-08-19 00:21:56 richard Exp $
'''
class MailUsageHelp(Exception):
pass
-class UnAuthorized(Exception):
+class Unauthorized(Exception):
""" Access denied """
+def initialiseSecurity(security):
+ ''' Create some Permissions and Roles on the security object
+
+ This function is directly invoked by security.Security.__init__()
+ as a part of the Security object instantiation.
+ '''
+ security.addPermission(name="Email Registration",
+ description="Anonymous may register through e-mail")
+ p = security.addPermission(name="Email Access",
+ description="User may use the email interface")
+ security.addPermissionToRole('Admin', p)
+
class Message(mimetools.Message):
''' subclass mimetools.Message so we can retrieve the parts of the
message...
self.instance = instance
self.db = db
+ # should we trap exceptions (normal usage) or pass them through
+ # (for testing)
+ self.trapExceptions = 1
+
def main(self, fp):
''' fp - the file from which to read the Message.
'''
# its way into here... try to handle it gracefully
sendto = message.getaddrlist('from')
if sendto:
+ if not self.trapExceptions:
+ return self.handle_message(message)
try:
return self.handle_message(message)
except MailUsageHelp:
m.append('\n\nMail Gateway Help\n=================')
m.append(fulldoc)
m = self.bounce_message(message, sendto, m)
- except UnAuthorized, value:
+ except Unauthorized, value:
# just inform the user that he is not authorized
sendto = [sendto[0][1]]
m = ['']
curvalue = []
# handle each add/remove in turn
+ # keep an extra list for all items that are
+ # definitely in the new list (in case of e.g.
+ # <propname>=A,+B, which should replace the old
+ # list with A,B)
+ set = 0
+ newvalue = []
for item in value.split(','):
item = item.strip()
item = item[1:]
elif item.startswith('+'):
item = item[1:]
+ else:
+ set = 1
# look up the value
try:
'for %s.'%(item, propname))
continue
else:
+ newvalue.append(item)
if item not in curvalue:
curvalue.append(item)
- # that's it, set the new Multilink property value
- props[propname] = curvalue
+ # that's it, set the new Multilink property value,
+ # or overwrite it completely
+ if set:
+ props[propname] = newvalue
+ else:
+ props[propname] = curvalue
+ elif isinstance(proptype, hyperdb.Boolean):
+ value = value.strip()
+ props[propname] = value.lower() in ('yes', 'true', 'on', '1')
+ elif isinstance(proptype, hyperdb.Number):
+ value = value.strip()
+ props[propname] = int(value)
# handle any errors parsing the argument list
if errors:
# handle the users
#
- # Don't create users if ANONYMOUS_REGISTER_MAIL is denied
- # ... fall back on ANONYMOUS_REGISTER if the other doesn't exist
+ # Don't create users if anonymous isn't allowed to register
create = 1
- if hasattr(self.instance, 'ANONYMOUS_REGISTER_MAIL'):
- if self.instance.ANONYMOUS_REGISTER_MAIL == 'deny':
- create = 0
- elif self.instance.ANONYMOUS_REGISTER == 'deny':
+ anonid = self.db.user.lookup('anonymous')
+ if not self.db.security.hasPermission('Email Registration', anonid):
create = 0
- author = self.db.uidFromAddress(message.getaddrlist('from')[0],
+ # ok, now figure out who the author is - create a new user if the
+ # "create" flag is true
+ author = uidFromAddress(self.db, message.getaddrlist('from')[0],
create=create)
+
+ # no author? means we're not author
if not author:
- raise UnAuthorized, '''
+ raise Unauthorized, '''
You are not a registered user.
Unknown address: %s
'''%message.getaddrlist('from')[0][1]
+ # make sure the author has permission to use the email interface
+ if not self.db.security.hasPermission('Email Access', author):
+ raise Unauthorized, 'You are not permitted to access this tracker.'
+
# the author may have been created - make sure the change is
# committed before we reopen the database
self.db.commit()
# look up the recipient - create if necessary (and we're
# allowed to)
- recipient = self.db.uidFromAddress(recipient, create)
+ recipient = uidFromAddress(self.db, recipient, create)
# if all's well, add the recipient to the list
if recipient:
return nodeid
+def extractUserFromList(userClass, users):
+ '''Given a list of users, try to extract the first non-anonymous user
+ and return that user, otherwise return None
+ '''
+ if len(users) > 1:
+ for user in users:
+ # make sure we don't match the anonymous or admin user
+ if userClass.get(user, 'username') in ('admin', 'anonymous'):
+ continue
+ # first valid match will do
+ return user
+ # well, I guess we have no choice
+ return user[0]
+ elif users:
+ return users[0]
+ return None
+
+def uidFromAddress(db, address, create=1):
+ ''' address is from the rfc822 module, and therefore is (name, addr)
+
+ user is created if they don't exist in the db already
+ '''
+ (realname, address) = address
+
+ # try a straight match of the address
+ user = extractUserFromList(db.user, db.user.stringFind(address=address))
+ if user is not None: return user
+
+ # try the user alternate addresses if possible
+ props = db.user.getprops()
+ if props.has_key('alternate_addresses'):
+ users = db.user.filter(None, {'alternate_addresses': address},
+ [], [])
+ user = extractUserFromList(db.user, users)
+ if user is not None: return user
+
+ # try to match the username to the address (for local
+ # submissions where the address is empty)
+ user = extractUserFromList(db.user, db.user.stringFind(username=address))
+
+ # couldn't match address or username, so create a new user
+ if create:
+ return db.user.create(username=address, address=address,
+ realname=realname, roles=db.config.NEW_EMAIL_USER_ROLES)
+ else:
+ return 0
+
def parseContent(content, keep_citations, keep_body,
blank_line=re.compile(r'[\r\n]+\s*[\r\n]+'),
eol=re.compile(r'[\r\n]+'),
#
# $Log: not supported by cvs2svn $
+# Revision 1.80 2002/08/01 00:56:22 richard
+# Added the web access and email access permissions, so people can restrict
+# access to users who register through the email interface (for example).
+# Also added "security" command to the roundup-admin interface to display the
+# Role/Permission config for an instance.
+#
+# Revision 1.79 2002/07/26 08:26:59 richard
+# Very close now. The cgi and mailgw now use the new security API. The two
+# templates have been migrated to that setup. Lots of unit tests. Still some
+# issue in the web form for editing Roles assigned to users.
+#
+# Revision 1.78 2002/07/25 07:14:06 richard
+# Bugger it. Here's the current shape of the new security implementation.
+# Still to do:
+# . call the security funcs from cgi and mailgw
+# . change shipped templates to include correct initialisation and remove
+# the old config vars
+# ... that seems like a lot. The bulk of the work has been done though. Honest :)
+#
+# Revision 1.77 2002/07/18 11:17:31 gmcm
+# Add Number and Boolean types to hyperdb.
+# Add conversion cases to web, mail & admin interfaces.
+# Add storage/serialization cases to back_anydbm & back_metakit.
+#
+# Revision 1.76 2002/07/10 06:39:37 richard
+# . made mailgw handle set and modify operations on multilinks (bug #579094)
+#
+# Revision 1.75 2002/07/09 01:21:24 richard
+# Added ability for unit tests to turn off exception handling in mailgw so
+# that exceptions are reported earlier (and hence make sense).
+#
+# Revision 1.74 2002/05/29 01:16:17 richard
+# Sorry about this huge checkin! It's fixing a lot of related stuff in one go
+# though.
+#
+# . #541941 ] changing multilink properties by mail
+# . #526730 ] search for messages capability
+# . #505180 ] split MailGW.handle_Message
+# - also changed cgi client since it was duplicating the functionality
+# . build htmlbase if tests are run using CVS checkout (removed note from
+# installation.txt)
+# . don't create an empty message on email issue creation if the email is empty
+#
# Revision 1.73 2002/05/22 04:12:05 richard
# . applied patch #558876 ] cgi client customization
# ... with significant additions and modifications ;)