Пример #1
0
def test_get_create_remote_token(models_fixture):
    """Test create remote token."""
    app = models_fixture
    existing_email = "*****@*****.**"
    datastore = app.extensions['invenio-accounts'].datastore
    user = datastore.find_user(email=existing_email)

    t = RemoteToken.create(user.id, "dev", "mytoken", "mysecret")
    assert t
    assert t.token() == ('mytoken', 'mysecret')

    acc = RemoteAccount.get(user.id, "dev")
    assert acc
    assert t.remote_account.id == acc.id
    assert t.token_type == ''

    t2 = RemoteToken.create(
        user.id, "dev", "mytoken2", "mysecret2",
        token_type='t2'
    )
    assert t2.remote_account.id == acc.id
    assert t2.token_type == 't2'

    t3 = RemoteToken.get(user.id, "dev")
    t4 = RemoteToken.get(user.id, "dev", token_type="t2")
    assert t4.token() != t3.token()

    assert RemoteToken.query.count() == 2
    acc.delete()
    assert RemoteToken.query.count() == 0
Пример #2
0
def test_get_create_remote_token(models_fixture, example):
    """Test create remote token."""
    app = models_fixture
    existing_email = "*****@*****.**"
    datastore = app.extensions['invenio-accounts'].datastore
    user = datastore.find_user(email=existing_email)

    t = RemoteToken.create(user.id, "dev", "mytoken", "mysecret")
    assert t
    assert t.token() == ('mytoken', 'mysecret')

    acc = RemoteAccount.get(user.id, "dev")
    assert acc
    assert t.remote_account.id == acc.id
    assert t.token_type == ''

    t2 = RemoteToken.create(user.id,
                            "dev",
                            "mytoken2",
                            "mysecret2",
                            token_type='t2')
    assert t2.remote_account.id == acc.id
    assert t2.token_type == 't2'

    t3 = RemoteToken.get(user.id, "dev")
    t4 = RemoteToken.get(user.id, "dev", token_type="t2")
    assert t4.token() != t3.token()

    assert RemoteToken.query.count() == 2
    acc.delete()
    assert RemoteToken.query.count() == 0
Пример #3
0
def test_get_create_remote_token(app, models_fixture):
    """Test create remote token."""
    existing_email = '*****@*****.**'
    datastore = app.extensions['invenio-accounts'].datastore
    user = datastore.find_user(email=existing_email)

    t = RemoteToken.create(user.id, 'dev', 'mytoken', 'mysecret')
    assert t
    assert t.token() == ('mytoken', 'mysecret')

    acc = RemoteAccount.get(user.id, 'dev')
    assert acc
    assert t.remote_account.id == acc.id
    assert t.token_type == ''

    t2 = RemoteToken.create(
        user.id, 'dev', 'mytoken2', 'mysecret2',
        token_type='t2'
    )
    assert t2.remote_account.id == acc.id
    assert t2.token_type == 't2'

    t3 = RemoteToken.get(user.id, 'dev')
    t4 = RemoteToken.get(user.id, 'dev', token_type='t2')
    assert t4.token() != t3.token()

    assert RemoteToken.query.count() == 2
    acc.delete()
    assert RemoteToken.query.count() == 0
def token_setter(remote, token, secret='', token_type='', extra_data=None,
                 user=None):
    """Set token for user.

    :param remote: The remote application.
    :param token: The token to set.
    :param token_type: The token type. (Default: ``''``)
    :param extra_data: Extra information. (Default: ``None``)
    :param user: The user owner of the remote token. If it's not defined,
        the current user is used automatically. (Default: ``None``)
    :returns: A :class:`invenio_oauthclient.models.RemoteToken` instance or
        ``None``.
    """
    session[token_session_key(remote.name)] = (token, secret)
    user = user or current_user

    # Save token if user is not anonymous (user exists but can be not active at
    # this moment)
    if not user.is_anonymous:
        uid = user.id
        cid = remote.consumer_key

        # Check for already existing token
        t = RemoteToken.get(uid, cid, token_type=token_type)

        if t:
            t.update_token(token, secret)
        else:
            t = RemoteToken.create(
                uid, cid, token, secret,
                token_type=token_type, extra_data=extra_data
            )
        return t
    return None
Пример #5
0
    def test_existent_token(self):
        # Create existing token: RemoteToken, RemoteAccount, UserIdentity.
        with db.session.begin_nested():
            # Create RemoteToken and RemoteAccount.
            RemoteToken.create(
                user_id=self.user.id,
                client_id=current_app.config['ORCID_APP_CREDENTIALS']['consumer_key'],
                token=self.token,
                secret=None,
                extra_data={
                    'orcid': self.orcid,
                    'full_name': self.name,
                    'allow_push': True,
                }
            )
            user_identity = UserIdentity(
                id=self.orcid,
                method='orcid',
                id_user=self.user.id
            )
            db.session.add(user_identity)

        with mock.patch('inspirehep.modules.orcid.tasks.oauth_link_external_id') as mock_oauth_link_external_id:
            # Mocking `oauth_link_external_id` is necessary because when running
            # with `isolated_app` it raises
            # "FlushError: New instance ... with identity key (...) conflicts with persistent instance ..."
            # rather than the standard and expected `IntegrityError` (which
            # is raised instead when run without `isolated_app`).
            mock_oauth_link_external_id.side_effect = AlreadyLinkedError(self.user, self.orcid)
            _link_user_and_token(self.user, self.name, self.orcid, self.token)

        self._assert_remote_account_and_remote_token_and_user_identity()
Пример #6
0
    def test_get_create(self):
        from invenio_oauthclient.models import RemoteAccount, RemoteToken

        t = RemoteToken.create(self.u1, "dev", "mytoken", "mysecret")
        assert t
        assert t.token() == ('mytoken', 'mysecret')

        acc = RemoteAccount.get(self.u1, "dev")
        assert acc
        assert t.remote_account.id == acc.id
        assert t.token_type == ''

        t2 = RemoteToken.create(
            self.u1, "dev", "mytoken2", "mysecret2",
            token_type='t2'
        )
        assert t2.remote_account.id == acc.id
        assert t2.token_type == 't2'

        t3 = RemoteToken.get(self.u1, "dev")
        t4 = RemoteToken.get(self.u1, "dev", token_type="t2")
        assert t4.token() != t3.token()

        assert RemoteToken.query.count() == 2
        acc.delete()
        assert RemoteToken.query.count() == 0
