3 # Author: Petter Reinholdtsen
6 # Synchronize netgroups and RT groups, and make sure only users with
7 # group membership are privileged users.
9 # The list of groups to synchronize is generated by looking for the
10 # regex (netgroupsync: .+) in the group description.
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';
18 use lib ("/www/var/rt/local/lib", "/www/var/rt/lib");
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);
30 getopts("dp", \%opts);
32 # Map from RT to netgroup group
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");
44 # Merge static and dynamic list until all synced groups in RT have
46 %groupmap = (%groupmap, get_synced_groups());
48 # Sync netgroup and RT group. The netgroup is authorative. Do this
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.
54 # - If a is listed in the RT group, but not in the netgroup, remove
55 # the user from the RT group.
57 for my $rtgroupname (sort keys %groupmap) {
58 my $netgroupname = $groupmap{$rtgroupname};
59 print "Updating RT group $rtgroupname from netgroup $netgroupname\n"
62 map { $in_netgroup{$_} = 1 if defined} netgroup_users($netgroupname);
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});
70 if ( ! $in_netgroup{$username}) {
71 my ($status, $statusmsg) = del_rtgroupmember($rtgroupname, $user);
73 my $msg = "Removed user '$username' as member ".
74 "of group $rtgroupname";
75 $RT::Logger->info("$msg"); print "$msg\n" if $opts{'d'};
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'};
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);
89 # if ($user->Id && !is_rtgroupmember($rtgroupname, $user)) {
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
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);
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.
106 # - Loop over all privileged users, check if they are member of at
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);
118 my $msg = "Made user '$username' non-privileged";
119 $RT::Logger->info("$msg"); print "$msg\n" if $opts{'d'};
121 my $msg = "Failed to make user '$username' non-privileged";
122 $RT::Logger->info("$msg"); print "$msg\n" if $opts{'d'};
129 # Locate all groups with "(netgroupsync: <group>)" as part of the
130 # group description, return a hash with netgroupname => rtgroupname.
132 sub get_synced_groups {
135 my $Groups = new RT::Groups($RT::SystemUser);
136 $Groups->LimitToUserDefinedGroups();
137 $Groups->LimitToEnabled();
139 while ( my $Group = $Groups->Next) {
140 next unless ($Group->Description =~ m/\(netgroupsync: *(\S+)\)/);
141 my $netgroupname = $1;
142 $groupmap{$Group->Name} = $netgroupname;
151 my $ldap = LdapConnect();
154 my $mesg = $ldap->search(
157 filter => "cn=$group",
158 attributes => ["nisNetgroupTriple","memberNisNetgroup"],
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);
169 if (1 != $mesg->count) {
170 LdapDisconnect($ldap);
174 while (my $entry = $mesg->shift_entry){
175 for ($entry->get_value("memberNisNetgroup")) {
176 $groups{$_} = 0 unless $groups{$_};
178 for ($entry->get_value("nisNetgroupTriple")) {
184 push (@users, netgroup_users ($_)) unless $groups{$_};
187 LdapDisconnect($ldap);
189 return keys %{{ map { $_ => 1 } @users }};
193 sub get_users_in_rtgroup {
194 my $rtgroupname = shift;
196 if ("privileged" eq $rtgroupname) {
197 $users = new RT::Users($RT::SystemUser);
198 $users->LimitToPrivileged();
200 my $group = RT::Group->new($RT::SystemUser);
201 $group->LoadUserDefinedGroup($rtgroupname);
202 $users = $group->UserMembersObj()
207 sub is_rtgroupmember {
208 my ($rtgroupname, $user) = @_;
209 return undef unless ($user->Id);
210 if ("privileged" eq $rtgroupname) {
211 return $user->Privileged;
213 my $group = RT::Group->new($RT::SystemUser);
214 $group->LoadUserDefinedGroup($rtgroupname);
215 return 1 if ($group->HasMember($user->PrincipalObj));
220 sub add_rtgroupmember {
221 my ($rtgroupname, $user) = @_;
222 if ("privileged" eq $rtgroupname) {
223 $user->SetPrivileged(1);
225 # Make sure user is privileged, to make it possible to add it
227 $user->SetPrivileged(1);
228 my $group = RT::Group->new($RT::SystemUser);
229 $group->LoadUserDefinedGroup($rtgroupname);
230 $group->AddMember($user->id);
234 sub del_rtgroupmember {
235 my ($rtgroupname, $user) = @_;
236 if ("privileged" eq $rtgroupname) {
237 return $user->SetPrivileged(0);
239 my $group = RT::Group->new($RT::SystemUser);
240 $group->LoadUserDefinedGroup($rtgroupname);
241 return $group->DeleteMember($user->id);
245 sub is_member_of_any_group {
247 my $groups = RT::Groups->new($RT::SystemUser);
248 $groups->LimitToUserDefinedGroups();
249 $groups->WithMember(PrincipalId => $user->id, Recursively => 1);
250 return ($groups->Count > 0)
258 my $ldap = Net::LDAP->new($LdapServer,
262 $RT::Logger->critical("rt-group-sync: Cannot connect to",
263 "LDAP server ", $LdapServer);
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));
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));