diff options
Diffstat (limited to 'mediagoblin/plugins')
-rw-r--r-- | mediagoblin/plugins/api/__init__.py | 17 | ||||
-rw-r--r-- | mediagoblin/plugins/flatpagesfile/__init__.py | 3 | ||||
-rw-r--r-- | mediagoblin/plugins/httpapiauth/__init__.py | 58 | ||||
-rw-r--r-- | mediagoblin/plugins/oauth/__init__.py | 29 | ||||
-rw-r--r-- | mediagoblin/plugins/oauth/migrations.py | 92 | ||||
-rw-r--r-- | mediagoblin/plugins/oauth/views.py | 10 | ||||
-rw-r--r-- | mediagoblin/plugins/trim_whitespace/README.rst | 25 | ||||
-rw-r--r-- | mediagoblin/plugins/trim_whitespace/__init__.py | 73 |
8 files changed, 273 insertions, 34 deletions
diff --git a/mediagoblin/plugins/api/__init__.py b/mediagoblin/plugins/api/__init__.py index 40722088..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__) @@ -33,12 +31,15 @@ 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/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/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 <http://www.gnu.org/licenses/>. + +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()} diff --git a/mediagoblin/plugins/oauth/__init__.py b/mediagoblin/plugins/oauth/__init__.py index 63bf49a8..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 @@ -36,21 +34,24 @@ 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.list_connections', '/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')) 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 <http://www.gnu.org/licenses/>. -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() 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({ 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 <http://www.gnu.org/licenses/>. +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 + } |