Пример #7
0
    def test_existent_token_for_same_user_but_different_orcid(self):
        # Create existing token: RemoteToken, RemoteAccount, UserIdentity.
        other_orcid = 'otherorcid'
        with db.session.begin_nested():
            # Create RemoteToken and RemoteAccount.
            RemoteToken.create(
                user_id=self.user.id,
                client_id=current_app.config['ORCID_APP_CREDENTIALS']['consumer_key'],
                token=self.token,
                secret=None,
                extra_data={
                    'orcid': other_orcid,
                    'full_name': self.name,
                    'allow_push': True,
                }
            )
            user_identity = UserIdentity(
                id=other_orcid,
                method='orcid',
                id_user=self.user.id
            )
            db.session.add(user_identity)

        with pytest.raises(RemoteTokenOrcidMismatch):
            _link_user_and_token(self.user, self.name, self.orcid, self.token)
Пример #8
0
def test_get_create_remote_token(app, example):
    """Test create remote token."""
    existing_email = "*****@*****.**"
    datastore = app.extensions["invenio-accounts"].datastore
    user = datastore.find_user(email=existing_email)

    t = RemoteToken.create(user.id, "dev", "mytoken", "mysecret")
    assert t
    assert t.token() == ("mytoken", "mysecret")

    acc = RemoteAccount.get(user.id, "dev")
    assert acc
    assert t.remote_account.id == acc.id
    assert t.token_type == ""

    t2 = RemoteToken.create(user.id, "dev", "mytoken2", "mysecret2", token_type="t2")
    assert t2.remote_account.id == acc.id
    assert t2.token_type == "t2"

    t3 = RemoteToken.get(user.id, "dev")
    t4 = RemoteToken.get(user.id, "dev", token_type="t2")
    assert t4.token() != t3.token()

    assert RemoteToken.query.count() == 2
    acc.delete()
    assert RemoteToken.query.count() == 0
    def test_get_create(self):
        from invenio_oauthclient.models import RemoteAccount, RemoteToken

        t = RemoteToken.create(self.u1, "dev", "mytoken", "mysecret")
        assert t
        assert t.token() == ('mytoken', 'mysecret')

        acc = RemoteAccount.get(self.u1, "dev")
        assert acc
        assert t.remote_account.id == acc.id
        assert t.token_type == ''

        t2 = RemoteToken.create(self.u1,
                                "dev",
                                "mytoken2",
                                "mysecret2",
                                token_type='t2')
        assert t2.remote_account.id == acc.id
        assert t2.token_type == 't2'

        t3 = RemoteToken.get(self.u1, "dev")
        t4 = RemoteToken.get(self.u1, "dev", token_type="t2")
        assert t4.token() != t3.token()

        assert RemoteToken.query.count() == 2
        acc.delete()
        assert RemoteToken.query.count() == 0
Пример #10
0
    def test_get_regression(self):
        from invenio_oauthclient.models import RemoteToken

        t3 = RemoteToken.create(self.u2, "dev", "mytoken", "mysecret")
        t4 = RemoteToken.create(self.u3, "dev", "mytoken", "mysecret")

        assert RemoteToken.get(self.u2, "dev").remote_account.user_id == t3.remote_account.user_id
        assert RemoteToken.get(self.u3, "dev").remote_account.user_id == t4.remote_account.user_id
    def test_get_regression(self):
        from invenio_oauthclient.models import RemoteToken

        t3 = RemoteToken.create(self.u2, "dev", "mytoken", "mysecret")
        t4 = RemoteToken.create(self.u3, "dev", "mytoken", "mysecret")

        assert RemoteToken.get(self.u2, "dev").remote_account.user_id == \
            t3.remote_account.user_id
        assert RemoteToken.get(self.u3, "dev").remote_account.user_id == \
            t4.remote_account.user_id
Пример #12
0
def _link_user_and_token(user, name, orcid, token):
    """Create a link between a user and token, if possible.

    Args:
        user (invenio_oauthclient.models.User): an existing user object to connect the token to
        orcid (string): user's ORCID identifier
        token (string): OAUTH token for the user

    Returns:
        str: the ORCID associated with the new token if we created one, or the
        ORCID associated with the token whose ``allow_push`` flag changed state.

    """
    result = None

    try:
        # Link user and ORCID
        oauth_link_external_id(user, {
            'id': orcid,
            'method': 'orcid'
        })
    except AlreadyLinkedError:
        # User already has their ORCID linked
        pass

    # Check whether there are already tokens associated with this
    # ORCID identifier.
    tokens = RemoteToken.query.join(RemoteAccount).join(User)\
        .join(UserIdentity).filter(UserIdentity.id == orcid).all()

    if tokens:
        # Force the allow_push.
        with db.session.begin_nested():
            for token in tokens:
                if not token.remote_account.extra_data['allow_push']:
                    result = orcid
                token.remote_account.extra_data['allow_push'] = True
    else:
        # If not, create and put the token entry
        with db.session.begin_nested():
            result = orcid
            RemoteToken.create(
                user_id=user.id,
                client_id=get_value(current_app.config, 'ORCID_APP_CREDENTIALS.consumer_key'),
                token=token,
                secret=None,
                extra_data={
                    'orcid': orcid,
                    'full_name': name,
                    'allow_push': True,
                }
            )

    return result
Пример #13
0
def test_utilities(models_fixture):
    """Test utilities."""
    app = models_fixture
    datastore = app.extensions['invenio-accounts'].datastore
    assert obj_or_import_string('invenio_oauthclient.errors')

    # User
    existing_email = '*****@*****.**'
    user = datastore.find_user(email=existing_email)

    # Authenticate
    assert not _get_external_id({})
    assert not oauth_authenticate('dev', user, require_existing_link=True)

    _security.confirmable = True
    _security.login_without_confirmation = False
    user.confirmed_at = None
    assert not oauth_authenticate('dev', user)

    # Tokens
    t = RemoteToken.create(user.id, 'dev', 'mytoken', 'mysecret')
    assert \
        RemoteToken.get(user.id, 'dev', access_token='mytoken') == \
        RemoteToken.get_by_token('dev', 'mytoken')

    assert oauth_get_user('dev', access_token=t.access_token) == user
    assert \
        oauth_get_user('dev', account_info={
            'user': {
                'email': existing_email
            }
        }) == user

    # Link user to external id
    external_id = {'id': '123', 'method': 'test_method'}
    oauth_link_external_id(user, external_id)

    with pytest.raises(AlreadyLinkedError):
        oauth_link_external_id(user, external_id)

    assert oauth_get_user('dev',
                          account_info={
                              'external_id': external_id['id'],
                              'external_method': external_id['method']
                          }) == user

    # Cleanup
    oauth_unlink_external_id(external_id)
    acc = RemoteAccount.get(user.id, 'dev')
    acc.delete()
