Adds more support for oauth - access_token & decorators still to do
This commit is contained in:
parent
d41c6a5349
commit
405aa45adc
@ -143,7 +143,7 @@ class RequestToken(Base):
|
|||||||
used = Column(Boolean, default=False)
|
used = Column(Boolean, default=False)
|
||||||
authenticated = Column(Boolean, default=False)
|
authenticated = Column(Boolean, default=False)
|
||||||
verifier = Column(Unicode, nullable=True)
|
verifier = Column(Unicode, nullable=True)
|
||||||
callback = Column(Unicode, nullable=True)
|
callback = Column(Unicode, nullable=False, default=u"oob")
|
||||||
created = Column(DateTime, nullable=False, default=datetime.datetime.now)
|
created = Column(DateTime, nullable=False, default=datetime.datetime.now)
|
||||||
updated = Column(DateTime, nullable=False, default=datetime.datetime.now)
|
updated = Column(DateTime, nullable=False, default=datetime.datetime.now)
|
||||||
|
|
||||||
|
8
mediagoblin/federation/forms.py
Normal file
8
mediagoblin/federation/forms.py
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import wtforms
|
||||||
|
from mediagoblin.tools.translate import lazy_pass_to_ugettext as _
|
||||||
|
|
||||||
|
class AuthorizeForm(wtforms.Form):
|
||||||
|
""" Form used to authorize the request token """
|
||||||
|
|
||||||
|
oauth_token = wtforms.HiddenField("oauth_token")
|
||||||
|
oauth_verifier = wtforms.HiddenField("oauth_verifier")
|
@ -14,15 +14,22 @@
|
|||||||
# You should have received a copy of the GNU Affero General Public License
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from oauthlib.oauth1 import RequestValidator, RequestTokenEndpoint
|
import datetime
|
||||||
|
|
||||||
|
import oauthlib.common
|
||||||
|
from oauthlib.oauth1 import (AuthorizationEndpoint, RequestValidator,
|
||||||
|
RequestTokenEndpoint)
|
||||||
|
|
||||||
|
from mediagoblin.decorators import require_active_login
|
||||||
from mediagoblin.tools.translate import pass_to_ugettext
|
from mediagoblin.tools.translate import pass_to_ugettext
|
||||||
from mediagoblin.meddleware.csrf import csrf_exempt
|
from mediagoblin.meddleware.csrf import csrf_exempt
|
||||||
from mediagoblin.tools.request import decode_request
|
from mediagoblin.tools.request import decode_request
|
||||||
from mediagoblin.tools.response import json_response, render_400
|
from mediagoblin.tools.response import (render_to_response, redirect,
|
||||||
|
json_response, render_400)
|
||||||
from mediagoblin.tools.crypto import random_string
|
from mediagoblin.tools.crypto import random_string
|
||||||
from mediagoblin.tools.validator import validate_email, validate_url
|
from mediagoblin.tools.validator import validate_email, validate_url
|
||||||
from mediagoblin.db.models import Client, RequestToken, AccessToken
|
from mediagoblin.db.models import User, Client, RequestToken, AccessToken
|
||||||
|
from mediagoblin.federation.forms import AuthorizeForm
|
||||||
|
|
||||||
# possible client types
|
# possible client types
|
||||||
client_types = ["web", "native"] # currently what pump supports
|
client_types = ["web", "native"] # currently what pump supports
|
||||||
@ -120,7 +127,8 @@ def client_register(request):
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
client.logo_url = logo_url
|
client.logo_url = logo_url
|
||||||
application_name=data.get("application_name", None)
|
|
||||||
|
client.application_name = data.get("application_name", None)
|
||||||
|
|
||||||
contacts = data.get("contact", None)
|
contacts = data.get("contact", None)
|
||||||
if contacts is not None:
|
if contacts is not None:
|
||||||
@ -171,7 +179,7 @@ class ValidationException(Exception):
|
|||||||
|
|
||||||
class GMGRequestValidator(RequestValidator):
|
class GMGRequestValidator(RequestValidator):
|
||||||
|
|
||||||
def __init__(self, data):
|
def __init__(self, data=None):
|
||||||
self.POST = data
|
self.POST = data
|
||||||
|
|
||||||
def save_request_token(self, token, request):
|
def save_request_token(self, token, request):
|
||||||
@ -183,8 +191,25 @@ class GMGRequestValidator(RequestValidator):
|
|||||||
secret=token["oauth_token_secret"],
|
secret=token["oauth_token_secret"],
|
||||||
)
|
)
|
||||||
request_token.client = client_id
|
request_token.client = client_id
|
||||||
|
request_token.callback = token.get("oauth_callback", None)
|
||||||
request_token.save()
|
request_token.save()
|
||||||
|
|
||||||
|
def save_verifier(self, token, verifier, request):
|
||||||
|
""" Saves the oauth request verifier """
|
||||||
|
request_token = RequestToken.query.filter_by(token=token).first()
|
||||||
|
request_token.verifier = verifier["oauth_verifier"]
|
||||||
|
request_token.save()
|
||||||
|
|
||||||
|
|
||||||
|
def save_access_token(self, token, request):
|
||||||
|
""" Saves access token in db """
|
||||||
|
access_token = AccessToken(
|
||||||
|
token=token["oauth_token"],
|
||||||
|
secret=token["oauth_secret"],
|
||||||
|
)
|
||||||
|
access_token.request_token = request.body["oauth_token"]
|
||||||
|
access_token.user = token["user"].id
|
||||||
|
access_token.save()
|
||||||
|
|
||||||
@csrf_exempt
|
@csrf_exempt
|
||||||
def request_token(request):
|
def request_token(request):
|
||||||
@ -195,10 +220,16 @@ def request_token(request):
|
|||||||
error = "Could not decode data."
|
error = "Could not decode data."
|
||||||
return json_response({"error": error}, status=400)
|
return json_response({"error": error}, status=400)
|
||||||
|
|
||||||
if data is "":
|
if data == "":
|
||||||
error = "Unknown Content-Type"
|
error = "Unknown Content-Type"
|
||||||
return json_response({"error": error}, status=400)
|
return json_response({"error": error}, status=400)
|
||||||
|
|
||||||
|
print data
|
||||||
|
|
||||||
|
if "Authorization" not in data:
|
||||||
|
error = "Missing required parameter."
|
||||||
|
return json_response({"error": error}, status=400)
|
||||||
|
|
||||||
|
|
||||||
# Convert 'Authorization' to a dictionary
|
# Convert 'Authorization' to a dictionary
|
||||||
authorization = {}
|
authorization = {}
|
||||||
@ -207,6 +238,10 @@ def request_token(request):
|
|||||||
authorization[key] = value
|
authorization[key] = value
|
||||||
data[u"Authorization"] = authorization
|
data[u"Authorization"] = authorization
|
||||||
|
|
||||||
|
if "oauth_consumer_key" not in data[u"Authorization"]:
|
||||||
|
error = "Missing required parameter."
|
||||||
|
return json_respinse({"error": error}, status=400)
|
||||||
|
|
||||||
# check the client_id
|
# check the client_id
|
||||||
client_id = data[u"Authorization"][u"oauth_consumer_key"]
|
client_id = data[u"Authorization"][u"oauth_consumer_key"]
|
||||||
client = Client.query.filter_by(id=client_id).first()
|
client = Client.query.filter_by(id=client_id).first()
|
||||||
@ -217,29 +252,137 @@ def request_token(request):
|
|||||||
|
|
||||||
request_validator = GMGRequestValidator(data)
|
request_validator = GMGRequestValidator(data)
|
||||||
rv = RequestTokenEndpoint(request_validator)
|
rv = RequestTokenEndpoint(request_validator)
|
||||||
tokens = rv.create_request_token(request, {})
|
tokens = rv.create_request_token(request, authorization)
|
||||||
|
|
||||||
tokenized = {}
|
tokenized = {}
|
||||||
for t in tokens.split("&"):
|
for t in tokens.split("&"):
|
||||||
key, value = t.split("=")
|
key, value = t.split("=")
|
||||||
tokenized[key] = value
|
tokenized[key] = value
|
||||||
|
|
||||||
|
print "[DEBUG] %s" % tokenized
|
||||||
|
|
||||||
# check what encoding to return them in
|
# check what encoding to return them in
|
||||||
return json_response(tokenized)
|
return json_response(tokenized)
|
||||||
|
|
||||||
|
class WTFormData(dict):
|
||||||
|
"""
|
||||||
|
Provides a WTForm usable dictionary
|
||||||
|
"""
|
||||||
|
def getlist(self, key):
|
||||||
|
v = self[key]
|
||||||
|
if not isinstance(v, (list, tuple)):
|
||||||
|
v = [v]
|
||||||
|
return v
|
||||||
|
|
||||||
|
@require_active_login
|
||||||
def authorize(request):
|
def authorize(request):
|
||||||
""" Displays a page for user to authorize """
|
""" Displays a page for user to authorize """
|
||||||
|
if request.method == "POST":
|
||||||
|
return authorize_finish(request)
|
||||||
|
|
||||||
_ = pass_to_ugettext
|
_ = pass_to_ugettext
|
||||||
token = request.args.get("oauth_token", None)
|
token = request.args.get("oauth_token", None)
|
||||||
if token is None:
|
if token is None:
|
||||||
# no token supplied, display a html 400 this time
|
# no token supplied, display a html 400 this time
|
||||||
err_msg = _("Must provide an oauth_token")
|
err_msg = _("Must provide an oauth_token.")
|
||||||
return render_400(request, err_msg=err_msg)
|
return render_400(request, err_msg=err_msg)
|
||||||
|
|
||||||
# AuthorizationEndpoint
|
oauth_request = RequestToken.query.filter_by(token=token).first()
|
||||||
|
if oauth_request is None:
|
||||||
|
err_msg = _("No request token found.")
|
||||||
|
return render_400(request, err_msg)
|
||||||
|
|
||||||
|
if oauth_request.used:
|
||||||
|
return authorize_finish(request)
|
||||||
|
|
||||||
|
if oauth_request.verifier is None:
|
||||||
|
orequest = oauthlib.common.Request(
|
||||||
|
uri=request.url,
|
||||||
|
http_method=request.method,
|
||||||
|
body=request.get_data(),
|
||||||
|
headers=request.headers
|
||||||
|
)
|
||||||
|
request_validator = GMGRequestValidator()
|
||||||
|
auth_endpoint = AuthorizationEndpoint(request_validator)
|
||||||
|
verifier = auth_endpoint.create_verifier(orequest, {})
|
||||||
|
oauth_request.verifier = verifier["oauth_verifier"]
|
||||||
|
|
||||||
|
oauth_request.user = request.user.id
|
||||||
|
oauth_request.save()
|
||||||
|
|
||||||
|
# find client & build context
|
||||||
|
client = Client.query.filter_by(id=oauth_request.client).first()
|
||||||
|
|
||||||
|
authorize_form = AuthorizeForm(WTFormData({
|
||||||
|
"oauth_token": oauth_request.token,
|
||||||
|
"oauth_verifier": oauth_request.verifier
|
||||||
|
}))
|
||||||
|
|
||||||
|
context = {
|
||||||
|
"user": request.user,
|
||||||
|
"oauth_request": oauth_request,
|
||||||
|
"client": client,
|
||||||
|
"authorize_form": authorize_form,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# AuthorizationEndpoint
|
||||||
|
return render_to_response(
|
||||||
|
request,
|
||||||
|
"mediagoblin/api/authorize.html",
|
||||||
|
context
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def authorize_finish(request):
|
||||||
|
""" Finishes the authorize """
|
||||||
|
_ = pass_to_ugettext
|
||||||
|
token = request.form["oauth_token"]
|
||||||
|
verifier = request.form["oauth_verifier"]
|
||||||
|
oauth_request = RequestToken.query.filter_by(token=token, verifier=verifier)
|
||||||
|
oauth_request = oauth_request.first()
|
||||||
|
|
||||||
|
if oauth_request is None:
|
||||||
|
# invalid token or verifier
|
||||||
|
err_msg = _("No request token found.")
|
||||||
|
return render_400(request, err_msg)
|
||||||
|
|
||||||
|
oauth_request.used = True
|
||||||
|
oauth_request.updated = datetime.datetime.now()
|
||||||
|
oauth_request.save()
|
||||||
|
|
||||||
|
if oauth_request.callback == "oob":
|
||||||
|
# out of bounds
|
||||||
|
context = {"oauth_request": oauth_request}
|
||||||
|
return render_to_response(
|
||||||
|
request,
|
||||||
|
"mediagoblin/api/oob.html",
|
||||||
|
context
|
||||||
|
)
|
||||||
|
|
||||||
|
# okay we need to redirect them then!
|
||||||
|
querystring = "?oauth_token={0}&oauth_verifier={1}".format(
|
||||||
|
oauth_request.token,
|
||||||
|
oauth_request.verifier
|
||||||
|
)
|
||||||
|
|
||||||
|
return redirect(
|
||||||
|
request,
|
||||||
|
querystring=querystring,
|
||||||
|
location=oauth_request.callback
|
||||||
|
)
|
||||||
|
|
||||||
@csrf_exempt
|
@csrf_exempt
|
||||||
def access_token(request):
|
def access_token(request):
|
||||||
""" Provides an access token based on a valid verifier and request token """
|
""" Provides an access token based on a valid verifier and request token """
|
||||||
pass
|
try:
|
||||||
|
data = decode_request(request)
|
||||||
|
except ValueError:
|
||||||
|
error = "Could not decode data."
|
||||||
|
return json_response({"error": error}, status=400)
|
||||||
|
|
||||||
|
if data == "":
|
||||||
|
error = "Unknown Content-Type"
|
||||||
|
return json_response({"error": error}, status=400)
|
||||||
|
|
||||||
|
print "debug: %s" % data
|
||||||
|
@ -753,3 +753,10 @@ pre {
|
|||||||
#exif_additional_info table tr {
|
#exif_additional_info table tr {
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
p.verifier {
|
||||||
|
text-align:center;
|
||||||
|
font-size:50px;
|
||||||
|
none repeat scroll 0% 0% rgb(221, 221, 221);
|
||||||
|
padding: 1em 0px;
|
||||||
|
}
|
||||||
|
56
mediagoblin/templates/mediagoblin/api/authorize.html
Normal file
56
mediagoblin/templates/mediagoblin/api/authorize.html
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
{#
|
||||||
|
# GNU MediaGoblin -- federated, autonomous media hosting
|
||||||
|
# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Affero General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#}
|
||||||
|
{% extends "mediagoblin/base.html" %}
|
||||||
|
|
||||||
|
{% block title -%}
|
||||||
|
{% trans %}Authorization{% endtrans %} — {{ super() }}
|
||||||
|
{%- endblock %}
|
||||||
|
|
||||||
|
{% block mediagoblin_content %}
|
||||||
|
|
||||||
|
<h1>{% trans %}Authorize{% endtrans %}</h1>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
{% trans %}You are logged in as{% endtrans %}
|
||||||
|
<strong>{{user.username}}</strong>
|
||||||
|
<br /><br />
|
||||||
|
|
||||||
|
{% trans %}Do you want to authorize {% endtrans %}
|
||||||
|
{% if client.application_name -%}
|
||||||
|
<em>{{ client.application_name }}</em>
|
||||||
|
{%- else -%}
|
||||||
|
<em>{% trans %}an unknown application{% endtrans %}</em>
|
||||||
|
{%- endif %}
|
||||||
|
{% trans %} to access your account? {% endtrans %}
|
||||||
|
<br /><br />
|
||||||
|
{% trans %}Applications with access to your account can: {% endtrans %}
|
||||||
|
<ul>
|
||||||
|
<li>{% trans %}Post new media as you{% endtrans %}</li>
|
||||||
|
<li>{% trans %}See your information (e.g profile, meida, etc...){% endtrans %}</li>
|
||||||
|
<li>{% trans %}Change your information{% endtrans %}</li>
|
||||||
|
</ul>
|
||||||
|
<br />
|
||||||
|
|
||||||
|
<form method="POST">
|
||||||
|
{{ csrf_token }}
|
||||||
|
{{ authorize_form.oauth_token }}
|
||||||
|
{{ authorize_form.oauth_verifier }}
|
||||||
|
<input type="submit" value="{% trans %}Authorize{% endtrans %}">
|
||||||
|
</form>
|
||||||
|
</p>
|
||||||
|
{% endblock %}
|
33
mediagoblin/templates/mediagoblin/api/oob.html
Normal file
33
mediagoblin/templates/mediagoblin/api/oob.html
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
{#
|
||||||
|
# GNU MediaGoblin -- federated, autonomous media hosting
|
||||||
|
# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Affero General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#}
|
||||||
|
{% extends "mediagoblin/base.html" %}
|
||||||
|
|
||||||
|
{% block title -%}
|
||||||
|
{% trans %}Authorization Finished{% endtrans %} — {{ super() }}
|
||||||
|
{%- endblock %}
|
||||||
|
|
||||||
|
{% block mediagoblin_content %}
|
||||||
|
|
||||||
|
<h1>{% trans %}Authorization Complete{% endtrans %}</h1>
|
||||||
|
|
||||||
|
<h4>{% trans %}Copy and paste this into your client:{% endtrans %}</h4>
|
||||||
|
|
||||||
|
<p class="verifier">
|
||||||
|
{{ oauth_request.verifier }}
|
||||||
|
</p>
|
||||||
|
{% endblock %}
|
@ -48,7 +48,7 @@ def decode_request(request):
|
|||||||
|
|
||||||
if request.content_type == json_encoded:
|
if request.content_type == json_encoded:
|
||||||
data = json.loads(data)
|
data = json.loads(data)
|
||||||
elif request.content_type == form_encoded:
|
elif request.content_type == form_encoded or request.content_type == "":
|
||||||
data = request.form
|
data = request.form
|
||||||
else:
|
else:
|
||||||
data = ""
|
data = ""
|
||||||
|
Loading…
x
Reference in New Issue
Block a user