38 |
39 |
39 sub import(@) { |
40 sub import(@) { |
40 $Cf = shift; |
41 $Cf = shift; |
41 |
42 |
42 require constant; |
43 require constant; |
43 import constant OU_ACCOUNTS => $Cf->ldap_ou_accounts; |
44 import constant OU_ACCOUNTS => $Cf->ldap_ou_accounts; |
44 import constant OU_ALIASES => $Cf->ldap_ou_aliases; |
45 import constant OU_ALIASES => $Cf->ldap_ou_aliases; |
45 import constant OU_GROUPS => $Cf->ldap_ou_groups; |
46 import constant OU_GROUPS => $Cf->ldap_ou_groups; |
46 import constant OC_RECIPIENT => $Cf->ldap_oc_recipient; |
47 import constant OC_RECIPIENT => $Cf->ldap_oc_recipient; |
47 import constant OC_ACCESSGROUP => $Cf->ldap_oc_accessgroup; |
48 import constant OC_ACCESSGROUP => $Cf->ldap_oc_accessgroup; |
48 import constant AT_PRIMARYADDRESS => $Cf->ldap_at_primaryaddress; |
49 import constant AT_PRIMARYADDRESS => $Cf->ldap_at_primaryaddress; |
49 import constant AT_ADDRESS => $Cf->ldap_at_address; |
50 import constant AT_ADDRESS => $Cf->ldap_at_address; |
50 import constant AT_GROUP => $Cf->ldap_at_group; |
51 import constant AT_GROUP => $Cf->ldap_at_group; |
51 import constant AT_FORWARDINGADDRESS => $Cf->ldap_at_forwardingaddress; |
52 import constant AT_FORWARDINGADDRESS => $Cf->ldap_at_forwardingaddress; |
52 import constant AT_MEMBERUID => "memberUid"; |
53 import constant AT_MEMBERUID => "memberUid"; |
53 |
54 |
54 $gbase = OU_GROUPS . "," . $Cf->ldap_base; |
55 $gbase = OU_GROUPS . "," . $Cf->ldap_base; |
55 $ubase = OU_ACCOUNTS . "," . $Cf->ldap_base; |
56 $ubase = OU_ACCOUNTS . "," . $Cf->ldap_base; |
56 $abase = OU_ALIASES . "," . $Cf->ldap_base; |
57 $abase = OU_ALIASES . "," . $Cf->ldap_base; |
57 } |
58 } |
58 |
59 |
59 sub run($) { |
60 sub run($) { |
|
61 |
60 # Eigentlich brauchen wir für alles imap und ldap |
62 # Eigentlich brauchen wir für alles imap und ldap |
61 $ldap = new Net::LDAP $Cf->ldap_server or die; |
63 $ldap = new Net::LDAP $Cf->ldap_server or die; |
62 my $r = $ldap->bind($Cf->ldap_bind_dn, |
64 my $r = $ldap->bind( $Cf->ldap_bind_dn, |
63 password => $Cf->ldap_password || $ENV{LDAP_PASS} || password::ask("LDAP (". $Cf->ldap_bind_dn .") password: ")); |
65 password => $Cf->ldap_password |
|
66 || $ENV{LDAP_PASS} |
|
67 || password::ask( "LDAP (" . $Cf->ldap_bind_dn . ") password: " ) ); |
64 die $r->error, "\n" if $r->code; |
68 die $r->error, "\n" if $r->code; |
65 |
69 |
66 $imap = new Cyrus::IMAP::Admin or die $@; |
70 $imap = new Cyrus::IMAP::Admin or die $@; |
67 $imap->authenticate(-server => $Cf->imap_server, -user => $Cf->imap_admin, |
71 $imap->authenticate( |
68 -password => $Cf->imap_password || $ENV{IMAP_PASS} || password::ask("IMAP (". $Cf->imap_admin .") password: ")) |
72 -server => $Cf->imap_server, |
69 or die $@; |
73 -user => $Cf->imap_admin, |
70 |
74 -password => $Cf->imap_password |
71 |
75 || $ENV{IMAP_PASS} |
72 if ($Cf->list) { _list() } |
76 || password::ask( "IMAP (" . $Cf->imap_admin . ") password: " ) |
73 elsif ($Cf->add) { _add() } |
77 ) or die $@; |
74 elsif ($Cf->delete) { _delete() } |
78 |
75 elsif ($Cf->modify) { _modify() } |
79 if ( $Cf->list ) { _list() } |
76 else { die "Need action (--add|--modify|--list|--delete)\n" }; |
80 elsif ( $Cf->add ) { _add() } |
|
81 elsif ( $Cf->delete ) { _delete() } |
|
82 elsif ( $Cf->modify ) { _modify() } |
|
83 else { die "Need action (--add|--modify|--list|--delete)\n" } |
77 |
84 |
78 } |
85 } |
79 |
86 |
80 sub _add() { |
87 sub _add() { |
81 # Beim Hinzufügen tragen wir nur das unbedingt notwendige |
88 |
82 # ein. Wenn es schon eine mailPrimaryAddress gibt oder eine |
89 # Beim Hinzufügen tragen wir nur das unbedingt notwendige |
83 # mail, machen wir gar nichts. |
90 # ein. Wenn es schon eine mailPrimaryAddress gibt oder eine |
84 # Ansonsten: |
91 # mail, machen wir gar nichts. |
85 # uid wird hinzugefügt |
92 # Ansonsten: |
86 # cn, sn bleiben unangetastet |
93 # uid wird hinzugefügt |
87 # Wenn die mailbox-Option gesetzt ist, wird die |
94 # cn, sn bleiben unangetastet |
88 # IMAP-Mailbox angelegt. |
95 # Wenn die mailbox-Option gesetzt ist, wird die |
89 |
96 # IMAP-Mailbox angelegt. |
90 |
97 |
91 die "Need group name for creation\n" if not @ARGV; |
98 die "Need group name for creation\n" if not @ARGV; |
92 my $group = shift @ARGV; |
99 my $group = shift @ARGV; |
93 my @members = split /,/, $Cf->members||""; |
100 my @members = split /,/, $Cf->members || ""; |
94 |
|
95 |
101 |
96 my $dn = "cn=$group,$gbase"; |
102 my $dn = "cn=$group,$gbase"; |
97 my $r; |
103 my $r; |
98 |
104 |
99 verbose("$group:\n"); |
105 verbose("$group:\n"); |
100 |
106 |
101 verbose("\t$dn..."); |
107 verbose("\t$dn..."); |
102 |
108 |
103 $r = $ldap->search(base => $gbase, filter => "(cn=$group)"); |
109 $r = $ldap->search( base => $gbase, filter => "(cn=$group)" ); |
104 |
110 |
105 die $r->error if $r->code; |
111 die $r->error if $r->code; |
106 die "entries not expected" if $r->count > 1; |
112 die "entries not expected" if $r->count > 1; |
107 |
113 |
108 my $e; |
114 my $e; |
109 if ($r->count) { |
115 if ( $r->count ) { |
110 $e = $r->shift_entry; |
116 $e = $r->shift_entry; |
111 } else { |
117 } else { |
112 $e = new Net::LDAP::Entry; |
118 $e = new Net::LDAP::Entry; |
113 # Jetzt eine neue ID finden |
119 |
114 foreach ($Cf->gid_min .. $Cf->gid_max) { |
120 # Jetzt eine neue ID finden |
115 # ist einfach eine lineare Suche, im Augenblick weiß ich nichts |
121 foreach ( $Cf->gid_min .. $Cf->gid_max ) { |
116 # clevereres |
122 |
117 my $r = $ldap->search(base => $gbase, |
123 # ist einfach eine lineare Suche, im Augenblick weiß ich nichts |
118 filter => "(gidNumber=$_)", |
124 # clevereres |
119 attrs => []); |
125 my $r = $ldap->search( |
120 if ($r->count == 0) { |
126 base => $gbase, |
121 $e->add(gidNumber => $_); |
127 filter => "(gidNumber=$_)", |
122 last; |
128 attrs => [] |
123 } |
129 ); |
124 } |
130 if ( $r->count == 0 ) { |
125 $e->dn($dn); |
131 $e->add( gidNumber => $_ ); |
126 $e->add(cn => $group); |
132 last; |
127 } |
133 } |
128 |
134 } |
129 grep /^CYRUS MAIL ACCESS GROUP/, $e->get("description") or $e->add(description => "CYRUS MAIL ACCESS GROUP"); |
135 $e->dn($dn); |
130 |
136 $e->add( cn => $group ); |
131 if (defined $Cf->description) { |
137 } |
132 my @d = map { s/^(CYRUS MAIL ACCESS GROUP).*/"$1: ".$Cf->description/eg; $_ } $e->get("description"); |
138 |
133 $e->replace(description => \@d); |
139 grep /^CYRUS MAIL ACCESS GROUP/, $e->get("description") |
134 } |
140 or $e->add( description => "CYRUS MAIL ACCESS GROUP" ); |
135 |
141 |
136 $e->replace(objectClass => [uniq $e->get("objectClass"), OC_ACCESSGROUP, "posixGroup"]); |
142 if ( defined $Cf->description ) { |
137 $e->replace((AT_MEMBERUID) => [uniq $e->get(AT_MEMBERUID), @members]) if @members; |
143 my @d = |
|
144 map { s/^(CYRUS MAIL ACCESS GROUP).*/"$1: ".$Cf->description/eg; $_ } |
|
145 $e->get("description"); |
|
146 $e->replace( description => \@d ); |
|
147 } |
|
148 |
|
149 $e->replace( objectClass => |
|
150 [ uniq $e->get("objectClass"), OC_ACCESSGROUP, "posixGroup" ] ); |
|
151 $e->replace( (AT_MEMBERUID) => [ uniq $e->get(AT_MEMBERUID), @members ] ) |
|
152 if @members; |
138 |
153 |
139 $r = $e->update($ldap); |
154 $r = $e->update($ldap); |
140 die $r->error if $r->code; |
155 die $r->error if $r->code; |
141 |
156 |
142 verbose("ok"); |
157 verbose("ok"); |
143 verbose("\n"); |
158 verbose("\n"); |
144 } |
159 } |
145 |
160 |
146 sub _modify() { |
161 sub _modify() { |
147 # Auch hier gehen wir davon aus, daß die dn direkt aus dem User-Namen folgt: |
162 |
148 # dn: uid=USER,... |
163 # Auch hier gehen wir davon aus, daß die dn direkt aus dem User-Namen folgt: |
|
164 # dn: uid=USER,... |
149 my (@groups) = @ARGV or die "Need groupname(s)\n"; |
165 my (@groups) = @ARGV or die "Need groupname(s)\n"; |
150 |
166 |
151 my $r = $ldap->search(base => $gbase, |
167 my $r = $ldap->search( |
152 filter => $_ = "(&(objectClass=".OC_ACCESSGROUP.")(|" . join("", map { "(cn=$_)" } @ARGV) . "))"); |
168 base => $gbase, |
|
169 filter => $_ = |
|
170 "(&(objectClass=" |
|
171 . OC_ACCESSGROUP . ")(|" |
|
172 . join( "", map { "(cn=$_)" } @ARGV ) . "))" |
|
173 ); |
153 die $r->error if $r->code; |
174 die $r->error if $r->code; |
154 die "No entries found.\n" if $r->count == 0; |
175 die "No entries found.\n" if $r->count == 0; |
155 |
176 |
156 while (my $e = $r->shift_entry) { |
177 while ( my $e = $r->shift_entry ) { |
157 my $r; |
178 my $r; |
158 |
179 |
159 my $group = $e->get_value("cn"); |
180 my $group = $e->get_value("cn"); |
160 my $dn = $e->dn; |
181 my $dn = $e->dn; |
161 |
182 |
162 my $modified = 0; |
183 my $modified = 0; |
163 verbose "$group:"; |
184 verbose "$group:"; |
164 |
185 |
165 verbose "\n\t$dn..."; |
186 verbose "\n\t$dn..."; |
166 |
187 |
167 if (defined $Cf->members) { |
188 if ( defined $Cf->members ) { |
168 my @m = split /,/, $Cf->members; |
189 my @m = split /,/, $Cf->members; |
169 grep { /^[+-]/ } @m or $e->delete(AT_MEMBERUID) |
190 grep { /^[+-]/ } @m |
170 if $e->get_value(AT_MEMBERUID); |
191 or $e->delete(AT_MEMBERUID) |
171 |
192 if $e->get_value(AT_MEMBERUID); |
172 foreach my $m (@m) { |
193 |
173 if ($m =~ s/^-//) { |
194 foreach my $m (@m) { |
174 $e->delete((AT_MEMBERUID) => [$m]) |
195 if ( $m =~ s/^-// ) { |
175 } else { |
196 $e->delete( (AT_MEMBERUID) => [$m] ); |
176 $m =~ s/^\+//; |
197 } else { |
177 $e->add((AT_MEMBERUID) => [$m]) |
198 $m =~ s/^\+//; |
178 } |
199 $e->add( (AT_MEMBERUID) => [$m] ); |
179 } |
200 } |
180 $modified++; |
201 } |
181 } |
202 $modified++; |
182 |
203 } |
183 if (defined $Cf->description) { |
204 |
184 my @d = map { s/^(CYRUS MAIL ACCESS GROUP).*/"$1: ".$Cf->description/eg; $_ } $e->get("description"); |
205 if ( defined $Cf->description ) { |
185 $e->replace(description => \@d); |
206 my @d = map { |
186 $modified++; |
207 s/^(CYRUS MAIL ACCESS GROUP).*/"$1: ".$Cf->description/eg; |
187 } |
208 $_ |
188 |
209 } $e->get("description"); |
189 $e->dump if $Cf->debug; |
210 $e->replace( description => \@d ); |
190 |
211 $modified++; |
191 if ($modified) { |
212 } |
192 $r = $e->update($ldap); |
213 |
193 die $r->error.$r->code if $r->code; |
214 $e->dump if $Cf->debug; |
194 } |
215 |
195 |
216 if ($modified) { |
196 verbose "ok\n"; |
217 $r = $e->update($ldap); |
197 |
218 die $r->error . $r->code if $r->code; |
198 print "\n"; |
219 } |
|
220 |
|
221 verbose "ok\n"; |
|
222 |
|
223 print "\n"; |
199 } |
224 } |
200 } |
225 } |
201 |
226 |
202 sub _delete() { |
227 sub _delete() { |
203 # Wir gehen davon aus, daß es einen dn uid=USER,ou=.... gibt, den wir löschen können. |
228 |
204 # Wir löschen den kompletten Container. Es kann natürlich sein, daß er noch jemand anders gehört. |
229 # Wir gehen davon aus, daß es einen dn uid=USER,ou=.... gibt, den wir löschen können. |
205 # Dann ist das Pech. Um es besser zu haben, müßten wir für alles unsere eigenen |
230 # Wir löschen den kompletten Container. Es kann natürlich sein, daß er noch jemand anders gehört. |
206 # Objektklassen haben... |
231 # Dann ist das Pech. Um es besser zu haben, müßten wir für alles unsere eigenen |
207 |
232 # Objektklassen haben... |
208 if (!@ARGV) { |
233 |
209 print "Group: "; |
234 if ( !@ARGV ) { |
210 chomp($_ = <>); |
235 print "Group: "; |
211 @ARGV = ($_); |
236 chomp( $_ = <> ); |
|
237 @ARGV = ($_); |
212 } |
238 } |
213 |
239 |
214 #my $filter = "(&((cn=%s)(objectClass=".OC_ACCESSGROUP.")))"; |
240 #my $filter = "(&((cn=%s)(objectClass=".OC_ACCESSGROUP.")))"; |
215 |
241 |
216 my $r = $ldap->search(base => $gbase, |
242 my $r = $ldap->search( |
217 filter => "(&(objectClass=".OC_ACCESSGROUP.")(|" . join("", map { "(cn=$_)" } @ARGV) . "))", |
243 base => $gbase, |
218 attrs => [AT_MEMBERUID, "cn"]); |
244 filter => "(&(objectClass=" |
219 |
245 . OC_ACCESSGROUP . ")(|" |
220 if ($r->count == 0) { |
246 . join( "", map { "(cn=$_)" } @ARGV ) . "))", |
221 verbose "No objects found\n"; |
247 attrs => [ AT_MEMBERUID, "cn" ] |
222 return; |
248 ); |
223 } |
249 |
224 |
250 if ( $r->count == 0 ) { |
225 while (my $e = $r->shift_entry) { |
251 verbose "No objects found\n"; |
226 my $dn = $e->dn; |
252 return; |
227 verbose $dn; |
253 } |
228 my $r = $ldap->delete($dn); |
254 |
229 |
255 while ( my $e = $r->shift_entry ) { |
230 if ($r->code == LDAP_NO_SUCH_OBJECT) { |
256 my $dn = $e->dn; |
231 verbose("doesn't exist"); |
257 verbose $dn; |
232 } elsif ($r->code == 0) { |
258 my $r = $ldap->delete($dn); |
233 verbose(" ok"); |
259 |
234 } else { |
260 if ( $r->code == LDAP_NO_SUCH_OBJECT ) { |
235 die $r->error; |
261 verbose("doesn't exist"); |
236 } |
262 } elsif ( $r->code == 0 ) { |
237 verbose("\n"); |
263 verbose(" ok"); |
|
264 } else { |
|
265 die $r->error; |
|
266 } |
|
267 verbose("\n"); |
238 } |
268 } |
239 } |
269 } |
240 |
270 |
241 sub _list() { |
271 sub _list() { |
242 my $filter; |
272 my $filter; |
243 @ARGV = ("*") unless @ARGV; |
273 @ARGV = ("*") unless @ARGV; |
|
274 |
244 #$filter = "(|" . join("", map { "(uid=$_)" } @ARGV) . ")"; |
275 #$filter = "(|" . join("", map { "(uid=$_)" } @ARGV) . ")"; |
245 $filter = "(objectClass=".OC_ACCESSGROUP.")"; |
276 $filter = "(objectClass=" . OC_ACCESSGROUP . ")"; |
246 |
277 |
247 my $r = $ldap->search( |
278 my $r = $ldap->search( |
248 filter => $filter, |
279 filter => $filter, |
249 base => $gbase, |
280 base => $gbase, |
250 attrs => [AT_MEMBERUID, qw/cn description/], |
281 attrs => [ AT_MEMBERUID, qw/cn description/ ], |
251 ); |
282 ); |
252 die $r->error if $r->code; |
283 die $r->error if $r->code; |
253 |
284 |
254 |
285 while ( my $e = $r->shift_entry ) { |
255 while (my $e = $r->shift_entry) { |
286 my $cn = $e->get_value("cn"); |
256 my $cn = $e->get_value("cn"); |
287 my $descr = $e->get_value("description"); |
257 my $descr = $e->get_value("description"); |
288 my @uids = $e->get_value(AT_MEMBERUID); |
258 my @uids = $e->get_value(AT_MEMBERUID); |
289 |
259 |
290 print "$cn: ($descr)\n"; |
260 print "$cn: ($descr)\n"; |
291 print "\t", join "\n\t", @uids; |
261 print "\t", join "\n\t", @uids; |
292 print "\n"; |
262 print "\n"; |
|
263 } |
293 } |
264 } |
294 } |
265 |
295 |
266 sub verbose(@) { |
296 sub verbose(@) { |
267 printf STDERR @_; |
297 printf STDERR @_; |