Пример #14
0
def test_token_getter(remote, models_fixture, app):
    """Test token getter on response from OAuth server."""
    datastore = app.extensions['invenio-accounts'].datastore
    existing_email = '*****@*****.**'
    user = datastore.find_user(email=existing_email)

    # Missing RemoteToken
    oauth_authenticate('dev', user)
    assert not token_getter(remote)

    # Populated RemoteToken
    RemoteToken.create(user.id, 'testkey', 'mytoken', 'mysecret')
    oauth_authenticate('dev', user)
    assert token_getter(remote) == ('mytoken', 'mysecret')
Пример #15
0
def test_get_regression(app, example):
    """Test regression."""
    datastore = app.extensions["invenio-accounts"].datastore

    email2 = "*****@*****.**"
    email3 = "*****@*****.**"

    user2 = datastore.find_user(email=email2)
    user3 = datastore.find_user(email=email3)

    t3 = RemoteToken.create(user2.id, "dev", "mytoken", "mysecret")
    t4 = RemoteToken.create(user3.id, "dev", "mytoken", "mysecret")

    assert RemoteToken.get(user2.id, "dev").remote_account.user_id == t3.remote_account.user_id
    assert RemoteToken.get(user3.id, "dev").remote_account.user_id == t4.remote_account.user_id
Пример #16
0
def test_token_getter(remote, models_fixture):
    """Test token getter on response from OAuth server."""
    app = models_fixture
    datastore = app.extensions['invenio-accounts'].datastore
    existing_email = '*****@*****.**'
    user = datastore.find_user(email=existing_email)

    # Missing RemoteToken
    oauth_authenticate('dev', user)
    assert not token_getter(remote)

    # Populated RemoteToken
    RemoteToken.create(user.id, 'testkey', 'mytoken', 'mysecret')
    oauth_authenticate('dev', user)
    assert token_getter(remote) == ('mytoken', 'mysecret')
Пример #17
0
def remote_account(app, user_without_role):
    """Create a remote token from user data."""
    remote_account = RemoteAccount.create(1, 'dev', dict())
    remote_token = RemoteToken.create(user_without_role.id, 'dev', 'token',
                                      'secret')

    return remote_account, remote_token
Пример #18
0
def mock_user(app, request):
    def teardown(app):
        with app.app_context():
            user = User.query.filter_by(id=2).first()
            token = RemoteToken.query.filter_by(access_token='123').first()
            user_identity = UserIdentity.query.filter_by(
                id='0000-0001-9412-8627', method='orcid').first()
            remote_account = RemoteAccount.query.filter_by(user_id=2).first()
            with db.session.begin_nested():
                db.session.delete(token)
                db.session.delete(user_identity)
                db.session.delete(remote_account)
                db.session.delete(user)
            db.session.commit()

    request.addfinalizer(lambda: teardown(app))

    user = User(id=2, )
    token = RemoteToken(id_remote_account=1, access_token='123')
    user_identity = UserIdentity(id='0000-0001-9412-8627',
                                 id_user='******',
                                 method='orcid')
    remote_account = RemoteAccount(id=1,
                                   user_id=2,
                                   extra_data={},
                                   client_id=1,
                                   user=user)
    with app.app_context():
        with db.session.begin_nested():
            db.session.add(user)
            db.session.add(user_identity)
            db.session.add(remote_account)
            db.session.add(token)
        db.session.commit()
    return MockUser(app)
def token_getter(remote, token=''):
    """Retrieve OAuth access token.

    Used by flask-oauthlib to get the access token when making requests.

    :param remote: The remote application.
    :param token: Type of token to get. Data passed from ``oauth.request()`` to
        identify which token to retrieve. (Default: ``''``)
    :returns: The token.
    """
    session_key = token_session_key(remote.name)

    if session_key not in session and current_user.is_authenticated:
        # Fetch key from token store if user is authenticated, and the key
        # isn't already cached in the session.
        remote_token = RemoteToken.get(
            current_user.get_id(),
            remote.consumer_key,
            token_type=token,
        )

        if remote_token is None:
            return None

        # Store token and secret in session
        session[session_key] = remote_token.token()

    return session.get(session_key, None)
def create_user(orcid,
                email,
                name,
                consumer_key,
                token=None,
                allow_push=False):
    user = User()
    user.email = email
    with db.session.begin_nested():
        db.session.add(user)

    oauth_link_external_id(user, {'id': orcid, 'method': 'orcid'})

    if token:
        with db.session.begin_nested():
            db.session.add(
                RemoteToken.create(user_id=user.id,
                                   client_id=consumer_key,
                                   token=token,
                                   secret=None,
                                   extra_data={
                                       'orcid': orcid,
                                       'full_name': name,
                                       'allow_push': allow_push,
                                   }))
Пример #21
0
 def access_token(self):
     """Return OAuth access token."""
     if self.user_id:
         return RemoteToken.get(
             self.user_id, self.remote.consumer_key
         ).access_token
     return self.remote.get_request_token()[0]
Пример #22
0
def test_rebuilding_access_tokens(app, models_fixture):
    """Test rebuilding access tokens with random new SECRET_KEY."""
    old_secret_key = app.secret_key

    datastore = app.extensions['invenio-accounts'].datastore
    existing_email = '*****@*****.**'
    user = datastore.find_user(email=existing_email)

    # Creating a new remote token and commiting to the db
    test_token = 'mytoken'
    token_type = 'testing'
    with db.session.begin_nested():
        rt = RemoteToken.create(user.id, 'testkey', test_token, app.secret_key,
                                token_type)
        db.session.add(rt)
    db.session.commit()

    # Changing application SECRET_KEY
    app.secret_key = 'NEW_SECRET_KEY'
    db.session.expunge_all()

    # Asserting the decoding error occurs with the stale SECRET_KEY
    if sys.version_info[0] < 3:  # python 2
        remote_token = RemoteToken.query.first()
        assert remote_token.access_token != test_token
    else:  # python 3
        with pytest.raises(ValueError):
            RemoteToken.query.first()

    db.session.expunge_all()
    rebuild_access_tokens(old_secret_key)
    remote_token = RemoteToken.query.filter_by(token_type=token_type).first()

    # Asserting the access_token is not changed after rebuilding
    assert remote_token.access_token == test_token
