1 # This file is part of Indico.
2 # Copyright (C) 2002 - 2015 European Organization for Nuclear Research (CERN).
4 # Indico is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License as
6 # published by the Free Software Foundation; either version 3 of the
7 # License, or (at your option) any later version.
9 # Indico is distributed in the hope that it will be useful, but
10 # WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 # General Public License for more details.
14 # You should have received a copy of the GNU General Public License
15 # along with Indico; if not, see <http://www.gnu.org/licenses/>.
17 from __future__
import unicode_literals
19 from flask
import session
, redirect
, request
20 from flask_multipass
import MultipassException
22 from indico
.core
import signals
23 from indico
.core
.auth
import multipass
24 from indico
.core
.db
import db
25 from indico
.core
.logger
import Logger
26 from indico
.modules
.auth
.models
.identities
import Identity
27 from indico
.modules
.auth
.util
import save_identity_info
28 from indico
.modules
.users
import User
29 from indico
.util
.i18n
import _
30 from indico
.web
.flask
.util
import url_for
31 from indico
.web
.menu
import MenuItem
32 from MaKaC
.common
.timezoneUtils
import SessionTZ
35 logger
= Logger
.get('auth')
38 @multipass.identity_handler
39 def process_identity(identity_info
):
40 logger
.info('Received identity info: {}'.format(identity_info
))
41 identity
= Identity
.query
.filter_by(provider
=identity_info
.provider
.name
,
42 identifier
=identity_info
.identifier
).first()
44 logger
.info('Identity does not exist in the database yet')
46 emails
= {email
.lower() for email
in identity_info
.data
.getlist('email') if email
}
48 identity_info
.data
.setlist('email', emails
)
49 users
= User
.query
.filter(~User
.is_deleted
, User
.all_emails
.contains(db
.func
.any(list(emails
)))).all()
53 # TODO: handle this case somehow.. let the user select which user to log in to?
54 raise NotImplementedError('Multiple emails matching multiple users')
55 save_identity_info(identity_info
, user
if user
and not user
.is_pending
else None)
56 if not user
or user
.is_pending
:
57 if user
and user
.is_pending
:
58 logger
.info('Found pending user with matching email: {}'.format(user
))
60 logger
.info('Email search did not find an existing user')
61 return redirect(url_for('auth.register', provider
=identity_info
.provider
.name
))
63 logger
.info('Found user with matching email: {}'.format(user
))
64 return redirect(url_for('auth.link_account', provider
=identity_info
.provider
.name
))
65 elif identity
.user
.is_deleted
:
66 raise MultipassException(_('Your Indico profile has been deleted.'))
70 # This should never happen!
71 raise ValueError('Got identity for pending user')
72 logger
.info('Found existing identity {} for user {}'.format(identity
, user
))
73 # Update the identity with the latest information
74 if identity
.multipass_data
!= identity_info
.multipass_data
:
75 logger
.info('Updated multipass data'.format(identity
, user
))
76 identity
.multipass_data
= identity_info
.multipass_data
77 if identity
.data
!= identity_info
.data
:
78 logger
.info('Updated data'.format(identity
, user
))
79 identity
.data
= identity_info
.data
81 raise MultipassException(_('Your Indico profile has been blocked.'))
82 login_user(user
, identity
)
85 def login_user(user
, identity
=None):
86 """Sets the session user and performs on-login logic
88 When specifying `identity`, the provider/identitifer information
89 is saved in the session so the identity management page can prevent
90 the user from removing the identity he used to login.
92 :param user: The :class:`~indico.modules.users.User` to log in to.
93 :param identity: The :class:`Identity` instance used to log in.
95 avatar
= user
.as_avatar
96 session
.timezone
= SessionTZ(avatar
).getSessionTZ()
98 session
.lang
= user
.settings
.get('lang')
100 identity
.register_login(request
.remote_addr
)
101 session
['login_identity'] = identity
.id
103 session
.pop('login_identity', None)
104 user
.synchronize_data()
107 @signals.users
.profile_sidemenu
.connect
108 def _extend_profile_menu(user
, **kwargs
):
109 return MenuItem(_('Accounts'), 'auth.accounts')