Working client registration

This commit is contained in:
xray7224 2013-06-28 17:59:32 +01:00 committed by xray7224
parent c840cb6618
commit 4990b47ce4
6 changed files with 101 additions and 54 deletions

View File

@ -105,6 +105,29 @@ class User(Base, UserMixin):
_log.info('Deleted user "{0}" account'.format(self.username)) _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): class MediaEntry(Base, MediaEntryMixin):
""" """
TODO: Consider fetching the media_files using join TODO: Consider fetching the media_files using join
@ -580,7 +603,7 @@ with_polymorphic(
[ProcessingNotification, CommentNotification]) [ProcessingNotification, CommentNotification])
MODELS = [ MODELS = [
User, MediaEntry, Tag, MediaTag, MediaComment, Collection, CollectionItem, User, Client, MediaEntry, Tag, MediaTag, MediaComment, Collection, CollectionItem,
MediaFile, FileKeynames, MediaAttachmentFile, ProcessingMetaData, MediaFile, FileKeynames, MediaAttachmentFile, ProcessingMetaData,
Notification, CommentNotification, ProcessingNotification, Notification, CommentNotification, ProcessingNotification,
CommentSubscription] CommentSubscription]

View File

@ -14,21 +14,47 @@
# You should have received a copy of the GNU Affero General Public License # 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/>. # 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 # possible client types
client_types = ["web", "native"] # currently what pump supports client_types = ["web", "native"] # currently what pump supports
@csrf_exempt
def client_register(request): def client_register(request):
""" Endpoint for client registration """ """ Endpoint for client registration """
if request.method == "POST": data = request.get_data()
# new client registration 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)
return json_response({"dir":dir(request)}) 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()
# check they haven't given us client_id or client_type, they're only used for updating return json_response(
pass {
"client_id":client_id,
elif request.method == "PUT": "client_secret":client_secret,
# updating client "expires_at":expirey,
pass })

View File

@ -36,6 +36,7 @@ def get_url_map():
import mediagoblin.webfinger.routing import mediagoblin.webfinger.routing
import mediagoblin.listings.routing import mediagoblin.listings.routing
import mediagoblin.notifications.routing import mediagoblin.notifications.routing
import mediagoblin.federation.routing
for route in PluginManager().get_routes(): for route in PluginManager().get_routes():
add_route(*route) add_route(*route)

View File

@ -14,6 +14,8 @@
# You should have received a copy of the GNU Affero General Public License # 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/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import base64
import string
import errno import errno
import itsdangerous import itsdangerous
import logging import logging
@ -24,6 +26,9 @@ from mediagoblin import mg_globals
_log = logging.getLogger(__name__) _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. # Use the system (hardware-based) random number generator if it exists.
# -- this optimization is lifted from Django # -- this optimization is lifted from Django
@ -111,3 +116,13 @@ def get_timed_signer_url(namespace):
assert __itsda_secret is not None assert __itsda_secret is not None
return itsdangerous.URLSafeTimedSerializer(__itsda_secret, return itsdangerous.URLSafeTimedSerializer(__itsda_secret,
salt=namespace) 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

View File

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

View File

@ -14,6 +14,8 @@
# You should have received a copy of the GNU Affero General Public License # 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/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import json
import werkzeug.utils import werkzeug.utils
from werkzeug.wrappers import Response as wz_Response from werkzeug.wrappers import Response as wz_Response
from mediagoblin.tools.template import render_template 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), render_template(request, template, context),
status=status) status=status)
def render_error(request, status=500, title=_('Oops!'), def render_error(request, status=500, title=_('Oops!'),
err_msg=_('An error occured')): err_msg=_('An error occured')):
"""Render any error page with a given error code, title and text body """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}), {'err_code': status, 'title': title, 'err_msg': err_msg}),
status=status) status=status)
def render_403(request): def render_403(request):
"""Render a standard 403 page""" """Render a standard 403 page"""
_ = pass_to_ugettext _ = pass_to_ugettext
@ -106,3 +106,26 @@ def redirect_obj(request, obj):
Requires obj to have a .url_for_self method.""" Requires obj to have a .url_for_self method."""
return redirect(request, location=obj.url_for_self(request.urlgen)) 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