Project

General

Profile

0001-WIP-clean-unused-accounts-depending-on-OUs-26909.patch

Paul Marillonnet, 26 Mar 2019 05:39 PM

Download (7.19 KB)

View differences:

Subject: [PATCH] WIP clean unused accounts depending on OUs (#26909)

 src/authentic2/a2_rbac/models.py              |  9 ++
 .../commands/clean-unused-accounts.py         | 94 ++++++++-----------
 2 files changed, 49 insertions(+), 54 deletions(-)
src/authentic2/a2_rbac/models.py
76 76
        choices=USER_ADD_PASSWD_POLICY_CHOICES,
77 77
        default=0)
78 78

  
79
    clean_unused_accounts_reminder = models.IntegerField(
80
        verbose_name=_('Days after which the user receives an account deletion '
81
            'warnin.'),
82
        default=0)
83

  
84
    clean_unused_accounts_deletion = models.IntegerField(
85
        verbose_name=_('Delay in days before cleaning unused accounts'),
86
        default=0)
87

  
79 88
    objects = managers.OrganizationalUnitManager()
80 89

  
81 90
    class Meta:
src/authentic2/management/commands/clean-unused-accounts.py
1 1
from __future__ import print_function
2 2

  
3 3
import logging
4
import datetime
5 4

  
5
from datetime import timedelta
6 6
from django.contrib.auth import get_user_model
7 7
from django.core.management.base import BaseCommand, CommandError
8 8
from django.core.mail import send_mail
9 9
from django.utils.timezone import now
10 10
from django.template.loader import render_to_string
11
from django_rbac.utils import get_ou_model
11 12

  
12 13
from authentic2.models import DeletedUser
13 14

  
......
24 25
    help = '''Clean unused accounts'''
25 26

  
26 27
    def add_arguments(self, parser):
27
        parser.add_argument('clean_threshold', type=int)
28
        parser.add_argument(
29
            "--alert-thresholds",
30
            help='list of durations before sending an alert '
31
                 'message for unused account, default is none',
32
            default=None)
33
        parser.add_argument(
34
            "--period", type=int,
35
            help='period between two calls to '
36
                 'clean-unused-accounts as days, default is 1',
37
            default=1
38
        )
39 28
        parser.add_argument("--fake", action='store_true', help='do nothing', default=False)
40 29
        parser.add_argument(
41 30
            "--filter", help='filter to apply to the user queryset, '
......
55 44
            log.exception('failure while cleaning unused accounts')
56 45

  
57 46
    def clean_unused_acccounts(self, *args, **options):
58
        if options['period'] < 1:
59
            raise CommandError('period must be > 0')
60

  
61
        clean_threshold = options['clean_threshold']
62
        if clean_threshold < 1:
63
            raise CommandError('clean_threshold must be an integer > 0')
64

  
65 47
        if options['verbosity'] == '0':
66 48
            logging.basicConfig(level=logging.CRITICAL)
67 49
        if options['verbosity'] == '1':
......
85 67
                    users = users.filter(**{key: value})
86 68
                except:
87 69
                    raise CommandError('invalid --filter %s' % f)
88
        if options['alert_thresholds']:
89
            alert_thresholds = options['alert_thresholds']
90
            alert_thresholds = alert_thresholds.split(',')
91
            try:
92
                alert_thresholds = map(int, alert_thresholds)
93
            except ValueError:
94
                raise CommandError('alert_thresholds must be a comma '
95
                        'separated list of integers')
96
            for threshold in alert_thresholds:
97
                if not (0 < threshold < clean_threshold):
98
                    raise CommandError('alert-threshold must a positive integer '
99
                            'inferior to clean-threshold: 0 < %d < %d' % (
100
                                threshold, clean_threshold))
101
            for threshold in alert_thresholds:
102
                a = n - datetime.timedelta(days=threshold)
103
                b = n - datetime.timedelta(days=threshold-options['period'])
104
                for user in users.filter(last_login__lt=b, last_login__gte=a):
105
                    log.info('%s last login %d days ago, sending alert', user, threshold)
106
                    self.send_alert(user, threshold, clean_threshold-threshold)
107
        threshold = n - datetime.timedelta(days=clean_threshold)
108
        for user in users.filter(last_login__lt=threshold):
109
            d = n - user.last_login
110
            log.info('%s last login %d days ago, deleting user', user, d.days)
111
            self.delete_user(user, clean_threshold)
112

  
113

  
114
    def send_alert(self, user, threshold, clean_threshold):
115
        ctx = { 'user': user, 'threshold': threshold,
116
                'clean_threshold': clean_threshold }
117
        self.send_mail('authentic2/unused_account_alert', user, ctx)
70
        for ou in get_ou_model().objects.all():
71
            if not ou.clean_unused_accounts_deletion:
72
                continue
73

  
74
            deletion = timedelta(days=ou.clean_unused_accounts_deletion)
75
            reminder = timedelta(days=ou.clean_unused_accounts_reminder)
76

  
77
            for user in users.filter(ou=ou, last_login_lt=n-reminder):
78
                if n - user.last_login >= deletion:
79
                    log.info(
80
                        '%s last login more than %d days ago, deleting user',
81
                        user, ou.clean_unused_accounts_deletion)
82
                    self.delete_user(user, clean_threshold)
83
                elif n - user.last_login - reminder <= timedelta(days=1):
84
                    log.info('%s last login %d days ago, sending alert', user,
85
                        ou.clean_unused_accounts_reminder)
86
                    self.send_alert(user)
87

  
88

  
89
    def send_alert(self, user):
90
        ctx = {
91
            'user': user,
92
            'threshold': user.ou.clean_unused_accounts_reminder,
93
            'clean_threshold': user.ou.clean_unused_accounts_deletion
94
        }
95
        self.send_mail(
96
                'authentic2/unused_account_alert_{}'.format(user.ou.slug),
97
                'authentic2/unused_account_alert', #default fallback template
98
                user, ctx)
118 99

  
119 100

  
120 101
    def send_mail(self, prefix, user, ctx):
......
132 113
                log.exception('email sending failure')
133 114

  
134 115

  
135
    def delete_user(self, user, threshold):
136
        ctx = { 'user': user, 'threshold': threshold }
137
        self.send_mail('authentic2/unused_account_delete', user,
138
                ctx)
116
    def delete_user(self, user):
117
        ctx = {
118
            'user': user,
119
            'threshold': user.ou.clean_unused_accounts_deletion
120
        }
121
        self.send_mail(
122
                'authentic2/unused_account_delete_{}'.format(user.ou.slug),
123
                'authentic2/unused_account_delete', #default fallback template
124
                user, ctx)
139 125
        if not self.fake:
140 126
            DeletedUser.objects.delete_user(user)
141
-