Adds more support for oauth - access_token & decorators still to do

This commit is contained in:
xray7224 2013-07-10 15:49:59 +01:00 committed by xray7224
parent d41c6a5349
commit 405aa45adc
7 changed files with 260 additions and 13 deletions

View File

@ -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)

View 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")

View File

@ -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

View File

@ -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;
}

View 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 %} &mdash; {{ 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 %}

View 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 %} &mdash; {{ 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 %}

View File

@ -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 = ""