Пример #23
0
def get_api(user_id=None):
    """Get an authenticated GitHub API interface."""
    if user_id:
        access_token = RemoteToken.get(user_id, get_client_id()).access_token
    else:
        access_token = get_remote().get_request_token()[0]
    return init_api(access_token)
Пример #24
0
def token_getter(remote, token=''):
    """Retrieve OAuth access token.

    Used by flask-oauthlib to get the access token when making requests.

    :param remote: The remote application.
    :param token: Type of token to get. Data passed from ``oauth.request()`` to
        identify which token to retrieve. (Default: ``''``)
    :returns: The token.
    """
    session_key = token_session_key(remote.name)

    if session_key not in session and current_user.is_authenticated:
        # Fetch key from token store if user is authenticated, and the key
        # isn't already cached in the session.
        remote_token = RemoteToken.get(
            current_user.get_id(),
            remote.consumer_key,
            token_type=token,
        )

        if remote_token is None:
            return None

        # Store token and secret in session
        session[session_key] = remote_token.token()

    return session.get(session_key, None)
Пример #25
0
def _link_user_and_token(user, name, orcid, token):
    """Create a link between a user and token, if possible.

    Args:
        user (invenio_oauthclient.models.User): an existing user object to connect the token to
        orcid (string): user's ORCID identifier
        token (string): OAUTH token for the user
    """
    try:
        # Link user and ORCID
        oauth_link_external_id(user, {
            'id': orcid,
            'method': 'orcid'
        })
    except AlreadyLinkedError:
        # User already has their ORCID linked
        pass

    # Is there already a token associated with this ORCID identifier?
    if RemoteToken.query.join(RemoteAccount).join(User).join(UserIdentity).filter(UserIdentity.id == orcid).count():
        return

    # If not, create and put the token entry
    with db.session.begin_nested():
        db.session.add(RemoteToken.create(
            user_id=user.id,
            client_id=get_value(current_app.config, 'ORCID_APP_CREDENTIALS.consumer_key'),
            token=token,
            secret=None,
            extra_data={
                'orcid': orcid,
                'full_name': name,
                'allow_push': True,
            }
        ))
Пример #26
0
def create_user(orcid,
                email,
                name,
                consumer_key,
                token=None,
                allow_push=False):
    user = User()
    user.email = email
    with db.session.begin_nested():
        db.session.add(user)

    oauth_link_external_id(user, {"id": orcid, "method": "orcid"})

    if token:
        with db.session.begin_nested():
            db.session.add(
                RemoteToken.create(
                    user_id=user.id,
                    client_id=consumer_key,
                    token=token,
                    secret=None,
                    extra_data={
                        "orcid": orcid,
                        "full_name": name,
                        "allow_push": allow_push,
                    },
                ))
def create_user(orcid, email, name, consumer_key, token=None, allow_push=False):
    user = User()
    user.email = email
    with db.session.begin_nested():
        db.session.add(user)

    oauth_link_external_id(user, {
        'id': orcid,
        'method': 'orcid'
    })

    if token:
        with db.session.begin_nested():
            db.session.add(
                RemoteToken.create(
                    user_id=user.id,
                    client_id=consumer_key,
                    token=token,
                    secret=None,
                    extra_data={
                        'orcid': orcid,
                        'full_name': name,
                        'allow_push': allow_push,
                    }
                )
            )
Пример #28
0
    def build_fs(self, current_user, credentials, root=None,
                 callback_url=None, request=None, session=None):
        """Build google drive filesystem."""
        url = url_for('oauthclient.login', remote_app='google_drive')

        client_id = oauth.remote_apps['google_drive'].consumer_key
        client_secret = oauth.remote_apps['google_drive'].consumer_secret
        user_id = current_user.get_id()
        token = RemoteToken.get(user_id, client_id)

        if token is not None:
            credentials = {
                'access_token': token.access_token,
                'client_id': client_id,
                'client_secret': client_secret,
                'refresh_token':
                token.remote_account.extra_data.get('refresh_token'),
                'token_expiry': None,
                'token_uri':
                'https://accounts.google.com/o/oauth2/token'
                }
            try:
                filesystem = GoogleDriveFS(root, credentials)
                filesystem.about()
                return filesystem
            except Exception:
                raise CloudRedirectUrl(url, __name__)
        else:
            raise CloudRedirectUrl(url, __name__)
Пример #29
0
def test_signup_handler(remote, app_rest, models_fixture):
    """Test signup handler."""
    datastore = app_rest.extensions['invenio-accounts'].datastore
    existing_email = '*****@*****.**'
    user = datastore.find_user(email=existing_email)
    # Already authenticated
    login_user(user)
    assert current_user.is_authenticated
    resp1 = signup_handler(remote)
    expected_url_args = {"message": "Successfully signed up.", "code": 200}
    check_response_redirect_url_args(resp1, expected_url_args)
    logout_user()
    assert not current_user.is_authenticated

    # No OAuth token
    resp2 = signup_handler(remote)
    expected_url_args = {"message": "Token not found.", "code": 400}

    check_response_redirect_url_args(resp2, expected_url_args)

    # Not coming from authorized request
    token = RemoteToken.create(user.id, 'testkey', 'mytoken', 'mysecret')
    token_setter(remote, token, 'mysecret')
    with pytest.raises(BuildError):
        signup_handler(remote)
Пример #30
0
def test_get_regression(app, models_fixture):
    """Test regression."""
    datastore = app.extensions['invenio-accounts'].datastore

    email2 = '*****@*****.**'
    email3 = '*****@*****.**'

    user2 = datastore.find_user(email=email2)
    user3 = datastore.find_user(email=email3)

    t3 = RemoteToken.create(user2.id, 'dev', 'mytoken', 'mysecret')
    t4 = RemoteToken.create(user3.id, 'dev', 'mytoken', 'mysecret')

    assert RemoteToken.get(user2.id, 'dev').remote_account.user_id == \
        t3.remote_account.user_id
    assert RemoteToken.get(user3.id, 'dev').remote_account.user_id == \
        t4.remote_account.user_id
