authentic/tests/test_commands.py

793 lines
28 KiB
Python

# authentic2 - versatile identity manager
# Copyright (C) 2010-2019 Entr'ouvert
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import datetime
import importlib
import json
from io import BufferedReader, BufferedWriter, TextIOWrapper
import py
import pytest
import responses
import webtest
from django.contrib.auth import get_user_model
from django.contrib.contenttypes.models import ContentType
from django.utils.timezone import now
from mellon.models import Issuer, UserSAMLIdentifier
from authentic2.a2_rbac.models import (
ADMIN_OP,
MANAGE_MEMBERS_OP,
VIEW_OP,
Operation,
OrganizationalUnit,
Permission,
Role,
)
from authentic2.a2_rbac.utils import get_default_ou, get_operation
from authentic2.apps.journal.models import Event
from authentic2.custom_user.models import DeletedUser
from authentic2.models import UserExternalId
from authentic2_auth_oidc.models import OIDCAccount, OIDCProvider
from .utils import call_command, login
User = get_user_model()
def test_changepassword(db, simple_user, monkeypatch):
import getpass
def _getpass(*args, **kwargs):
return 'pass'
monkeypatch.setattr(getpass, 'getpass', _getpass)
call_command('changepassword', 'user')
old_pass = simple_user.password
simple_user.refresh_from_db()
assert old_pass != simple_user.password
def test_clean_unused_account(db, simple_user, mailoutbox, freezer, settings):
settings.LDAP_AUTH_SETTINGS = [{'realm': 'ldap', 'url': 'ldap://ldap.com/', 'basedn': 'dc=ldap,dc=com'}]
ldap_user = User.objects.create(username='ldap-user', email='ldap-user@example.com', ou=simple_user.ou)
oidc_user = User.objects.create(username='oidc-user', email='oidc-user@example.com', ou=simple_user.ou)
saml_user = User.objects.create(username='saml-user', email='saml-user@example.com', ou=simple_user.ou)
UserExternalId.objects.create(user=ldap_user, source='ldap', external_id='whatever')
provider = OIDCProvider.objects.create(name='oidc', ou=simple_user.ou)
OIDCAccount.objects.create(user=oidc_user, provider=provider, sub='1')
issuer = Issuer.objects.create(entity_id='https://idp1.example.com/', slug='idp1')
UserSAMLIdentifier.objects.create(user=saml_user, issuer=issuer, name_id='1234')
email = simple_user.email
freezer.move_to('2018-01-01')
simple_user.ou.clean_unused_accounts_alert = 2
simple_user.ou.clean_unused_accounts_deletion = 3
simple_user.ou.save()
last_login = now() - datetime.timedelta(days=2, seconds=30)
for user in (simple_user, ldap_user, oidc_user, saml_user):
user.last_login = last_login
user.save()
call_command('clean-unused-accounts')
assert User.objects.count() == 4
assert len(mailoutbox) == 1
assert (
Event.objects.filter(
type__name='user.notification.inactivity', user=simple_user, data__identifier=simple_user.email
).count()
== 1
)
freezer.move_to('2018-01-01 12:00:00')
# no new mail, no deletion
call_command('clean-unused-accounts')
assert User.objects.count() == 4
assert len(mailoutbox) == 1
freezer.move_to('2018-01-02')
call_command('clean-unused-accounts')
assert User.objects.count() == 3
deleted_user = DeletedUser.objects.get()
assert deleted_user.old_user_id == simple_user.id
assert len(mailoutbox) == 2
assert mailoutbox[-1].to == [email]
assert (
Event.objects.filter(
type__name='user.deletion.inactivity', user=simple_user, data__identifier=simple_user.email
).count()
== 1
)
@responses.activate
def test_clean_unused_account_sms(db, nomail_user, mailoutbox, freezer, settings, phone_activated_authn):
settings.LDAP_AUTH_SETTINGS = [{'realm': 'ldap', 'url': 'ldap://ldap.com/', 'basedn': 'dc=ldap,dc=com'}]
settings.SMS_URL = 'https://foo.whatever.none/'
rsps = responses.post(
settings.SMS_URL,
json={
'headers': {
'content-type': 'application/json',
},
'status_code': 200,
'data': {},
},
)
ldap_user = User.objects.create(username='ldap-user', ou=nomail_user.ou)
ldap_user.attributes.phone = '+33122334455'
ldap_user.save()
oidc_user = User.objects.create(username='oidc-user', ou=nomail_user.ou)
oidc_user.attributes.phone = '+33122334456'
oidc_user.save()
saml_user = User.objects.create(username='saml-user', ou=nomail_user.ou)
saml_user.attributes.phone = '+33122334457'
saml_user.save()
UserExternalId.objects.create(user=ldap_user, source='ldap', external_id='whatever')
provider = OIDCProvider.objects.create(name='oidc', ou=nomail_user.ou)
OIDCAccount.objects.create(user=oidc_user, provider=provider, sub='1')
issuer = Issuer.objects.create(entity_id='https://idp1.example.com/', slug='idp1')
UserSAMLIdentifier.objects.create(user=saml_user, issuer=issuer, name_id='1234')
freezer.move_to('2018-01-01')
nomail_user.attributes.phone = '+33611223344'
nomail_user.save()
nomail_user.ou.clean_unused_accounts_alert = 2
nomail_user.ou.clean_unused_accounts_deletion = 3
nomail_user.ou.save()
last_login = now() - datetime.timedelta(days=2, seconds=30)
for user in (nomail_user, ldap_user, oidc_user, saml_user):
user.last_login = last_login
user.save()
call_command('clean-unused-accounts')
assert rsps.call_count == 1
assert 'Your account is inactive, please log in' in json.loads(rsps.calls[-1].request.body)['message']
# check message contains login url
assert 'https://testserver/login/' in json.loads(rsps.calls[-1].request.body)['message']
assert User.objects.count() == 4
assert len(mailoutbox) == 0
assert (
Event.objects.filter(
type__name='user.notification.inactivity',
user=nomail_user,
data__identifier=nomail_user.attributes.phone,
).count()
== 1
)
freezer.move_to('2018-01-01 12:00:00')
# no new sms, no deletion
call_command('clean-unused-accounts')
assert rsps.call_count == 1
assert User.objects.count() == 4
assert len(mailoutbox) == 0
freezer.move_to('2018-01-02')
call_command('clean-unused-accounts')
assert rsps.call_count == 2
assert (
'Your account was inactive and has therefore been deleted.'
in json.loads(rsps.calls[-1].request.body)['message']
)
assert User.objects.count() == 3
deleted_user = DeletedUser.objects.get()
assert deleted_user.old_user_id == nomail_user.id
assert (
Event.objects.filter(
type__name='user.deletion.inactivity',
user=nomail_user,
data__identifier=nomail_user.attributes.phone,
).count()
== 1
)
def test_clean_unused_account_user_logs_in(app, db, simple_user, mailoutbox, freezer):
freezer.move_to('2018-01-01')
simple_user.ou.clean_unused_accounts_alert = 2
simple_user.ou.clean_unused_accounts_deletion = 3
simple_user.ou.save()
simple_user.last_login = now() - datetime.timedelta(days=2)
simple_user.save()
call_command('clean-unused-accounts')
assert len(mailoutbox) == 1
login(app, simple_user)
# the day of deletion, nothing happens
freezer.move_to('2018-01-02')
simple_user.refresh_from_db()
assert len(mailoutbox) == 1
# when new alert delay is reached, user gets alerted again
freezer.move_to('2018-01-04')
call_command('clean-unused-accounts')
simple_user.refresh_from_db()
assert len(mailoutbox) == 2
def test_clean_unused_account_keepalive(app, db, simple_user, mailoutbox, freezer):
freezer.move_to('2018-01-01')
simple_user.ou.clean_unused_accounts_alert = 20
simple_user.ou.clean_unused_accounts_deletion = 30
simple_user.ou.save()
simple_user.last_login = None
simple_user.date_joined = datetime.date.fromisoformat('2015-01-01')
simple_user.keepalive = datetime.date.fromisoformat('2018-01-01')
simple_user.save()
call_command('clean-unused-accounts')
assert len(mailoutbox) == 0
freezer.move_to('2018-01-22')
call_command('clean-unused-accounts')
assert len(mailoutbox) == 1
freezer.move_to('2018-02-23')
# new command execution is past the theoretical deletion date, however
# a new keepalive has been pushed in the meantime, the user should not
# be deleted
simple_user.keepalive = datetime.date.fromisoformat('2018-02-10')
simple_user.save()
call_command('clean-unused-accounts')
assert len(mailoutbox) == 1
assert DeletedUser.objects.count() == 0
freezer.move_to('2018-03-22')
call_command('clean-unused-accounts')
# new alert
assert len(mailoutbox) == 2
assert DeletedUser.objects.count() == 0
freezer.move_to('2018-04-22')
call_command('clean-unused-accounts')
# deletion notification
assert len(mailoutbox) == 3
assert DeletedUser.objects.get().old_user_id == simple_user.id
def test_clean_unused_account_keepalive_alert_inconsistency_failsafe(
app, db, simple_user, mailoutbox, freezer
):
freezer.move_to('2018-01-01')
simple_user.ou.clean_unused_accounts_alert = 20
simple_user.ou.clean_unused_accounts_deletion = 30
simple_user.ou.save()
simple_user.last_login = None
simple_user.date_joined = datetime.date.fromisoformat('2015-01-01')
simple_user.keepalive = datetime.date.fromisoformat('2018-01-01')
simple_user.save()
call_command('clean-unused-accounts')
assert len(mailoutbox) == 0
freezer.move_to('2018-01-22')
call_command('clean-unused-accounts')
assert len(mailoutbox) == 1
freezer.move_to('2018-02-23')
# oops, something went wrong, last account deletion alert has not been saved right or has been
# erroneously modified to a more recent timestamp
simple_user.last_account_deletion_alert = datetime.date.fromisoformat('2018-02-04')
# and keepalive has been righteously updated in between
simple_user.keepalive = datetime.date.fromisoformat('2018-02-03')
simple_user.save()
call_command('clean-unused-accounts')
# no redundant alert, no deletion, i.e. the most restrictive choice in case of such
# inconsistency
assert len(mailoutbox) == 1
assert DeletedUser.objects.count() == 0
freezer.move_to('2018-02-04')
# another way things can go wrong: the last alert is right but the keepalive has been updated
# to an anterior (yet more recent than first execution) date
simple_user.last_account_deletion_alert = datetime.date.fromisoformat('2018-01-22')
simple_user.keepalive = datetime.date.fromisoformat('2018-01-21')
simple_user.save()
call_command('clean-unused-accounts')
# no redundant alert, no deletion, i.e. the most restrictive choice also
assert len(mailoutbox) == 1
assert DeletedUser.objects.count() == 0
def test_clean_unused_account_never_logged_in(app, db, simple_user, mailoutbox, freezer):
freezer.move_to('2018-01-01')
simple_user.ou.clean_unused_accounts_alert = 2
simple_user.ou.clean_unused_accounts_deletion = 3
simple_user.ou.save()
simple_user.last_login = None
simple_user.keepalive = None
simple_user.date_joined = now() - datetime.timedelta(days=4)
simple_user.save()
call_command('clean-unused-accounts')
assert len(mailoutbox) == 1
freezer.move_to('2018-01-03')
call_command('clean-unused-accounts')
assert len(mailoutbox) == 2
assert (
Event.objects.filter(
type__name='user.deletion.inactivity', user=simple_user, data__identifier=simple_user.email
).count()
== 1
)
deleted_user = DeletedUser.objects.get()
assert deleted_user.old_user_id == simple_user.id
def test_clean_unused_federated_account_never_logged_in(app, db, simple_user, mailoutbox, freezer, settings):
freezer.move_to('2018-01-01')
settings.LDAP_AUTH_SETTINGS = [{'realm': 'ldap', 'url': 'ldap://ldap.com/', 'basedn': 'dc=ldap,dc=com'}]
ldap_user = User.objects.create(username='ldap-user', email='ldap-user@example.com', ou=simple_user.ou)
UserExternalId.objects.create(user=ldap_user, source='ldap', external_id='whatever')
simple_user.ou.clean_unused_accounts_alert = 2
simple_user.ou.clean_unused_accounts_deletion = 3
simple_user.ou.save()
simple_user.last_login = simple_user.keepalive = None
simple_user.date_joined = now() - datetime.timedelta(days=4)
simple_user.save()
oidc_provider = OIDCProvider.objects.create(name='Foo', slug='foo', enabled=True)
OIDCAccount.objects.create(
provider=oidc_provider,
user=simple_user,
sub='abc',
)
ldap_user.last_login = ldap_user.keepalive = None
ldap_user.date_joined = now() - datetime.timedelta(days=4)
ldap_user.save()
saml_user = User.objects.create(username='saml-user', email='saml_user@example.com', ou=simple_user.ou)
saml_issuer = Issuer.objects.create(entity_id='https://idp1.example.com/', slug='idp1')
UserSAMLIdentifier.objects.create(
user=saml_user,
issuer=saml_issuer,
name_id='1234',
)
saml_user.last_login = saml_user.keepalive = None
saml_user.date_joined = now() - datetime.timedelta(days=4)
saml_user.save()
call_command('clean-unused-accounts')
assert len(mailoutbox) == 0
freezer.move_to('2018-01-03')
call_command('clean-unused-accounts')
assert len(mailoutbox) == 0
assert (
Event.objects.filter(
type__name='user.deletion.inactivity', user=simple_user, data__identifier=simple_user.email
).count()
== 1
)
assert (
Event.objects.filter(
type__name='user.deletion.inactivity', user=ldap_user, data__identifier=ldap_user.email
).count()
== 0
)
assert (
Event.objects.filter(
type__name='user.deletion.inactivity', user=saml_user, data__identifier=saml_user.email
).count()
== 1
)
assert DeletedUser.objects.count() == 2
assert {deleted.old_user_id for deleted in DeletedUser.objects.all()} == {saml_user.id, simple_user.id}
ldap_user.last_login = ldap_user.keepalive = now() - datetime.timedelta(days=4)
ldap_user.date_joined = now() - datetime.timedelta(days=5)
ldap_user.save()
call_command('clean-unused-accounts')
assert len(mailoutbox) == 0
assert (
Event.objects.filter(
type__name='user.deletion.inactivity', user=ldap_user, data__identifier=ldap_user.email
).count()
== 0
)
assert DeletedUser.objects.count() == 2
def test_clean_unused_federated_account_logged_in_untouched(app, db, simple_user, mailoutbox, freezer):
freezer.move_to('2018-01-01')
simple_user.ou.clean_unused_accounts_alert = 2
simple_user.ou.clean_unused_accounts_deletion = 3
simple_user.ou.save()
simple_user.last_login = simple_user.date_joined = now() - datetime.timedelta(days=4)
simple_user.keepalive = None
simple_user.save()
provider = OIDCProvider.objects.create(name='Foo', slug='foo', enabled=True)
OIDCAccount.objects.create(
provider=provider,
user=simple_user,
sub='abc',
)
saml_user = User.objects.create(username='saml-user', email='saml_user@example.com', ou=simple_user.ou)
saml_issuer = Issuer.objects.create(entity_id='https://idp1.example.com/', slug='idp1')
UserSAMLIdentifier.objects.create(
user=saml_user,
issuer=saml_issuer,
name_id='1234',
)
saml_user.last_login = simple_user.date_joined = now() - datetime.timedelta(days=4)
saml_user.keepalive = None
saml_user.save()
call_command('clean-unused-accounts')
assert len(mailoutbox) == 0
freezer.move_to('2018-01-03')
call_command('clean-unused-accounts')
assert len(mailoutbox) == 0
assert (
Event.objects.filter(
type__name='user.deletion.inactivity', user=simple_user, data__identifier=simple_user.email
).count()
== 0
)
assert (
Event.objects.filter(
type__name='user.deletion.inactivity', user=saml_user, data__identifier=saml_user.email
).count()
== 0
)
assert not DeletedUser.objects.count()
def test_clean_unused_account_keepalive_after_alert(app, db, simple_user, mailoutbox, freezer):
freezer.move_to('2018-01-01')
simple_user.ou.clean_unused_accounts_alert = 2
simple_user.ou.clean_unused_accounts_deletion = 3
simple_user.ou.save()
simple_user.last_login = now() - datetime.timedelta(days=2)
simple_user.save()
call_command('clean-unused-accounts')
assert len(mailoutbox) == 1
login(app, simple_user)
# the day of deletion, nothing happens
freezer.move_to('2018-01-02')
assert len(mailoutbox) == 1
freezer.move_to('2018-01-03')
# set keepalive
simple_user.keepalive = now()
simple_user.save()
# when new alert delay is reached, no mail is sent and last_account_deletion_alert is reset
freezer.move_to('2018-01-04')
call_command('clean-unused-accounts')
assert len(mailoutbox) == 1
simple_user.refresh_from_db()
assert simple_user.last_account_deletion_alert is None
def test_clean_unused_account_disabled_by_default(db, simple_user, mailoutbox):
simple_user.last_login = now() - datetime.timedelta(days=2)
simple_user.save()
call_command('clean-unused-accounts')
simple_user.refresh_from_db()
assert len(mailoutbox) == 0
def test_clean_unused_account_a2_user_exclude(app, db, simple_user, mailoutbox, freezer, settings):
settings.A2_USER_EXCLUDE = {'username': simple_user.username}
freezer.move_to('2018-01-01')
simple_user.ou.clean_unused_accounts_alert = 2
simple_user.ou.clean_unused_accounts_deletion = 3
simple_user.ou.save()
simple_user.last_login = now() - datetime.timedelta(days=2)
simple_user.save()
call_command('clean-unused-accounts')
assert len(mailoutbox) == 0
def test_clean_unused_account_always_alert(db, simple_user, mailoutbox, freezer):
simple_user.ou.clean_unused_accounts_alert = 2
simple_user.ou.clean_unused_accounts_deletion = 3 # one day between alert and actual deletion
simple_user.ou.save()
simple_user.last_login = now() - datetime.timedelta(days=4)
simple_user.save()
# even if account last login in past deletion delay, an alert is always sent first
call_command('clean-unused-accounts')
simple_user.refresh_from_db()
assert len(mailoutbox) == 1
# and calling again as no effect, since one day must pass before account is deleted
call_command('clean-unused-accounts')
simple_user.refresh_from_db()
assert len(mailoutbox) == 1
@pytest.mark.parametrize('deletion_delay', [730, 500, 65])
def test_clean_unused_account_displayed_message(simple_user, mailoutbox, deletion_delay):
simple_user.ou.clean_unused_accounts_alert = deletion_delay - 30
simple_user.ou.clean_unused_accounts_deletion = deletion_delay
simple_user.ou.save()
simple_user.last_login = now() - datetime.timedelta(days=deletion_delay + 30)
simple_user.save()
# alert email
call_command('clean-unused-accounts')
mail = mailoutbox[0]
assert mail.subject == 'Alert: Jôhn Dôe, your account is inactive and is pending deletion'
assert 'Jôhn Dôe' in mail.body
assert 'In order to keep your account, you must log in within 30 days.' in mail.body
# deletion email
simple_user.last_account_deletion_alert = now() - datetime.timedelta(days=31)
simple_user.save()
call_command('clean-unused-accounts')
mail = mailoutbox[1]
assert mail.subject == 'Notification: Jôhn Dôe, your account has been deleted'
assert 'Jôhn Dôe' in mail.body
assert 'Your account was inactive, it has been deleted.' in mail.body
def test_clean_unused_account_login_url(simple_user, mailoutbox):
simple_user.ou.clean_unused_accounts_alert = 1
simple_user.ou.clean_unused_accounts_deletion = 2
simple_user.ou.save()
simple_user.last_login = now() - datetime.timedelta(days=1)
simple_user.save()
call_command('clean-unused-accounts')
mail = mailoutbox[0]
assert 'href="https://testserver/login/"' in mail.message().as_string()
def test_clean_unused_account_with_no_email(simple_user, mailoutbox, caplog):
simple_user.email = ''
simple_user.ou.clean_unused_accounts_alert = 1
simple_user.ou.clean_unused_accounts_deletion = 2
simple_user.ou.save()
simple_user.last_login = now() - datetime.timedelta(days=1)
simple_user.save()
call_command('clean-unused-accounts')
assert len(mailoutbox) == 0
assert 'clean-unused-accounts failed' not in caplog.text
def test_cleanupauthentic(db):
call_command('cleanupauthentic')
def test_load_ldif(db, monkeypatch, tmpdir):
FileType = (TextIOWrapper, BufferedReader, BufferedWriter)
ldif = tmpdir.join('some.ldif')
ldif.ensure()
class MockPArser:
def __init__(self, *args, **kwargs):
self.users = []
assert len(args) == 1
assert isinstance(args[0], FileType)
assert kwargs['options']['extra_attribute'] == {'ldap_attr': 'first_name'}
assert kwargs['options']['result'] == 'result'
def parse(self):
pass
oidc_cmd = importlib.import_module('authentic2.management.commands.load-ldif')
monkeypatch.setattr(oidc_cmd, 'DjangoUserLDIFParser', MockPArser)
call_command('load-ldif', ldif.strpath, result='result', extra_attribute={'ldap_attr': 'first_name'})
# test ExtraAttributeAction
class MockPArser: # pylint: disable=E0102
def __init__(self, *args, **kwargs):
self.users = []
assert len(args) == 1
assert isinstance(args[0], FileType)
assert kwargs['options']['extra_attribute'] == {'ldap_attr': 'first_name'}
assert kwargs['options']['result'] == 'result'
def parse(self):
pass
monkeypatch.setattr(oidc_cmd, 'DjangoUserLDIFParser', MockPArser)
call_command(
'load-ldif', '--extra-attribute', 'ldap_attr', 'first_name', '--result', 'result', ldif.strpath
)
def test_resetpassword(simple_user):
call_command('resetpassword', 'user')
old_pass = simple_user.password
simple_user.refresh_from_db()
assert old_pass != simple_user.password
def test_sync_metadata(db):
test_file = py.path.local(__file__).dirpath('metadata.xml').strpath
call_command('sync-metadata', test_file, source='abcd')
def test_check_and_repair_managers_of_roles(db, capsys):
default_ou = get_default_ou()
admin_op = get_operation(ADMIN_OP)
OrganizationalUnit.objects.create(name='Orgunit1', slug='orgunit1')
role1 = Role.objects.create(name='Role 1', slug='role-1', ou=default_ou)
perm1 = Permission.objects.create(
operation=admin_op,
target_id=role1.id,
ou=default_ou,
target_ct=ContentType.objects.get_for_model(Role),
)
manager_role1 = Role.objects.create(name='Managers of Role 1', slug='_a2-managers-of-role-role1')
manager_role1.permissions.add(perm1)
manager_role1.save()
call_command('check-and-repair', '--repair', '--noinput')
captured = capsys.readouterr()
assert '"Managers of Role 1": no admin scope' in captured.out
assert 'Managers of Role 1" wrong ou, should be "Default organizational unit"' in captured.out
assert 'invalid permission "Management / role / Role 1": not manage_members operation' in captured.out
assert (
'invalid permission "Management / role / Role 1": not admin_scope and not self manage permission'
in captured.out
)
assert (
'invalid admin role "Managers of Role 1" wrong ou, should be "Default organizational unit" is "None"'
in captured.out
)
perm1.refresh_from_db()
assert perm1.ou is None
manager_role1 = role1.get_admin_role()
assert manager_role1.ou == get_default_ou()
assert manager_role1.permissions.count() == 3
assert manager_role1.permissions.get(
operation=get_operation(MANAGE_MEMBERS_OP), target_id=manager_role1.id
)
assert manager_role1.permissions.get(operation=get_operation(MANAGE_MEMBERS_OP), target_id=role1.id)
assert manager_role1.permissions.get(
operation=get_operation(VIEW_OP),
target_ct=ContentType.objects.get_for_model(ContentType),
target_id=ContentType.objects.get_for_model(User).pk,
)
manage_members_op = get_operation(MANAGE_MEMBERS_OP)
perm1.op = manage_members_op
perm1.save()
call_command('check-and-repair', '--repair', '--noinput')
perm1 = Permission.objects.get(operation=manage_members_op, target_id=role1.id)
assert perm1.ou is None
def test_check_and_delete_unused_permissions(db, capsys, simple_user):
role1 = Role.objects.create(name='Role1', slug='role1')
op1 = Operation.objects.create(slug='operation-1')
used_perm = Permission.objects.create(
operation=op1, target_id=role1.id, target_ct=ContentType.objects.get_for_model(Role)
)
role1.admin_scope = used_perm
role1.save()
Permission.objects.create(
operation=op1, target_id=simple_user.id, target_ct=ContentType.objects.get_for_model(get_user_model())
)
call_command('check-and-repair', '--fake', '--noinput')
n_perm = len(Permission.objects.all())
call_command('check-and-repair', '--repair', '--noinput')
assert len(Permission.objects.all()) == n_perm - 1
def test_check_identifiers_uniqueness(db, capsys, settings):
settings.A2_USERNAME_IS_UNIQUE = False
ou = get_default_ou()
ou.email_is_unique = True
ou.save()
User.objects.create(username='foo', email='foo@example.net', first_name='Toto', last_name='Foo', ou=ou)
User.objects.create(username='foo', email='bar@example.net', first_name='Bar', last_name='Foo', ou=ou)
User.objects.create(username='bar', email='bar@example.net', first_name='Tutu', last_name='Bar', ou=ou)
settings.A2_EMAIL_IS_UNIQUE = True
settings.A2_USERNAME_IS_UNIQUE = True
call_command('check-and-repair', '--repair', '--noinput')
captured = capsys.readouterr()
assert 'found 2 user accounts with same username' in captured.out
assert 'found 2 user accounts with same email' in captured.out
def test_clean_unused_account_max_mails_per_period(settings, db, mailoutbox, freezer):
ou = get_default_ou()
ou.clean_unused_accounts_alert = 1
ou.clean_unused_accounts_deletion = 2
ou.save()
settings.A2_CLEAN_UNUSED_ACCOUNTS_MAX_MAIL_PER_PERIOD = 4
for i in range(100):
User.objects.create(ou=ou, email='user-%s@example.com' % i, last_login=now())
call_command('clean-unused-accounts')
assert len(mailoutbox) == 0
freezer.move_to(datetime.timedelta(days=1))
call_command('clean-unused-accounts')
# 4 alerts
assert len(mailoutbox) == 4
freezer.move_to(datetime.timedelta(days=1))
call_command('clean-unused-accounts')
# 4 new alerts and 4 deletions notifications
assert len(mailoutbox) == 4 + 8
def test_clean_user_exports(settings, app, superuser, freezer):
users = [User(username='user%s' % i) for i in range(10)]
User.objects.bulk_create(users)
# export directory does not exist yet
call_command('clean-user-exports')
resp = login(app, superuser, '/manage/users/')
resp = resp.click('CSV').follow()
file_creation_time = now()
assert resp.click('Download CSV')
freezer.move_to(file_creation_time + datetime.timedelta(days=5))
call_command('clean-user-exports')
assert resp.click('Download CSV')
freezer.move_to(file_creation_time + datetime.timedelta(days=8))
call_command('clean-user-exports')
with pytest.raises(webtest.app.AppError):
resp.click('Download CSV')