View | Details | Raw Unified | Return to bug 27929
Collapse All | Expand All

(-)a/branches/ucs-3.0/ucs/management/univention-directory-manager-modules/scripts/proof_uniqueMembers (-138 / +203 lines)
 Lines 1-8    Link Here 
1
#!/usr/bin/python2.6
1
#!/usr/bin/python2.6
2
# -*- coding: utf-8 -*-
2
# -*- coding: utf-8 -*-
3
# pylint: disable-msg=C0103
3
#
4
#
4
# Univention Directory Manager Modules
5
# Univention Directory Manager Modules
5
#  check if users are member of their primary group
6
"""Check if users are member of their primary group."""
6
#
7
#
7
# Copyright 2004-2012 Univention GmbH
8
# Copyright 2004-2012 Univention GmbH
8
#
9
#
 Lines 31-174    Link Here 
31
# /usr/share/common-licenses/AGPL-3; if not, see
32
# /usr/share/common-licenses/AGPL-3; if not, see
32
# <http://www.gnu.org/licenses/>.
33
# <http://www.gnu.org/licenses/>.
33
34
34
35
import ldap
35
import ldap, string
36
import sys
36
import sys
37
import univention.baseconfig
37
from univention.config_registry import ConfigRegistry
38
import pprint
39
from optparse import OptionParser
38
from optparse import OptionParser
40
39
41
baseConfig=univention.baseconfig.baseConfig()
42
baseConfig.load()
43
binddn= "cn=admin," + baseConfig['ldap/base']
44
basedn = baseConfig['ldap/base']
45
count_changes = 0
46
warning = 0
47
pp = pprint.PrettyPrinter(indent=4)
48
49
50
51
parser = OptionParser()
52
parser.add_option("-b", "--base-dn", help="ldap base dn", dest="basedn", action="store", type="str")
53
(options, args) = parser.parse_args()
54
55
def ldapbind (binddn):
56
	bindpw=open('/etc/ldap.secret').read()
57
	bindpw = bindpw.split("\n")[0]
58
	lo=ldap.open('localhost', 7389)
59
	try:
60
		lo.simple_bind_s(binddn, bindpw)
61
	except:
62
		print "could not bind to %s" % binddn
63
		sys.exit(1)
64
65
	return lo
66
67
68
lo = ldapbind(binddn)
69
70
71
if options.basedn: basedn = options.basedn
72
73
74
# GID's will only be found in posixAccount
75
try:
76
	res_pA=lo.search_s(basedn, ldap.SCOPE_SUBTREE, 'objectClass=posixAccount', ['gidNumber', 'uid'])
77
except ldap.NO_SUCH_OBJECT:
78
	print "ldap search in %s failed (no such base dn)" % basedn
79
	sys.exit(1)
80
81
print "\n  proof if users are member of their primary group"
82
for i in range(0,len(res_pA)):
83
	gidNumber_pA=''
84
	dn_pA=res_pA[i][0]
85
	memberUid_pA = res_pA[i][1]['uid'][0]
86
	if res_pA[i][1].has_key('gidNumber'):
87
		gidNumber_pA=res_pA[i][1]['gidNumber'][0]
88
	else:
89
		print "Warning, posixAccount without gidNumber",res_pA[i]
90
		warning = warning + 1
91
92
	# search corresponding group
93
	res_uG=lo.search_s(basedn, ldap.SCOPE_SUBTREE, '(&(objectClass=univentionGroup)(gidNumber='+gidNumber_pA+"))", ['uniqueMember', 'memberUid'])
94
95
	# there must be excactly one group with this gid
96
	if len(res_uG)>1:
97
		print "Warning: found more than one univentionGroup for",dn_pA,"gidNumber",gidNumber_pA,"!"
98
		warning = warning + 1
99
	if len(res_uG)<1 and not gidNumber_pA=="0":
100
		print "Warning: found no univentionGroup for",dn_pA,"gidNumber",gidNumber_pA,"!"
101
		warning = warning +1
102
	# well change all if there are more -- the user needs to delete all but one of them
103
	for group in res_uG:
104
		dn_uG = group[0]
105
		# look for the needed entry
106
		uniqueMembers_uG = group[1].get('uniqueMember', [])
107
		memberUids_uG = group[1].get('memberUid', [])
108
		modlist = []
109
		for el in uniqueMembers_uG:
110
			if el == dn_pA:
111
				break
112
		else:
113
			modlist.append((ldap.MOD_ADD,'uniqueMember',dn_pA))
114
115
		for el in memberUids_uG:
116
			if el == memberUid_pA:
117
				break
118
		else:
119
			modlist.append((ldap.MOD_ADD,'memberUid',memberUid_pA))
120
		# no entry found, gonna add one
121
		if modlist:
122
			print "add uniqueMember and memberUid entry for",dn_pA,"in",dn_uG
123
			try:
124
				lo.modify_s(dn_uG,modlist)
125
				count_changes = count_changes +1
126
			except:
127
				warning = warning + 1
128
				print "Warning: failed to modify Group %s"%dn_uG
