aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorxray7224 <xray7224@googlemail.com>2013-06-28 17:59:32 +0100
committerxray7224 <jessica@megworld.co.uk>2013-07-11 18:21:43 +0100
commit4990b47ce401dc86353a261825771a6811be4a8c (patch)
tree450facb956139ae4b1c99ab8d124241c187cb8c2
parentc840cb66180a77e630c261c21967a6afc87411e9 (diff)
downloadmediagoblin-4990b47ce401dc86353a261825771a6811be4a8c.tar.lz
mediagoblin-4990b47ce401dc86353a261825771a6811be4a8c.tar.xz
mediagoblin-4990b47ce401dc86353a261825771a6811be4a8c.zip
Working client registration
-rw-r--r--mediagoblin/db/models.py25
-rw-r--r--mediagoblin/federation/views.py46
-rw-r--r--mediagoblin/routing.py1
-rw-r--r--mediagoblin/tools/crypto.py15
-rw-r--r--mediagoblin/tools/json.py41
-rw-r--r--mediagoblin/tools/response.py27
6 files changed, 101 insertions, 54 deletions
diff --git a/mediagoblin/db/models.py b/mediagoblin/db/models.py
index 826d47ba..4c39c025 100644
--- a/mediagoblin/db/models.py
+++ b/mediagoblin/db/models.py
@@ -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]
diff --git a/mediagoblin/federation/views.py b/mediagoblin/federation/views.py
index 097dc625..bfd58d27 100644
--- a/mediagoblin/federation/views.py
+++ b/mediagoblin/federation/views.py
@@ -14,21 +14,47 @@
# 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,
+ })
diff --git a/mediagoblin/routing.py b/mediagoblin/routing.py
index 986eb2ed..3a54aaa0 100644
--- a/mediagoblin/routing.py
+++ b/mediagoblin/routing.py
@@ -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)
diff --git a/mediagoblin/tools/crypto.py b/mediagoblin/tools/crypto.py
index 1379d21b..917e674c 100644
--- a/mediagoblin/tools/crypto.py
+++ b/mediagoblin/tools/crypto.py
@@ -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
index a8437b82..00000000
--- a/mediagoblin/tools/json.py
+++ /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
diff --git a/mediagoblin/tools/response.py b/mediagoblin/tools/response.py
index 0be1f835..1fd242fb 100644
--- a/mediagoblin/tools/response.py
+++ b/mediagoblin/tools/response.py
@@ -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