Working client registration
authorxray7224 <xray7224@googlemail.com>
Fri, 28 Jun 2013 16:59:32 +0000 (17:59 +0100)
committerxray7224 <jessica@megworld.co.uk>
Thu, 11 Jul 2013 17:21:43 +0000 (18:21 +0100)
mediagoblin/db/models.py
mediagoblin/federation/views.py
mediagoblin/routing.py
mediagoblin/tools/crypto.py
mediagoblin/tools/json.py [deleted file]
mediagoblin/tools/response.py

index 826d47baa0a36b47aaf8699c23027d4b3afa9d4d..4c39c025f8a3450bee550cf466eeaa48e36c207c 100644 (file)
@@ -105,6 +105,29 @@ class User(Base, UserMixin):
         _log.info('Deleted user "{0}" account'.format(self.username))
 
 
+class Client(Base):
+    """
+        Model representing a client - Used for API Auth
+    """
+    __tablename__ = "core__clients"
+
+    id = Column(Unicode, nullable=True, primary_key=True)
+    secret = Column(Unicode, nullable=False)
+    expirey = Column(DateTime, nullable=True)
+    application_type = Column(Unicode, nullable=False)
+    created = Column(DateTime, nullable=False, default=datetime.datetime.now)
+    updated = Column(DateTime, nullable=False, default=datetime.datetime.now)
+
+    # optional stuff
+    redirect_uri = Column(Unicode, nullable=True)
+    logo_uri = Column(Unicode, nullable=True)
+    application_name = Column(Unicode, nullable=True)
+    
+    def __repr__(self):
+        return "<Client {0}>".format(self.id)
+
+
+
 class MediaEntry(Base, MediaEntryMixin):
     """
     TODO: Consider fetching the media_files using join
@@ -580,7 +603,7 @@ with_polymorphic(
     [ProcessingNotification, CommentNotification])
 
 MODELS = [
-    User, MediaEntry, Tag, MediaTag, MediaComment, Collection, CollectionItem,
+    User, Client, MediaEntry, Tag, MediaTag, MediaComment, Collection, CollectionItem,
     MediaFile, FileKeynames, MediaAttachmentFile, ProcessingMetaData,
     Notification, CommentNotification, ProcessingNotification,
     CommentSubscription]
index 097dc62586da3314ea037704800cc7b73104827c..bfd58d270e1c2b8d8c8633ec71107788199dc123 100644 (file)
 # 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 mediagoblin.tools.json import json_response 
+import json
+
+from mediagoblin.meddleware.csrf import csrf_exempt
+from mediagoblin.tools.response import json_response 
+from mediagoblin.tools.crypto import random_string
+from mediagoblin.db.models import Client
 
 # possible client types
 client_types = ["web", "native"] # currently what pump supports
 
+@csrf_exempt
 def client_register(request):
     """ Endpoint for client registration """
-    if request.method == "POST":
-        # new client registration
-
-        return json_response({"dir":dir(request)}) 
+    data = request.get_data()
+    if request.content_type == "application/json":
+        try:
+            data = json.loads(data)
+        except ValueError:
+            return json_response({"error":"Could not decode JSON"})
+    else:
+        return json_response({"error":"Unknown Content-Type"}, status=400)
 
-        # check they haven't given us client_id or client_type, they're only used for updating
-        pass 
+    if "type" not in data:
+        return json_response({"error":"No registration type provided"}, status=400)
+   
+    # generate the client_id and client_secret
+    client_id = random_string(22) # seems to be what pump uses
+    client_secret = random_string(43) # again, seems to be what pump uses
+    expirey = 0 # for now, lets not have it expire
+    expirey_db = None if expirey == 0 else expirey
+    client = Client(
+        id=client_id, 
+        secret=client_secret, 
+        expirey=expirey_db,
+        application_type=data["type"]
+    )
+    client.save()
 
-    elif request.method == "PUT":
-        # updating client
-        pass
+    return json_response(
+        {
+            "client_id":client_id,
+            "client_secret":client_secret,
+            "expires_at":expirey,
+        })
index 986eb2edd10e9242e9df842d3787de0545012df5..3a54aaa0d5fc9523ff2ff3c4e54a5e553ea6eaf8 100644 (file)
@@ -36,6 +36,7 @@ def get_url_map():
     import mediagoblin.webfinger.routing
     import mediagoblin.listings.routing
     import mediagoblin.notifications.routing
+    import mediagoblin.federation.routing
 
     for route in PluginManager().get_routes():
         add_route(*route)
index 1379d21b7751f1b9be09d3a70c9e29ae45c48b73..917e674cf9dade8dc99d4a716155844ac793140a 100644 (file)
@@ -14,6 +14,8 @@
 # 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 base64
+import string
 import errno
 import itsdangerous
 import logging
@@ -24,6 +26,9 @@ from mediagoblin import mg_globals
 
 _log = logging.getLogger(__name__)
 
+# produces base64 alphabet
+alphabet = string.ascii_letters + "-_"
+base = len(alphabet)
 
 # Use the system (hardware-based) random number generator if it exists.
 # -- this optimization is lifted from Django
@@ -111,3 +116,13 @@ def get_timed_signer_url(namespace):
     assert __itsda_secret is not None
     return itsdangerous.URLSafeTimedSerializer(__itsda_secret,
          salt=namespace)
+
+def random_string(length):
+    """ Returns a URL safe base64 encoded crypographically strong string """
+    rstring = ""
+    for i in range(length):
+        n = getrandbits(6) # 6 bytes = 2^6 = 64
+        n = divmod(n, base)[1]
+        rstring += alphabet[n]
+
+    return rstring
diff --git a/mediagoblin/tools/json.py b/mediagoblin/tools/json.py
deleted file mode 100644 (file)
index a8437b8..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-# 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/>.
-
-import json
-
-from werkzeug.wrappers import Response
-
-def json_response(serializable, _disable_cors=False, *args, **kw):
-    '''
-    Serializes a json objects and returns a werkzeug Response object with the
-    serialized value as the response body and Content-Type: application/json.
-
-    :param serializable: A json-serializable object
-
-    Any extra arguments and keyword arguments are passed to the
-    Response.__init__ method.
-    '''
-    response = Response(json.dumps(serializable), *args, content_type='application/json', **kw)
-
-    if not _disable_cors:
-        cors_headers = {
-                'Access-Control-Allow-Origin': '*',
-                'Access-Control-Allow-Methods': 'POST, GET, OPTIONS',
-                'Access-Control-Allow-Headers': 'Content-Type, X-Requested-With'}
-        for key, value in cors_headers.iteritems():
-            response.headers.set(key, value)
-
-    return response
index 0be1f83546ebbadc44535c321034166577972f59..1fd242fb18a8f21abb2a6a5c81231768caf1427d 100644 (file)
@@ -14,6 +14,8 @@
 # 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 json
+
 import werkzeug.utils
 from werkzeug.wrappers import Response as wz_Response
 from mediagoblin.tools.template import render_template
@@ -31,7 +33,6 @@ def render_to_response(request, template, context, status=200):
         render_template(request, template, context),
         status=status)
 
-
 def render_error(request, status=500, title=_('Oops!'),
                  err_msg=_('An error occured')):
     """Render any error page with a given error code, title and text body
