Remove deprecated oauth 2 plugin
authorJessica Tallon <jessica@megworld.co.uk>
Mon, 12 Jan 2015 16:24:36 +0000 (16:24 +0000)
committerJessica Tallon <jessica@megworld.co.uk>
Sun, 15 Feb 2015 16:17:01 +0000 (17:17 +0100)
15 files changed:
docs/source/plugindocs/oauth.rst [deleted file]
mediagoblin/plugins/oauth/README.rst [deleted file]
mediagoblin/plugins/oauth/__init__.py [deleted file]
mediagoblin/plugins/oauth/forms.py [deleted file]
mediagoblin/plugins/oauth/migrations.py [deleted file]
mediagoblin/plugins/oauth/models.py [deleted file]
mediagoblin/plugins/oauth/templates/oauth/authorize.html [deleted file]
mediagoblin/plugins/oauth/templates/oauth/client/connections.html [deleted file]
mediagoblin/plugins/oauth/templates/oauth/client/list.html [deleted file]
mediagoblin/plugins/oauth/templates/oauth/client/register.html [deleted file]
mediagoblin/plugins/oauth/tools.py [deleted file]
mediagoblin/plugins/oauth/views.py [deleted file]
mediagoblin/tests/test_http_callback.py [deleted file]
mediagoblin/tests/test_mgoblin_app.ini
mediagoblin/tests/test_oauth2.py [deleted file]

