45
44
import ivle.webapp.security
46
45
from ivle.webapp.base.plugins import ViewPlugin, PublicViewPlugin
47
46
from ivle.webapp.base.xhtml import XHTMLView, XHTMLErrorView
48
from ivle.webapp.errors import BadRequest, HTTPError, NotFound, Unauthorized
49
from ivle.webapp.publisher import Publisher, PublishingError
47
from ivle.webapp.errors import HTTPError, Unauthorized, NotFound
48
from ivle.webapp.routing import Router, RoutingError
50
49
from ivle.webapp import ApplicationRoot
52
51
config = ivle.config.Config()
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):
53
def generate_router(view_plugins, root):
79
55
Build a Mapper object for doing URL matching using 'routes', based on the
80
56
given plugin registry.
82
r = ObjectPermissionCheckingPublisher(root=root)
84
60
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'
96
62
for plugin in view_plugins:
97
if hasattr(plugin, forward_route_attr):
98
for fr in getattr(plugin, forward_route_attr):
63
if hasattr(plugin, 'forward_routes'):
64
for fr in plugin.forward_routes:
99
65
# An annotated function can also be passed in directly.
100
66
if hasattr(fr, '_forward_route_meta'):
101
67
r.add_forward_func(fr)
103
69
r.add_forward(*fr)
105
if hasattr(plugin, reverse_route_attr):
106
for rr in getattr(plugin, reverse_route_attr):
71
if hasattr(plugin, 'reverse_routes'):
72
for rr in plugin.reverse_routes:
107
73
# An annotated function can also be passed in directly.
108
74
if hasattr(rr, '_reverse_route_src'):
109
75
r.add_reverse_func(rr)
111
77
r.add_reverse(*rr)
113
if hasattr(plugin, view_attr):
114
for v in getattr(plugin, view_attr):
79
if hasattr(plugin, 'views'):
80
for v in plugin.views:
126
92
# Make the request object into an IVLE request which can be given to views
127
93
req = Request(apachereq, config)
129
req.publisher = generate_publisher(
130
config.plugin_index[ViewPlugin], ApplicationRoot(req),
131
publicmode=req.publicmode)
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))
134
obj, viewcls, subpath = req.publisher.resolve(req.uri.decode('utf-8'))
111
obj, viewcls, subpath = req.router.resolve(req.uri.decode('utf-8'))
136
113
# We 404 if we have a subpath but the view forbids it.
137
114
if not viewcls.subpath_allowed and subpath:
143
120
# Check that the request (mainly the user) is permitted to access
145
122
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.")
163
124
# Render the output
165
126
except HTTPError, e:
191
152
handle_unknown_exception(req, *sys.exc_info())
194
# Commit the transaction if we have a store open.
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:
157
except RoutingError, e:
206
159
XHTMLErrorView(req, NotFound('Not found: ' +
207
str(e.args)), e[0]).render(req)
160
str(e.args))).render(req)
209
XHTMLErrorView(req, NotFound(), e[0]).render(req)
162
XHTMLErrorView(req, NotFound()).render(req)
213
# Make sure we close the store.
216
166
def handle_unknown_exception(req, exc_type, exc_value, exc_traceback):