1 # GNU MediaGoblin -- federated, autonomous media hosting
2 # Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
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.
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.
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/>.
17 from functools
import wraps
19 from werkzeug
.exceptions
import Forbidden
, NotFound
20 from oauthlib
.oauth1
import ResourceEndpoint
22 from six
.moves
.urllib
.parse
import urljoin
24 from mediagoblin
import mg_globals
as mgg
25 from mediagoblin
import messages
26 from mediagoblin
.db
.models
import MediaEntry
, LocalUser
, TextComment
, \
28 from mediagoblin
.tools
.response
import (
30 render_user_banned
, json_response
)
31 from mediagoblin
.tools
.translate
import pass_to_ugettext
as _
33 from mediagoblin
.oauth
.tools
.request
import decode_authorization_header
34 from mediagoblin
.oauth
.oauth
import GMGRequestValidator
37 def user_not_banned(controller
):
39 Requires that the user has not been banned. Otherwise redirects to the page
40 explaining why they have been banned
43 def wrapper(request
, *args
, **kwargs
):
45 if request
.user
.is_banned():
46 return render_user_banned(request
)
47 return controller(request
, *args
, **kwargs
)
52 def require_active_login(controller
):
54 Require an active login from the user. If the user is banned, redirects to
55 the "You are Banned" page.
59 def new_controller_func(request
, *args
, **kwargs
):
61 not request
.user
.has_privilege(u
'active'):
63 request
, 'mediagoblin.user_pages.user_home',
64 user
=request
.user
.username
)
65 elif not request
.user
or not request
.user
.has_privilege(u
'active'):
67 request
.urlgen('mediagoblin.auth.login',
71 return redirect(request
, 'mediagoblin.auth.login',
74 return controller(request
, *args
, **kwargs
)
76 return new_controller_func
79 def user_has_privilege(privilege_name
, allow_admin
=True):
81 Requires that a user have a particular privilege in order to access a page.
82 In order to require that a user have multiple privileges, use this
83 decorator twice on the same view. This decorator also makes sure that the
84 user is not banned, or else it redirects them to the "You are Banned" page.
86 :param privilege_name A unicode object that is that represents
87 the privilege object. This object is
88 the name of the privilege, as assigned
89 in the Privilege.privilege_name column
91 :param allow_admin If this is true then if the user is an admin
92 it will allow the user even if the user doesn't
93 have the privilage given in privilage_name.
96 def user_has_privilege_decorator(controller
):
99 def wrapper(request
, *args
, **kwargs
):
100 if not request
.user
.has_privilege(privilege_name
, allow_admin
):
103 return controller(request
, *args
, **kwargs
)
106 return user_has_privilege_decorator
109 def active_user_from_url(controller
):
110 """Retrieve LocalUser() from <user> URL pattern and pass in as url_user=...
112 Returns a 404 if no such active user has been found"""
114 def wrapper(request
, *args
, **kwargs
):
115 user
= LocalUser
.query
.filter_by(username
=request
.matchdict
['user']).first()
117 return render_404(request
)
119 return controller(request
, *args
, url_user
=user
, **kwargs
)
123 def path_subtitle(controller
):
124 """Retrieve <path> URL pattern and pass in as path=..."""
128 def wrapper(request
, *args
, **kwargs
):
129 path_sub
= request
.matchdict
['path']
131 return controller(request
, *args
, path
=path_sub
, **kwargs
)
135 def path_subtitle(controller
):
136 """Retrieve <path> URL pattern and pass in as path=..."""
140 def wrapper(request
, *args
, **kwargs
):
141 path_sub
= request
.matchdict
['path']
143 return controller(request
, *args
, path
=path_sub
, **kwargs
)
148 def user_may_delete_media(controller
):
150 Require user ownership of the MediaEntry to delete.
153 def wrapper(request
, *args
, **kwargs
):
154 uploader_id
= kwargs
['media'].actor
155 if not (request
.user
.has_privilege(u
'admin') or
156 request
.user
.id == uploader_id
):
159 return controller(request
, *args
, **kwargs
)
164 def user_may_alter_collection(controller
):
166 Require user ownership of the Collection to modify.
169 def wrapper(request
, *args
, **kwargs
):
170 creator_id
= request
.db
.LocalUser
.query
.filter_by(
171 username
=request
.matchdict
['user']).first().id
172 if not (request
.user
.has_privilege(u
'admin') or
173 request
.user
.id == creator_id
):
176 return controller(request
, *args
, **kwargs
)
181 def uses_pagination(controller
):
183 Check request GET 'page' key for wrong values
186 def wrapper(request
, *args
, **kwargs
):
188 page
= int(request
.GET
.get('page', 1))
190 return render_404(request
)
192 return render_404(request
)
194 return controller(request
, page
=page
, *args
, **kwargs
)
199 def get_user_media_entry(controller
):
201 Pass in a MediaEntry based off of a url component
204 def wrapper(request
, *args
, **kwargs
):
205 user
= LocalUser
.query
.filter_by(username
=request
.matchdict
['user']).first()
211 # might not be a slug, might be an id, but whatever
212 media_slug
= request
.matchdict
['media']
214 # if it starts with id: it actually isn't a slug, it's an id.
215 if media_slug
.startswith(u
'id:'):
217 media
= MediaEntry
.query
.filter_by(
218 id=int(media_slug
[3:]),
220 actor
=user
.id).first()
224 # no magical id: stuff? It's a slug!
225 media
= MediaEntry
.query
.filter_by(
228 actor
=user
.id).first()
231 # Didn't find anything? Okay, 404.
234 return controller(request
, media
=media
, *args
, **kwargs
)
239 def get_user_collection(controller
):
241 Pass in a Collection based off of a url component
244 def wrapper(request
, *args
, **kwargs
):
245 user
= request
.db
.LocalUser
.query
.filter_by(
246 username
=request
.matchdict
['user']).first()
249 return render_404(request
)
251 collection
= request
.db
.Collection
.query
.filter_by(
252 slug
=request
.matchdict
['collection'],
253 actor
=user
.id).first()
255 # Still no collection? Okay, 404.
257 return render_404(request
)
259 return controller(request
, collection
=collection
, *args
, **kwargs
)
264 def get_user_collection_item(controller
):
266 Pass in a CollectionItem based off of a url component
269 def wrapper(request
, *args
, **kwargs
):
270 user
= request
.db
.LocalUser
.query
.filter_by(
271 username
=request
.matchdict
['user']).first()
274 return render_404(request
)
276 collection_item
= request
.db
.CollectionItem
.query
.filter_by(
277 id=request
.matchdict
['collection_item']).first()
279 # Still no collection item? Okay, 404.
280 if not collection_item
:
281 return render_404(request
)
283 return controller(request
, collection_item
=collection_item
, *args
, **kwargs
)
288 def get_media_entry_by_id(controller
):
290 Pass in a MediaEntry based off of a url component
293 def wrapper(request
, *args
, **kwargs
):
294 media
= MediaEntry
.query
.filter_by(
295 id=request
.matchdict
['media_id'],
296 state
=u
'processed').first()
297 # Still no media? Okay, 404.
299 return render_404(request
)
301 given_username
= request
.matchdict
.get('user')
302 if given_username
and (given_username
!= media
.get_actor
.username
):
303 return render_404(request
)
305 return controller(request
, media
=media
, *args
, **kwargs
)
310 def get_workbench(func
):
311 """Decorator, passing in a workbench as kwarg which is cleaned up afterwards"""
314 def new_func(*args
, **kwargs
):
315 with mgg
.workbench_manager
.create() as workbench
:
316 return func(*args
, workbench
=workbench
, **kwargs
)
321 def allow_registration(controller
):
322 """ Decorator for if registration is enabled"""
324 def wrapper(request
, *args
, **kwargs
):
325 if not mgg
.app_config
["allow_registration"]:
326 messages
.add_message(
329 _('Sorry, registration is disabled on this instance.'))
330 return redirect(request
, "index")
332 return controller(request
, *args
, **kwargs
)
336 def allow_reporting(controller
):
337 """ Decorator for if reporting is enabled"""
339 def wrapper(request
, *args
, **kwargs
):
340 if not mgg
.app_config
["allow_reporting"]:
341 messages
.add_message(
344 _('Sorry, reporting is disabled on this instance.'))
345 return redirect(request
, 'index')
347 return controller(request
, *args
, **kwargs
)
351 def get_optional_media_comment_by_id(controller
):
353 Pass in a Comment based off of a url component. Because of this decor-
354 -ator's use in filing Reports, it has two valid outcomes.
356 :returns The view function being wrapped with kwarg `comment` set to
357 the Comment who's id is in the URL. If there is a
358 comment id in the URL and if it is valid.
359 :returns The view function being wrapped with kwarg `comment` set to
360 None. If there is no comment id in the URL.
361 :returns A 404 Error page, if there is a comment if in the URL and it
365 def wrapper(request
, *args
, **kwargs
):
366 if 'comment' in request
.matchdict
:
367 comment
= Comment
.query
.filter_by(
368 id=request
.matchdict
['comment']
372 return render_404(request
)
374 return controller(request
, comment
=comment
, *args
, **kwargs
)
376 return controller(request
, comment
=None, *args
, **kwargs
)
380 def auth_enabled(controller
):
381 """Decorator for if an auth plugin is enabled"""
383 def wrapper(request
, *args
, **kwargs
):
385 messages
.add_message(
388 _('Sorry, authentication is disabled on this instance.'))
389 return redirect(request
, 'index')
391 return controller(request
, *args
, **kwargs
)
395 def require_admin_or_moderator_login(controller
):
397 Require a login from an administrator or a moderator.
400 def new_controller_func(request
, *args
, **kwargs
):
401 if request
.user
and \
402 not (request
.user
.has_privilege(u
'admin')
403 or request
.user
.has_privilege(u
'moderator')):
406 elif not request
.user
:
408 request
.urlgen('mediagoblin.auth.login',
412 return redirect(request
, 'mediagoblin.auth.login',
415 return controller(request
, *args
, **kwargs
)
417 return new_controller_func
421 def oauth_required(controller
):
422 """ Used to wrap API endpoints where oauth is required """
424 def wrapper(request
, *args
, **kwargs
):
425 data
= request
.headers
426 authorization
= decode_authorization_header(data
)
428 if authorization
== dict():
429 error
= "Missing required parameter."
430 return json_response({"error": error
}, status
=400)
433 request_validator
= GMGRequestValidator()
434 resource_endpoint
= ResourceEndpoint(request_validator
)
435 valid
, r
= resource_endpoint
.validate_protected_resource_request(
437 http_method
=request
.method
,
439 headers
=dict(request
.headers
),
443 error
= "Invalid oauth parameter."
444 return json_response({"error": error
}, status
=400)
446 # Fill user if not already
447 token
= authorization
[u
"oauth_token"]
448 request
.access_token
= AccessToken
.query
.filter_by(token
=token
).first()
449 if request
.access_token
is not None and request
.user
is None:
450 user_id
= request
.access_token
.actor
451 request
.user
= LocalUser
.query
.filter_by(id=user_id
).first()
453 return controller(request
, *args
, **kwargs
)