diff --git a/roundup/cgi/actions.py b/roundup/cgi/actions.py
index fc2d5c7d236cc26fcf8c94f47a974d4d89e9bade..41f5979467dee8b5dceed43bdc0cd8298dd08fcf 100755 (executable)
--- a/roundup/cgi/actions.py
+++ b/roundup/cgi/actions.py
-import re, cgi, StringIO, urllib, time, random, csv, codecs
+import re, cgi, time, random, csv, codecs
from roundup import hyperdb, token, date, password
from roundup.actions import Action as BaseAction
import roundup.exceptions
from roundup.cgi import exceptions, templating
from roundup.mailgw import uidFromAddress
+from roundup.anypy import io_, urllib_
__all__ = ['Action', 'ShowAction', 'RetireAction', 'SearchAction',
'EditCSVAction', 'EditItemAction', 'PassResetAction',
if (self.permissionType and
not self.hasPermission(self.permissionType)):
info = {'action': self.name, 'classname': self.classname}
- raise exceptions.Unauthorised, self._(
+ raise exceptions.Unauthorised(self._(
'You do not have permission to '
- '%(action)s the %(classname)s class.')%info
+ '%(action)s the %(classname)s class.')%info)
_marker = []
def hasPermission(self, permission, classname=_marker, itemid=None, property=None):
def handle(self):
"""Show a node of a particular class/id."""
t = n = ''
- for key in self.form.keys():
+ for key in self.form:
if self.typere.match(key):
t = self.form[key].value.strip()
elif self.numre.match(key):
n = self.form[key].value.strip()
if not t:
- raise ValueError, self._('No type specified')
+ raise ValueError(self._('No type specified'))
if not n:
- raise exceptions.SeriousError, self._('No ID entered')
+ raise exceptions.SeriousError(self._('No ID entered'))
try:
int(n)
except ValueError:
d = {'input': n, 'classname': t}
- raise exceptions.SeriousError, self._(
- '"%(input)s" is not an ID (%(classname)s ID required)')%d
+ raise exceptions.SeriousError(self._(
+ '"%(input)s" is not an ID (%(classname)s ID required)')%d)
url = '%s%s%s'%(self.base, t, n)
- raise exceptions.Redirect, url
+ raise exceptions.Redirect(url)
class RetireAction(Action):
name = 'retire'
"""Retire the context item."""
# ensure modification comes via POST
if self.client.env['REQUEST_METHOD'] != 'POST':
- self.client.error_message.append(self._('Invalid request'))
+ raise roundup.exceptions.Reject(self._('Invalid request'))
# if we want to view the index template now, then unset the itemid
# context info (a special-case for retire actions on the index page)
# make sure we don't try to retire admin or anonymous
if self.classname == 'user' and \
self.db.user.get(itemid, 'username') in ('admin', 'anonymous'):
- raise ValueError, self._(
- 'You may not retire the admin or anonymous user')
+ raise ValueError(self._(
+ 'You may not retire the admin or anonymous user'))
# check permission
if not self.hasPermission('Retire', classname=self.classname,
itemid=itemid):
- raise exceptions.Unauthorised, self._(
+ raise exceptions.Unauthorised(self._(
'You do not have permission to retire %(class)s'
- ) % {'class': self.classname}
+ ) % {'class': self.classname})
# do the retire
self.db.getclass(self.classname).retire(itemid)
try:
qid = self.db.query.lookup(old_queryname)
if not self.hasPermission('Edit', 'query', itemid=qid):
- raise exceptions.Unauthorised, self._(
- "You do not have permission to edit queries")
+ raise exceptions.Unauthorised(self._(
+ "You do not have permission to edit queries"))
self.db.query.set(qid, klass=self.classname, url=url)
except KeyError:
# create a query
if not self.hasPermission('Create', 'query'):
- raise exceptions.Unauthorised, self._(
- "You do not have permission to store queries")
+ raise exceptions.Unauthorised(self._(
+ "You do not have permission to store queries"))
qid = self.db.query.create(name=queryname,
klass=self.classname, url=url)
else:
if old_queryname != self.db.query.get(qid, 'name'):
continue
if not self.hasPermission('Edit', 'query', itemid=qid):
- raise exceptions.Unauthorised, self._(
- "You do not have permission to edit queries")
+ raise exceptions.Unauthorised(self._(
+ "You do not have permission to edit queries"))
self.db.query.set(qid, klass=self.classname,
url=url, name=queryname)
else:
# create a query
if not self.hasPermission('Create', 'query'):
- raise exceptions.Unauthorised, self._(
- "You do not have permission to store queries")
+ raise exceptions.Unauthorised(self._(
+ "You do not have permission to store queries"))
qid = self.db.query.create(name=queryname,
klass=self.classname, url=url, private_for=uid)
def fakeFilterVars(self):
"""Add a faked :filter form variable for each filtering prop."""
cls = self.db.classes[self.classname]
- for key in self.form.keys():
+ for key in self.form:
prop = cls.get_transitive_prop(key)
if not prop:
continue
def getFromForm(self, name):
for key in ('@' + name, ':' + name):
- if self.form.has_key(key):
+ if key in self.form:
return self.form[key].value.strip()
return ''
"""
# ensure modification comes via POST
if self.client.env['REQUEST_METHOD'] != 'POST':
- self.client.error_message.append(self._('Invalid request'))
+ raise roundup.exceptions.Reject(self._('Invalid request'))
# figure the properties list for the class
cl = self.db.classes[self.classname]
- props_without_id = cl.getprops(protected=0).keys()
+ props_without_id = list(cl.getprops(protected=0))
# the incoming CSV data will always have the properties in colums
# sorted and starting with the "id" column
props = ['id'] + props_without_id
# do the edit
- rows = StringIO.StringIO(self.form['rows'].value)
+ rows = io_.StringIO(self.form['rows'].value)
reader = csv.reader(rows)
found = {}
line = 0
# check permission to create this item
if not self.hasPermission('Create', classname=self.classname):
- raise exceptions.Unauthorised, self._(
+ raise exceptions.Unauthorised(self._(
'You do not have permission to create %(class)s'
- ) % {'class': self.classname}
+ ) % {'class': self.classname})
+ elif cl.hasnode(itemid) and cl.is_retired(itemid):
+ # If a CSV line just mentions an id and the corresponding
+ # item is retired, then the item is restored.
+ cl.restore(itemid)
+ continue
else:
exists = 1
# check permission to edit this property on this item
if exists and not self.hasPermission('Edit', itemid=itemid,
classname=self.classname, property=name):
- raise exceptions.Unauthorised, self._(
+ raise exceptions.Unauthorised(self._(
'You do not have permission to edit %(class)s'
- ) % {'class': self.classname}
+ ) % {'class': self.classname})
prop = cl.properties[name]
value = value.strip()
# retire the removed entries
for itemid in cl.list():
- if not found.has_key(itemid):
+ if itemid not in found:
# check permission to retire this item
if not self.hasPermission('Retire', itemid=itemid,
classname=self.classname):
- raise exceptions.Unauthorised, self._(
+ raise exceptions.Unauthorised(self._(
'You do not have permission to retire %(class)s'
- ) % {'class': self.classname}
+ ) % {'class': self.classname})
cl.retire(itemid)
# all OK
links = {}
for cn, nodeid, propname, vlist in all_links:
numeric_id = int (nodeid or 0)
- if not (numeric_id > 0 or all_props.has_key((cn, nodeid))):
+ if not (numeric_id > 0 or (cn, nodeid) in all_props):
# link item to link to doesn't (and won't) exist
continue
for value in vlist:
- if not all_props.has_key(value):
+ if value not in all_props:
# link item to link to doesn't (and won't) exist
continue
deps.setdefault((cn, nodeid), []).append(value)
# loop detection
change = 0
while len(all_props) != len(done):
- for needed in all_props.keys():
- if done.has_key(needed):
+ for needed in all_props:
+ if needed in done:
continue
tlist = deps.get(needed, [])
for target in tlist:
- if not done.has_key(target):
+ if target not in done:
break
else:
done[needed] = 1
order.append(needed)
change = 1
if not change:
- raise ValueError, 'linking must not loop!'
+ raise ValueError('linking must not loop!')
# now, edit / create
m = []
# and some nice feedback for the user
if props:
- info = ', '.join(map(self._, props.keys()))
+ info = ', '.join(map(self._, props))
m.append(
self._('%(class)s %(id)s %(properties)s edited ok')
% {'class':cn, 'id':nodeid, 'properties':info})
% {'class':cn, 'id':newid})
# fill in new ids in links
- if links.has_key(needed):
+ if needed in links:
for linkcn, linkid, linkprop in links[needed]:
props = all_props[(linkcn, linkid)]
cl = self.db.classes[linkcn]
propdef = cl.getprops()[linkprop]
- if not props.has_key(linkprop):
+ if linkprop not in props:
if linkid is None or linkid.startswith('-'):
# linking to a new item
if isinstance(propdef, hyperdb.Multilink):
- props[linkprop] = [newid]
+ props[linkprop] = [nodeid]
else:
- props[linkprop] = newid
+ props[linkprop] = nodeid
else:
# linking to an existing item
if isinstance(propdef, hyperdb.Multilink):
existing.append(nodeid)
props[linkprop] = existing
else:
- props[linkprop] = newid
+ props[linkprop] = nodeid
return '<br>'.join(m)
"""Change the node based on the contents of the form."""
# check for permission
if not self.editItemPermission(props, classname=cn, itemid=nodeid):
- raise exceptions.Unauthorised, self._(
+ raise exceptions.Unauthorised(self._(
'You do not have permission to edit %(class)s'
- ) % {'class': cn}
+ ) % {'class': cn})
# make the changes
cl = self.db.classes[cn]
"""Create a node based on the contents of the form."""
# check for permission
if not self.newItemPermission(props, classname=cn):
- raise exceptions.Unauthorised, self._(
+ raise exceptions.Unauthorised(self._(
'You do not have permission to create %(class)s'
- ) % {'class': cn}
+ ) % {'class': cn})
# create the node and return its id
cl = self.db.classes[cn]
Base behaviour is to check the user can edit this class. No additional
property checks are made.
"""
+
if not classname :
classname = self.client.classname
- return self.hasPermission('Create', classname=classname)
+
+ if not self.hasPermission('Create', classname=classname):
+ return 0
+
+ # Check Create permission for each property, to avoid being able
+ # to set restricted ones on new item creation
+ for key in props:
+ if not self.hasPermission('Create', classname=classname,
+ property=key):
+ return 0
+ return 1
class EditItemAction(EditCommon):
def lastUserActivity(self):
- if self.form.has_key(':lastactivity'):
+ if ':lastactivity' in self.form:
d = date.Date(self.form[':lastactivity'].value)
- elif self.form.has_key('@lastactivity'):
+ elif '@lastactivity' in self.form:
d = date.Date(self.form['@lastactivity'].value)
else:
return None
props, links = self.client.parsePropsFromForm()
key = (self.classname, self.nodeid)
# we really only collide for direct prop edit conflicts
- return props[key].keys()
+ return list(props[key])
else:
return []
"""
# ensure modification comes via POST
if self.client.env['REQUEST_METHOD'] != 'POST':
- self.client.error_message.append(self._('Invalid request'))
+ raise roundup.exceptions.Reject(self._('Invalid request'))
user_activity = self.lastUserActivity()
if user_activity:
# we will want to include index-page args in this URL too
if self.nodeid is not None:
url += self.nodeid
- url += '?@ok_message=%s&@template=%s'%(urllib.quote(message),
- urllib.quote(self.template))
+ url += '?@ok_message=%s&@template=%s'%(urllib_.quote(message),
+ urllib_.quote(self.template))
if self.nodeid is None:
req = templating.HTMLRequest(self.client)
url += '&' + req.indexargs_url('', {})[1:]
- raise exceptions.Redirect, url
+ raise exceptions.Redirect(url)
class NewItemAction(EditCommon):
def handle(self):
'''
# ensure modification comes via POST
if self.client.env['REQUEST_METHOD'] != 'POST':
- self.client.error_message.append(self._('Invalid request'))
+ raise roundup.exceptions.Reject(self._('Invalid request'))
# parse the props from the form
try:
% str(message))
return
- # guard against new user creation that would bypass security checks
- for key in props:
- if 'user' in key:
- return
-
# handle the props - edit or create
try:
# when it hits the None element, it'll set self.nodeid
self.db.commit()
# redirect to the new item's page
- raise exceptions.Redirect, '%s%s%s?@ok_message=%s&@template=%s' % (
- self.base, self.classname, self.nodeid, urllib.quote(messages),
- urllib.quote(self.template))
+ raise exceptions.Redirect('%s%s%s?@ok_message=%s&@template=%s' % (
+ self.base, self.classname, self.nodeid, urllib_.quote(messages),
+ urllib_.quote(self.template)))
class PassResetAction(Action):
def handle(self):
"""
otks = self.db.getOTKManager()
- if self.form.has_key('otk'):
+ if 'otk' in self.form:
# pull the rego information out of the otk database
otk = self.form['otk'].value
uid = otks.get(otk, 'uid', default=None)
return
# no OTK, so now figure the user
- if self.form.has_key('username'):
+ if 'username' in self.form:
name = self.form['username'].value
try:
uid = self.db.user.lookup(name)
self.client.error_message.append(self._('Unknown username'))
return
address = self.db.user.get(uid, 'address')
- elif self.form.has_key('address'):
+ elif 'address' in self.form:
address = self.form['address'].value
uid = uidFromAddress(self.db, ('', address), create=0)
if not uid:
# nice message
message = self._('You are now registered, welcome!')
url = '%suser%s?@ok_message=%s'%(self.base, self.userid,
- urllib.quote(message))
+ urllib_.quote(message))
# redirect to the user's page (but not 302, as some email clients seem
# to want to reload the page, or something)
class RegisterAction(RegoCommon, EditCommon):
name = 'register'
- permissionType = 'Create'
+ permissionType = 'Register'
def handle(self):
"""Attempt to create a new user based on the contents of the form
"""
# ensure modification comes via POST
if self.client.env['REQUEST_METHOD'] != 'POST':
- self.client.error_message.append(self._('Invalid request'))
+ raise roundup.exceptions.Reject(self._('Invalid request'))
# parse the props from the form
try:
% str(message))
return
- # registration isn't allowed to supply roles
- user_props = props[('user', None)]
- if user_props.has_key('roles'):
- raise exceptions.Unauthorised, self._(
- "It is not permitted to supply roles at registration.")
-
# skip the confirmation step?
if self.db.config['INSTANT_REGISTRATION']:
# handle the create now
return self.finishRego()
# generate the one-time-key and store the props for later
- for propname, proptype in self.db.user.getprops().items():
+ user_props = props[('user', None)]
+ for propname, proptype in self.db.user.getprops().iteritems():
value = user_props.get(propname, None)
if value is None:
pass
self.db.commit()
# redirect to the "you're almost there" page
- raise exceptions.Redirect, '%suser?@template=rego_progress'%self.base
+ raise exceptions.Redirect('%suser?@template=rego_progress'%self.base)
+
+ def newItemPermission(self, props, classname=None):
+ """Just check the "Register" permission.
+ """
+ # registration isn't allowed to supply roles
+ if 'roles' in props:
+ raise exceptions.Unauthorised(self._(
+ "It is not permitted to supply roles at registration."))
+
+ # technically already checked, but here for clarity
+ return self.hasPermission('Register', classname=classname)
class LogoutAction(Action):
def handle(self):
"""
# ensure modification comes via POST
if self.client.env['REQUEST_METHOD'] != 'POST':
- self.client.error_message.append(self._('Invalid request'))
+ raise roundup.exceptions.Reject(self._('Invalid request'))
# we need the username at a minimum
- if not self.form.has_key('__login_name'):
+ if '__login_name' not in self.form:
self.client.error_message.append(self._('Username required'))
return
# get the login info
self.client.user = self.form['__login_name'].value
- if self.form.has_key('__login_password'):
+ if '__login_password' in self.form:
password = self.form['__login_password'].value
else:
password = ''
# save user in session
self.client.session_api.set(user=self.client.user)
- if self.form.has_key('remember'):
+ if 'remember' in self.form:
self.client.session_api.update(set_cookie=True, expire=24*3600*365)
# If we came from someplace, go back there
- if self.form.has_key('__came_from'):
- raise exceptions.Redirect, self.form['__came_from'].value
+ if '__came_from' in self.form:
+ raise exceptions.Redirect(self.form['__came_from'].value)
def verifyLogin(self, username, password):
# make sure the user exists
try:
self.client.userid = self.db.user.lookup(username)
except KeyError:
- raise exceptions.LoginError, self._('Invalid login')
+ raise exceptions.LoginError(self._('Invalid login'))
# verify the password
if not self.verifyPassword(self.client.userid, password):
- raise exceptions.LoginError, self._('Invalid login')
+ raise exceptions.LoginError(self._('Invalid login'))
# Determine whether the user has permission to log in.
# Base behaviour is to check the user has "Web Access".
if not self.hasPermission("Web Access"):
- raise exceptions.LoginError, self._(
- "You do not have permission to login")
+ raise exceptions.LoginError(self._(
+ "You do not have permission to login"))
- def verifyPassword(self, userid, password):
- '''Verify the password that the user has supplied'''
- stored = self.db.user.get(userid, 'password')
- if password == stored:
+ def verifyPassword(self, userid, givenpw):
+ '''Verify the password that the user has supplied.
+ Optionally migrate to new password scheme if configured
+ '''
+ db = self.db
+ stored = db.user.get(userid, 'password')
+ if givenpw == stored:
+ if db.config.WEB_MIGRATE_PASSWORDS and stored.needs_migration():
+ db.user.set(userid, password=password.Password(givenpw))
+ db.commit()
return 1
- if not password and not stored:
+ if not givenpw and not stored:
return 1
return 0
row = []
for name in columns:
# check permission to view this property on this item
- if exists and not self.hasPermission('View', itemid=itemid,
+ if not self.hasPermission('View', itemid=itemid,
classname=request.classname, property=name):
- raise exceptions.Unauthorised, self._(
+ raise exceptions.Unauthorised(self._(
'You do not have permission to view %(class)s'
- ) % {'class': request.classname}
+ ) % {'class': request.classname})
row.append(str(klass.get(itemid, name)))
self.client._socket_op(writer.writerow, row)
def execute_cgi(self):
args = {}
- for key in self.form.keys():
+ for key in self.form:
args[key] = self.form.getvalue(key)
self.permission(args)
return self.handle(args)
def permission(self, args):
"""Raise Unauthorised if the current user is not allowed to execute
this action. Users may override this method."""
-
+
pass
def handle(self, args):
-
+
raise NotImplementedError
# vim: set filetype=python sts=4 sw=4 et si :