Пример #31
0
def test_get_regression(app, example):
    """Test regression."""
    datastore = app.extensions['invenio-accounts'].datastore

    email2 = "*****@*****.**"
    email3 = "*****@*****.**"

    user2 = datastore.find_user(email=email2)
    user3 = datastore.find_user(email=email3)

    t3 = RemoteToken.create(user2.id, "dev", "mytoken", "mysecret")
    t4 = RemoteToken.create(user3.id, "dev", "mytoken", "mysecret")

    assert RemoteToken.get(user2.id, "dev").remote_account.user_id == \
        t3.remote_account.user_id
    assert RemoteToken.get(user3.id, "dev").remote_account.user_id == \
        t4.remote_account.user_id
Пример #32
0
def test_utilities(models_fixture):
    """Test utilities."""
    app = models_fixture
    datastore = app.extensions['invenio-accounts'].datastore
    assert obj_or_import_string('invenio_oauthclient.errors')

    # User
    existing_email = '*****@*****.**'
    user = datastore.find_user(email=existing_email)

    # Authenticate
    assert not _get_external_id({})
    assert not oauth_authenticate('dev', user, require_existing_link=True)

    _security.confirmable = True
    _security.login_without_confirmation = False
    user.confirmed_at = None
    assert not oauth_authenticate('dev', user)

    # Tokens
    t = RemoteToken.create(user.id, 'dev', 'mytoken', 'mysecret')
    assert \
        RemoteToken.get(user.id, 'dev', access_token='mytoken') == \
        RemoteToken.get_by_token('dev', 'mytoken')

    assert oauth_get_user('dev', access_token=t.access_token) == user
    assert \
        oauth_get_user('dev', account_info={'user': {'email': existing_email}}) == user

    # Link user to external id
    external_id = {'id': '123', 'method': 'test_method'}
    oauth_link_external_id(user, external_id)

    with pytest.raises(AlreadyLinkedError):
        oauth_link_external_id(user, external_id)

    assert oauth_get_user('dev',
                          account_info={
                              'external_id': external_id['id'],
                              'external_method': external_id['method']
                          }) == user

    # Cleanup
    oauth_unlink_external_id(external_id)
    acc = RemoteAccount.get(user.id, 'dev')
    acc.delete()
Пример #33
0
def remote_account_fixture(app, models_fixture):
    """Create a remote token from user data."""
    datastore = app.extensions['security'].datastore
    user = datastore.find_user(email='*****@*****.**')

    remote_account = RemoteAccount.create(1, 'dev', dict())
    remote_token = RemoteToken.create(user.id, 'dev', 'token', 'secret')

    return remote_account, remote_token
Пример #34
0
def test_get_regression(models_fixture):
    """Test regression."""
    app = models_fixture
    datastore = app.extensions['invenio-accounts'].datastore

    email2 = '*****@*****.**'
    email3 = '*****@*****.**'

    user2 = datastore.find_user(email=email2)
    user3 = datastore.find_user(email=email3)

    t3 = RemoteToken.create(user2.id, 'dev', 'mytoken', 'mysecret')
    t4 = RemoteToken.create(user3.id, 'dev', 'mytoken', 'mysecret')

    assert RemoteToken.get(user2.id, 'dev').remote_account.user_id == \
        t3.remote_account.user_id
    assert RemoteToken.get(user3.id, 'dev').remote_account.user_id == \
        t4.remote_account.user_id
Пример #35
0
def test_utilities(models_fixture):
    """Test utilities."""
    app = models_fixture
    datastore = app.extensions["invenio-accounts"].datastore
    assert obj_or_import_string("invenio_oauthclient.errors")

    # User
    existing_email = "*****@*****.**"
    user = datastore.find_user(email=existing_email)

    # Authenticate
    assert not _get_external_id({})
    assert not oauth_authenticate("dev", user, require_existing_link=True)

    _security.confirmable = True
    _security.login_without_confirmation = False
    user.confirmed_at = None
    assert not oauth_authenticate("dev", user)

    # Tokens
    t = RemoteToken.create(user.id, "dev", "mytoken", "mysecret")
    assert RemoteToken.get(user.id, "dev", access_token="mytoken") == RemoteToken.get_by_token("dev", "mytoken")

    assert oauth_get_user("dev", access_token=t.access_token) == user
    assert oauth_get_user("dev", account_info={"user": {"email": existing_email}}) == user

    # Link user to external id
    external_id = {"id": "123", "method": "test_method"}
    oauth_link_external_id(user, external_id)

    with pytest.raises(AlreadyLinkedError):
        oauth_link_external_id(user, external_id)

    assert (
        oauth_get_user("dev", account_info={"external_id": external_id["id"], "external_method": external_id["method"]})
        == user
    )

    # Cleanup
    oauth_unlink_external_id(external_id)
    acc = RemoteAccount.get(user.id, "dev")
    acc.delete()
Пример #36
0
 def session_token(self):
     """Return OAuth session token."""
     session_token = None
     if self.user_id is not None:
         session_token = token_getter(self.remote)
     if session_token:
         token = RemoteToken.get(self.user_id,
                                 self.remote.consumer_key,
                                 access_token=session_token[0])
         return token
     return None
Пример #37
0
def get_token(user_id=None):
    """Retrieve token for linked GitHub account."""
    session_token = None
    if user_id is None:
        session_token = token_getter(get_remote())
    if session_token:
        token = RemoteToken.get(
            current_user.get_id(), get_client_id(),
            access_token=session_token[0]
        )
        return token
    return None
Пример #38
0
def test_repr(app, models_fixture):
    """Test representation of RemoteAccount and RemoteToken."""
    datastore = app.extensions['invenio-accounts'].datastore
    user = datastore.find_user(email='*****@*****.**')

    assert 'Remote Token <token_type=type access_token=****oken>' == \
           repr(RemoteToken.create(user.id, 'dev',
                                   'mytoken', 'mysecret',
                                   token_type='type'))

    assert 'Remote Account <id=1, user_id=1>' == \
           repr(RemoteAccount.get(user.id, 'dev'))
Пример #39
0
def test_repr(models_fixture):
    """Test representation of RemoteAccount adn RemoteToken."""
    datastore = models_fixture.extensions['invenio-accounts'].datastore
    user = datastore.find_user(email='*****@*****.**')

    assert 'Remote Token <token_type=type access_token=mytoken>' == \
           repr(RemoteToken.create(user.id, 'dev',
                                   'mytoken', 'mysecret',
                                   token_type='type'))

    assert 'Remote Account <id=1, user_id=1>' == \
           repr(RemoteAccount.get(user.id, 'dev'))
