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)
|
||||
authenticated = Column(Boolean, default=False)
|
||||
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)
|
||||
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
|
||||
# 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.meddleware.csrf import csrf_exempt
|
||||
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.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
|
||||
client_types = ["web", "native"] # currently what pump supports
|
||||
@ -120,7 +127,8 @@ def client_register(request):
|
||||
)
|
||||
else:
|
||||
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)
|
||||
if contacts is not None:
|
||||
@ -171,7 +179,7 @@ class ValidationException(Exception):
|
||||
|
||||
class GMGRequestValidator(RequestValidator):
|
||||
|
||||
def __init__(self, data):
|
||||
def __init__(self, data=None):
|
||||
self.POST = data
|
||||
|
||||
def save_request_token(self, token, request):
|
||||
@ -183,8 +191,25 @@ class GMGRequestValidator(RequestValidator):
|
||||
secret=token["oauth_token_secret"],
|
||||
)
|
||||
request_token.client = client_id
|
||||
request_token.callback = token.get("oauth_callback", None)
|
||||
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
|
||||
def request_token(request):
|
||||
@ -195,10 +220,16 @@ def request_token(request):
|
||||
error = "Could not decode data."
|
||||
return json_response({"error": error}, status=400)
|
||||
|
||||
if data is "":
|
||||
if data == "":
|
||||
error = "Unknown Content-Type"
|
||||
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
|
||||
authorization = {}
|
||||
@ -207,6 +238,10 @@ def request_token(request):
|
||||
authorization[key] = value
|
||||
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
|
||||
client_id = data[u"Authorization"][u"oauth_consumer_key"]
|
||||
client = Client.query.filter_by(id=client_id).first()
|
||||
@ -217,29 +252,137 @@ def request_token(request):
|
||||
|
||||
request_validator = GMGRequestValidator(data)
|
||||
rv = RequestTokenEndpoint(request_validator)
|
||||
tokens = rv.create_request_token(request, {})
|
||||
tokens = rv.create_request_token(request, authorization)
|
||||
|
||||
tokenized = {}
|
||||
for t in tokens.split("&"):
|
||||
key, value = t.split("=")
|
||||
tokenized[key] = value
|
||||
|
||||
print "[DEBUG] %s" % tokenized
|
||||
|
||||
# check what encoding to return them in
|
||||
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):
|
||||
""" Displays a page for user to authorize """
|
||||
if request.method == "POST":
|
||||
return authorize_finish(request)
|
||||
|
||||
_ = pass_to_ugettext
|
||||
token = request.args.get("oauth_token", None)
|
||||
if token is None:
|
||||
# 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)
|
||||
|
||||
# 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
|
||||
def access_token(request):
|
||||
""" 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 {
|
||||
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:
|
||||
data = json.loads(data)
|
||||
elif request.content_type == form_encoded:
|
||||
elif request.content_type == form_encoded or request.content_type == "":
|
||||
data = request.form
|
||||
else:
|
||||
data = ""
|
||||
|
Loading…
x
Reference in New Issue
Block a user