]> git.uio.no Git - usit-rt.git/blame - local/bin/rt-update-groups
Needed some cleanup.
[usit-rt.git] / local / bin / rt-update-groups
CommitLineData
84fb5b46
MKG
1#!/usr/bin/perl
2#
3# Author: Petter Reinholdtsen
4# Date: 2005-06-29
5#
6# Synchronize netgroups and RT groups, and make sure only users with
7# group membership are privileged users.
8#
9# The list of groups to synchronize is generated by looking for the
10# regex (netgroupsync: .+) in the group description.
11
12# Location of RT's libs and scripts
13# Remember to change to correct path on current RT instance
14use lib '/www/data/rt/rt-perl/current-perl10/share/perl5';
15use lib '/www/data/rt/rt-perl/current-perl10/lib/perl5';
16use lib '/www/data/rt/rt-perl/current-perl10/lib64/perl5';
17
18use lib ("/www/var/rt/local/lib", "/www/var/rt/lib");
19use strict;
20use warnings;
21
22use Getopt::Std;
23use Net::LDAP qw(LDAP_SUCCESS LDAP_PARTIAL_RESULTS);
24use Net::LDAP::Util qw(ldap_error_name);
25use RT::Interface::CLI qw(CleanEnv);
26use RT::User;
27
28my %opts;
29
30getopts("dp", \%opts);
31
32# Map from RT to netgroup group
33my %groupmap;
34
35CleanEnv();
36RT::LoadConfig();
37RT::Init();
38
39my $LdapServer = "ldap.uio.no";
40my $LdapNGBase = "cn=netgroups,cn=system,dc=uio,dc=no";
41my %system_users = ('root' => "Superuser",
42 'rt-user' => "Internal user");
43
44# Merge static and dynamic list until all synced groups in RT have
45# updated comments.
46%groupmap = (%groupmap, get_synced_groups());
47
48# Sync netgroup and RT group. The netgroup is authorative. Do this
49# often.
50#
51# - If a user exist in RT, and is listed in the netgroup but not in
52# the RT group, give it privilege rights and add it to the group.
53#
54# - If a is listed in the RT group, but not in the netgroup, remove
55# the user from the RT group.
56
57for my $rtgroupname (sort keys %groupmap) {
58 my $netgroupname = $groupmap{$rtgroupname};
59 print "Updating RT group $rtgroupname from netgroup $netgroupname\n"
60 if $opts{'d'};
61 my %in_netgroup;
62 map { $in_netgroup{$_} = 1 if defined} netgroup_users($netgroupname);
63
64 # First, revoke membership for users missing in the netgroup
65 my $users = get_users_in_rtgroup($rtgroupname);
66 while (my $user = $users->Next) {
67 my $username = $user->Name;
68 next if ($system_users{$username});
69
70 if ( ! $in_netgroup{$username}) {
71 my ($status, $statusmsg) = del_rtgroupmember($rtgroupname, $user);
72 if (0 != $status) {
73 my $msg = "Removed user '$username' as member ".
74 "of group $rtgroupname";
75 $RT::Logger->info("$msg"); print "$msg\n" if $opts{'d'};
76 } else {
77 my $msg = "Failed do remove user '$username' as member ".
78 "of group $rtgroupname";
79 $RT::Logger->info("$msg"); print "$msg\n" if $opts{'d'};
80 }
81 }
82 }
83
84 # Next, make sure all users in the netgroup are members of the rt group
85 my $user = new RT::User($RT::SystemUser);
86 for my $username (sort keys %in_netgroup) {
87 $user->Load($username);
88#
89# if ($user->Id && !is_rtgroupmember($rtgroupname, $user)) {
90#
91# There is someting strange about the RT api - something has changed in RT4
92# Need to look into it ref https://rt.uio.no/Ticket/Display.html?id=954612
93
94 if ($user->Id) {
95 my $msg = "Adding user '$username' as member to group $rtgroupname";
96 $RT::Logger->info("$msg"); print "$msg\n" if $opts{'d'};
97 add_rtgroupmember($rtgroupname, $user);
98 }
99 }
100}
101
102# Remove privilege rights from privileged users without any user group
103# membership. Do this at night, to reduce the chance of conflict with
104# manual group updates.
105
106# - Loop over all privileged users, check if they are member of at
107# least one group
108
109if ($opts{'p'}) {
110 my $users = get_users_in_rtgroup("privileged");
111 while (my $user = $users->Next) {
112 my $username = $user->Name;
113 next if ($system_users{$username});
114 if (! is_member_of_any_group($user)) {
115 del_rtgroupmember("privileged", $user);
116 my ($status, $statusmsg) = del_rtgroupmember("privileged", $user);
117 if (0 != $status) {
118 my $msg = "Made user '$username' non-privileged";
119 $RT::Logger->info("$msg"); print "$msg\n" if $opts{'d'};
120 } else {
121 my $msg = "Failed to make user '$username' non-privileged";
122 $RT::Logger->info("$msg"); print "$msg\n" if $opts{'d'};
123 }
124 }
125 }
126}
127
128#
129# Locate all groups with "(netgroupsync: <group>)" as part of the
130# group description, return a hash with netgroupname => rtgroupname.
131#
132sub get_synced_groups {
133 my %groupmap;
134
135 my $Groups = new RT::Groups($RT::SystemUser);
136 $Groups->LimitToUserDefinedGroups();
137 $Groups->LimitToEnabled();
138
139 while ( my $Group = $Groups->Next) {
140 next unless ($Group->Description =~ m/\(netgroupsync: *(\S+)\)/);
141 my $netgroupname = $1;
142 $groupmap{$Group->Name} = $netgroupname;
143 }
144 return %groupmap;
145}
146
147
148
149sub netgroup_users {
150 my $group = shift;
151 my $ldap = LdapConnect();
152 my @users;
153 my %groups;
154 my $mesg = $ldap->search(
155 base => $LdapNGBase,
156 scope => "one",
157 filter => "cn=$group",
158 attributes => ["nisNetgroupTriple","memberNisNetgroup"],
159 );
160 if ( ($mesg->code != LDAP_SUCCESS) and
161 ($mesg->code != LDAP_PARTIAL_RESULTS) ) {
162 $RT::Logger->critical("rt-group-sync: Search failed: ",
163 "retval=", $mesg->code, " ",
164 ldap_error_name($mesg->code));
165 LdapDisconnect($ldap);
166 return undef;
167 }
168
169 if (1 != $mesg->count) {
170 LdapDisconnect($ldap);
171 return undef;
172 }
173
174 while (my $entry = $mesg->shift_entry){
175 for ($entry->get_value("memberNisNetgroup")) {
176 $groups{$_} = 0 unless $groups{$_};
177 }
178 for ($entry->get_value("nisNetgroupTriple")) {
179 m/\(,(.*?),\)/;
180 push (@users, $1);
181 }
182 }
183 for (keys %groups) {
184 push (@users, netgroup_users ($_)) unless $groups{$_};
185 $groups{$_}=1;
186 }
187 LdapDisconnect($ldap);
188 chomp @users;
189 return keys %{{ map { $_ => 1 } @users }};
190}
191
192
193sub get_users_in_rtgroup {
194 my $rtgroupname = shift;
195 my $users;
196 if ("privileged" eq $rtgroupname) {
197 $users = new RT::Users($RT::SystemUser);
198 $users->LimitToPrivileged();
199 } else {
200 my $group = RT::Group->new($RT::SystemUser);
201 $group->LoadUserDefinedGroup($rtgroupname);
202 $users = $group->UserMembersObj()
203 }
204 return $users;
205}
206
207sub is_rtgroupmember {
208 my ($rtgroupname, $user) = @_;
209 return undef unless ($user->Id);
210 if ("privileged" eq $rtgroupname) {
211 return $user->Privileged;
212 } else {
213 my $group = RT::Group->new($RT::SystemUser);
214 $group->LoadUserDefinedGroup($rtgroupname);
215 return 1 if ($group->HasMember($user->PrincipalObj));
216 }
217 return undef;
218}
219
220sub add_rtgroupmember {
221 my ($rtgroupname, $user) = @_;
222 if ("privileged" eq $rtgroupname) {
223 $user->SetPrivileged(1);
224 } else {
225 # Make sure user is privileged, to make it possible to add it
226 # to a group.
227 $user->SetPrivileged(1);
228 my $group = RT::Group->new($RT::SystemUser);
229 $group->LoadUserDefinedGroup($rtgroupname);
230 $group->AddMember($user->id);
231 }
232}
233
234sub del_rtgroupmember {
235 my ($rtgroupname, $user) = @_;
236 if ("privileged" eq $rtgroupname) {
237 return $user->SetPrivileged(0);
238 } else {
239 my $group = RT::Group->new($RT::SystemUser);
240 $group->LoadUserDefinedGroup($rtgroupname);
241 return $group->DeleteMember($user->id);
242 }
243}
244
245sub is_member_of_any_group {
246 my $user = shift;
247 my $groups = RT::Groups->new($RT::SystemUser);
248 $groups->LimitToUserDefinedGroups();
249 $groups->WithMember(PrincipalId => $user->id, Recursively => 1);
250 return ($groups->Count > 0)
251};
252
253
254
255sub LdapConnect {
256
257 my $mesg;
258 my $ldap = Net::LDAP->new($LdapServer,
259 version => 3);
260
261 unless ($ldap) {
262 $RT::Logger->critical("rt-group-sync: Cannot connect to",
263 "LDAP server ", $LdapServer);
264 return undef;
265 }
266 $mesg = $ldap->bind;
267 if ($mesg->code != LDAP_SUCCESS) {
268 $RT::Logger->critical("rt-group-sync: Cannot bind to LDAP: ",
269 "retval=", $mesg->code, " ",
270 ldap_error_name($mesg->code));
271 return undef;
272 }
273 return $ldap;
274}
275
276sub LdapDisconnect {
277 my $ldap = shift;
278 my $mesg = $ldap->unbind();
279 if ($mesg->code != LDAP_SUCCESS) {
280 $RT::Logger->critical("LdapDisconnect: unbind failed: ",
281 "retval=", $mesg->code, " ",
282 ldap_error_name($mesg->code));
283 }
284}
285