Пример #40
0
 def session_token(self):
     """Return OAuth session token."""
     session_token = None
     if self.user_id is not None:
         session_token = token_getter(self.remote)
     if session_token:
         token = RemoteToken.get(
             self.user_id, self.remote.consumer_key,
             access_token=session_token[0]
         )
         return token
     return None
Пример #41
0
def token_setter(remote,
                 token,
                 secret='',
                 token_type='',
                 extra_data=None,
                 user=None):
    """Set token for user.

    :param remote: The remote application.
    :param token: The token to set.
    :param token_type: The token type. (Default: ``''``)
    :param extra_data: Extra information. (Default: ``None``)
    :param user: The user owner of the remote token. If it's not defined,
        the current user is used automatically. (Default: ``None``)
    :returns: A :class:`invenio_oauthclient.models.RemoteToken` instance or
        ``None``.
    """
    session[token_session_key(remote.name)] = (token, secret)
    user = user or current_user

    # Save token if user is not anonymous (user exists but can be not active at
    # this moment)
    if not user.is_anonymous:
        uid = user.id
        cid = remote.consumer_key

        # Check for already existing token
        t = RemoteToken.get(uid, cid, token_type=token_type)

        if t:
            t.update_token(token, secret)
        else:
            t = RemoteToken.create(uid,
                                   cid,
                                   token,
                                   secret,
                                   token_type=token_type,
                                   extra_data=extra_data)
        return t
    return None
Пример #42
0
def remote_token(app, db, tester_id):
    """Create a remove token for accessing GitHub API."""
    from invenio_oauthclient.models import RemoteToken

    # Create GitHub link
    token = RemoteToken.create(
        tester_id,
        GitHubAPI.remote.consumer_key,
        'test',
        '',
    )
    db.session.commit()
    return token
Пример #43
0
def remote_token(app, db, tester_id):
    """Create a remove token for accessing GitHub API."""
    from invenio_oauthclient.models import RemoteToken

    # Create GitHub link
    token = RemoteToken.create(
        tester_id,
        GitHubAPI.remote.consumer_key,
        'test',
        '',
    )
    db.session.commit()
    return token
Пример #44
0
def remote_token(app, db, tester_id):
    """Create a remove token for accessing GitHub API."""
    from invenio_oauthclient.models import RemoteToken
    from invenio_github.helpers import get_client_id

    # Create GitHub link
    token = RemoteToken.create(
        tester_id,
        get_client_id(),
        'test',
        '',
    )
    db.session.commit()
    return token
Пример #45
0
    def _create(cls,
                model_class,
                role="superuser",
                orcid=None,
                email=None,
                allow_push=None,
                token=None,
                *args,
                **kwargs):
        ds = current_app.extensions["invenio-accounts"].datastore
        role = ds.find_or_create_role(role)
        user = ds.create_user(
            id=fake.random_number(digits=8, fix_len=True),
            email=fake.email() if not email else email,
            password=hash_password(fake.password()),
            active=True,
            roles=[role],
        )

        if orcid:
            user_orcid_id = UserIdentity(id=orcid,
                                         method="orcid",
                                         id_user=user.get_id())
            db.session.add(user_orcid_id)

            RemoteToken.create(
                user_id=user.get_id(),
                client_id="orcid",
                token=token,
                secret=None,
                extra_data={
                    "orcid": orcid,
                    "allow_push": allow_push
                },
            )

        return user
Пример #46
0
def disconnect(remote):
    """Disconnect callback handler for GitHub.

    This is a test!
    """
    # User must be authenticated
    if not current_user.is_authenticated():
        return current_app.login_manager.unauthorized()

    token = RemoteToken.get(current_user.get_id(), remote.consumer_key)

    if token:
        disconnect_github.delay(remote.name, token.access_token,
                                token.remote_account.extra_data)
        token.remote_account.delete()

    return redirect(url_for('oauthclient_settings.index'))
Пример #47
0
def disconnect(remote):
    """Disconnect callback handler for GitHub.

    This is a test!
    """
    # User must be authenticated
    if not current_user.is_authenticated():
        return current_app.login_manager.unauthorized()

    token = RemoteToken.get(current_user.get_id(), remote.consumer_key)

    if token:
        disconnect_github.delay(
            remote.name, token.access_token, token.remote_account.extra_data
        )
        token.remote_account.delete()

    return redirect(url_for('oauthclient_settings.index'))
Пример #48
0
def disconnect(remote):
    """Disconnect callback handler for GitHub."""
    # User must be authenticated
    if not current_user.is_authenticated:
        return current_app.login_manager.unauthorized()

    external_method = 'github'
    external_ids = [i.id for i in current_user.external_identifiers
                    if i.method == external_method]
    if external_ids:
        oauth_unlink_external_id(dict(id=external_ids[0],
                                      method=external_method))

    user_id = current_user.get_id()
    token = RemoteToken.get(user_id, remote.consumer_key)
    if token:
        extra_data = token.remote_account.extra_data

        # Delete the token that we issued for GitHub to deliver webhooks
        webhook_token_id = extra_data.get('tokens', {}).get('webhook')
        ProviderToken.query.filter_by(id=webhook_token_id).delete()

        # Disable GitHub webhooks from our side
        db_repos = Repository.query.filter_by(user_id=user_id).all()
        # Keep repositories with hooks to pass to the celery task later on
        repos_with_hooks = [(r.github_id, r.hook) for r in db_repos if r.hook]
        for repo in db_repos:
            try:
                Repository.disable(user_id=user_id,
                                   github_id=repo.github_id,
                                   name=repo.name)
            except NoResultFound:
                # If the repository doesn't exist, no action is necessary
                pass
        db.session.commit()

        # Send Celery task for webhooks removal and token revocation
        disconnect_github.delay(token.access_token, repos_with_hooks)
        # Delete the RemoteAccount (along with the associated RemoteToken)
        token.remote_account.delete()

    return redirect(url_for('invenio_oauthclient_settings.index'))
