Finishes most of oauth, just decorator to complete
authorxray7224 <jessica@megworld.co.uk>
Thu, 11 Jul 2013 16:58:58 +0000 (17:58 +0100)
committerxray7224 <jessica@megworld.co.uk>
Thu, 11 Jul 2013 17:21:43 +0000 (18:21 +0100)
mediagoblin/decorators.py
mediagoblin/federation/views.py
mediagoblin/tools/request.py
mediagoblin/tools/response.py

index ece222f5d7ce29e8bbf5dfd2bcbd5d156be50dcc..ce26e46c64ca17690a39feda620156c639428dca 100644 (file)
@@ -22,7 +22,8 @@ from werkzeug.exceptions import Forbidden, NotFound
 from mediagoblin import mg_globals as mgg
 from mediagoblin import messages
 from mediagoblin.db.models import MediaEntry, User
-from mediagoblin.tools.response import redirect, render_404
+from mediagoblin.tools.request import decode_authorization_header
+from mediagoblin.tools.response import json_response, redirect, render_404
 from mediagoblin.tools.translate import pass_to_ugettext as _
 
 
@@ -268,3 +269,16 @@ def auth_enabled(controller):
         return controller(request, *args, **kwargs)
 
     return wrapper
+
+def oauth_requeired(controller):
+    """ Used to wrap API endpoints where oauth is required """
+    @wraps(controller)
+    def wrapper(request, *args, **kwargs):
+        data = request.headers
+        authorization = decode_authorization_header(data)
+
+        if authorization == dict():
+            error = "Missing required parameter."
+            return json_response({"error": error}, status=400)
+
+        
index 9559df10c6215f8c3b378ec05408b04ab7835cc8..a6dcc79b48affaf902f8d92e2181526007608071 100644 (file)
@@ -18,14 +18,15 @@ import datetime
 
 import oauthlib.common
 from oauthlib.oauth1 import (AuthorizationEndpoint, RequestValidator, 
-                             RequestTokenEndpoint)
+                             RequestTokenEndpoint, AccessTokenEndpoint)
 
 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.request import decode_request, decode_authorization_header
 from mediagoblin.tools.response import (render_to_response, redirect, 
-                                        json_response, render_400)
+                                        json_response, render_400,
+                                        form_response)
 from mediagoblin.tools.crypto import random_string
 from mediagoblin.tools.validator import validate_email, validate_url
 from mediagoblin.db.models import User, Client, RequestToken, AccessToken
@@ -184,7 +185,7 @@ class GMGRequestValidator(RequestValidator):
 
     def save_request_token(self, token, request):
         """ Saves request token in db """
-        client_id = self.POST[u"Authorization"][u"oauth_consumer_key"]
+        client_id = self.POST[u"oauth_consumer_key"]
 
         request_token = RequestToken(
                 token=token["oauth_token"],
@@ -200,17 +201,21 @@ class GMGRequestValidator(RequestValidator):
         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"],
+                secret=token["oauth_token_secret"],
         )
-        access_token.request_token = request.body["oauth_token"]
-        access_token.user = token["user"].id
+        access_token.request_token = request.oauth_token
+        request_token = RequestToken.query.filter_by(token=request.oauth_token).first()
+        access_token.user = request_token.user
         access_token.save()
 
+    def get_realms(*args, **kwargs):
+        """ Currently a stub - called when making AccessTokens """
+        return list()
+
 @csrf_exempt
 def request_token(request):
     """ Returns request token """
@@ -224,45 +229,32 @@ def request_token(request):
         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)
+    if not data and request.headers:
+        data = request.headers
+    
+    data = dict(data) # mutableifying
 
+    authorization = decode_authorization_header(data)
 
-    # Convert 'Authorization' to a dictionary
-    authorization = {}
-    for item in data["Authorization"].split(","):
-        key, value = item.split("=", 1)
-        authorization[key] = value
-    data[u"Authorization"] = authorization
 
-    if "oauth_consumer_key" not in data[u"Authorization"]:
+    if authorization == dict() or u"oauth_consumer_key" not in authorization:
         error = "Missing required parameter."
-        return json_respinse({"error": error}, status=400)
+        return json_response({"error": error}, status=400)
 
     # check the client_id
-    client_id = data[u"Authorization"][u"oauth_consumer_key"]
+    client_id = authorization[u"oauth_consumer_key"]
     client = Client.query.filter_by(id=client_id).first()
     if client is None:
         # client_id is invalid
         error = "Invalid client_id"
         return json_response({"error": error}, status=400)
 
-    request_validator = GMGRequestValidator(data)
+    # make request token and return to client
+    request_validator = GMGRequestValidator(authorization)
     rv = RequestTokenEndpoint(request_validator)
     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)
+    return form_response(tokens)
 
 class WTFormData(dict):
     """
@@ -375,14 +367,18 @@ def authorize_finish(request):
 @csrf_exempt
 def access_token(request):
     """ Provides an access token based on a valid verifier and request token """ 
-    try:
-        data = decode_request(request)
-    except ValueError:
-        error = "Could not decode data."
-        return json_response({"error": error}, status=400)
+    data = request.headers
 
-    if data == "":
-        error = "Unknown Content-Type"
+    parsed_tokens = decode_authorization_header(data)    
+
+    if parsed_tokens == dict() or "oauth_token" not in parsed_tokens:
+        error = "Missing required parameter."
         return json_response({"error": error}, status=400)
 
-    print "debug: %s" % data
+
+    request.oauth_token = parsed_tokens["oauth_token"]
+    request_validator = GMGRequestValidator(data)
+    av = AccessTokenEndpoint(request_validator)
+    tokens = av.create_access_token(request, {})
+    return form_response(tokens)
index 2c9e609d68700a4398db5c8e1f580c981927c442..0c0fc557c1e69dea0253ce87dfaf196faff7120f 100644 (file)
@@ -14,6 +14,7 @@
 # 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/>.
 
+import re
 import json
 import logging
 from mediagoblin.db.models import User
@@ -25,6 +26,9 @@ _log = logging.getLogger(__name__)
 form_encoded = "application/x-www-form-urlencoded"
 json_encoded = "application/json"
 
+# Regex for Authorization header
+auth_header_re = re.compile('(\w+)[:=] ?"?(\w+)"?')
+
 def setup_user_in_request(request):
     """
     Examine a request and tack on a request.user parameter if that's
@@ -53,3 +57,9 @@ def decode_request(request):
     else:
         data = ""
     return data
+
+def decode_authorization_header(header):
+    """ Decodes a HTTP Authorization Header to python dictionary """
+    authorization = header.get("Authorization", "")
+    tokens = dict(auth_header_re.findall(authorization))
+    return tokens
index db8fc38828a0fad3517a0e20b06d293ee251e361..b0401e08b6002442063038befd0a8596a85d3664 100644 (file)
@@ -138,3 +138,22 @@ def json_response(serializable, _disable_cors=False, *args, **kw):
             response.headers.set(key, value)
 
     return response
+
+def form_response(data, *args, **kwargs):
+    """
+        Responds using application/x-www-form-urlencoded and returns a werkzeug
+        Response object with the data argument as the body
+        and 'application/x-www-form-urlencoded' as the Content-Type.
+
+        Any extra arguments and keyword arguments are passed to the
+        Response.__init__ method.
+    """
+
+    response = wz_Response(
+            data,
+            content_type="application/x-www-form-urlencoded",
+            *args,
+            **kwargs
+            )
+
+    return response