]> git.uio.no Git - usit-rt.git/blob - local/bin/rt-update-groups
Initial commit 4.0.5-3
[usit-rt.git] / local / bin / rt-update-groups
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
14 use lib '/www/data/rt/rt-perl/current-perl10/share/perl5';
15 use lib '/www/data/rt/rt-perl/current-perl10/lib/perl5';
16 use lib '/www/data/rt/rt-perl/current-perl10/lib64/perl5';
17
18 use lib ("/www/var/rt/local/lib", "/www/var/rt/lib");
19 use strict;
20 use warnings;
21
22 use Getopt::Std;
23 use Net::LDAP qw(LDAP_SUCCESS LDAP_PARTIAL_RESULTS);
24 use Net::LDAP::Util qw(ldap_error_name);
25 use RT::Interface::CLI qw(CleanEnv);
26 use RT::User;
27
28 my %opts;
29
30 getopts("dp", \%opts);
31
32 # Map from RT to netgroup group
33 my %groupmap;
34
35 CleanEnv();
36 RT::LoadConfig();
37 RT::Init();
38
39 my $LdapServer = "ldap.uio.no";
40 my $LdapNGBase = "cn=netgroups,cn=system,dc=uio,dc=no";
41 my %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
57 for 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
109 if ($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 #
132 sub 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
149 sub 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
193 sub 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
207 sub 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
220 sub 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
234 sub 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
245 sub 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
255 sub 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
276 sub 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