Merge branch 'Evarin/install_params' into 'master'

New longterm_install options

See merge request klub-dev-ens/django-allauth-ens!18
This commit is contained in:
Aurélien Delobelle 2019-01-06 13:58:08 +01:00
commit a216a0cad1
5 changed files with 171 additions and 12 deletions

View file

@ -216,11 +216,23 @@ Customize
behaviour, and for instance attribute a default entrance year.
Initial migration
If you used allauth without LongTermClipperAccountAdapter, or another CAS
interface to log in, you need to update the Users to the new username policy,
and (in the second case) to create the SocialAccount instances to link CAS and
Users. This can be done easily with ``$ python manage.py install_longterm``.
Description
If you used allauth without LongTermClipperAccountAdapter, or another CAS
interface to log in, you need to update the Users to the new username policy,
and (in the second case) to create the SocialAccount instances to link CAS and
Users. This can be done easily with ``$ python manage.py install_longterm``.
Install_longterm options
- ``--use-socialaccounts``: Use the existing SocialAccounts rather than all the Users. Useful if you are already using Allauth and don't want ``install_longterm`` to mess with the non-clipper authentications.
- ``--keep-usernames``: Do not apply the username template (e.g. ``clipper@promo``) to the existing accounts, only populate the SocialAccounts with LDAP informations. Useful if you don't want to change the usernames of previous users, but do want such a template for future accounts.
- ``--clipper-field <field_name>``: Use a special field rather than the username to get the clipper username (for LDAP lookup and SocialAccount creation/update). This parameter is compatible with ForeignKeys (e.g. ``profile.clipper``). Note: ``--use-socialaccounts`` will ignore the ``--clipper-field`` parameter.
- ``--fake``: Do not modify the database. Use it to test there is no conflict, and be sure the changes are the ones expected. This command does not check for uniqueness errors, so there it may succeed and the actual command fail eventually.
Typical use cases
- *Django-cas-ng -> Longterm*: Use ``install_longterm`` without parameters, or maybe ``--keep-usernames``. If you had a custom username handling, ``--clipper_field`` may be useful.
- *Allauth -> Longterm*: Use ``install_longterm`` with ``--use-socialaccounts``, and maybe ``--keep-usernames``.
*********
Demo Site
*********

View file

@ -147,14 +147,20 @@ def deprecate_clippers():
clippers.update(provider='clipper_inactive')
def install_longterm_adapter(fake=False):
def install_longterm_adapter(fake=False, accounts=None, keep_usernames=False):
"""
Manages the transition from an older django_cas or an allauth_ens
installation without LongTermClipperAccountAdapter
accounts is an optional dictionary containing the association between
clipper usernames and django's User accounts. If not provided, the
function will assumer Users' usernames are their clipper uid.
"""
accounts = {u.username: u for u in User.objects.all()
if u.username.isalnum()}
if accounts is None:
accounts = {u.username: u for u in User.objects.all()
if u.username.isalnum()}
ldap_connection = init_ldap()
ltc_adapter = get_adapter()
@ -178,7 +184,10 @@ def install_longterm_adapter(fake=False):
user = accounts.get(clipper_uid, None)
if user is None:
continue
user.username = ltc_adapter.get_username(clipper_uid, data)
if not keep_usernames:
user.username = ltc_adapter.get_username(clipper_uid, data)
if fake:
cases.append(clipper_uid)
else:

View file

@ -1,6 +1,9 @@
# coding: utf-8
from django.contrib.auth import get_user_model
from django.core.management.base import BaseCommand
from allauth.socialaccount.models import SocialAccount
from allauth_ens.adapter import install_longterm_adapter
@ -17,10 +20,52 @@ class Command(BaseCommand):
help=('Does not save the models created/updated,'
'only shows the list'),
)
parser.add_argument(
'--use-socialaccounts',
action='store_true',
default=False,
help=('Use the existing SocialAccounts rather than all the Users'),
)
parser.add_argument(
'--keep-usernames',
action='store_true',
default=False,
help=('Do not apply the username template (e.g. clipper@promo) to'
'the existing account, only populate the SocialAccounts with'
'ldap informations'),
)
parser.add_argument(
'--clipper-field',
default=None,
type=str
)
pass
def handle(self, *args, **options):
logs = install_longterm_adapter(options.get("fake", False))
fake = options.get("fake", False)
keep_usernames = options.get("keep_usernames", False)
if options.get('use_socialaccounts', False):
accounts = {account.uid: account.user for account in
(SocialAccount.objects.filter(provider="clipper")
.prefetch_related("user"))}
elif options.get('clipper_field', None):
fields = options['clipper_field'].split('.')
User = get_user_model()
def get_subattr(obj, fields):
# Allows to follow OneToOne relationships
if len(fields) == 1:
return getattr(obj, fields[0])
return get_subattr(getattr(obj, fields[0]), fields[1:])
accounts = {get_subattr(account, fields): account for account in
User.objects.all()}
else:
accounts = None
logs = install_longterm_adapter(fake, accounts, keep_usernames)
self.stdout.write("Social accounts created : %d"
% len(logs["created"]))
self.stdout.write(" ".join(("%s -> %s" % s) for s in logs["created"]))