129
130
print "  proof of",len(res_pA),"posixAccounts finished, changed",count_changes,"of them.\n"
131
132
count_changes=0
133
print "  proof if group-members exist"
134
res_pG=lo.search_s(basedn, ldap.SCOPE_SUBTREE, 'objectClass=posixGroup', ['uniqueMember'])
135
for i in range(0,len(res_pG)):
136
	dn_pG = res_pG[i][0]
137
	members = []
138
	if res_pG[i][1].has_key('uniqueMember'):
139
		members = res_pG[i][1]['uniqueMember']
140
141
	remmembers=[]
142
	
143
	for member in members:
144
		n=string.find(member,',')
145
		base=member[n+1:]
146
		filter=member[:n]
147
148
		try:
149
			res_pU=lo.search_s(base, ldap.SCOPE_ONELEVEL, filter, ['objectClass'])
150
			if len(res_pU) > 1:
151
				print "Warning: more than one object found while searching for %s of group %s -- clear manually"%(member,dn_pG)
152
				warning+=1
153
			elif len(res_pU) < 1:
154
				print "  no object found while searching for %s, will be removed"%member
155
				remmembers.append(member)
156
		except:
157
			print "Warning: Search for member %s of group %s failed -- clear manually"%(member,dn_pG)
158
			warning+=1
159
160
161
	for member in remmembers:
162
		modlist = [(ldap.MOD_DELETE,'uniqueMember',member)]
163
		try:
164
			lo.modify_s(dn_pG,modlist)
165
			count_changes = count_changes +1
166
		except:
167
			print "Warning: failed to remove %s from Group %s"%(member,dn_pG)
168
			warning = warning + 1
169
			
170
print "  proof of",len(res_pG),"posixGroups finished, changed",count_changes,"of them.\n"
171
172
173
if warning: print "There were %s warning(s) !"%warning
174
40
41
def info(msg, *args, **kwargs):
42
    """Print info."""
43
    print msg % (args or kwargs)
44
45
46
def warn(msg, *args, **kwargs):
47
    """Print warning."""
48
    print >> sys.stderr, 'Warning: ' + (msg % (args or kwargs))
49
    warn.warnings += 1
50
warn.warnings = 0
51
52
53
def fatal(msg, *args, **kwargs):
54
    """Print error."""
55
    print >> sys.stderr, 'Error: ' + (msg % (args or kwargs))
56
    sys.exit(1)
57
58
59
def ldapbind(basedn):
60
    """Open local LDAP connection and do bind."""
61
    binddn = "cn=admin,%s" % (basedn,)
62
    try:
63
        bindpw = open('/etc/ldap.secret').read().rstrip('\n')
64
    except IOError:
65
        fatal('Could no read /etc/ldap.secrer')
66
    conn = ldap.open('localhost', 7389)
67
    try:
68
        conn.simple_bind_s(binddn, bindpw)
69
    except ldap.LDAPError:
70
        fatal("Could not bind to %s", binddn)
71
72
    return conn
73
74
75
def check_primary(conn, basedn):
76
    """Check if users are member of their primary group."""
77
    info("Checking if users are member of their primary group...")
78
    try:
79
        # GID's will only be found in posixAccount
80
        user_result = conn.search_s(basedn, ldap.SCOPE_SUBTREE,
81
                '(objectClass=posixAccount)', ['gidNumber', 'uid'])
82
    except ldap.NO_SUCH_OBJECT:
83
        fatal("ldap search in %s failed (no such base dn)", basedn)
84
    count_changes = 0
85
    for user_dn, account in user_result:
86
        user_uid = account['uid'][0]
87
        user_gid = account.get('gidNumber', [])[0]
88
        if not user_gid:
89
            warn("posixAccount without gidNumber: %s", user_dn)
90
91
        # search corresponding group
92
        group_result = conn.search_s(basedn, ldap.SCOPE_SUBTREE,
93
                '(&(objectClass=univentionGroup)(gidNumber=%s))' % (user_gid,),
94
                ['uniqueMember', 'memberUid'])
95
96
        # there must be exactly one group with this gid
97
        if len(group_result) > 1:
98
            warn("found more than one univentionGroup for gidNumber=%s!",
99
                    user_gid)
100
        elif len(group_result) < 1 and not user_gid == "0":
101
            warn("found no univentionGroup for gidNumber=%s!", user_gid)
102
        # we change them all -- the user needs to delete all but one of them
103
        for group_dn, group in group_result:
104
            # look for the needed entry
105
            group_member_dns = group.get('uniqueMember', [])
106
            group_member_uids = group.get('memberUid', [])
107
            modlist = []
108
            if user_dn not in group_member_dns:
109
                modlist.append((ldap.MOD_ADD, 'uniqueMember', user_dn))
110
            if user_uid not in group_member_uids:
111
                modlist.append((ldap.MOD_ADD, 'memberUid', user_uid))
112
            # no entry found, gonna add one
113
            if modlist:
114
                info("Adding uniqueMember and memberUid entry for '%s' in '%s'",
115
                        user_dn, group_dn)
