Source code for invenio_oauthclient.models

# -*- coding: utf-8 -*-
#
# This file is part of Invenio.
# Copyright (C) 2014, 2015, 2016 CERN.
#
# Invenio is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of the
# License, or (at your option) any later version.
#
# Invenio 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
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Invenio; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.

"""Models for storing access tokens and links between users and remote apps."""

from flask import current_app
from invenio_accounts.models import User
from invenio_db import db
from sqlalchemy.ext.mutable import MutableDict
from sqlalchemy.orm import backref
from sqlalchemy_utils import JSONType
from sqlalchemy_utils.types.encrypted import EncryptedType


def _secret_key():
    """Return secret key from current application."""
    return current_app.config.get('SECRET_KEY')


[docs]class RemoteAccount(db.Model): """Storage for remote linked accounts.""" __tablename__ = 'oauthclient_remoteaccount' __table_args__ = ( db.UniqueConstraint('user_id', 'client_id'), ) # # Fields # id = db.Column( db.Integer, primary_key=True, autoincrement=True ) """Primary key.""" user_id = db.Column( db.Integer, db.ForeignKey(User.id), nullable=False ) """Local user linked with a remote app via the access token.""" client_id = db.Column(db.String(255), nullable=False) """Client ID of remote application (defined in OAUTHCLIENT_REMOTE_APPS).""" extra_data = db.Column(MutableDict.as_mutable(JSONType), nullable=False) """Extra data associated with this linked account.""" # # Relationships properties # user = db.relationship(User, backref='remote_accounts') """SQLAlchemy relationship to user.""" @classmethod
[docs] def get(cls, user_id, client_id): """Get RemoteAccount object for user. :param user_id: User id :param client_id: Client id. :returns: A :class:`invenio_oauthclient.models.RemoteAccount` instance. """ return cls.query.filter_by( user_id=user_id, client_id=client_id, ).first()
@classmethod
[docs] def create(cls, user_id, client_id, extra_data): """Create new remote account for user. :param user_id: User id. :param client_id: Client id. :param extra_data: JSON-serializable dictionary of any extra data that needs to be save together with this link. :returns: A :class:`invenio_oauthclient.models.RemoteAccount` instance. """ with db.session.begin_nested(): account = cls( user_id=user_id, client_id=client_id, extra_data=extra_data or dict() ) db.session.add(account) return account
[docs] def delete(self): """Delete remote account together with all stored tokens.""" with db.session.begin_nested(): db.session.delete(self)
def __repr__(self): """String representation for model.""" return 'Remote Account <id={0.id}, user_id={0.user.id}>'.format(self)
[docs]class RemoteToken(db.Model): """Storage for the access tokens for linked accounts.""" __tablename__ = 'oauthclient_remotetoken' # # Fields # id_remote_account = db.Column( db.Integer, db.ForeignKey( RemoteAccount.id, name='fk_oauthclient_remote_token_remote_account' ), nullable=False, primary_key=True ) """Foreign key to account.""" token_type = db.Column( db.String(40), default='', nullable=False, primary_key=True ) """Type of token.""" access_token = db.Column( EncryptedType(type_in=db.Text, key=_secret_key), nullable=False ) """Access token to remote application.""" secret = db.Column(db.Text(), default='', nullable=False) """Used only by OAuth 1.""" # # Relationships properties # remote_account = db.relationship( RemoteAccount, backref=backref('remote_tokens', cascade='all, delete-orphan') ) """SQLAlchemy relationship to RemoteAccount objects.""" def __repr__(self): """String representation for model.""" return ('Remote Token <token_type={0.token_type} ' 'access_token={0.access_token}>'.format(self))
[docs] def token(self): """Get token as expected by Flask-OAuthlib.""" return (self.access_token, self.secret)
[docs] def update_token(self, token, secret): """Update token with new values. :param token: The token value. :param secret: The secret key. """ if self.access_token != token or self.secret != secret: with db.session.begin_nested(): self.access_token = token self.secret = secret db.session.add(self)
@classmethod
[docs] def get(cls, user_id, client_id, token_type='', access_token=None): """Get RemoteToken for user. :param user_id: The user id. :param client_id: The client id. :param token_type: The token type. (Default: ``''``) :param access_token: If set, will filter also by access token. (Default: ``None``) :returns: A :class:`invenio_oauthclient.models.RemoteToken` instance. """ args = [ RemoteAccount.id == RemoteToken.id_remote_account, RemoteAccount.user_id == user_id, RemoteAccount.client_id == client_id, RemoteToken.token_type == token_type, ] if access_token: args.append(RemoteToken.access_token == access_token) return cls.query.options( db.joinedload('remote_account') ).filter(*args).first()
@classmethod
[docs] def get_by_token(cls, client_id, access_token, token_type=''): """Get RemoteAccount object for token. :param client_id: The client id. :param access_token: The access token. :param token_type: The token type. (Default: ``''``) :returns: A :class:`invenio_oauthclient.models.RemoteToken` instance. """ return cls.query.options(db.joinedload('remote_account')).filter( RemoteAccount.id == RemoteToken.id_remote_account, RemoteAccount.client_id == client_id, RemoteToken.token_type == token_type, RemoteToken.access_token == access_token, ).first()
@classmethod
[docs] def create(cls, user_id, client_id, token, secret, token_type='', extra_data=None): """Create a new access token. .. note:: Creates RemoteAccount as well if it does not exists. :param user_id: The user id. :param client_id: The client id. :param token: The token. :param secret: The secret key. :param token_type: The token type. (Default: ``''``) :param extra_data: Extra data to set in the remote account if the remote account doesn't exists. (Default: ``None``) :returns: A :class:`invenio_oauthclient.models.RemoteToken` instance. """ account = RemoteAccount.get(user_id, client_id) with db.session.begin_nested(): if account is None: account = RemoteAccount( user_id=user_id, client_id=client_id, extra_data=extra_data or dict(), ) db.session.add(account) token = cls( token_type=token_type, remote_account=account, access_token=token, secret=secret, ) db.session.add(token) return token
[docs]class UserIdentity(db.Model): """Represent a UserIdentity record.""" __tablename__ = 'oauthclient_useridentity' id = db.Column(db.String(255), primary_key=True, nullable=False) method = db.Column(db.String(255), primary_key=True, nullable=False) id_user = db.Column(db.Integer(), db.ForeignKey(User.id), nullable=False) user = db.relationship(User, backref='external_identifiers') __table_args__ = ( db.Index('useridentity_id_user_method', id_user, method, unique=True), )
__all__ = ('RemoteAccount', 'RemoteToken', 'UserIdentity')