Пример #49
0
def disconnect(remote):
    """Disconnect callback handler for GitHub."""
    # User must be authenticated
    if not current_user.is_authenticated:
        return current_app.login_manager.unauthorized()

    external_method = 'github'
    external_ids = [i.id for i in current_user.external_identifiers
                    if i.method == external_method]
    if external_ids:
        oauth_unlink_external_id(dict(id=external_ids[0],
                                      method=external_method))

    user_id = int(current_user.get_id())
    token = RemoteToken.get(user_id, remote.consumer_key)
    if token:
        extra_data = token.remote_account.extra_data

        # Delete the token that we issued for GitHub to deliver webhooks
        webhook_token_id = extra_data.get('tokens', {}).get('webhook')
        ProviderToken.query.filter_by(id=webhook_token_id).delete()

        # Disable GitHub webhooks from our side
        db_repos = Repository.query.filter_by(user_id=user_id).all()
        # Keep repositories with hooks to pass to the celery task later on
        repos_with_hooks = [(r.github_id, r.hook) for r in db_repos if r.hook]
        for repo in db_repos:
            try:
                Repository.disable(user_id=user_id,
                                   github_id=repo.github_id,
                                   name=repo.name)
            except NoResultFound:
                # If the repository doesn't exist, no action is necessary
                pass
        db.session.commit()

        # Send Celery task for webhooks removal and token revocation
        disconnect_github.delay(token.access_token, repos_with_hooks)
        # Delete the RemoteAccount (along with the associated RemoteToken)
        token.remote_account.delete()

    return redirect(url_for('invenio_oauthclient_settings.index'))
def test_account_setup(app, example_cern, models_fixture):
    """Test account setup after login."""
    client = app.test_client()
    ioc = app.extensions['oauthlib.client']

    # Ensure remote apps have been loaded (due to before first request)
    client.get(url_for("invenio_oauthclient.login", remote_app='cern'))

    example_response, example_token, example_account_info = example_cern
    res = get_dict_from_response(example_response)

    mock_remote_get(ioc, 'cern', example_response)

    app = models_fixture
    datastore = app.extensions['invenio-accounts'].datastore
    user = datastore.find_user(email="*****@*****.**")
    token = RemoteToken.create(
        user.id, 'client_id', example_token['access_token'], 'secret',
        token_type=example_token['token_type']
    )
    account_setup(ioc.remote_apps['cern'], token, None)
def test_signup_handler(remote, app, models_fixture):
    """Test signup handler."""
    datastore = app.extensions['invenio-accounts'].datastore
    existing_email = '*****@*****.**'
    user = datastore.find_user(email=existing_email)

    # Already authenticated
    login_user(user)
    assert current_user.is_authenticated
    resp1 = signup_handler(remote)
    check_redirect_location(resp1, '/')
    logout_user()
    assert not current_user.is_authenticated

    # No OAuth token
    resp2 = signup_handler(remote)
    check_redirect_location(resp2, '/')

    # Not coming from authorized request
    token = RemoteToken.create(user.id, 'testkey', 'mytoken', 'mysecret')
    token_setter(remote, token, 'mysecret')
    with pytest.raises(BuildError):
        signup_handler(remote)
Пример #52
0
def test_rebuilding_access_tokens(models_fixture):
    """Test rebuilding access tokens with random new SECRET_KEY."""
    app = models_fixture
    old_secret_key = app.secret_key

    datastore = app.extensions['invenio-accounts'].datastore
    existing_email = '*****@*****.**'
    user = datastore.find_user(email=existing_email)

    # Creating a new remote token and commiting to the db
    test_token = 'mytoken'
    token_type = 'testing'
    with db.session.begin_nested():
        rt = RemoteToken.create(user.id, 'testkey', test_token,
                                app.secret_key, token_type)
        db.session.add(rt)
    db.session.commit()

    # Changing application SECRET_KEY
    app.secret_key = 'NEW_SECRET_KEY'
    db.session.expunge_all()

    # Asserting the decoding error occurs with the stale SECRET_KEY
    if sys.version_info[0] < 3:  # python 2
        remote_token = RemoteToken.query.first()
        assert remote_token.access_token != test_token
    else:  # python 3
        with pytest.raises(UnicodeDecodeError):
            RemoteToken.query.first()

    db.session.expunge_all()
    rebuild_access_tokens(old_secret_key)
    remote_token = RemoteToken.query.filter_by(token_type=token_type).first()

    # Asserting the access_token is not changed after rebuilding
    assert remote_token.access_token == test_token
Пример #53
0
    def build_fs(self, current_user, credentials, root=None,
                 callback_url=None, request=None, session=None):
        """Build dropbox filesystem."""
        url = url_for('oauthclient.login', remote_app='dropbox')
        client_id = oauth.remote_apps['dropbox'].consumer_key
        user_id = current_user.get_id()
        token = RemoteToken.get(user_id, client_id)

        if token is not None:
            credentials = {'access_token': token.access_token}
            try:
                filesystem = DropboxFS(root, credentials)
                filesystem.about()
                return filesystem
            except ResourceNotFoundError:
                if(root != "/"):
                    filesystem = DropboxFS("/", credentials)
                filesystem.makedir(root, recursive=True)
                filesystem = DropboxFS(root, credentials)
                return filesystem
            except Exception:
                raise CloudRedirectUrl(url, __name__)
        else:
            raise CloudRedirectUrl(url, __name__)
Пример #54
0
def test_signup_handler(remote, models_fixture):
    """Test signup handler."""
    app = models_fixture
    datastore = app.extensions['invenio-accounts'].datastore
    existing_email = '*****@*****.**'
    user = datastore.find_user(email=existing_email)

    # Already authenticated
    login_user(user)
    assert current_user.is_authenticated
    resp1 = signup_handler(remote)
    check_redirect_location(resp1, '/')
    logout_user()
    assert not current_user.is_authenticated

    # No OAuth token
    resp2 = signup_handler(remote)
    check_redirect_location(resp2, '/')

    # Not coming from authorized request
    token = RemoteToken.create(user.id, 'testkey', 'mytoken', 'mysecret')
    token_setter(remote, token, 'mysecret')
    with pytest.raises(BuildError):
        signup_handler(remote)
