Skip to content

Commit

Permalink
global: user registration fix
Browse files Browse the repository at this point in the history
* Introduces the possibility to "ask for email confirmation" in case
  the provider does not return any user email (e.g. with orcid).

Signed-off-by: Leonardo Rossi <leonardo.r@cern.ch>
  • Loading branch information
Leonardo Rossi authored and hachreak committed Dec 15, 2015
1 parent c394d64 commit 07eafa9
Show file tree
Hide file tree
Showing 7 changed files with 60 additions and 38 deletions.
2 changes: 1 addition & 1 deletion examples/github_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@
from flask import Flask, redirect, url_for
from flask_babelex import Babel
from flask_cli import FlaskCLI
from flask_login import current_user
from flask_security import current_user
from flask_menu import Menu as FlaskMenu
from flask_oauthlib.client import OAuth as FlaskOAuth
from invenio_accounts import InvenioAccounts
Expand Down
10 changes: 8 additions & 2 deletions examples/orcid_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
.. code-block:: console
cdvirtualenv src/invenio-oauthclient
pip install -e .
pip install -e .[orcid]
3. Grab the *Client ID* and *Client Secret* after registering the application
and add them to your instance configuration as `consumer_key` and
Expand Down Expand Up @@ -83,7 +83,7 @@
from flask import Flask, redirect, url_for
from flask_babelex import Babel
from flask_cli import FlaskCLI
from flask_login import current_user
from flask_security import current_user
from flask_menu import Menu as FlaskMenu
from flask_oauthlib.client import OAuth as FlaskOAuth
from invenio_accounts import InvenioAccounts
Expand All @@ -94,6 +94,7 @@
from invenio_oauthclient.contrib import orcid
from invenio_oauthclient.views.client import blueprint as blueprint_client
from invenio_oauthclient.views.settings import blueprint as blueprint_settings
from invenio_mail import InvenioMail as Mail

# [ Configure application credentials ]
ORCID_APP_CREDENTIALS = dict(
Expand All @@ -105,6 +106,7 @@
app = Flask(__name__)

app.config.update(
SQLALCHEMY_ECHO=False,
SQLALCHEMY_DATABASE_URI=os.environ.get(
'SQLALCHEMY_DATABASE_URI', 'sqlite:///orcid_app.db'
),
Expand All @@ -115,11 +117,15 @@
DEBUG=True,
SECRET_KEY='TEST',
SECURITY_PASSWORD_SALT='security-password-salt',
SECURITY_LOGIN_WITHOUT_CONFIRMATION=False,
# MAIL_SUPPRESS_SEND=False,
TESTING=True,
)

FlaskCLI(app)
Babel(app)
FlaskMenu(app)
Mail(app)
InvenioDB(app)
InvenioAccounts(app)
FlaskOAuth(app)
Expand Down
2 changes: 1 addition & 1 deletion invenio_oauthclient/contrib/orcid.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@
import copy

from flask import current_app, redirect, url_for
from flask_login import current_user
from flask_security import current_user
from invenio_db import db

REMOTE_APP = dict(
Expand Down
34 changes: 20 additions & 14 deletions invenio_oauthclient/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
from flask import current_app, flash, redirect, render_template, request, \
session, url_for
from flask_babelex import gettext as _
from flask_login import current_user
from flask_security import current_user
from invenio_db import db
from werkzeug.utils import import_string

Expand Down Expand Up @@ -107,13 +107,16 @@ def oauth2_token_setter(remote, resp, token_type='', extra_data=None):
)


def token_setter(remote, token, secret='', token_type='', extra_data=None):
def token_setter(remote, token, secret='', token_type='', extra_data=None,
user=None):
"""Set token for user."""
session[token_session_key(remote.name)] = (token, secret)
user = user or current_user

# Save token if used is authenticated
if current_user.is_authenticated:
uid = current_user.get_id()
# 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
Expand Down Expand Up @@ -328,16 +331,9 @@ def signup_handler(remote, *args, **kwargs):
# Remove session key
session.pop(session_prefix + '_autoregister', None)

# Authenticate the user
if not oauth_authenticate(remote.consumer_key, user,
require_existing_link=False,
remember=current_app.config[
'OAUTHCLIENT_REMOTE_APPS']
[remote.name].get('remember', False)):
return current_app.login_manager.unauthorized()

# Link account and set session data
token = token_setter(remote, oauth_token[0], secret=oauth_token[1])
token = token_setter(remote, oauth_token[0], secret=oauth_token[1],
user=user)
handlers = current_oauthclient.signup_handlers[remote.name]

if token is None:
Expand All @@ -350,6 +346,16 @@ def signup_handler(remote, *args, **kwargs):
account_setup=account_setup
)

# Authenticate the user
if not oauth_authenticate(remote.consumer_key, user,
require_existing_link=False,
remember=current_app.config[
'OAUTHCLIENT_REMOTE_APPS']
[remote.name].get('remember', False)):
# redirect the user after registration (which doesn't include the
# activation), waiting user confirm his email
return redirect(url_for("security.login"))

# Remove account info from session
session.pop(session_prefix + '_account_info', None)
session.pop(session_prefix + '_response', None)
Expand Down
44 changes: 25 additions & 19 deletions invenio_oauthclient/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,29 @@

"""Utility methods to help find, authenticate or register a remote user."""

from flask import current_app, request
from flask import current_app, request, after_this_request
from flask_security import login_user, logout_user
from flask_security.registerable import register_user
from flask_security.confirmable import requires_confirmation
from invenio_db import db
from uritools import urisplit
from werkzeug.local import LocalProxy

from invenio_accounts.models import User

from .models import RemoteAccount, RemoteToken, UserIdentity


_security = LocalProxy(lambda: current_app.extensions['security'])

_datastore = LocalProxy(lambda: _security.datastore)


def _commit(response=None):
_datastore.commit()
return response


def _get_external_id(account_info):
"""Get external id from account info."""
if all(k in account_info for k in ("external_id", "external_method")):
Expand Down Expand Up @@ -64,13 +77,15 @@ def oauth_authenticate(client_id, user, require_existing_link=False,
remember=False):
"""Authenticate an oauth authorized callback."""
# Authenticate via the access token (access token used to get user_id)
if login_user(user):
if require_existing_link:
account = RemoteAccount.get(user.id, client_id)
if account is None:
logout_user()
return False
return True
if not requires_confirmation(user):
after_this_request(_commit)
if login_user(user):
if require_existing_link:
account = RemoteAccount.get(user.id, client_id)
if account is None:
logout_user()
return False
return True
return False


Expand All @@ -80,18 +95,9 @@ def oauth_register(account_info, form_data=None):
if form_data and form_data.get("email"):
email = form_data.get("email")

datastore = current_app.extensions['invenio-accounts'].datastore

if email:
with db.session.begin_nested():
if not datastore.find_user(email=email):
kwargs = dict(email=email, password=None, active=True)
try:
user = datastore.create_user(**kwargs)
except Exception:
current_app.logger.exception("Cannot create user")
raise

form_data['password'] = None
user = register_user(**form_data)
return user

return None
Expand Down
3 changes: 3 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@
'github': [
'github3.py>=0.9',
],
'orcid': [
'invenio-mail>=1.0.0a2',
]
}

extras_require['all'] = []
Expand Down
3 changes: 2 additions & 1 deletion tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -348,8 +348,9 @@ def test_token_getter_setter(monkeypatch):

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

with patch('flask_login._get_user', return_value=user):
with app.test_client() as c:
Expand Down

0 comments on commit 07eafa9

Please sign in to comment.