View file

@ -7,6 +7,7 @@ from django.contrib.auth import HASH_SESSION_KEY, get_user_model
from django.contrib.sites.models import Site
from django.core import mail
from django.test import TestCase, override_settings
from django.test.utils import captured_stdout
from allauth.socialaccount.models import SocialAccount
@ -18,6 +19,7 @@ from mock import patch
from allauth_ens.utils import get_ldap_infos
from .adapter import deprecate_clippers, install_longterm_adapter
from .management.commands.install_longterm import Command as InstallLongterm
_mock_ldap = MockLDAP()
ldap_patcher = patch('allauth_ens.utils.ldap.initialize',
@ -331,6 +333,98 @@ class LongTermClipperTests(CASTestCase):
self.assertEqual(user1.username, "test@12")
self.assertEqual(conn.extra_data['ldap']['entrance_year'], '12')
def test_longterm_installer_from_allauth_command_using_username(self):
self._setup_ldap(12)
with self.settings(
SOCIALACCOUNT_ADAPTER='allauth.socialaccount.'
'adapter.DefaultSocialAccountAdapter'):
r = self.client_cas_login(self.client, provider_id="clipper",
username='test')
user0 = r.context["user"]
nsa0 = SocialAccount.objects.count()
self.assertEqual(user0.username, "test")
self.client.logout()
with captured_stdout() as stdout:
command = InstallLongterm()
command.handle(clipper_field='username')
output = stdout.getvalue()
self.assertIn('test -> test@12', output)
r = self.client_cas_login(self.client, provider_id="clipper",
username='test')
user1 = r.context["user"]
nsa1 = SocialAccount.objects.count()
conn = user1.socialaccount_set.get(provider='clipper')
self.assertEqual(user1.id, user0.id)
self.assertEqual(nsa1, nsa0)
self.assertEqual(user1.username, "test@12")
self.assertEqual(conn.extra_data['ldap']['entrance_year'], '12')
def test_longterm_installer_from_allauth_command_keeping_username(self):
self._setup_ldap(12)
with self.settings(
SOCIALACCOUNT_ADAPTER='allauth.socialaccount.'
'adapter.DefaultSocialAccountAdapter'):
r = self.client_cas_login(self.client, provider_id="clipper",
username='test')
user0 = r.context["user"]
nsa0 = SocialAccount.objects.count()
self.assertEqual(user0.username, "test")
self.client.logout()
with captured_stdout() as stdout:
command = InstallLongterm()
command.handle(keep_usernames=True)
output = stdout.getvalue()
self.assertIn('test -> test', output)
r = self.client_cas_login(self.client, provider_id="clipper",
username='test')
user1 = r.context["user"]
nsa1 = SocialAccount.objects.count()
conn = user1.socialaccount_set.get(provider='clipper')
self.assertEqual(user1.id, user0.id)
self.assertEqual(nsa1, nsa0)
self.assertEqual(user1.username, "test")
self.assertEqual(conn.extra_data['ldap']['entrance_year'], '12')
def test_longterm_installer_from_allauth_command_socialaccounts(self):
self._setup_ldap(12)
with self.settings(
SOCIALACCOUNT_ADAPTER='allauth.socialaccount.'
'adapter.DefaultSocialAccountAdapter'):
r = self.client_cas_login(self.client, provider_id="clipper",
username='test')
user0 = r.context["user"]
self.assertEqual(user0.username, "test")
self.client.logout()
user1 = User.objects.create_user('bidule', 'bidule@clipper.ens.fr',
'bidule')
nsa0 = SocialAccount.objects.count()
with captured_stdout() as stdout:
command = InstallLongterm()
command.handle(use_socialaccounts=True)
output = stdout.getvalue()
self.assertIn('test -> test@12', output)
self.assertNotIn('bidule ->', output)
r = self.client_cas_login(self.client, provider_id="clipper",
username='test')
user1 = r.context["user"]
nsa1 = SocialAccount.objects.count()
conn = user1.socialaccount_set.get(provider='clipper')
self.assertEqual(user1.id, user0.id)
self.assertEqual(nsa1, nsa0)
self.assertEqual(nsa1, nsa0)
self.assertEqual(user1.username, "test@12")
self.assertEqual(conn.extra_data['ldap']['entrance_year'], '12')
def test_longterm_installer_from_djangocas(self):
with self.settings(
SOCIALACCOUNT_ADAPTER='allauth.socialaccount.'

View file

@ -116,8 +116,7 @@ def remove_email(user, email):
# Prefer a verified mail.
new_primary = (
others.filter(verified=True).last() or
others.last()
others.filter(verified=True).last() or others.last()
)
if new_primary: