44
45
import ivle.webapp.security
45
46
from ivle.webapp.base.plugins import ViewPlugin, PublicViewPlugin
46
47
from ivle.webapp.base.xhtml import XHTMLView, XHTMLErrorView
47
from ivle.webapp.errors import HTTPError, Unauthorized, NotFound
48
from ivle.webapp.routing import Router, RoutingError
48
from ivle.webapp.errors import BadRequest, HTTPError, NotFound, Unauthorized
49
from ivle.webapp.publisher import Publisher, PublishingError
49
50
from ivle.webapp import ApplicationRoot
51
52
config = ivle.config.Config()
53
def generate_router(view_plugins, root):
54
class ObjectPermissionCheckingPublisher(Publisher):
55
"""A specialised publisher that checks object permissions.
57
This publisher verifies that the user holds any permission at all
58
on the model objects through which the resolution path passes. If
59
no permission is held, resolution is aborted with an Unauthorized
62
IMPORTANT: This does NOT check view permissions. It only checks
63
the objects in between the root and the view, exclusive!
66
def traversed_to_object(self, obj):
67
"""Check that the user has any permission at all over the object."""
68
if (hasattr(obj, 'get_permissions') and
69
len(obj.get_permissions(self.root.user, config)) == 0):
70
# Indicate the forbidden object if this is an admin.
71
if self.root.user and self.root.user.admin:
72
raise Unauthorized('Unauthorized: %s' % obj)
77
def generate_publisher(view_plugins, root, publicmode=False):
55
79
Build a Mapper object for doing URL matching using 'routes', based on the
56
80
given plugin registry.
82
r = ObjectPermissionCheckingPublisher(root=root)
60
84
r.add_set_switch('api', 'api')
87
view_attr = 'public_views'
88
forward_route_attr = 'public_forward_routes'
89
reverse_route_attr = 'public_reverse_routes'
92
forward_route_attr = 'forward_routes'
93
reverse_route_attr = 'reverse_routes'
62
96
for plugin in view_plugins:
63
if hasattr(plugin, 'forward_routes'):
64
for fr in plugin.forward_routes:
97
if hasattr(plugin, forward_route_attr):
98
for fr in getattr(plugin, forward_route_attr):
65
99
# An annotated function can also be passed in directly.
66
100
if hasattr(fr, '_forward_route_meta'):
67
101
r.add_forward_func(fr)
69
103
r.add_forward(*fr)
71
if hasattr(plugin, 'reverse_routes'):
72
for rr in plugin.reverse_routes:
105
if hasattr(plugin, reverse_route_attr):
106
for rr in getattr(plugin, reverse_route_attr):
73
107
# An annotated function can also be passed in directly.
74
108
if hasattr(rr, '_reverse_route_src'):
75
109
r.add_reverse_func(rr)
77
111
r.add_reverse(*rr)
79
if hasattr(plugin, 'views'):
80
for v in plugin.views:
113
if hasattr(plugin, view_attr):
114
for v in getattr(plugin, view_attr):
92
126
# Make the request object into an IVLE request which can be given to views
93
127
req = Request(apachereq, config)
95
# Hack? Try and get the user login early just in case we throw an error
96
# (most likely 404) to stop us seeing not logged in even when we are.
97
if not req.publicmode:
98
user = ivle.webapp.security.get_user_details(req)
100
# Don't set the user if it is disabled or hasn't accepted the ToS.
101
if user and user.valid:
105
raise NotImplementedError("no public mode with obtrav yet!")
107
req.router = generate_router(config.plugin_index[ViewPlugin],
108
ApplicationRoot(req.config, req.store))
129
req.publisher = generate_publisher(
130
config.plugin_index[ViewPlugin], ApplicationRoot(req),
131
publicmode=req.publicmode)
111
obj, viewcls, subpath = req.router.resolve(req.uri.decode('utf-8'))
134
obj, viewcls, subpath = req.publisher.resolve(req.uri.decode('utf-8'))
113
136
# We 404 if we have a subpath but the view forbids it.
114
137
if not viewcls.subpath_allowed and subpath:
120
143
# Check that the request (mainly the user) is permitted to access
122
145
if not view.authorize(req):
146
# Indicate the forbidden object if this is an admin.
147
if req.user and req.user.admin:
148
raise Unauthorized('Unauthorized: %s' % view)
152
# Non-GET requests from other sites leave us vulnerable to
154
referer = req.headers_in.get('Referer')
155
if (referer is None or
156
urlparse.urlparse(req.headers_in.get('Referer')).netloc !=
158
if req.method != 'GET' and not view.offsite_posts_allowed:
160
"Non-GET requests from external sites are forbidden "
161
"for security reasons.")
124
163
# Render the output
126
165
except HTTPError, e:
152
191
handle_unknown_exception(req, *sys.exc_info())
194
# Commit the transaction if we have a store open.
157
except RoutingError, e:
197
except Unauthorized, e:
198
# Resolution failed due to a permission check. Display a pretty
199
# error, or maybe a login page.
200
XHTMLView.get_error_view(e)(req, e, req.publisher.root).render(req)
202
except PublishingError, e:
205
if req.user and req.user.admin:
159
206
XHTMLErrorView(req, NotFound('Not found: ' +
160
str(e.args))).render(req)
207
str(e.args)), e[0]).render(req)
162
XHTMLErrorView(req, NotFound()).render(req)
209
XHTMLErrorView(req, NotFound(), e[0]).render(req)
213
# Make sure we close the store.
166
216
def handle_unknown_exception(req, exc_type, exc_value, exc_traceback):