@@ -44,7 +45,6 @@ def render_error(request, status=500, title=_('Oops!'),
         {'err_code': status, 'title': title, 'err_msg': err_msg}),
         status=status)
 
-
 def render_403(request):
     """Render a standard 403 page"""
     _ = pass_to_ugettext
@@ -106,3 +106,26 @@ def redirect_obj(request, obj):
 
     Requires obj to have a .url_for_self method."""
     return redirect(request, location=obj.url_for_self(request.urlgen))
+
+def json_response(serializable, _disable_cors=False, *args, **kw):
+    '''
+    Serializes a json objects and returns a werkzeug Response object with the
+    serialized value as the response body and Content-Type: application/json.
+
+    :param serializable: A json-serializable object
+
+    Any extra arguments and keyword arguments are passed to the
+    Response.__init__ method.
+    '''
+    
+    response = wz_Response(json.dumps(serializable), *args, content_type='application/json', **kw)
+
+    if not _disable_cors:
+        cors_headers = {
+                'Access-Control-Allow-Origin': '*',
+                'Access-Control-Allow-Methods': 'POST, GET, OPTIONS',
+                'Access-Control-Allow-Headers': 'Content-Type, X-Requested-With'}
+        for key, value in cors_headers.iteritems():
+            response.headers.set(key, value)
+
+    return response