diff --git a/docs/source/plugindocs/oauth.rst b/docs/source/plugindocs/oauth.rst
deleted file mode 100644 (file)
index 0685c9d..0000000
+++ /dev/null
@@ -1 +0,0 @@
-.. include:: ../../../mediagoblin/plugins/oauth/README.rst
diff --git a/mediagoblin/plugins/oauth/README.rst b/mediagoblin/plugins/oauth/README.rst
deleted file mode 100644 (file)
index 753b180..0000000
+++ /dev/null
@@ -1,148 +0,0 @@
-==============
- OAuth plugin
-==============
-
-.. warning::
-    In its current state. This plugin has received no security audit.
-    Development has been entirely focused on Making It Work(TM). Use this
-    plugin with caution.
-
-    Additionally, this and the API may break... consider it pre-alpha.
-    There's also a known issue that the OAuth client doesn't do
-    refresh tokens so this might result in issues for users.
-
-The OAuth plugin enables third party web applications to authenticate as one or
-more GNU MediaGoblin users in a safe way in order retrieve, create and update
-content stored on the GNU MediaGoblin instance.
-
-The OAuth plugin is based on the `oauth v2.25 draft`_ and is pointing by using
-the ``oauthlib.oauth2.draft25.WebApplicationClient`` from oauthlib_ to a
-mediagoblin instance and building the OAuth 2 provider logic around the client.
-
-There are surely some aspects of the OAuth v2.25 draft that haven't made it
-into this plugin due to the technique used to develop it.
-
-.. _`oauth v2.25 draft`: http://tools.ietf.org/html/draft-ietf-oauth-v2-25
-.. _oauthlib: http://pypi.python.org/pypi/oauthlib
-
-
-Set up the OAuth plugin
-=======================
-
-1. Add the following to your MediaGoblin .ini file in the ``[plugins]`` section::
-
-    [[mediagoblin.plugins.oauth]]
-
-2. Run::
-
-        gmg dbupdate
-
-   in order to create and apply migrations to any database tables that the
-   plugin requires.
-
-.. note::
-    This only enables the OAuth plugin. To be able to let clients fetch data
-    from the MediaGoblin instance you should also enable the API plugin or some
-    other plugin that supports authenticating with OAuth credentials.
-
-
-Authenticate against GNU MediaGoblin
-====================================
-
-.. note::
-    As mentioned in `capabilities`_ GNU MediaGoblin currently only supports the
-    `Authorization Code Grant`_ procedure for obtaining an OAuth access token.
-
-Authorization Code Grant
-------------------------
-
-.. note::
-    As mentioned in `incapabilities`_ GNU MediaGoblin currently does not
-    support `client registration`_
-
-The `authorization code grant`_ works in the following way:
-
-`Definitions`
-
-    Authorization server
-        The GNU MediaGoblin instance
-    Resource server
-        Also the GNU MediaGoblin instance ;)
-    Client
-        The web application intended to use the data
-    Redirect uri
-        An URI pointing to a page controlled by the *client*
-    Resource owner
-        The GNU MediaGoblin user who's resources the client requests access to
-    User agent
-        Commonly the GNU MediaGoblin user's web browser
-    Authorization code
-        An intermediate token that is exchanged for an *access token*
-    Access token
-        A secret token that the *client* uses to authenticate itself agains the
-        *resource server* as a specific *resource owner*.
-
-
-Brief description of the procedure
-++++++++++++++++++++++++++++++++++
-
-1. The *client* requests an *authorization code* from the *authorization
-   server* by redirecting the *user agent* to the `Authorization Endpoint`_.
-   Which parameters should be included in the redirect are covered later in
-   this document.
-2. The *authorization server* authenticates the *resource owner* and redirects
-   the *user agent* back to the *redirect uri* (covered later in this
-   document).
-3. The *client* receives the request from the *user agent*, attached is the
-   *authorization code*.
-4. The *client* requests an *access token* from the *authorization server*
-5. \?\?\?\?\?
-6. Profit!
-
-
-Detailed description of the procedure
-+++++++++++++++++++++++++++++++++++++
-
-TBD, in the meantime here is a proof-of-concept GNU MediaGoblin client:
-
-https://github.com/jwandborg/omgmg/
-
-and here are some detailed descriptions from other OAuth 2
-providers:
-
-- https://developers.google.com/accounts/docs/OAuth2WebServer
-- https://developers.facebook.com/docs/authentication/server-side/
-
-and if you're unsure about anything, there's the `OAuth v2.25 draft
-<http://tools.ietf.org/html/draft-ietf-oauth-v2-25>`_, the `OAuth plugin
-source code
-<http://gitorious.org/mediagoblin/mediagoblin/trees/master/mediagoblin/plugins/oauth>`_
-and the `#mediagoblin IRC channel <http://mediagoblin.org/pages/join.html#irc>`_.
-
-
-Capabilities
-============
-
-- `Authorization endpoint`_ - Located at ``/oauth/authorize``
-- `Token endpoint`_ - Located at ``/oauth/access_token``
-- `Authorization Code Grant`_
-- `Client Registration`_
-
-.. _`Authorization endpoint`: http://tools.ietf.org/html/draft-ietf-oauth-v2-25#section-3.1
-.. _`Token endpoint`: http://tools.ietf.org/html/draft-ietf-oauth-v2-25#section-3.2
-.. _`Authorization Code Grant`: http://tools.ietf.org/html/draft-ietf-oauth-v2-25#section-4.1
-.. _`Client Registration`: http://tools.ietf.org/html/draft-ietf-oauth-v2-25#section-2
-
-Incapabilities
-==============
-
-- Only `bearer tokens`_ are issued.
-- `Implicit Grant`_
-- `Force TLS for token endpoint`_ - This one is up the the siteadmin
-- Authorization `scope`_ and `state`
-- ...
-
-.. _`bearer tokens`: http://tools.ietf.org/html/draft-ietf-oauth-v2-bearer-08
-.. _`scope`: http://tools.ietf.org/html/draft-ietf-oauth-v2-25#section-3.3
-.. _`Implicit Grant`: http://tools.ietf.org/html/draft-ietf-oauth-v2-25#section-4.2
-.. _`Force TLS for token endpoint`: http://tools.ietf.org/html/draft-ietf-oauth-v2-25#section-3.2
diff --git a/mediagoblin/plugins/oauth/__init__.py b/mediagoblin/plugins/oauth/__init__.py
deleted file mode 100644 (file)
index 82c1f38..0000000
+++ /dev/null
@@ -1,109 +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 os
-import logging
-
-from mediagoblin.tools import pluginapi
-from mediagoblin.plugins.oauth.models import OAuthToken, OAuthClient, \
-        OAuthUserClient
-from mediagoblin.plugins.api.tools import Auth
-
-_log = logging.getLogger(__name__)
-
-PLUGIN_DIR = os.path.dirname(__file__)
-
-
-def setup_plugin():
-    config = pluginapi.get_config('mediagoblin.plugins.oauth')
-
-    _log.info('Setting up OAuth...')
-    _log.debug('OAuth config: {0}'.format(config))
-
-    routes = [
-        ('mediagoblin.plugins.oauth.authorize',
-            '/oauth-2/authorize',
-            'mediagoblin.plugins.oauth.views:authorize'),
-        ('mediagoblin.plugins.oauth.authorize_client',
-            '/oauth-2/client/authorize',
-            'mediagoblin.plugins.oauth.views:authorize_client'),
-        ('mediagoblin.plugins.oauth.access_token',
-            '/oauth-2/access_token',
-            'mediagoblin.plugins.oauth.views:access_token'),
-        ('mediagoblin.plugins.oauth.list_connections',
-            '/oauth-2/client/connections',
-            'mediagoblin.plugins.oauth.views:list_connections'),
-        ('mediagoblin.plugins.oauth.register_client',
-            '/oauth-2/client/register',
-            'mediagoblin.plugins.oauth.views:register_client'),
-        ('mediagoblin.plugins.oauth.list_clients',
-            '/oauth-2/client/list',
-            'mediagoblin.plugins.oauth.views:list_clients')]
-
-    pluginapi.register_routes(routes)
-    pluginapi.register_template_path(os.path.join(PLUGIN_DIR, 'templates'))
-
-
-class OAuthAuth(Auth):
-    def trigger(self, request):
-        if 'access_token' in request.GET:
-            return True
-
-        return False
-
-    def __call__(self, request, *args, **kw):
-        self.errors = []
-        # TODO: Add suport for client credentials authorization
-        client_id = request.GET.get('client_id')  # TODO: Not used
-        client_secret = request.GET.get('client_secret')  # TODO: Not used
-        access_token = request.GET.get('access_token')
-
-        _log.debug('Authorizing request {0}'.format(request.url))
-
-        if access_token:
-            token = OAuthToken.query.filter(OAuthToken.token == access_token)\
-                    .first()
-
-            if not token:
-                self.errors.append('Invalid access token')
-                return False
-
-            _log.debug('Access token: {0}'.format(token))
-            _log.debug('Client: {0}'.format(token.client))
-
-            relation = OAuthUserClient.query.filter(
-                    (OAuthUserClient.user == token.user)
-                    & (OAuthUserClient.client == token.client)
-                    & (OAuthUserClient.state == u'approved')).first()
-
-            _log.debug('Relation: {0}'.format(relation))
-
-            if not relation:
-                self.errors.append(
-                        u'Client has not been approved by the resource owner')
-                return False
-
-            request.user = token.user
-            return True
-
-        self.errors.append(u'No access_token specified')
-
-        return False
-
-hooks = {
-    'setup': setup_plugin,
-    'auth': OAuthAuth()
-    }
diff --git a/mediagoblin/plugins/oauth/forms.py b/mediagoblin/plugins/oauth/forms.py
deleted file mode 100644 (file)
index 4585c27..0000000
+++ /dev/null
@@ -1,69 +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 wtforms
-
-from six.moves.urllib.parse import urlparse
-
-from mediagoblin.tools.extlib.wtf_html5 import URLField
-from mediagoblin.tools.translate import lazy_pass_to_ugettext as _
-
-
-class AuthorizationForm(wtforms.Form):
-    client_id = wtforms.HiddenField(u'',
-                                    validators=[wtforms.validators.InputRequired()])
-    next = wtforms.HiddenField(u'', validators=[wtforms.validators.InputRequired()])
-    allow = wtforms.SubmitField(_(u'Allow'))
-    deny = wtforms.SubmitField(_(u'Deny'))
-
-
-class ClientRegistrationForm(wtforms.Form):
-    name = wtforms.TextField(_('Name'), [wtforms.validators.InputRequired()],
-            description=_('The name of the OAuth client'))
-    description = wtforms.TextAreaField(_('Description'),
-            [wtforms.validators.Length(min=0, max=500)],
-            description=_('''This will be visible to users allowing your
-                application to authenticate as them.'''))
-    type = wtforms.SelectField(_('Type'),
-            [wtforms.validators.InputRequired()],
-            choices=[
-                ('confidential', 'Confidential'),
-                ('public', 'Public')],
-            description=_('''<strong>Confidential</strong> - The client can
-                make requests to the GNU MediaGoblin instance that can not be
-                intercepted by the user agent (e.g. server-side client).<br />
-                <strong>Public</strong> - The client can't make confidential
-                requests to the GNU MediaGoblin instance (e.g. client-side
-                JavaScript client).'''))
-
-    redirect_uri = URLField(_('Redirect URI'),
-            [wtforms.validators.Optional(), wtforms.validators.URL()],
-            description=_('''The redirect URI for the applications, this field
-            is <strong>required</strong> for public clients.'''))
-
-    def __init__(self, *args, **kw):
-        wtforms.Form.__init__(self, *args, **kw)
-
-    def validate(self):
-        if not wtforms.Form.validate(self):
-            return False
-
-        if self.type.data == 'public' and not self.redirect_uri.data:
-            self.redirect_uri.errors.append(
-                _('This field is required for public clients'))
-            return False
-
-        return True
diff --git a/mediagoblin/plugins/oauth/migrations.py b/mediagoblin/plugins/oauth/migrations.py
deleted file mode 100644 (file)
index d7b89da..0000000
+++ /dev/null
@@ -1,158 +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/>.
-
-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.migration_tools import RegisterMigration
-from mediagoblin.db.models import User
-
-
-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)
-
-
-class OAuthRefreshToken_v0(declarative_base()):
-    __tablename__ = 'oauth__refresh_tokens'
-
-    id = Column(Integer, primary_key=True)
-    created = Column(DateTime, nullable=False,
-                     default=datetime.now)
-
-    token = Column(Unicode, index=True)
-
-    user_id = Column(Integer, ForeignKey(User.id), nullable=False)
-
-    # XXX: Is it OK to use OAuthClient_v0.id in this way?
-    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)
-
-    token_table = Table('oauth__tokens', metadata, autoload=True,
-            autoload_with=db.bind)
-
-    token_table.drop()
-
-    code_table = Table('oauth__codes', metadata, autoload=True,
-            autoload_with=db.bind)
-
-    code_table.drop()
-
-    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()
-
-
-@RegisterMigration(2, MIGRATIONS)
-def remove_refresh_token_field(db):
-    metadata = MetaData(bind=db.bind)
-
-    token_table = Table('oauth__tokens', metadata, autoload=True,
-                        autoload_with=db.bind)
-
-    refresh_token = token_table.columns['refresh_token']
-
-    refresh_token.drop()
-    db.commit()
-
-@RegisterMigration(3, MIGRATIONS)
-def create_refresh_token_table(db):
-    OAuthRefreshToken_v0.__table__.create(db.bind)
-
-    db.commit()
diff --git a/mediagoblin/plugins/oauth/models.py b/mediagoblin/plugins/oauth/models.py
deleted file mode 100644 (file)
index 3fe562a..0000000
+++ /dev/null
@@ -1,188 +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/>.
-
-
-from datetime import datetime, timedelta
-
-
-from sqlalchemy import (
-        Column, Unicode, Integer, DateTime, ForeignKey, Enum)
-from sqlalchemy.orm import relationship, backref
-from mediagoblin.db.base import Base
-from mediagoblin.db.models import User
-from mediagoblin.plugins.oauth.tools import generate_identifier, \
-    generate_secret, generate_token, generate_code, generate_refresh_token
-
-
-class OAuthClient(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,
-                        default=generate_identifier)
-    secret = Column(Unicode, index=True, default=generate_secret)
-
-    owner_id = Column(Integer, ForeignKey(User.id))
-    owner = relationship(
-        User,
-        backref=backref('registered_clients', cascade='all, delete-orphan'))
-
-    redirect_uri = Column(Unicode)
-
-    type = Column(Enum(
-        u'confidential',
-        u'public',
-        name=u'oauth__client_type'))
-
-    def update_secret(self):
-        self.secret = generate_secret()
-
-    def __repr__(self):
-        return '<{0} {1}:{2} ({3})>'.format(
-                self.__class__.__name__,
-                self.id,
-                self.name.encode('ascii', 'replace'),
-                self.owner.username.encode('ascii', 'replace'))
-
-
-class OAuthUserClient(Base):
-    __tablename__ = 'oauth__user_client'
-    id = Column(Integer, primary_key=True)
-
-    user_id = Column(Integer, ForeignKey(User.id))
-    user = relationship(
-        User,
-        backref=backref('oauth_client_relations',
-                        cascade='all, delete-orphan'))
-
-    client_id = Column(Integer, ForeignKey(OAuthClient.id))
-    client = relationship(
-        OAuthClient,
-        backref=backref('oauth_user_relations', cascade='all, delete-orphan'))
-
-    state = Column(Enum(
-        u'approved',
-        u'rejected',
-        name=u'oauth__relation_state'))
-
-    def __repr__(self):
-        return '<{0} #{1} {2} [{3}, {4}]>'.format(
-                self.__class__.__name__,
-                self.id,
-                self.state.encode('ascii', 'replace'),
-                self.user,
-                self.client)
-
-
-class OAuthToken(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, default=generate_token)
-
-    user_id = Column(Integer, ForeignKey(User.id), nullable=False,
-            index=True)
-    user = relationship(
-        User,
-        backref=backref('oauth_tokens', cascade='all, delete-orphan'))
-
-    client_id = Column(Integer, ForeignKey(OAuthClient.id), nullable=False)
-    client = relationship(
-        OAuthClient,
-        backref=backref('oauth_tokens', cascade='all, delete-orphan'))
-
-    def __repr__(self):
-        return '<{0} #{1} expires {2} [{3}, {4}]>'.format(
-                self.__class__.__name__,
-                self.id,
-                self.expires.isoformat(),
-                self.user,
-                self.client)
-
-class OAuthRefreshToken(Base):
-    __tablename__ = 'oauth__refresh_tokens'
-
-    id = Column(Integer, primary_key=True)
-    created = Column(DateTime, nullable=False,
-                     default=datetime.now)
-
-    token = Column(Unicode, index=True,
-                   default=generate_refresh_token)
-
-    user_id = Column(Integer, ForeignKey(User.id), nullable=False)
-
-    user = relationship(User, backref=backref('oauth_refresh_tokens',
-                                              cascade='all, delete-orphan'))
-
-    client_id = Column(Integer, ForeignKey(OAuthClient.id), nullable=False)
-    client = relationship(OAuthClient,
-                          backref=backref(
-                              'oauth_refresh_tokens',
-                              cascade='all, delete-orphan'))
-
-    def __repr__(self):
-        return '<{0} #{1} [{3}, {4}]>'.format(
-                self.__class__.__name__,
-                self.id,
-                self.user,
-                self.client)
-
-
-class OAuthCode(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, default=generate_code)
-
-    user_id = Column(Integer, ForeignKey(User.id), nullable=False,
-            index=True)
-    user = relationship(User, backref=backref('oauth_codes',
-                                              cascade='all, delete-orphan'))
-
-    client_id = Column(Integer, ForeignKey(OAuthClient.id), nullable=False)
-    client = relationship(OAuthClient, backref=backref(
-        'oauth_codes',
-        cascade='all, delete-orphan'))
-
-    def __repr__(self):
-        return '<{0} #{1} expires {2} [{3}, {4}]>'.format(
-                self.__class__.__name__,
-                self.id,
-                self.expires.isoformat(),
-                self.user,
-                self.client)
-
-
-MODELS = [
-        OAuthToken,
-        OAuthRefreshToken,
-        OAuthCode,
-        OAuthClient,
-        OAuthUserClient]
diff --git a/mediagoblin/plugins/oauth/templates/oauth/authorize.html b/mediagoblin/plugins/oauth/templates/oauth/authorize.html
deleted file mode 100644 (file)
index 8a00c92..0000000
+++ /dev/null
@@ -1,31 +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.
-#, se, seee
-# 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/>.
--#}
-{% extends "mediagoblin/base.html" %}
-{% import "mediagoblin/utils/wtforms.html" as wtforms_util %}
-
-{% block mediagoblin_content %}
-<form action="{{ request.urlgen('mediagoblin.plugins.oauth.authorize_client') }}"
-    method="POST">
-    <div class="form_box_xl">
-        {{ csrf_token }}
-        <h2>Authorize {{ client.name }}?</h2>
-        <p class="client-description">{{ client.description }}</p>
-        {{ wtforms_util.render_divs(form) }}
-    </div>
-</form>
-{% endblock %}
diff --git a/mediagoblin/plugins/oauth/templates/oauth/client/connections.html b/mediagoblin/plugins/oauth/templates/oauth/client/connections.html
deleted file mode 100644 (file)
index 63b0230..0000000
+++ /dev/null
@@ -1,34 +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/>.
--#}
-{% extends "mediagoblin/base.html" %}
-{% import "/mediagoblin/utils/wtforms.html" as wtforms_util %}
-
-{% block mediagoblin_content %}
-<h1>{% trans %}OAuth client connections{% endtrans %}</h1>
-{% if connections %}
-<ul>
-    {% for connection in connections %}
-    <li><span title="{{ connection.client.description }}">{{
-        connection.client.name }}</span> - {{ connection.state }}
-    </li>
-    {% endfor %}
-</ul>
-{% else %}
-<p>You haven't connected using an OAuth client before.</p>
-{% endif %}
-{% endblock %}
diff --git a/mediagoblin/plugins/oauth/templates/oauth/client/list.html b/mediagoblin/plugins/oauth/templates/oauth/client/list.html
deleted file mode 100644 (file)
index 21024bb..0000000
+++ /dev/null
@@ -1,45 +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/>.
--#}
-{% extends "mediagoblin/base.html" %}
-{% import "/mediagoblin/utils/wtforms.html" as wtforms_util %}
-
-{% block mediagoblin_content %}
-<h1>{% trans %}Your OAuth clients{% endtrans %}</h1>
-{% if clients %}
-<ul>
-    {% for client in clients %}
-    <li>{{ client.name }}
-    <dl>
-        <dt>Type</dt>
-        <dd>{{ client.type }}</dd>
-        <dt>Description</dt>
-        <dd>{{ client.description }}</dd>
-        <dt>Identifier</dt>
-        <dd>{{ client.identifier }}</dd>
-        <dt>Secret</dt>
-        <dd>{{ client.secret }}</dd>
-        <dt>Redirect URI<dt>
-        <dd>{{ client.redirect_uri }}</dd>
-    </dl>
-    </li>
-    {% endfor %}
-</ul>
-{% else %}
-<p>You don't have any clients yet. <a href="{{ request.urlgen('mediagoblin.plugins.oauth.register_client') }}">Add one</a>.</p>
-{% endif %}
-{% endblock %}
diff --git a/mediagoblin/plugins/oauth/templates/oauth/client/register.html b/mediagoblin/plugins/oauth/templates/oauth/client/register.html
deleted file mode 100644 (file)
index 6fd700d..0000000
+++ /dev/null
@@ -1,34 +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/>.
--#}
-{% extends "mediagoblin/base.html" %}
-{% import "/mediagoblin/utils/wtforms.html" as wtforms_util %}
-
-{% block mediagoblin_content %}
-<form action="{{ request.urlgen('mediagoblin.plugins.oauth.register_client') }}"
-    method="POST">
-    <div class="form_box_xl">
-        <h1>Register OAuth client</h1>
-        {{ wtforms_util.render_divs(form) }}
-        <div class="form_submit_buttons">
-            {{ csrf_token }}
-            <input type="submit" value="{% trans %}Add{% endtrans %}"
-            class="button_form" />
-        </div>
-    </div>
-</form>
-{% endblock %}
diff --git a/mediagoblin/plugins/oauth/tools.py b/mediagoblin/plugins/oauth/tools.py
deleted file mode 100644 (file)
index 2053d5d..0000000
+++ /dev/null
@@ -1,116 +0,0 @@
-# -*- coding: utf-8 -*-
-# 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 uuid
-
-from random import getrandbits
-
-from datetime import datetime
-
-from functools import wraps
-
-import six
-
-from mediagoblin.tools.response import json_response
-
-
-def require_client_auth(controller):
-    '''
-    View decorator
-
-    - Requires the presence of ``?client_id``
-    '''
-    # Avoid circular import
-    from mediagoblin.plugins.oauth.models import OAuthClient
-
-    @wraps(controller)
-    def wrapper(request, *args, **kw):
-        if not request.GET.get('client_id'):
-            return json_response({
-                'status': 400,
-                'errors': [u'No client identifier in URL']},
-                _disable_cors=True)
-
-        client = OAuthClient.query.filter(
-                OAuthClient.identifier == request.GET.get('client_id')).first()
-
-        if not client:
-            return json_response({
-                'status': 400,
-                'errors': [u'No such client identifier']},
-                _disable_cors=True)
-
-        return controller(request, client)
-
-    return wrapper
-
-
-def create_token(client, user):
-    '''
-    Create an OAuthToken and an OAuthRefreshToken entry in the database
-
-    Returns the data structure expected by the OAuth clients.
-    '''
-    from mediagoblin.plugins.oauth.models import OAuthToken, OAuthRefreshToken
-
-    token = OAuthToken()
-    token.user = user
-    token.client = client
-    token.save()
-
-    refresh_token = OAuthRefreshToken()
-    refresh_token.user = user
-    refresh_token.client = client
-    refresh_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
-
-    return {'access_token': token.token, 'token_type': 'bearer',
-            'refresh_token': refresh_token.token, 'expires_in': exp_in}
-
-
-def generate_identifier():
-    ''' Generates a ``uuid.uuid4()`` '''
-    return six.text_type(uuid.uuid4())
-
-
-def generate_token():
-    ''' Uses generate_identifier '''
-    return generate_identifier()
-
-
-def generate_refresh_token():
-    ''' Uses generate_identifier '''
-    return generate_identifier()
-
-
-def generate_code():
-    ''' Uses generate_identifier '''
-    return generate_identifier()
-
-
-def generate_secret():
-    '''
-    Generate a long string of pseudo-random characters
-    '''
-    # XXX: We might not want it to use bcrypt, since bcrypt takes its time to
-    # generate the result.
-    return six.text_type(getrandbits(192))
-
diff --git a/mediagoblin/plugins/oauth/views.py b/mediagoblin/plugins/oauth/views.py
deleted file mode 100644 (file)
index 8ca7352..0000000
+++ /dev/null
@@ -1,255 +0,0 @@
-# -*- coding: utf-8 -*-
-# 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
-
-from six.moves.urllib.parse import urlencode
-
-import six
-
-from werkzeug.exceptions import BadRequest
-
-from mediagoblin.tools.response import render_to_response, redirect, json_response
-from mediagoblin.decorators import require_active_login
-from mediagoblin.messages import add_message, SUCCESS
-from mediagoblin.tools.translate import pass_to_ugettext as _
-from mediagoblin.plugins.oauth.models import OAuthCode, OAuthClient, \
-        OAuthUserClient, OAuthRefreshToken
-from mediagoblin.plugins.oauth.forms import ClientRegistrationForm, \
-        AuthorizationForm
-from mediagoblin.plugins.oauth.tools import require_client_auth, \
-        create_token
-
-_log = logging.getLogger(__name__)
-
-
-@require_active_login
-def register_client(request):
-    '''
-    Register an OAuth client
-    '''
-    form = ClientRegistrationForm(request.form)
-
-    if request.method == 'POST' and form.validate():
-        client = OAuthClient()
-        client.name = six.text_type(form.name.data)
-        client.description = six.text_type(form.description.data)
-        client.type = six.text_type(form.type.data)
-        client.owner_id = request.user.id
-        client.redirect_uri = six.text_type(form.redirect_uri.data)
-
-        client.save()
-
-        add_message(request, SUCCESS, _('The client {0} has been registered!')\
-                .format(
-                    client.name))
-
-        return redirect(request, 'mediagoblin.plugins.oauth.list_clients')
-
-    return render_to_response(
-            request,
-            'oauth/client/register.html',
-            {'form': form})
-
-
-@require_active_login
-def list_clients(request):
-    clients = request.db.OAuthClient.query.filter(
-            OAuthClient.owner_id == request.user.id).all()
-    return render_to_response(request, 'oauth/client/list.html',
-            {'clients': clients})
-
-
-@require_active_login
-def list_connections(request):
-    connections = OAuthUserClient.query.filter(
-            OAuthUserClient.user == request.user).all()
-    return render_to_response(request, 'oauth/client/connections.html',
-            {'connections': connections})
-
-
-@require_active_login
-def authorize_client(request):
-    form = AuthorizationForm(request.form)
-
-    client = OAuthClient.query.filter(OAuthClient.id ==
-        form.client_id.data).first()
-
-    if not client:
-        _log.error('No such client id as received from client authorization \
-form.')
-        raise BadRequest()
-
-    if form.validate():
-        relation = OAuthUserClient()
-        relation.user_id = request.user.id
-        relation.client_id = form.client_id.data
-        if form.allow.data:
-            relation.state = u'approved'
-        elif form.deny.data:
-            relation.state = u'rejected'
-        else:
-            raise BadRequest()
-
-        relation.save()
-
-        return redirect(request, location=form.next.data)
-
-    return render_to_response(
-        request,
-        'oauth/authorize.html',
-        {'form': form,
-            'client': client})
-
-
-@require_client_auth
-@require_active_login
-def authorize(request, client):
-    # TODO: Get rid of the JSON responses in this view, it's called by the
-    # user-agent, not the client.
-    user_client_relation = OAuthUserClient.query.filter(
-            (OAuthUserClient.user == request.user)
-            & (OAuthUserClient.client == client))
-
-    if user_client_relation.filter(OAuthUserClient.state ==
-            u'approved').count():
-        redirect_uri = None
-
-        if client.type == u'public':
-            if not client.redirect_uri:
-                return json_response({
-                    'status': 400,
-                    'errors':
-                        [u'Public clients should have a redirect_uri pre-set.']},
-                        _disable_cors=True)
-
-            redirect_uri = client.redirect_uri
-
-        if client.type == u'confidential':
-            redirect_uri = request.GET.get('redirect_uri', client.redirect_uri)
-            if not redirect_uri:
-                return json_response({
-                    'status': 400,
-                    'errors': [u'No redirect_uri supplied!']},
-                    _disable_cors=True)
-
-        code = OAuthCode()
-        code.user = request.user
-        code.client = client
-        code.save()
-
-        redirect_uri = ''.join([
-            redirect_uri,
-            '?',
-            urlencode({'code': code.code})])
-
-        _log.debug('Redirecting to {0}'.format(redirect_uri))
-
-        return redirect(request, location=redirect_uri)
-    else:
-        # Show prompt to allow client to access data
-        # - on accept: send the user agent back to the redirect_uri with the
-        # code parameter
-        # - on deny: send the user agent back to the redirect uri with error
-        # information
-        form = AuthorizationForm(request.form)
-        form.client_id.data = client.id
-        form.next.data = request.url
-        return render_to_response(
-                request,
-                'oauth/authorize.html',
-                {'form': form,
-                'client': client})
-
-
-def access_token(request):
-    '''
-    Access token endpoint provides access tokens to any clients that have the
-    right grants/credentials
-    '''
-
-    client = None
-    user = None
-
-    if request.GET.get('code'):
-        # Validate the code arg, then get the client object from the db.
-        code = OAuthCode.query.filter(OAuthCode.code ==
-                request.GET.get('code')).first()
-
-        if not code:
-            return json_response({
-                'error': 'invalid_request',
-                'error_description':
-                    'Invalid code.'})
-
-        client = code.client
-        user = code.user
-
-    elif request.args.get('refresh_token'):
-        # Validate a refresh token, then get the client object from the db.
-        refresh_token = OAuthRefreshToken.query.filter(
-            OAuthRefreshToken.token ==
-            request.args.get('refresh_token')).first()
-
-        if not refresh_token:
-            return json_response({
-                'error': 'invalid_request',
-                'error_description':
-                    'Invalid refresh token.'})
-
-        client = refresh_token.client
-        user = refresh_token.user
-
-    if client:
-        client_identifier = request.GET.get('client_id')
-
-        if not client_identifier:
-            return json_response({
-                'error': 'invalid_request',
-                'error_description':
-                    'Missing client_id in request.'})
-
-        if not client_identifier == client.identifier:
-            return json_response({
-                'error': 'invalid_client',
-                'error_description':
-                    'Mismatching client credentials.'})
-
-        if client.type == u'confidential':
-            client_secret = request.GET.get('client_secret')
-
-            if not client_secret:
-                return json_response({
-                    'error': 'invalid_request',
-                    'error_description':
-                        'Missing client_secret in request.'})
-
-            if not client_secret == client.secret:
-                return json_response({
-                    'error': 'invalid_client',
-                    'error_description':
-                        'Mismatching client credentials.'})
-
-
-        access_token_data = create_token(client, user)
-
-        return json_response(access_token_data, _disable_cors=True)
-
-    return json_response({
-        'error': 'invalid_request',
-        'error_description':
-            'Missing `code` or `refresh_token` parameter in request.'})
diff --git a/mediagoblin/tests/test_http_callback.py b/mediagoblin/tests/test_http_callback.py
deleted file mode 100644 (file)
index 38f1cfa..0000000
+++ /dev/null
@@ -1,85 +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
-
-import pytest
-import six
-
-from six.moves.urllib.parse import parse_qs, urlparse
-
-from mediagoblin import mg_globals
-from mediagoblin.tools import processing
-from mediagoblin.tests.tools import fixture_add_user
-from mediagoblin.tests.test_submission import GOOD_PNG
-from mediagoblin.tests import test_oauth2 as oauth
-
-
-class TestHTTPCallback(object):
-    @pytest.fixture(autouse=True)
-    def setup(self, test_app):
-        self.test_app = test_app
-
-        self.db = mg_globals.database
-
-        self.user_password = u'secret'
-        self.user = fixture_add_user(u'call_back', self.user_password)
-
-        self.login()
-
-    def login(self):
-        self.test_app.post('/auth/login/', {
-            'username': self.user.username,
-            'password': self.user_password})
-
-    def get_access_token(self, client_id, client_secret, code):
-        response = self.test_app.get('/oauth-2/access_token', {
-                'code': code,
-                'client_id': client_id,
-                'client_secret': client_secret})
-
-        response_data = json.loads(response.body.decode())
-
-        return response_data['access_token']
-
-    def test_callback(self):
-        ''' Test processing HTTP callback '''
-        self.oauth = oauth.TestOAuth()
-        self.oauth.setup(self.test_app)
-
-        redirect, client_id = self.oauth.test_4_authorize_confidential_client()
-
-        code = parse_qs(urlparse(redirect.location).query)['code'][0]
-
-        client = self.db.OAuthClient.query.filter(
-                self.db.OAuthClient.identifier == six.text_type(client_id)).first()
-
-        client_secret = client.secret
-
-        access_token = self.get_access_token(client_id, client_secret, code)
-
-        callback_url = 'https://foo.example?secrettestmediagoblinparam'
-
-        self.test_app.post('/api/submit?client_id={0}&access_token={1}\
-&client_secret={2}'.format(
-                    client_id,
-                    access_token,
-                    client_secret), {
-            'title': 'Test',
-            'callback_url': callback_url},
-            upload_files=[('file', GOOD_PNG)])
-
-        assert processing.TESTS_CALLBACKS[callback_url]['state'] == u'processed'
index 4cd3d9b6e8efc0cfb60ab9a59e20a8a2c35f71bd..c351d3fc2bb0666d7a2620f8d7086fcc943d5303 100644 (file)
@@ -31,7 +31,6 @@ BROKER_URL = "sqlite:///%(here)s/test_user_dev/kombu.db"
 
 [plugins]
 [[mediagoblin.plugins.api]]