Пример #55
0
    def test_token_getter_setter(self, session, save_session):
        from invenio_oauthclient.models import RemoteToken
        from invenio_oauthclient.handlers import token_getter
        from invenio_oauthclient.client import oauth

        # Mock user
        user = MagicMock()
        user.get_id = MagicMock(return_value=1)
        user.is_authenticated = MagicMock(return_value=True)

        # Mock session id
        session.sid = '1234'

        with patch('flask_login._get_user', return_value=user):
            with self.app.test_client() as c:
                # First call login to be redirected
                res = c.get(url_for("oauthclient.login", remote_app='full'))
                assert res.status_code == 302
                assert res.location.startswith(
                    oauth.remote_apps['full'].authorize_url
                )
                state = parse_qs(urlparse(res.location).query)['state'][0]

                # Mock resposen class
                self.mock_response(app='full')

                # Imitate that the user authorized our request in the remote
                # application.
                c.get(url_for(
                    "oauthclient.authorized", remote_app='full', code='test',
                    state=state,
                ))

                # Assert if everything is as it should be.
                from flask import session as flask_session
                assert flask_session['oauth_token_full'] == \
                    ('test_access_token', '')

                t = RemoteToken.get(1, "fullid")
                assert t.remote_account.client_id == 'fullid'
                assert t.access_token == 'test_access_token'
                assert RemoteToken.query.count() == 1

                # Mock a new authorized request
                self.mock_response(app='full', data={
                    "access_token": "new_access_token",
                    "scope": "",
                    "token_type": "bearer"
                })

                c.get(url_for(
                    "oauthclient.authorized", remote_app='full', code='test',
                    state=state
                ))

                t = RemoteToken.get(1, "fullid")
                assert t.access_token == 'new_access_token'
                assert RemoteToken.query.count() == 1

                val = token_getter(oauth.remote_apps['full'])
                assert val == ('new_access_token', '')

                # Disconnect account
                res = c.get(url_for(
                    "oauthclient.disconnect", remote_app='full',
                ))
                assert res.status_code == 302
                assert res.location.endswith(
                    url_for('oauthclient_settings.index')
                )
                # Assert that remote account have been removed.
                t = RemoteToken.get(1, "fullid")
                assert t is None
Пример #56
0
def test_token_getter_setter(monkeypatch):
    """Test token getter setter."""
    # Mock session id
    monkeypatch.setattr('flask_login._create_identifier', lambda: '1234')
    monkeypatch.setattr(
        'invenio_oauthclient.views.client._create_identifier', lambda: '1234')

    app = setup_app()
    oauth = app.extensions['oauthlib.client']

    # Mock user
    user = MagicMock()
    user.id = 1
    user.get_id = MagicMock(return_value=1)
    user.is_anonymous = False

    with patch('flask_login._get_user', return_value=user):
        with app.test_client() as c:
            # First call login to be redirected
            res = c.get(url_for("invenio_oauthclient.login",
                                remote_app='full'))
            assert res.status_code == 302
            assert res.location.startswith(
                oauth.remote_apps['full'].authorize_url
            )
            state = parse_qs(urlparse(res.location).query)['state'][0]

            # Mock resposen class
            mock_response(app.extensions['oauthlib.client'], 'full')

            # Imitate that the user authorized our request in the remote
            # application.
            c.get(url_for(
                "invenio_oauthclient.authorized", remote_app='full',
                code='test', state=state,
            ))

            # Assert if everything is as it should be.
            from flask import session as flask_session
            assert flask_session['oauth_token_full'] == \
                ('test_access_token', '')

            t = RemoteToken.get(1, "fullid")
            assert t.remote_account.client_id == 'fullid'
            assert t.access_token == 'test_access_token'
            assert RemoteToken.query.count() == 1

            # Mock a new authorized request
            mock_response(app.extensions['oauthlib.client'], 'full', data={
                "access_token": "new_access_token",
                "scope": "",
                "token_type": "bearer"
            })

            c.get(url_for(
                "invenio_oauthclient.authorized", remote_app='full',
                code='test', state=state
            ))

            t = RemoteToken.get(1, "fullid")
            assert t.access_token == 'new_access_token'
            assert RemoteToken.query.count() == 1

            val = token_getter(
                app.extensions['oauthlib.client'].remote_apps['full'])
            assert val == ('new_access_token', '')

            # Disconnect account
            res = c.get(url_for(
                "invenio_oauthclient.disconnect", remote_app='full',
            ))
            assert res.status_code == 302
            assert res.location.endswith(
                url_for('invenio_oauthclient_settings.index')
            )
            # Assert that remote account have been removed.
            t = RemoteToken.get(1, "fullid")
            assert t is None
Пример #57
0
def _link_user_and_token(user, name, orcid, access_token):
    """Create a link between a user and token, if possible.

    Args:
        user (invenio_oauthclient.models.User): an existing user object to connect the token to
        orcid (string): user's ORCID identifier
        access_token (string): OAUTH token for the user

    Returns:
        str: the ORCID associated with the token newly created/enabled.
    """
    try:
        # Link user and ORCID
        oauth_link_external_id(user, {
            'id': orcid,
            'method': 'orcid'
        })
    # Note: AlreadyLinkedError becomes FlushError when testing with isolated_app.
    except (AlreadyLinkedError, FlushError):
        # User already has their ORCID linked
        pass

    # Search for existing tokens.
    # Note: there can be only 1 RemoteToken per given RemoteAccount.
    existing_remote_token = RemoteToken.query.join(RemoteAccount).filter_by(
        user=user).one_or_none()

    # If not existing_remote_token, create one.
    if not existing_remote_token:
        with db.session.begin_nested():
            RemoteToken.create(
                user_id=user.id,
                client_id=get_value(current_app.config, 'ORCID_APP_CREDENTIALS.consumer_key'),
                token=access_token,
                secret=None,
                extra_data={
                    'orcid': orcid,
                    'full_name': name,
                    'allow_push': True,
                }
            )
        return orcid

    # If there is an existing_remote_token:
    #    ensure it is associated to this ORCID and
    #    set allow_push to True.
    #
    # Get the ORCID associated with this token.
    user_identity = UserIdentity.query.filter_by(user=user, method='orcid').one_or_none()

    # Ensure data consistency.
    if not user_identity:
        msg = 'No UserIdentity for user={}, method="orcid", while' \
              ' instead there is a RemoteAccount={} and RemoteToken={}'
        raise Exception(msg.format(
            user, existing_remote_token.remote_account, existing_remote_token))
    if user_identity.id != existing_remote_token.remote_account.extra_data['orcid']:
        msg = 'UserIdentity={} and RemoteToken={} ORCID mismatch: {} != {}'
        raise Exception(msg.format(
            user_identity, existing_remote_token, user_identity.id,
            existing_remote_token.remote_account.extra_data['orcid']
        ))
    if existing_remote_token.remote_account.extra_data['orcid'] != orcid:
        raise RemoteTokenOrcidMismatch(user, [existing_remote_token.remote_account.extra_data['orcid'], orcid])

    # Force the allow_push.
    with db.session.begin_nested():
        if not existing_remote_token.remote_account.extra_data['allow_push']:
            existing_remote_token.remote_account.extra_data['allow_push'] = True
            return orcid
    return None