API: Fixed media file URLs, limits
[mediagoblin.git] / mediagoblin / plugins / api / tools.py
CommitLineData
a062149e
JW
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
17import logging
42c83752 18import json
a062149e
JW
19
20from functools import wraps
42c83752 21from webob import exc, Response
c92aa0d0 22from urlparse import urljoin
a062149e 23
42c83752 24from mediagoblin import mg_globals
a062149e 25from mediagoblin.tools.pluginapi import PluginManager
42c83752 26from mediagoblin.storage.filestorage import BasicFileStorage
a062149e
JW
27
28_log = logging.getLogger(__name__)
29
30
42c83752
JW
31class Auth(object):
32 '''
33 An object with two significant methods, 'trigger' and 'run'.
34
35 Using a similar object to this, plugins can register specific
36 authentication logic, for example the GET param 'access_token' for OAuth.
37
38 - trigger: Analyze the 'request' argument, return True if you think you
39 can handle the request, otherwise return False
40 - run: The authentication logic, set the request.user object to the user
41 you intend to authenticate and return True, otherwise return False.
42
43 If run() returns False, an HTTP 403 Forbidden error will be shown.
44
45 You may also display custom errors, just raise them within the run()
46 method.
47 '''
48 def trigger(self, request):
49 raise NotImplemented()
50
51 def __call__(self, request, *args, **kw):
52 raise NotImplemented()
53
54
85726f73
JW
55def json_response(serializable, *args, **kw):
56 '''
57 Serializes a json objects and returns a webob.Response object with the
58 serialized value as the response body and Content-Type: application/json.
59
60 :param serializable: A json-serializable object
61
62 Any extra arguments and keyword arguments are passed to the
63 webob.Response.__init__ method.
64 '''
65 response = Response(json.dumps(serializable), *args, **kw)
42c83752 66 response.headers['Content-Type'] = 'application/json'
965b39a8
JW
67 cors_headers = {
68 'Access-Control-Allow-Origin': '*',
69 'Access-Control-Allow-Methods': 'POST, GET, OPTIONS',
70 'Access-Control-Allow-Headers': 'Content-Type, X-Requested-With'}
71 response.headers.update(cors_headers)
42c83752
JW
72 return response
73
74
85726f73
JW
75def get_entry_serializable(entry, urlgen):
76 '''
77 Returns a serializable dict() of a MediaEntry instance.
78
79 :param entry: A MediaEntry instance
80 :param urlgen: An urlgen instance, can be found on the request object passed
81 to views.
82 '''
42c83752
JW
83 return {
84 'user': entry.get_uploader.username,
85 'user_id': entry.get_uploader.id,
85726f73
JW
86 'user_bio': entry.get_uploader.bio,
87 'user_bio_html': entry.get_uploader.bio_html,
88 'user_permalink': urlgen('mediagoblin.user_pages.user_home',
89 user=entry.get_uploader.username,
90 qualified=True),
42c83752
JW
91 'id': entry.id,
92 'created': entry.created.isoformat(),
93 'title': entry.title,
94 'license': entry.license,
95 'description': entry.description,
96 'description_html': entry.description_html,
97 'media_type': entry.media_type,
85726f73
JW
98 'permalink': entry.url_for_self(urlgen, qualified=True),
99 'media_files': get_media_file_paths(entry.media_files, urlgen)}
100
42c83752 101
85726f73
JW
102def get_media_file_paths(media_files, urlgen):
103 '''
104 Returns a dictionary of media files with `file_handle` => `qualified URL`
42c83752 105
85726f73
JW
106 :param media_files: dict-like object consisting of `file_handle => `listy
107 filepath` pairs.
108 :param urlgen: An urlgen object, usually found on request.urlgen.
109 '''
42c83752
JW
110 media_urls = {}
111
112 for key, val in media_files.items():
c92aa0d0
JW
113 if isinstance(mg_globals.public_store, BasicFileStorage):
114 # BasicFileStorage does not provide a qualified URI
115 media_urls[key] = urljoin(
116 urlgen('index', qualified=True),
117 mg_globals.public_store.file_url(val))
118 else:
119 media_urls[key] = mg_globals.public_store.file_url(val)
42c83752
JW
120
121 return media_urls
122
123
a062149e 124def api_auth(controller):
85726f73
JW
125 '''
126 Decorator, allows plugins to register auth methods that will then be
127 evaluated against the request, finally a worthy authenticator object is
128 chosen and used to decide whether to grant or deny access.
129 '''
a062149e
JW
130 @wraps(controller)
131 def wrapper(request, *args, **kw):
132 auth_candidates = []
133
134 for auth in PluginManager().get_hook_callables('auth'):
135 _log.debug('Plugin auth: {0}'.format(auth))
136 if auth.trigger(request):
137 auth_candidates.append(auth)
138
139 # If we can't find any authentication methods, we should not let them
140 # pass.
141 if not auth_candidates:
142 return exc.HTTPForbidden()
143
144 # For now, just select the first one in the list
145 auth = auth_candidates[0]
146
147 _log.debug('Using {0} to authorize request {1}'.format(
148 auth, request.url))
149
150 if not auth(request, *args, **kw):
151 return exc.HTTPForbidden()
152
153 return controller(request, *args, **kw)
154
155 return wrapper