This commit was solely to remove unused imports in the code that I have written
[mediagoblin.git] / mediagoblin / decorators.py
1 # GNU MediaGoblin -- federated, autonomous media hosting
2 # Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
3 #
4 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License as published by
6 # the Free Software Foundation, either version 3 of the License, or
7 # (at your option) any later version.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU Affero General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16
17 from functools import wraps
18
19 from urlparse import urljoin
20 from werkzeug.exceptions import Forbidden, NotFound
21 from oauthlib.oauth1 import ResourceEndpoint
22
23 from mediagoblin import mg_globals as mgg
24 from mediagoblin import messages
25 from mediagoblin.db.models import MediaEntry, User, MediaComment
26 from mediagoblin.tools.response import (redirect, render_404,
27 render_user_banned, json_response)
28 from mediagoblin.tools.translate import pass_to_ugettext as _
29
30 from mediagoblin.oauth.tools.request import decode_authorization_header
31 from mediagoblin.oauth.oauth import GMGRequestValidator
32
33
34 def user_not_banned(controller):
35 """
36 Requires that the user has not been banned. Otherwise redirects to the page
37 explaining why they have been banned
38 """
39 @wraps(controller)
40 def wrapper(request, *args, **kwargs):
41 if request.user:
42 if request.user.is_banned():
43 return render_user_banned(request)
44 return controller(request, *args, **kwargs)
45
46 return wrapper
47
48
49 def require_active_login(controller):
50 """
51 Require an active login from the user. If the user is banned, redirects to
52 the "You are Banned" page.
53 """
54 @wraps(controller)
55 @user_not_banned
56 def new_controller_func(request, *args, **kwargs):
57 if request.user and \
58 not request.user.has_privilege(u'active'):
59 return redirect(
60 request, 'mediagoblin.user_pages.user_home',
61 user=request.user.username)
62 elif not request.user or not request.user.has_privilege(u'active'):
63 next_url = urljoin(
64 request.urlgen('mediagoblin.auth.login',
65 qualified=True),
66 request.url)
67
68 return redirect(request, 'mediagoblin.auth.login',
69 next=next_url)
70
71 return controller(request, *args, **kwargs)
72
73 return new_controller_func
74
75
76 def user_has_privilege(privilege_name):
77 """
78 Requires that a user have a particular privilege in order to access a page.
79 In order to require that a user have multiple privileges, use this
80 decorator twice on the same view. This decorator also makes sure that the
81 user is not banned, or else it redirects them to the "You are Banned" page.
82
83 :param privilege_name A unicode object that is that represents
84 the privilege object. This object is
85 the name of the privilege, as assigned
86 in the Privilege.privilege_name column
87 """
88
89 def user_has_privilege_decorator(controller):
90 @wraps(controller)
91 @require_active_login
92 def wrapper(request, *args, **kwargs):
93 user_id = request.user.id
94 if not request.user.has_privilege(privilege_name):
95 raise Forbidden()
96
97 return controller(request, *args, **kwargs)
98
99 return wrapper
100 return user_has_privilege_decorator
101
102
103 def active_user_from_url(controller):
104 """Retrieve User() from <user> URL pattern and pass in as url_user=...
105
106 Returns a 404 if no such active user has been found"""
107 @wraps(controller)
108 def wrapper(request, *args, **kwargs):
109 user = User.query.filter_by(username=request.matchdict['user']).first()
110 if user is None:
111 return render_404(request)
112
113 return controller(request, *args, url_user=user, **kwargs)
114
115 return wrapper
116
117
118 def user_may_delete_media(controller):
119 """
120 Require user ownership of the MediaEntry to delete.
121 """
122 @wraps(controller)
123 def wrapper(request, *args, **kwargs):
124 uploader_id = kwargs['media'].uploader
125 if not (request.user.has_privilege(u'admin') or
126 request.user.id == uploader_id):
127 raise Forbidden()
128
129 return controller(request, *args, **kwargs)
130
131 return wrapper
132
133
134 def user_may_alter_collection(controller):
135 """
136 Require user ownership of the Collection to modify.
137 """
138 @wraps(controller)
139 def wrapper(request, *args, **kwargs):
140 creator_id = request.db.User.query.filter_by(
141 username=request.matchdict['user']).first().id
142 if not (request.user.has_privilege(u'admin') or
143 request.user.id == creator_id):
144 raise Forbidden()
145
146 return controller(request, *args, **kwargs)
147
148 return wrapper
149
150
151 def uses_pagination(controller):
152 """
153 Check request GET 'page' key for wrong values
154 """
155 @wraps(controller)
156 def wrapper(request, *args, **kwargs):
157 try:
158 page = int(request.GET.get('page', 1))
159 if page < 0:
160 return render_404(request)
161 except ValueError:
162 return render_404(request)
163
164 return controller(request, page=page, *args, **kwargs)
165
166 return wrapper
167
168
169 def get_user_media_entry(controller):
170 """
171 Pass in a MediaEntry based off of a url component
172 """
173 @wraps(controller)
174 def wrapper(request, *args, **kwargs):
175 user = User.query.filter_by(username=request.matchdict['user']).first()
176 if not user:
177 raise NotFound()
178
179 media = None
180
181 # might not be a slug, might be an id, but whatever
182 media_slug = request.matchdict['media']
183
184 # if it starts with id: it actually isn't a slug, it's an id.
185 if media_slug.startswith(u'id:'):
186 try:
187 media = MediaEntry.query.filter_by(
188 id=int(media_slug[3:]),
189 state=u'processed',
190 uploader=user.id).first()
191 except ValueError:
192 raise NotFound()
193 else:
194 # no magical id: stuff? It's a slug!
195 media = MediaEntry.query.filter_by(
196 slug=media_slug,
197 state=u'processed',
198 uploader=user.id).first()
199
200 if not media:
201 # Didn't find anything? Okay, 404.
202 raise NotFound()
203
204 return controller(request, media=media, *args, **kwargs)
205
206 return wrapper
207
208
209 def get_user_collection(controller):
210 """
211 Pass in a Collection based off of a url component
212 """
213 @wraps(controller)
214 def wrapper(request, *args, **kwargs):
215 user = request.db.User.query.filter_by(
216 username=request.matchdict['user']).first()
217
218 if not user:
219 return render_404(request)
220
221 collection = request.db.Collection.query.filter_by(
222 slug=request.matchdict['collection'],
223 creator=user.id).first()
224
225 # Still no collection? Okay, 404.
226 if not collection:
227 return render_404(request)
228
229 return controller(request, collection=collection, *args, **kwargs)
230
231 return wrapper
232
233
234 def get_user_collection_item(controller):
235 """
236 Pass in a CollectionItem based off of a url component
237 """
238 @wraps(controller)
239 def wrapper(request, *args, **kwargs):
240 user = request.db.User.query.filter_by(
241 username=request.matchdict['user']).first()
242
243 if not user:
244 return render_404(request)
245
246 collection_item = request.db.CollectionItem.query.filter_by(
247 id=request.matchdict['collection_item']).first()
248
249 # Still no collection item? Okay, 404.
250 if not collection_item:
251 return render_404(request)
252
253 return controller(request, collection_item=collection_item, *args, **kwargs)
254
255 return wrapper
256
257
258 def get_media_entry_by_id(controller):
259 """
260 Pass in a MediaEntry based off of a url component
261 """
262 @wraps(controller)
263 def wrapper(request, *args, **kwargs):
264 media = MediaEntry.query.filter_by(
265 id=request.matchdict['media_id'],
266 state=u'processed').first()
267 # Still no media? Okay, 404.
268 if not media:
269 return render_404(request)
270
271 given_username = request.matchdict.get('user')
272 if given_username and (given_username != media.get_uploader.username):
273 return render_404(request)
274
275 return controller(request, media=media, *args, **kwargs)
276
277 return wrapper
278
279
280 def get_workbench(func):
281 """Decorator, passing in a workbench as kwarg which is cleaned up afterwards"""
282
283 @wraps(func)
284 def new_func(*args, **kwargs):
285 with mgg.workbench_manager.create() as workbench:
286 return func(*args, workbench=workbench, **kwargs)
287
288 return new_func
289
290
291 def allow_registration(controller):
292 """ Decorator for if registration is enabled"""
293 @wraps(controller)
294 def wrapper(request, *args, **kwargs):
295 if not mgg.app_config["allow_registration"]:
296 messages.add_message(
297 request,
298 messages.WARNING,
299 _('Sorry, registration is disabled on this instance.'))
300 return redirect(request, "index")
301
302 return controller(request, *args, **kwargs)
303
304 return wrapper
305
306 def allow_reporting(controller):
307 """ Decorator for if reporting is enabled"""
308 @wraps(controller)
309 def wrapper(request, *args, **kwargs):
310 if not mgg.app_config["allow_reporting"]:
311 messages.add_message(
312 request,
313 messages.WARNING,
314 _('Sorry, reporting is disabled on this instance.'))
315 return redirect(request, 'index')
316
317 return controller(request, *args, **kwargs)
318
319 return wrapper
320
321 def get_optional_media_comment_by_id(controller):
322 """
323 Pass in a MediaComment based off of a url component. Because of this decor-
324 -ator's use in filing Media or Comment Reports, it has two valid outcomes.
325
326 :returns The view function being wrapped with kwarg `comment` set to
327 the MediaComment who's id is in the URL. If there is a
328 comment id in the URL and if it is valid.
329 :returns The view function being wrapped with kwarg `comment` set to
330 None. If there is no comment id in the URL.
331 :returns A 404 Error page, if there is a comment if in the URL and it
332 is invalid.
333 """
334 @wraps(controller)
335 def wrapper(request, *args, **kwargs):
336 if 'comment' in request.matchdict:
337 comment = MediaComment.query.filter_by(
338 id=request.matchdict['comment']).first()
339
340 if comment is None:
341 return render_404(request)
342
343 return controller(request, comment=comment, *args, **kwargs)
344 else:
345 return controller(request, comment=None, *args, **kwargs)
346 return wrapper
347
348
349 def auth_enabled(controller):
350 """Decorator for if an auth plugin is enabled"""
351 @wraps(controller)
352 def wrapper(request, *args, **kwargs):
353 if not mgg.app.auth:
354 messages.add_message(
355 request,
356 messages.WARNING,
357 _('Sorry, authentication is disabled on this instance.'))
358 return redirect(request, 'index')
359
360 return controller(request, *args, **kwargs)
361
362 return wrapper
363
364 def require_admin_or_moderator_login(controller):
365 """
366 Require a login from an administrator or a moderator.
367 """
368 @wraps(controller)
369 def new_controller_func(request, *args, **kwargs):
370 if request.user and \
371 not request.user.has_privilege(u'admin',u'moderator'):
372
373 raise Forbidden()
374 elif not request.user:
375 next_url = urljoin(
376 request.urlgen('mediagoblin.auth.login',
377 qualified=True),
378 request.url)
379
380 return redirect(request, 'mediagoblin.auth.login',
381 next=next_url)
382
383 return controller(request, *args, **kwargs)
384
385 return new_controller_func
386
387
388
389 def oauth_required(controller):
390 """ Used to wrap API endpoints where oauth is required """
391 @wraps(controller)
392 def wrapper(request, *args, **kwargs):
393 data = request.headers
394 authorization = decode_authorization_header(data)
395
396 if authorization == dict():
397 error = "Missing required parameter."
398 return json_response({"error": error}, status=400)
399
400
401 request_validator = GMGRequestValidator()
402 resource_endpoint = ResourceEndpoint(request_validator)
403 valid, request = resource_endpoint.validate_protected_resource_request(
404 uri=request.url,
405 http_method=request.method,
406 body=request.get_data(),
407 headers=dict(request.headers),
408 )
409
410 if not valid:
411 error = "Invalid oauth prarameter."
412 return json_response({"error": error}, status=400)
413
414 return controller(request, *args, **kwargs)
415
416 return wrapper