-[[mediagoblin.plugins.oauth]]
 [[mediagoblin.plugins.httpapiauth]]
 [[mediagoblin.plugins.piwigo]]
 [[mediagoblin.plugins.basic_auth]]
diff --git a/mediagoblin/tests/test_oauth2.py b/mediagoblin/tests/test_oauth2.py
deleted file mode 100644 (file)
index 1637273..0000000
+++ /dev/null
@@ -1,225 +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
-import logging
-
-import pytest
-import six
-
-from six.moves.urllib.parse import parse_qs, urlparse
-
-from mediagoblin import mg_globals
-from mediagoblin.tools import template, pluginapi
-from mediagoblin.tests.tools import fixture_add_user
-
-
-_log = logging.getLogger(__name__)
-
-
-class TestOAuth(object):
-    @pytest.fixture(autouse=True)
-    def setup(self, test_app):
-        self.test_app = test_app
-
-        self.db = mg_globals.database
-
-        self.pman = pluginapi.PluginManager()
-
-        self.user_password = u'4cc355_70k3N'
-        self.user = fixture_add_user(u'joauth', self.user_password,
-            privileges=[u'active'])
-
-        self.login()
-
-    def login(self):
-        self.test_app.post(
-            '/auth/login/', {
-                'username': self.user.username,
-                'password': self.user_password})
-
-    def register_client(self, name, client_type, description=None,
-                        redirect_uri=''):
-        return self.test_app.post(
-                '/oauth-2/client/register', {
-                    'name': name,
-                    'description': description,
-                    'type': client_type,
-                    'redirect_uri': redirect_uri})
-
-    def get_context(self, template_name):
-        return template.TEMPLATE_TEST_CONTEXT[template_name]
-
-    def test_1_public_client_registration_without_redirect_uri(self):
-        ''' Test 'public' OAuth client registration without any redirect uri '''
-        response = self.register_client(
-            u'OMGOMGOMG', 'public', 'OMGOMG Apache License v2')
-
-        ctx = self.get_context('oauth/client/register.html')
-
-        client = self.db.OAuthClient.query.filter(
-                self.db.OAuthClient.name == u'OMGOMGOMG').first()
-
-        assert response.status_int == 200
-
-        # Should display an error
-        assert len(ctx['form'].redirect_uri.errors)
-
-        # Should not pass through
-        assert not client
-
-    def test_2_successful_public_client_registration(self):
-        ''' Successfully register a public client '''
-        uri = 'http://foo.example'
-        self.register_client(
-            u'OMGOMG', 'public', 'OMG!', uri)
-
-        client = self.db.OAuthClient.query.filter(
-                self.db.OAuthClient.name == u'OMGOMG').first()
-
-        # redirect_uri should be set
-        assert client.redirect_uri == uri
-
-        # Client should have been registered
-        assert client
-
-    def test_3_successful_confidential_client_reg(self):
-        ''' Register a confidential OAuth client '''
-        response = self.register_client(
-            u'GMOGMO', 'confidential', 'NO GMO!')
-
-        assert response.status_int == 302
-
-        client = self.db.OAuthClient.query.filter(
-                self.db.OAuthClient.name == u'GMOGMO').first()
-
-        # Client should have been registered
-        assert client
-
-        return client
-
-    def test_4_authorize_confidential_client(self):
-        ''' Authorize a confidential client as a logged in user '''
-        client = self.test_3_successful_confidential_client_reg()
-
-        client_identifier = client.identifier
-
-        redirect_uri = 'https://foo.example'
-        response = self.test_app.get('/oauth-2/authorize', {
-                'client_id': client.identifier,
-                'scope': 'all',
-                'redirect_uri': redirect_uri})
-
-        # User-agent should NOT be redirected
-        assert response.status_int == 200
-
-        ctx = self.get_context('oauth/authorize.html')
-
-        form = ctx['form']
-
-        # Short for client authorization post reponse
-        capr = self.test_app.post(
-                '/oauth-2/client/authorize', {
-                    'client_id': form.client_id.data,
-                    'allow': 'Allow',
-                    'next': form.next.data})
-
-        assert capr.status_int == 302
-
-        authorization_response = capr.follow()
-
-        assert authorization_response.location.startswith(redirect_uri)
-
-        return authorization_response, client_identifier
-
-    def get_code_from_redirect_uri(self, uri):
-        ''' Get the value of ?code= from an URI '''
-        return parse_qs(urlparse(uri).query)['code'][0]
-
-    def test_token_endpoint_successful_confidential_request(self):
-        ''' Successful request against token endpoint '''
-        code_redirect, client_id = self.test_4_authorize_confidential_client()
-
-        code = self.get_code_from_redirect_uri(code_redirect.location)
-
-        client = self.db.OAuthClient.query.filter(
-                self.db.OAuthClient.identifier == six.text_type(client_id)).first()
-
-        token_res = self.test_app.get('/oauth-2/access_token?client_id={0}&\
-code={1}&client_secret={2}'.format(client_id, code, client.secret))
-
-        assert token_res.status_int == 200
-
-        token_data = json.loads(token_res.body.decode())
-
-        assert not 'error' in token_data
-        assert 'access_token' in token_data
-        assert 'token_type' in token_data
-        assert 'expires_in' in token_data
-        assert type(token_data['expires_in']) == int
-        assert token_data['expires_in'] > 0
-
-        # There should be a refresh token provided in the token data
-        assert len(token_data['refresh_token'])
-
-        return client_id, token_data
-
-    def test_token_endpont_missing_id_confidential_request(self):
-        ''' Unsuccessful request against token endpoint, missing client_id '''
-        code_redirect, client_id = self.test_4_authorize_confidential_client()
-
-        code = self.get_code_from_redirect_uri(code_redirect.location)
-
-        client = self.db.OAuthClient.query.filter(
-                self.db.OAuthClient.identifier == six.text_type(client_id)).first()
-
-        token_res = self.test_app.get('/oauth-2/access_token?\
-code={0}&client_secret={1}'.format(code, client.secret))
-
-        assert token_res.status_int == 200
-
-        token_data = json.loads(token_res.body.decode())
-
-        assert 'error' in token_data
-        assert not 'access_token' in token_data
-        assert token_data['error'] == 'invalid_request'
-        assert len(token_data['error_description'])
-
-    def test_refresh_token(self):
-        ''' Try to get a new access token using the refresh token '''
-        # Get an access token and a refresh token
-        client_id, token_data =\
-            self.test_token_endpoint_successful_confidential_request()
-
-        client = self.db.OAuthClient.query.filter(
-            self.db.OAuthClient.identifier == client_id).first()
-
-        token_res = self.test_app.get('/oauth-2/access_token',
-                     {'refresh_token': token_data['refresh_token'],
-                      'client_id': client_id,
-                      'client_secret': client.secret
-                      })
-
-        assert token_res.status_int == 200
-
-        new_token_data = json.loads(token_res.body.decode())
-
-        assert not 'error' in new_token_data
-        assert 'access_token' in new_token_data
-        assert 'token_type' in new_token_data
-        assert 'expires_in' in new_token_data
-        assert type(new_token_data['expires_in']) == int
-        assert new_token_data['expires_in'] > 0