|
|
|
1 |
# -*- coding: utf-8 -*- |
2 |
# xxxxxx-delete-deactivated |
3 |
# listener plugin to delete home directories |
4 |
# |
5 |
# Copyright 2011 Univention GmbH |
6 |
# |
7 |
# http://www.univention.de/ |
8 |
# |
9 |
# All rights reserved. |
10 |
# |
11 |
# The source code of this program is made available |
12 |
# under the terms of the GNU Affero General Public License version 3 |
13 |
# (GNU AGPL V3) as published by the Free Software Foundation. |
14 |
# |
15 |
# Binary versions of this program provided by Univention to you as |
16 |
# well as other copyrighted, protected or trademarked materials like |
17 |
# Logos, graphics, fonts, specific documentations and configurations, |
18 |
# cryptographic keys etc. are subject to a license agreement between |
19 |
# you and Univention and not subject to the GNU AGPL V3. |
20 |
# |
21 |
# In the case you use this program under the terms of the GNU AGPL V3, |
22 |
# the program is provided in the hope that it will be useful, |
23 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
24 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
25 |
# GNU Affero General Public License for more details. |
26 |
# |
27 |
# You should have received a copy of the GNU Affero General Public |
28 |
# License with the Debian GNU/Linux or Univention distribution in file |
29 |
# /usr/share/common-licenses/AGPL-3; if not, see |
30 |
# <http://www.gnu.org/licenses/>. |
31 |
|
32 |
name = 'delete-deactivated-master' |
33 |
description = 'Stores the timestamp when a user is deactivated' |
34 |
filter = '(&(|(&(objectClass=posixAccount)(objectClass=shadowAccount))(objectClass=univentionMail)(objectClass=sambaSamAccount)(objectClass=simpleSecurityObject)(&(objectClass=person)(objectClass=organizationalPerson)(objectClass=inetOrgPerson)))(!(uidNumber=0))(!(uid=*$)))' |
35 |
attributes = ['krb5KDCFlags', 'shadowExpire', 'sambaAcctFlags'] |
36 |
modrdn = '1' |
37 |
|
38 |
import listener |
39 |
import univention.debug |
40 |
import univention.uldap |
41 |
import datetime |
42 |
import time |
43 |
|
44 |
UCRV_ACTIVATED = 'xxxxxx/delete/deactivated' |
45 |
UCRV_BLACKLIST = 'xxxxxx/delete/deactivated/blacklist' |
46 |
UCRV_DAYS = 'xxxxxx/delete/deactivated/days' |
47 |
UCRV_MAIL_ERROR = 'xxxxxx/delete/deactivated/mailaddress/error' |
48 |
|
49 |
def userIsDeactivated(user): |
50 |
if not user.get('krb5KDCFlags', []) == ['254']: |
51 |
return False |
52 |
if not user.get('shadowExpire', []) == ['1']: |
53 |
return False |
54 |
if not user.get('sambaAcctFlags', []) in (['[UD ]'], ['[ULD ]'], ): |
55 |
return False |
56 |
return True |
57 |
|
58 |
def encodeTimestamp(timestamp): |
59 |
return (timestamp - datetime.timedelta(10000)).strftime('%Y%m%d%H%M%SZ') |
60 |
|
61 |
def decodeTimestamp(text): |
62 |
return datetime.date(*time.strptime(text, '%Y%m%d%H%M%SZ')[:3]) + datetime.timedelta(10000) |
63 |
|
64 |
def now(): |
65 |
return datetime.date(*time.gmtime()[:3]) |
66 |
|
67 |
def userHasDeactivationTimestamp(user): |
68 |
return user.get('krb5ValidEnd') and decodeTimestamp(user['krb5ValidEnd'][0]) <= now() |
69 |
|
70 |
def userHasExpired(user, expiryAfterDays): |
71 |
if not expiryAfterDays: |
72 |
return False |
73 |
return decodeTimestamp(user['krb5ValidEnd'][0]) + datetime.timedelta(expiryAfterDays) <= now() |
74 |
|
75 |
import email.MIMEText |
76 |
import smtplib |
77 |
def errorMail(message, user): |
78 |
textMessage = 'Hallo,\n\nxxxxxx-delete-deactivated meldet folgendes Problem:\n' |
79 |
textMessage+= message.strip() + '\n' |
80 |
textMessage+= '\n\nDie Attribute des Benutzers waren:\n{\n' |
81 |
for (key, value, ) in user.items(): |
82 |
textMessage+= '%r: %r,' % (key, value, ) |
83 |
textMessage+= '}\n' |
84 |
mailMessage = email.MIMEText.MIMEText(message, 'plain', 'utf-8') |
85 |
mailMessage['Subject'] = message |
86 |
mailMessage['To'] = listener.baseConfig.get(UCRV_MAIL_ERROR, 'root') |
87 |
mailMessage['From'] = 'root@' + listener.baseConfig.get('hostname', 'localhost') |
88 |
s = smtplib.SMTP('localhost') |
89 |
s.sendmail(mailMessage['From'], [mailMessage['To']], mailMessage.as_string()) |
90 |
s.quit() |
91 |
|
92 |
def userIsBlacklisted(dn): |
93 |
if UCRV_BLACKLIST not in listener.baseConfig: |
94 |
return False |
95 |
blacklist = listener.baseConfig[UCRV_BLACKLIST].split('|') |
96 |
return dn in blacklist |
97 |
|
98 |
def handler(dn, new, old, command): |
99 |
if listener.baseConfig.get(UCRV_ACTIVATED, '').lower() not in ('yes', 'true', '1', 'enable', 'enabled', 'on'): |
100 |
univention.debug.debug(univention.debug.LISTENER, univention.debug.INFO, "[delete-deactivated-master] disabled via UCRV " + UCRV_ACTIVATED) |
101 |
return |
102 |
univention.debug.debug(univention.debug.LISTENER, univention.debug.INFO, "[delete-deactivated-master] command == %r" % (command, )) |
103 |
if userIsBlacklisted(dn): |
104 |
univention.debug.debug(univention.debug.LISTENER, univention.debug.INFO, "[delete-deactivated-master] ignoring blacklisted %r" % (dn, )) |
105 |
return |
106 |
if command == 'm': # object modified |
107 |
if old and new: # modification |
108 |
univention.debug.debug(univention.debug.LISTENER, univention.debug.INFO, "[delete-deactivated-master] userIsDeactivated(old) == %s userIsDeactivated(new) == %s" % (userIsDeactivated(old), userIsDeactivated(new), )) |
109 |
if not userIsDeactivated(old) and userIsDeactivated(new): # activated → deactivated |
110 |
postrun.actions.append( ('deactivated', dn, ) ) |
111 |
if userIsDeactivated(old) and not userIsDeactivated(new): # deactivated → activated |
112 |
postrun.actions.append( ( 'activated', dn, ) ) |
113 |
else: |
114 |
univention.debug.debug(univention.debug.LISTENER, univention.debug.ERROR, "[delete-deactivated-master] command == 'm' and not (old and new)! old=%r new=%r" % (old, new, )) |
115 |
elif command == 'd': # object deleted |
116 |
if userHasDeactivationTimestamp(old): # deleted correctly (==automatically) |
117 |
expiryAfterDays = None |
118 |
try: |
119 |
expiryAfterDays = max(0, int(listener.baseConfig.get(UCRV_DAYS))) |
120 |
except (TypeError, ValueError, ): |
121 |
pass |
122 |
univention.debug.debug(univention.debug.LISTENER, univention.debug.INFO, "[delete-deactivated-master] userHasExpired(old, %r) == %r" % (expiryAfterDays, userHasExpired(old, expiryAfterDays), )) |
123 |
if userHasExpired(old, expiryAfterDays): |
124 |
pass # don't care (the other listeners process automatic user deletion) |
125 |
else: |
126 |
univention.debug.debug(univention.debug.LISTENER, univention.debug.WARN, "[delete-deactivated-master] user %r deleted before expiry" % (dn, )) |
127 |
errorMail('Benutzer %r wurde vor Ablauf gelöscht' % (dn, ), old) |
128 |
else: # deleted incorrectly (==manually) |
129 |
univention.debug.debug(univention.debug.LISTENER, univention.debug.WARN, "[delete-deactivated-master] user %r deleted incorrectly" % (dn, )) |
130 |
errorMail('Benutzer %r wurde manuell gelöscht' % (dn, ), old) |
131 |
elif command == 'r': # object "deleted" for rename |
132 |
pass # don't care |
133 |
elif command == 'a': # object added || object "added" for rename (last was 'r') |
134 |
pass # don't care |
135 |
elif command == 'n': # initial/resync |
136 |
pass # don't care |
137 |
else: |
138 |
univention.debug.debug(univention.debug.LISTENER, univention.debug.ERROR, "[delete-deactivated-master] command == %r is unknown!" % (command, )) |
139 |
|
140 |
def postrun(): |
141 |
univention.debug.debug(univention.debug.LISTENER, univention.debug.INFO, "[delete-deactivated-master] postrun.actions == %r" % (postrun.actions, )) |
142 |
actions = {} |
143 |
for (action, dn, ) in postrun.actions: |
144 |
actions[dn] = action |
145 |
postrun.actions = [] |
146 |
for (dn, action, ) in actions.items(): |
147 |
if not ldap_info['lo']: |
148 |
ldap_reconnect() |
149 |
if action == 'deactivated': |
150 |
changes = [('krb5ValidEnd', ldap_info['lo'].getAttr(dn, 'krb5ValidEnd'), [encodeTimestamp(now())], ), |
151 |
('shadowExpire', ldap_info['lo'].getAttr(dn, 'shadowExpire'), ['1'], )] |
152 |
elif action == 'activated': |
153 |
changes = [('krb5ValidEnd', ldap_info['lo'].getAttr(dn, 'krb5ValidEnd'), [], ), |
154 |
('shadowExpire', ldap_info['lo'].getAttr(dn, 'shadowExpire'), [], )] |
155 |
if ldap_info['lo']: |
156 |
ldap_info['lo'].modify(dn, changes) |
157 |
else: |
158 |
univention.debug.debug(univention.debug.LISTENER, univention.debug.ERROR, "[delete-deactivated-master] ldap_info['lo'] is %r! Could not set timestamp for %r" % (ldap_info['lo'], dn, )) |
159 |
postrun.actions = [] |
160 |
|
161 |
def clean(): |
162 |
pass |
163 |
|
164 |
def initialize(): |
165 |
pass |
166 |
|
167 |
ldap_info = {} |
168 |
def ldap_reconnect(): |
169 |
univention.debug.debug(univention.debug.LISTENER, univention.debug.INFO, '[delete-deactivated-master] ldap reconnect triggered') |
170 |
if 'ldapserver' in ldap_info and 'basedn' in ldap_info and 'binddn' in ldap_info and 'bindpw' in ldap_info: |
171 |
try: |
172 |
ldap_info['lo'] = univention.uldap.access(host = ldap_info['ldapserver'], |
173 |
base = ldap_info['basedn'], |
174 |
binddn = ldap_info['binddn'], |
175 |
bindpw = ldap_info['bindpw'], |
176 |
start_tls = 2) |
177 |
except ValueError, ex: |
178 |
univention.debug.debug(univention.debug.LISTENER, univention.debug.ERROR, '[delete-deactivated-master] ldap reconnect failed: %s' % str(ex)) |
179 |
ldap_info['lo'] = None |
180 |
else: |
181 |
if ldap_info['lo'] == None: |
182 |
univention.debug.debug(univention.debug.LISTENER, univention.debug.ERROR, '[delete-deactivated-master] ldap reconnect failed') |
183 |
|
184 |
def setdata(key, value): |
185 |
if key == 'bindpw': |
186 |
univention.debug.debug(univention.debug.LISTENER, univention.debug.INFO, '[delete-deactivated-master] listener passed key="%s" value="<HIDDEN>"' % key) |
187 |
else: |
188 |
univention.debug.debug(univention.debug.LISTENER, univention.debug.INFO, '[delete-deactivated-master] listener passed key="%s" value="%s"' % (key, value)) |
189 |
if key in [ 'ldapserver', 'basedn', 'binddn', 'bindpw' ]: |
190 |
ldap_info[ key ] = value |
191 |
else: |
192 |
univention.debug.debug(univention.debug.LISTENER, univention.debug.INFO, '[delete-deactivated-master] listener passed unknown data (key="%s" value="%s")' % (key, value)) |
193 |
if key == 'ldapserver': |
194 |
univention.debug.debug(univention.debug.LISTENER, univention.debug.INFO, '[delete-deactivated-master] ldap server changed to %s' % value) |
195 |
ldap_reconnect() |