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 oauthlib
.oauth1
import RequestValidator
, RequestTokenEndpoint
19 from mediagoblin
.tools
.translate
import pass_to_ugettext
20 from mediagoblin
.meddleware
.csrf
import csrf_exempt
21 from mediagoblin
.tools
.request
import decode_request
22 from mediagoblin
.tools
.response
import json_response
, render_400
23 from mediagoblin
.tools
.crypto
import random_string
24 from mediagoblin
.tools
.validator
import validate_email
, validate_url
25 from mediagoblin
.db
.models
import Client
, RequestToken
, AccessToken
27 # possible client types
28 client_types
= ["web", "native"] # currently what pump supports
31 def client_register(request
):
32 """ Endpoint for client registration """
34 data
= decode_request(request
)
36 error
= "Could not decode data."
37 return json_response({"error": error
}, status
=400)
40 error
= "Unknown Content-Type"
41 return json_response({"error": error
}, status
=400)
43 if "type" not in data
:
44 error
= "No registration type provided."
45 return json_response({"error": error
}, status
=400)
46 if data
.get("application_type", None) not in client_types
:
47 error
= "Unknown application_type."
48 return json_response({"error": error
}, status
=400)
50 client_type
= data
["type"]
52 if client_type
== "client_update":
54 if "client_id" not in data
:
55 error
= "client_id is requried to update."
56 return json_response({"error": error
}, status
=400)
57 elif "client_secret" not in data
:
58 error
= "client_secret is required to update."
59 return json_response({"error": error
}, status
=400)
61 client
= Client
.query
.filter_by(
63 secret
=data
["client_secret"]
67 error
= "Unauthorized."
68 return json_response({"error": error
}, status
=403)
70 client
.application_name
= data
.get(
72 client
.application_name
75 client
.application_type
= data
.get(
77 client
.application_type
80 app_name
= ("application_type", client
.application_name
)
81 if app_name
in client_types
:
82 client
.application_name
= app_name
84 elif client_type
== "client_associate":
86 if "client_id" in data
:
87 error
= "Only set client_id for update."
88 return json_response({"error": error
}, status
=400)
89 elif "access_token" in data
:
90 error
= "access_token not needed for registration."
91 return json_response({"error": error
}, status
=400)
92 elif "client_secret" in data
:
93 error
= "Only set client_secret for update."
94 return json_response({"error": error
}, status
=400)
96 # generate the client_id and client_secret
97 client_id
= random_string(22) # seems to be what pump uses
98 client_secret
= random_string(43) # again, seems to be what pump uses
99 expirey
= 0 # for now, lets not have it expire
100 expirey_db
= None if expirey
== 0 else expirey
105 secret
=client_secret
,
107 application_type
=data
["application_type"],
111 error
= "Invalid registration type"
112 return json_response({"error": error
}, status
=400)
114 logo_url
= data
.get("logo_url", client
.logo_url
)
115 if logo_url
is not None and not validate_url(logo_url
):
116 error
= "Logo URL {0} is not a valid URL.".format(logo_url
)
117 return json_response(
122 client
.logo_url
= logo_url
123 application_name
=data
.get("application_name", None)
125 contacts
= data
.get("contact", None)
126 if contacts
is not None:
127 if type(contacts
) is not unicode:
128 error
= "Contacts must be a string of space-seporated email addresses."
129 return json_response({"error": error
}, status
=400)
131 contacts
= contacts
.split()
132 for contact
in contacts
:
133 if not validate_email(contact
):
135 error
= "Email {0} is not a valid email.".format(contact
)
136 return json_response({"error": error
}, status
=400)
139 client
.contacts
= contacts
141 request_uri
= data
.get("request_uris", None)
142 if request_uri
is not None:
143 if type(request_uri
) is not unicode:
144 error
= "redirect_uris must be space-seporated URLs."
145 return json_respinse({"error": error
}, status
=400)
147 request_uri
= request_uri
.split()
149 for uri
in request_uri
:
150 if not validate_url(uri
):
152 error
= "URI {0} is not a valid URI".format(uri
)
153 return json_response({"error": error
}, status
=400)
155 client
.request_uri
= request_uri
160 expirey
= 0 if client
.expirey
is None else client
.expirey
162 return json_response(
164 "client_id": client
.id,
165 "client_secret": client
.secret
,
166 "expires_at": expirey
,
169 class ValidationException(Exception):
172 class GMGRequestValidator(RequestValidator
):
174 def __init__(self
, data
):
177 def save_request_token(self
, token
, request
):
178 """ Saves request token in db """
179 client_id
= self
.POST
[u
"Authorization"][u
"oauth_consumer_key"]
181 request_token
= RequestToken(
182 token
=token
["oauth_token"],
183 secret
=token
["oauth_token_secret"],
185 request_token
.client
= client_id
190 def request_token(request
):
191 """ Returns request token """
193 data
= decode_request(request
)
195 error
= "Could not decode data."
196 return json_response({"error": error
}, status
=400)
199 error
= "Unknown Content-Type"
200 return json_response({"error": error
}, status
=400)
203 # Convert 'Authorization' to a dictionary
205 for item
in data
["Authorization"].split(","):
206 key
, value
= item
.split("=", 1)
207 authorization
[key
] = value
208 data
[u
"Authorization"] = authorization
210 # check the client_id
211 client_id
= data
[u
"Authorization"][u
"oauth_consumer_key"]
212 client
= Client
.query
.filter_by(id=client_id
).first()
214 # client_id is invalid
215 error
= "Invalid client_id"
216 return json_response({"error": error
}, status
=400)
218 request_validator
= GMGRequestValidator(data
)
219 rv
= RequestTokenEndpoint(request_validator
)
220 tokens
= rv
.create_request_token(request
, {})
223 for t
in tokens
.split("&"):
224 key
, value
= t
.split("=")
225 tokenized
[key
] = value
227 # check what encoding to return them in
228 return json_response(tokenized
)
230 def authorize(request
):
231 """ Displays a page for user to authorize """
233 token
= request
.args
.get("oauth_token", None)
235 # no token supplied, display a html 400 this time
236 err_msg
= _("Must provide an oauth_token")
237 return render_400(request
, err_msg
=err_msg
)
239 # AuthorizationEndpoint
243 def access_token(request
):
244 """ Provides an access token based on a valid verifier and request token """