From 7742dcc1fbda04c3a1c76a057a1a93a8f504502e Mon Sep 17 00:00:00 2001 From: Joar Wandborg Date: Sun, 14 Oct 2012 13:46:31 +0200 Subject: Switched most stuff over from Routes Removed the Routes routing functionality and replaced it with werkzeug.routes. Most views are functional. Known issues: - Translation integration with the request object is not yet figured out. This breaks 404 pages. --- mediagoblin/plugins/api/__init__.py | 12 ++++++------ mediagoblin/plugins/oauth/__init__.py | 24 ++++++++++++------------ 2 files changed, 18 insertions(+), 18 deletions(-) (limited to 'mediagoblin/plugins') diff --git a/mediagoblin/plugins/api/__init__.py b/mediagoblin/plugins/api/__init__.py index 40722088..f370cca6 100644 --- a/mediagoblin/plugins/api/__init__.py +++ b/mediagoblin/plugins/api/__init__.py @@ -33,12 +33,12 @@ def setup_plugin(): _log.debug('API config: {0}'.format(config)) routes = [ - Route('mediagoblin.plugins.api.test', '/api/test', - controller='mediagoblin.plugins.api.views:api_test'), - Route('mediagoblin.plugins.api.entries', '/api/entries', - controller='mediagoblin.plugins.api.views:get_entries'), - Route('mediagoblin.plugins.api.post_entry', '/api/submit', - controller='mediagoblin.plugins.api.views:post_entry')] + ('mediagoblin.plugins.api.test', '/api/test', + 'mediagoblin.plugins.api.views:api_test'), + ('mediagoblin.plugins.api.entries', '/api/entries', + 'mediagoblin.plugins.api.views:get_entries'), + ('mediagoblin.plugins.api.post_entry', '/api/submit', + 'mediagoblin.plugins.api.views:post_entry')] pluginapi.register_routes(routes) diff --git a/mediagoblin/plugins/oauth/__init__.py b/mediagoblin/plugins/oauth/__init__.py index 63bf49a8..64acf0e7 100644 --- a/mediagoblin/plugins/oauth/__init__.py +++ b/mediagoblin/plugins/oauth/__init__.py @@ -36,21 +36,21 @@ def setup_plugin(): _log.debug('OAuth config: {0}'.format(config)) routes = [ - Route('mediagoblin.plugins.oauth.authorize', '/oauth/authorize', - controller='mediagoblin.plugins.oauth.views:authorize'), - Route('mediagoblin.plugins.oauth.authorize_client', '/oauth/client/authorize', - controller='mediagoblin.plugins.oauth.views:authorize_client'), - Route('mediagoblin.plugins.oauth.access_token', '/oauth/access_token', - controller='mediagoblin.plugins.oauth.views:access_token'), - Route('mediagoblin.plugins.oauth.access_token', + ('mediagoblin.plugins.oauth.authorize', '/oauth/authorize', + 'mediagoblin.plugins.oauth.views:authorize'), + ('mediagoblin.plugins.oauth.authorize_client', '/oauth/client/authorize', + 'mediagoblin.plugins.oauth.views:authorize_client'), + ('mediagoblin.plugins.oauth.access_token', '/oauth/access_token', + 'mediagoblin.plugins.oauth.views:access_token'), + ('mediagoblin.plugins.oauth.access_token', '/oauth/client/connections', - controller='mediagoblin.plugins.oauth.views:list_connections'), - Route('mediagoblin.plugins.oauth.register_client', + 'mediagoblin.plugins.oauth.views:list_connections'), + ('mediagoblin.plugins.oauth.register_client', '/oauth/client/register', - controller='mediagoblin.plugins.oauth.views:register_client'), - Route('mediagoblin.plugins.oauth.list_clients', + 'mediagoblin.plugins.oauth.views:register_client'), + ('mediagoblin.plugins.oauth.list_clients', '/oauth/client/list', - controller='mediagoblin.plugins.oauth.views:list_clients')] + 'mediagoblin.plugins.oauth.views:list_clients')] pluginapi.register_routes(routes) pluginapi.register_template_path(os.path.join(PLUGIN_DIR, 'templates')) -- cgit v1.2.3 From d56e82635f85f7a8a7d184a3eae539c09a7b001d Mon Sep 17 00:00:00 2001 From: Joar Wandborg Date: Mon, 15 Oct 2012 00:12:58 +0200 Subject: Fixed OAuth access_token duplicate route Changed route name to "[...]list_connections" --- mediagoblin/plugins/api/__init__.py | 9 ++++++--- mediagoblin/plugins/oauth/__init__.py | 11 +++++++---- 2 files changed, 13 insertions(+), 7 deletions(-) (limited to 'mediagoblin/plugins') diff --git a/mediagoblin/plugins/api/__init__.py b/mediagoblin/plugins/api/__init__.py index f370cca6..3b7ced0c 100644 --- a/mediagoblin/plugins/api/__init__.py +++ b/mediagoblin/plugins/api/__init__.py @@ -33,11 +33,14 @@ def setup_plugin(): _log.debug('API config: {0}'.format(config)) routes = [ - ('mediagoblin.plugins.api.test', '/api/test', + ('mediagoblin.plugins.api.test', + '/api/test', 'mediagoblin.plugins.api.views:api_test'), - ('mediagoblin.plugins.api.entries', '/api/entries', + ('mediagoblin.plugins.api.entries', + '/api/entries', 'mediagoblin.plugins.api.views:get_entries'), - ('mediagoblin.plugins.api.post_entry', '/api/submit', + ('mediagoblin.plugins.api.post_entry', + '/api/submit', 'mediagoblin.plugins.api.views:post_entry')] pluginapi.register_routes(routes) diff --git a/mediagoblin/plugins/oauth/__init__.py b/mediagoblin/plugins/oauth/__init__.py index 64acf0e7..3ed695de 100644 --- a/mediagoblin/plugins/oauth/__init__.py +++ b/mediagoblin/plugins/oauth/__init__.py @@ -36,13 +36,16 @@ def setup_plugin(): _log.debug('OAuth config: {0}'.format(config)) routes = [ - ('mediagoblin.plugins.oauth.authorize', '/oauth/authorize', + ('mediagoblin.plugins.oauth.authorize', + '/oauth/authorize', 'mediagoblin.plugins.oauth.views:authorize'), - ('mediagoblin.plugins.oauth.authorize_client', '/oauth/client/authorize', + ('mediagoblin.plugins.oauth.authorize_client', + '/oauth/client/authorize', 'mediagoblin.plugins.oauth.views:authorize_client'), - ('mediagoblin.plugins.oauth.access_token', '/oauth/access_token', - 'mediagoblin.plugins.oauth.views:access_token'), ('mediagoblin.plugins.oauth.access_token', + '/oauth/access_token', + 'mediagoblin.plugins.oauth.views:access_token'), + ('mediagoblin.plugins.oauth.list_connections', '/oauth/client/connections', 'mediagoblin.plugins.oauth.views:list_connections'), ('mediagoblin.plugins.oauth.register_client', -- cgit v1.2.3 From 5b60ec41ee5d0f25d66190b2a0114a8e1b216f86 Mon Sep 17 00:00:00 2001 From: Joar Wandborg Date: Sat, 20 Oct 2012 12:09:23 +0200 Subject: Removed Routes dependency, added admin routes --- mediagoblin/plugins/api/__init__.py | 2 -- mediagoblin/plugins/flatpagesfile/__init__.py | 3 +-- mediagoblin/plugins/oauth/__init__.py | 2 -- 3 files changed, 1 insertion(+), 6 deletions(-) (limited to 'mediagoblin/plugins') diff --git a/mediagoblin/plugins/api/__init__.py b/mediagoblin/plugins/api/__init__.py index 3b7ced0c..d3fdf2ef 100644 --- a/mediagoblin/plugins/api/__init__.py +++ b/mediagoblin/plugins/api/__init__.py @@ -17,8 +17,6 @@ import os import logging -from routes.route import Route - from mediagoblin.tools import pluginapi _log = logging.getLogger(__name__) diff --git a/mediagoblin/plugins/flatpagesfile/__init__.py b/mediagoblin/plugins/flatpagesfile/__init__.py index b9b52012..3d797809 100644 --- a/mediagoblin/plugins/flatpagesfile/__init__.py +++ b/mediagoblin/plugins/flatpagesfile/__init__.py @@ -19,7 +19,6 @@ import logging import os import jinja2 -from routes.route import Route from mediagoblin.tools import pluginapi from mediagoblin.tools.response import render_to_response @@ -68,7 +67,7 @@ def setup_plugin(): name = 'flatpagesfile.%s' % name.strip() controller = flatpage_handler_builder(template) routes.append( - Route(name, url, controller=controller)) + (name, url, controller)) pluginapi.register_routes(routes) _log.info('Done setting up flatpagesfile!') diff --git a/mediagoblin/plugins/oauth/__init__.py b/mediagoblin/plugins/oauth/__init__.py index 3ed695de..4714d95d 100644 --- a/mediagoblin/plugins/oauth/__init__.py +++ b/mediagoblin/plugins/oauth/__init__.py @@ -17,8 +17,6 @@ import os import logging -from routes.route import Route - from mediagoblin.tools import pluginapi from mediagoblin.plugins.oauth.models import OAuthToken, OAuthClient, \ OAuthUserClient -- cgit v1.2.3 From ac4c5aef5775d5c4a3d8ebd5528e6d2db8062174 Mon Sep 17 00:00:00 2001 From: Joar Wandborg Date: Sat, 20 Oct 2012 23:56:00 +0200 Subject: Added HTTP API auth plugin --- mediagoblin/plugins/httpapiauth/__init__.py | 58 +++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 mediagoblin/plugins/httpapiauth/__init__.py (limited to 'mediagoblin/plugins') diff --git a/mediagoblin/plugins/httpapiauth/__init__.py b/mediagoblin/plugins/httpapiauth/__init__.py new file mode 100644 index 00000000..d3d2065e --- /dev/null +++ b/mediagoblin/plugins/httpapiauth/__init__.py @@ -0,0 +1,58 @@ +# 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 . + +import logging +import base64 + +from werkzeug.exceptions import BadRequest, Unauthorized + +from mediagoblin.plugins.api.tools import Auth + +_log = logging.getLogger(__name__) + + +def setup_http_api_auth(): + _log.info('Setting up HTTP API Auth...') + + +class HTTPAuth(Auth): + def trigger(self, request): + if request.authorization: + return True + + return False + + def __call__(self, request, *args, **kw): + _log.debug('Trying to authorize the user agent via HTTP Auth') + if not request.authorization: + return False + + user = request.db.User.query.filter_by( + username=request.authorization['username']).first() + + if user.check_login(request.authorization['password']): + request.user = user + return True + else: + raise Unauthorized() + + return False + + + +hooks = { + 'setup': setup_http_api_auth, + 'auth': HTTPAuth()} -- cgit v1.2.3 From 316e1dfddeb7955c3bb8a5183c53024c68184a22 Mon Sep 17 00:00:00 2001 From: Elrond Date: Sat, 24 Nov 2012 19:19:18 +0100 Subject: SQL Migrations: Rewrite table creation completely. We have migrations creating new tables. Those currently use "raw" table definitions. This easily gives errors (we already had this problem). So instead rewrite those to use declarative tables and use those to create new tables. Just copy the new table over to the migration, strip it down to the bare minimum, rename to _v0, base it on declarative_base() and be done! Do this for the current migrations. --- mediagoblin/plugins/oauth/migrations.py | 92 ++++++++++++++++++++++++++++++--- 1 file changed, 85 insertions(+), 7 deletions(-) (limited to 'mediagoblin/plugins') diff --git a/mediagoblin/plugins/oauth/migrations.py b/mediagoblin/plugins/oauth/migrations.py index f2af3907..797e7585 100644 --- a/mediagoblin/plugins/oauth/migrations.py +++ b/mediagoblin/plugins/oauth/migrations.py @@ -14,16 +14,94 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -from sqlalchemy import MetaData, Table +from datetime import datetime, timedelta +from sqlalchemy import (MetaData, Table, Column, + Integer, Unicode, Enum, DateTime, ForeignKey) +from sqlalchemy.ext.declarative import declarative_base from mediagoblin.db.sql.util import RegisterMigration +from mediagoblin.db.sql.models import User -from mediagoblin.plugins.oauth.models import OAuthClient, OAuthToken, \ - OAuthUserClient, OAuthCode MIGRATIONS = {} +class OAuthClient_v0(declarative_base()): + __tablename__ = 'oauth__client' + + id = Column(Integer, primary_key=True) + created = Column(DateTime, nullable=False, + default=datetime.now) + + name = Column(Unicode) + description = Column(Unicode) + + identifier = Column(Unicode, unique=True, index=True) + secret = Column(Unicode, index=True) + + owner_id = Column(Integer, ForeignKey(User.id)) + redirect_uri = Column(Unicode) + + type = Column(Enum( + u'confidential', + u'public', + name=u'oauth__client_type')) + + +class OAuthUserClient_v0(declarative_base()): + __tablename__ = 'oauth__user_client' + id = Column(Integer, primary_key=True) + + user_id = Column(Integer, ForeignKey(User.id)) + client_id = Column(Integer, ForeignKey(OAuthClient_v0.id)) + + state = Column(Enum( + u'approved', + u'rejected', + name=u'oauth__relation_state')) + + +class OAuthToken_v0(declarative_base()): + __tablename__ = 'oauth__tokens' + + id = Column(Integer, primary_key=True) + created = Column(DateTime, nullable=False, + default=datetime.now) + expires = Column(DateTime, nullable=False, + default=lambda: datetime.now() + timedelta(days=30)) + token = Column(Unicode, index=True) + refresh_token = Column(Unicode, index=True) + + user_id = Column(Integer, ForeignKey(User.id), nullable=False, + index=True) + + client_id = Column(Integer, ForeignKey(OAuthClient_v0.id), nullable=False) + + def __repr__(self): + return '<{0} #{1} expires {2} [{3}, {4}]>'.format( + self.__class__.__name__, + self.id, + self.expires.isoformat(), + self.user, + self.client) + + +class OAuthCode_v0(declarative_base()): + __tablename__ = 'oauth__codes' + + id = Column(Integer, primary_key=True) + created = Column(DateTime, nullable=False, + default=datetime.now) + expires = Column(DateTime, nullable=False, + default=lambda: datetime.now() + timedelta(minutes=5)) + code = Column(Unicode, index=True) + + user_id = Column(Integer, ForeignKey(User.id), nullable=False, + index=True) + + client_id = Column(Integer, ForeignKey(OAuthClient_v0.id), nullable=False) + + @RegisterMigration(1, MIGRATIONS) def remove_and_replace_token_and_code(db): metadata = MetaData(bind=db.bind) @@ -38,9 +116,9 @@ def remove_and_replace_token_and_code(db): code_table.drop() - OAuthClient.__table__.create(db.bind) - OAuthUserClient.__table__.create(db.bind) - OAuthToken.__table__.create(db.bind) - OAuthCode.__table__.create(db.bind) + OAuthClient_v0.__table__.create(db.bind) + OAuthUserClient_v0.__table__.create(db.bind) + OAuthToken_v0.__table__.create(db.bind) + OAuthCode_v0.__table__.create(db.bind) db.commit() -- cgit v1.2.3 From 9945468603b8476f735d735dbc7505ee47ff9f42 Mon Sep 17 00:00:00 2001 From: Sebastian Spaeth Date: Thu, 29 Nov 2012 11:28:25 +0100 Subject: trim_whitespace meddleware plugin Our HTML output is very verbose (=whitespacy) as our templates are written with an 80 char limit and lots of newlines between blocks, variables etc.... This is a plugin that naively strips of all but the first whitespace from the HTML response. We might want to have an all-fancy html tidy interface here at some point, but it nicely decreases the HTML size about a third on some simple pages. Signed-off-by: Sebastian Spaeth --- mediagoblin/plugins/trim_whitespace/README.rst | 25 +++++++++ mediagoblin/plugins/trim_whitespace/__init__.py | 73 +++++++++++++++++++++++++ 2 files changed, 98 insertions(+) create mode 100644 mediagoblin/plugins/trim_whitespace/README.rst create mode 100644 mediagoblin/plugins/trim_whitespace/__init__.py (limited to 'mediagoblin/plugins') diff --git a/mediagoblin/plugins/trim_whitespace/README.rst b/mediagoblin/plugins/trim_whitespace/README.rst new file mode 100644 index 00000000..b55ce35e --- /dev/null +++ b/mediagoblin/plugins/trim_whitespace/README.rst @@ -0,0 +1,25 @@ +======================= + Trim whitespace plugin +======================= + +Mediagoblin templates are written with 80 char limit for better +readability. However that means that the html output is very verbose +containing LOTS of whitespace. This plugin inserts a Middleware that +filters out whitespace from the returned HTML in the Response() objects. + +Simply enable this plugin by putting it somewhere where python can reach it and put it's path into the [plugins] section of your mediagoblin.ini or mediagoblin_local.ini like for example this: + + [plugins] + [[mediagoblin.plugins.trim_whitespace]] + +There is no further configuration required. If this plugin is enabled, +all text/html documents should not have lots of whitespace in between +elements, although it does a very naive filtering right now (just keep +the first whitespace and delete all subsequent ones). + +Nonetheless, it is a useful plugin that might serve as inspiration for +other plugin writers. + +It was originally conceived by Sebastian Spaeth. It is licensed under +the GNU AGPL v3 (or any later version) license. + diff --git a/mediagoblin/plugins/trim_whitespace/__init__.py b/mediagoblin/plugins/trim_whitespace/__init__.py new file mode 100644 index 00000000..3da1e8b4 --- /dev/null +++ b/mediagoblin/plugins/trim_whitespace/__init__.py @@ -0,0 +1,73 @@ +# 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 . +from __future__ import unicode_literals +import logging +import re + +from mediagoblin import meddleware + +_log = logging.getLogger(__name__) + +class TrimWhiteSpaceMeddleware(meddleware.BaseMeddleware): + _setup_plugin_called = 0 + RE_MULTI_WHITESPACE = re.compile(b'(\s)\s+', re.M) + + def process_response(self, request, response): + """Perform very naive html tidying by removing multiple whitespaces""" + # werkzeug.BaseResponse has no content_type attr, this comes via + # werkzeug.wrappers.CommonRequestDescriptorsMixin (part of + # wrappers.Response) + if getattr(response ,'content_type', None) != 'text/html': + return + + # This is a tad more complex than needed to be able to handle + # response.data and response.body, depending on whether we have + # a werkzeug Resonse or a webob one. Let's kill webob soon! + if hasattr(response, 'body') and not hasattr(response, 'data'): + # Old-style webob Response object. + # TODO: Remove this once we transition away from webob + resp_attr = 'body' + else: + resp_attr = 'data' + # Don't flatten iterator to list when we fudge the response body + # (see werkzeug.Response documentation) + response.implicit_sequence_conversion = False + + # Set the tidied text. Very naive tidying for now, just strip all + # subsequent whitespaces (this preserves most newlines) + setattr(response, resp_attr, re.sub( + TrimWhiteSpaceMeddleware.RE_MULTI_WHITESPACE, br'\1', + getattr(response, resp_attr))) + + @classmethod + def setup_plugin(cls): + """Set up this meddleware as a plugin during 'setup' hook""" + global _log + if cls._setup_plugin_called: + _log.info('Trim whitespace plugin was already set up.') + return + + _log.debug('Trim whitespace plugin set up.') + cls._setup_plugin_called += 1 + + # Append ourselves to the list of enabled Meddlewares + meddleware.ENABLED_MEDDLEWARE.append( + '{0}:{1}'.format(cls.__module__, cls.__name__)) + + +hooks = { + 'setup': TrimWhiteSpaceMeddleware.setup_plugin + } -- cgit v1.2.3 From 2f5926a65d4e56d4ab9c7bfd6b3de25a032b8be5 Mon Sep 17 00:00:00 2001 From: Sebastian Spaeth Date: Fri, 14 Dec 2012 10:54:53 +0100 Subject: Fiy python2.7'ism (#566) The oauth plugin used timedelta.total_seconds which was introduced in python 2.7 only. To preserve backwards compatability, we simply calculate the time difference in seconds manually. I considered monkeypatching total_seconds to the timedelta object, but it is a built-in type written in C (I believe) and modifying attributes failed horribly. Switch this to use total_seconds once we require python 2.7 as minimum version. Signed-off-by: Sebastian Spaeth --- mediagoblin/plugins/oauth/views.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'mediagoblin/plugins') diff --git a/mediagoblin/plugins/oauth/views.py b/mediagoblin/plugins/oauth/views.py index cf605fd2..643c2783 100644 --- a/mediagoblin/plugins/oauth/views.py +++ b/mediagoblin/plugins/oauth/views.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- # GNU MediaGoblin -- federated, autonomous media hosting # Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS. # @@ -216,12 +217,15 @@ def access_token(request): token.client = code.client token.save() + # expire time of token in full seconds + # timedelta.total_seconds is python >= 2.7 or we would use that + td = token.expires - datetime.now() + exp_in = 86400*td.days + td.seconds # just ignore µsec + access_token_data = { 'access_token': token.token, 'token_type': 'bearer', - 'expires_in': int( - round( - (token.expires - datetime.now()).total_seconds()))} + 'expires_in': exp_in} return json_response(access_token_data, _disable_cors=True) else: return json_response({ -- cgit v1.2.3