class PermissionsExceptor(object): """ Trap permissions exceptions and turn them into HTTP exceptions so the errors are propagated to client code. """ def __init__(self, application): self.application = application def __call__(self, environ, start_response): try: output = self.application(environ, start_response) return output except ForbiddenError, exc: raise HTTP403(exc) except UserRequiredError, exc: # We only send to the challenger on a GET # request. Otherwise we're in for major confusion # on dealing with redirects and the like in # scripts and javascript, where follow # behavior is inconsistent. if environ['REQUEST_METHOD'] == 'GET': url = _challenge_url(environ) raise HTTP302(url) raise HTTP403(exc)
def list_collection(environ, start_response): """ WSGI application handling PROPFIND requests """ if environ.get("HTTP_DEPTH", "0") not in ("0", "1"): raise HTTP403("excessive depth requested") doc = { "multistatus": { "@xmlns": "DAV:", "response": (multistatus_response(entry) for entry in determine_entries(environ)) } } doc = """<?xml version="1.0" encoding="utf-8" ?>\n%s""" % dict2xml(doc) # XXX: DEBUG from .util import prettify print "%s\n%s\n%s" % ("=" * 80, prettify(doc, "application/xml"), "-" * 80) headers = merge( {}, DEFAULT_HEADERS, { "Content-Type": "application/xml", # XXX: charset? "Content-Length": "%s" % len(doc) }) start_response("207 Multi-Status", headers.items()) return doc
def get_identities(environ, start_response): """ Get a list of the identities associated with the named user. That named user must match the current user or the current user must be an admin. """ store = environ['tiddlyweb.store'] username = environ['wsgiorg.routing_args'][1]['username'] usersign = environ['tiddlyweb.usersign']['name'] roles = environ['tiddlyweb.usersign']['roles'] if username != usersign and 'ADMIN' not in roles: raise HTTP403('Bad user for action') identities = [] try: mapped_bag = store.get(Bag('MAPUSER')) tiddlers = store.list_bag_tiddlers(mapped_bag) matched_tiddlers = control.filter_tiddlers( tiddlers, 'select=mapped_user:%s' % username, environ) identities = [tiddler.title for tiddler in matched_tiddlers] except NoBagError: pass start_response('200 OK', [('Content-Type', 'application/json; charset=UTF-8')]) return [simplejson.dumps(identities)]
def put_user(environ, start_response): """ Allow a user or an admin to set the password for a user at /users/{usersign}. A non-admin can only set their own password. The sent data is a JSON dict with at least the key 'password' with a value of whatever the password should be. Users of this method should take not that that password is being sent in the clear over what is likely an unencrypted network. """ store = environ['tiddlyweb.store'] current_user = environ['tiddlyweb.usersign'] target_user = environ['wsgiorg.routing_args'][1]['usersign'] target_user = urllib.unquote(target_user) target_user = unicode(target_user, 'utf-8') if not ('ADMIN' in current_user['roles'] or current_user['name'] == target_user): raise HTTP403('Incorrect User') content_type = environ['tiddlyweb.type'] length = environ['CONTENT_LENGTH'] if content_type != 'application/json': raise HTTP415('application/json required') content = environ['wsgi.input'].read(int(length)) try: user_info = simplejson.loads(content) except ValueError, exc: raise HTTP400('Invalid JSON, %s' % exc)
def safe_mode(environ, start_response): """ Serve up a space in safe mode. Safe mode means that non-required plugins are turned off and plugins that duplicate those in the core bags (system and tiddlyspace) are deleted from the store of the space in question. """ http_host, _ = determine_host(environ) space_name = determine_space(environ, http_host) recipe_name = determine_space_recipe(environ, space_name) if recipe_name != '%s_private' % space_name: raise HTTP403('membership required for safe mode') if environ['REQUEST_METHOD'] == 'GET': return _send_safe_mode(environ, start_response) store = environ['tiddlyweb.store'] # Get the list of core plugins core_plugin_tiddler_titles = _get_core_plugins(store) # Delete those plugins in the space's recipes which # duplicate the core plugins recipe = _delete_duplicates(environ, core_plugin_tiddler_titles, recipe_name, space_name) # Process the recipe. For those tiddlers which do not have a bag # in CORE_RECIPE, remove the systemConfig tag. try: candidate_tiddlers = control.get_tiddlers_from_recipe(recipe, environ) except NoBagError, exc: raise HTTP404('recipe %s lists an unknown bag: %s' % (recipe.name, exc))
def _same_space_required(environ, space_name): """ Raise 403 unless the current space (http_host) is the same as the target space. """ current_space = determine_space(environ, determine_host(environ)[0]) if current_space != space_name: raise HTTP403('space membership changes only allowed from space')
remove=None, current_user=None): """ The guts of adding a member to space. """ try: space = Space(space_name) except ValueError, exc: raise HTTP404('Space %s invalid: %s' % (space_name, exc)) private_bag = store.get(Bag(space.private_bag())) if current_user: private_bag.policy.allows(current_user, 'manage') if remove and len(private_bag.policy.manage) == 1: raise HTTP403('must not remove the last member from a space') # This will throw NoUserError (to be handled by the caller) # if the user does not exist. if add: store.get(User(add)) bags = [store.get(Bag(bag)) for bag in space.list_bags()] recipes = [store.get(Recipe(bag)) for bag in space.list_recipes()] for entity in bags + recipes: new_policy = _update_policy(entity.policy, add=add, subtract=remove) entity.policy = new_policy store.put(entity) def confirm_space(environ, start_response):
def __call__(self, environ, start_response): try: output = self.application(environ, start_response) return output except ForbiddenError, exc: raise HTTP403(exc)