New longterm_install options #24

Merged
champeno merged 5 commits from Evarin/install_params into master 2019-01-06 13:58:10 +01:00
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. behaviour, and for instance attribute a default entrance year.
Initial migration Initial migration
If you used allauth without LongTermClipperAccountAdapter, or another CAS Description
interface to log in, you need to update the Users to the new username policy, If you used allauth without LongTermClipperAccountAdapter, or another CAS
and (in the second case) to create the SocialAccount instances to link CAS and interface to log in, you need to update the Users to the new username policy,
Users. This can be done easily with ``$ python manage.py install_longterm``. 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 Demo Site
********* *********

View file

@ -147,14 +147,20 @@ def deprecate_clippers():
clippers.update(provider='clipper_inactive') 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 Manages the transition from an older django_cas or an allauth_ens
installation without LongTermClipperAccountAdapter 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 accounts is None:
if u.username.isalnum()} accounts = {u.username: u for u in User.objects.all()
if u.username.isalnum()}
ldap_connection = init_ldap() ldap_connection = init_ldap()
ltc_adapter = get_adapter() ltc_adapter = get_adapter()
@ -178,7 +184,10 @@ def install_longterm_adapter(fake=False):
user = accounts.get(clipper_uid, None) user = accounts.get(clipper_uid, None)
if user is None: if user is None:
continue 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: if fake:
cases.append(clipper_uid) cases.append(clipper_uid)
else: else:

View file

@ -1,6 +1,9 @@
# coding: utf-8 # coding: utf-8
from django.contrib.auth import get_user_model
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from allauth.socialaccount.models import SocialAccount
from allauth_ens.adapter import install_longterm_adapter from allauth_ens.adapter import install_longterm_adapter
@ -17,10 +20,52 @@ class Command(BaseCommand):
help=('Does not save the models created/updated,' help=('Does not save the models created/updated,'
'only shows the list'), '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 pass
def handle(self, *args, **options): 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" self.stdout.write("Social accounts created : %d"
% len(logs["created"])) % len(logs["created"]))
self.stdout.write(" ".join(("%s -> %s" % s) for s in 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.contrib.sites.models import Site
from django.core import mail from django.core import mail
from django.test import TestCase, override_settings from django.test import TestCase, override_settings
from django.test.utils import captured_stdout
from allauth.socialaccount.models import SocialAccount from allauth.socialaccount.models import SocialAccount
@ -18,6 +19,7 @@ from mock import patch
from allauth_ens.utils import get_ldap_infos from allauth_ens.utils import get_ldap_infos
from .adapter import deprecate_clippers, install_longterm_adapter from .adapter import deprecate_clippers, install_longterm_adapter
from .management.commands.install_longterm import Command as InstallLongterm
_mock_ldap = MockLDAP() _mock_ldap = MockLDAP()
ldap_patcher = patch('allauth_ens.utils.ldap.initialize', ldap_patcher = patch('allauth_ens.utils.ldap.initialize',
@ -331,6 +333,98 @@ class LongTermClipperTests(CASTestCase):
self.assertEqual(user1.username, "test@12") self.assertEqual(user1.username, "test@12")
self.assertEqual(conn.extra_data['ldap']['entrance_year'], '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): def test_longterm_installer_from_djangocas(self):
with self.settings( with self.settings(
SOCIALACCOUNT_ADAPTER='allauth.socialaccount.' SOCIALACCOUNT_ADAPTER='allauth.socialaccount.'

View file

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