116
                try:
117
                    conn.modify_s(group_dn, modlist)
118
                    count_changes += 1
119
                except ldap.LDAPError:
120
                    warn("failed to modify group %s", group_dn)
121
    info("Checked %d posixAccounts, fixed %d issues.",
122
            len(user_result), count_changes)
123
124
125
def check_groups(conn, basedn):
126
    """Check if members of group exist."""
127
    info("Checking if group-members exist...")
128
    try:
129
        group_result = conn.search_s(basedn, ldap.SCOPE_SUBTREE,
130
                '(objectClass=posixGroup)', ['uniqueMember', 'memberUid'])
131
    except ldap.NO_SUCH_OBJECT:
132
        fatal("ldap search in %s failed (no such base dn)", basedn)
133
134
    count_changes = 0
135
    for group_dn, group in group_result:
136
        count_changes += check_groups_by_dn(conn, group_dn, group)
137
        count_changes += check_groups_by_uid(conn, basedn, group_dn, group)
138
139
    info("Checked %d posixGroups, fixed %d issues.",
140
            len(group_result), count_changes)
141
142
143
def check_groups_by_dn(conn, group_dn, group):
144
    """Check by 'uniqueMember'."""
145
    group_member_dns = group.get('uniqueMember', [])
146
    count_changes = 0
147
    remmembers = set()
148
    for member_dn in group_member_dns:
149
        # Split uid=USER, cn=user,dc=FQDN
150
        member_filter, base = member_dn.split(',', 1)
151
        try:
152
            member_result = conn.search_s(base, ldap.SCOPE_ONELEVEL,
153
                    member_filter, ['objectClass'])
154
        except ldap.LDAPError:
155
            warn("Manual: Search for member DN '%s' of group '%s' failed",
156
                    member_dn, group_dn)
157
        else:
158
            if len(member_result) > 1:
159
                warn("Manual: Multiple members for DN '%s' of group '%s'",
160
                        member_dn, group_dn)
161
            elif len(member_result) < 1:
162
                warn("No member for DN '%s', will be removed", member_dn)
163
                remmembers.add(member_dn)
164
    for member_dn in remmembers:
165
        info("Removing member DN '%s' from '%s'", member_dn, group_dn)
166
        modlist = [(ldap.MOD_DELETE, 'uniqueMember', member_dn)]
167
        try:
168
            conn.modify_s(group_dn, modlist)
169
            count_changes += 1
170
        except ldap.LDAPError:
171
            warn("failed to remove DN '%s' from group '%s'",
172
                    member_dn, group_dn)
173
    return count_changes
174
175
176
def check_groups_by_uid(conn, basedn, group_dn, group):
177
    """Check by 'memberUid'."""
178
    group_member_uids = group.get('memberUid', [])
179
    count_changes = 0
180
    remmembers = set()
181
    for member_uid in group_member_uids:
182
        try:
183
            member_result = conn.search_s(basedn, ldap.SCOPE_SUBTREE,
184
                    '(uid=%s)' % (member_uid,), ['objectClass'])
185
        except ldap.LDAPError:
186
            warn("Manual: Search for member UID '%s' of group '%s' failed",
187
                    member_uid, group_dn)
188
        else:
189
            if len(member_result) > 1:
190
                warn("Manual: Multiple members for UID '%s' of group '%s'",
191
                        member_uid, group_dn)
192
            elif len(member_result) < 1:
193
                warn("No member for UID '%s', will be removed", member_uid)
194
                remmembers.add(member_uid)
195
    for member_uid in remmembers:
196
        info("Removing member UID '%s' from '%s'", member_uid, group_dn)
197
        modlist = [(ldap.MOD_DELETE, 'memberUid', member_uid)]
198
        try:
199
            conn.modify_s(group_dn, modlist)
200
            count_changes += 1
201
        except ldap.LDAPError:
202
            warn("Failed to remove UID '%s' from group '%s'",
203
                    member_uid, group_dn)
204
    return count_changes
205
206
207
def main():
208
    """Check group membership."""
209
    parser = OptionParser()
210
    parser.add_option("-b", "--base-dn",
211
            dest="basedn", action="store",
212
            help="ldap base DN for user search")
213
    parser.add_option("-c", "--check",
214
            dest="check", action="store_true",
215
            help="Only check, do not modify")
216
    (options, _args) = parser.parse_args()
217
218
    ucr = ConfigRegistry()
219
    ucr.load()
220
    basedn = ucr['ldap/base']
221
222
    conn = ldapbind(basedn)
223
224
    if options.basedn:
225
        basedn = options.basedn
226
    if options.check:
227
        conn.modify_s = lambda dn, modlist: None
228
229
    check_primary(conn, basedn)
230
    check_groups(conn, basedn)
231
    if warn.warnings:
232
        info("There were %d warning(s)!", warn.warnings)
233
        sys.exit(2)
234
    else:
235
        sys.exit(0)
236
237
238
if __name__ == '__main__':
239
    main()

Return to bug 27929