# This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. # # This Source Code Form is "Incompatible With Secondary Licenses", as # defined by the Mozilla Public License, v. 2.0. package Bugzilla::Extension::Mageia::Util; use 5.16.0; # required to use fc() for string comparison. use strict; use warnings; use Bugzilla::Auth::Verify::LDAP; use Bugzilla::Group; use Bugzilla::User; use Bugzilla::Util qw(diff_arrays); use parent qw(Exporter); our @EXPORT = qw(compare_datetimes sync_ldap_groups_check); # Use Mageia Robot user ID = 2122 for LDAP groups sync. use constant LDAP_SYNC_USER_ID => 2122; # This file can be loaded by your extension via # "use Bugzilla::Extension::Mageia::Util". You can put functions # used by your extension in here. (Make sure you also list them in # @EXPORT.) # Return -1 if $t1 < $t2, 0 if $t1 == $t2, 1 if $t1 > $t2, undef if a date is invalid. sub compare_datetimes { my ($t1, $t2) = @_; my (@time1, @time2); if ($t1 =~ /^(\d{4})[\.-](\d{2})[\.-](\d{2})(?: (\d{2}):(\d{2}):(\d{2}))?$/) { @time1 = ($1, $2, $3, $4, $5, $6); } if ($t2 =~ /^(\d{4})[\.-](\d{2})[\.-](\d{2})(?: (\d{2}):(\d{2}):(\d{2}))?$/) { @time2 = ($1, $2, $3, $4, $5, $6); } return undef unless scalar(@time1) && scalar(@time2); for my $i (0..5) { $t1 = $time1[$i] // 0; $t2 = $time2[$i] // 0; next if $t1 == $t2; return $t1 <=> $t2; } return 0; } sub sync_ldap_groups_check { my ($status, $repair) = @_; my $ldap = Bugzilla::Auth::Verify::LDAP->new(); $ldap->_bind_ldap_for_search; my $result = $ldap->ldap->search( base => 'ou=Group,dc=mageia,dc=org', filter => '(objectClass=groupOfNames)', attrs => ['cn', 'member'] ); if ($result->is_error) { &$status('ldap_sync_alert', { ldap_error => $result->error }, 'alert'); return; } my (%groups, %users); my $dbh = Bugzilla->dbh; my %bz_groups = map { $_->name => $_ } grep { $_->name =~ /^mga-/ } Bugzilla::Group->get_all; foreach my $ldap_group ($result->entries) { my $group = $ldap_group->get_value('cn'); next unless $bz_groups{$group}; $groups{$group} = {}; foreach my $user ($ldap_group->get_value('member')) { unless (exists $users{$user}) { my $result = $ldap->ldap->search( base => $user, scope => 'base', filter => '(objectClass=inetOrgPerson)', attrs => ['uid', 'mail'] ); if ($result->is_error) { &$status('ldap_sync_alert', { ldap_error => $result->error }, 'alert'); next; } # Some entries are not valid user accounts. my $ldap_user = $result->entry(0) or next; my $uid = $ldap_user->get_value('uid'); my $mail = $ldap_user->get_value('mail'); # Some accounts lack an email address. They cannot log into Bugzilla. next unless $uid && $mail; # Ignore the LDAP account if it does not exist in Bugzilla. # extern_id may differ from UID as UID is always lowercase, # so we also check the email address to validate the account. my $bz_user = Bugzilla::User->new({ extern_id => $uid }) || Bugzilla::User->new({ name => $mail }); next unless $bz_user && fc($bz_user->extern_id) eq fc($uid); $users{$user} = $bz_user; } $groups{$group}->{$users{$user}->extern_id} = $users{$user}; } } my $current_user = Bugzilla->user; my $ldap_sync_needs_repair = 0; if ($repair) { # We need to log in as a super user in order to update group memberships. # Bugzilla needs a valid user ID to log changes, though. my $super_user = Bugzilla::User->super_user; $super_user->{userid} = LDAP_SYNC_USER_ID; Bugzilla->set_user($super_user); } foreach my $group (sort keys %groups) { my %members = map { $_->extern_id => $_ } @{$bz_groups{$group}->members_direct}; my ($removed, $added) = diff_arrays([keys %members], [keys %{$groups{$group}}]); if ($repair) { foreach my $old_user (@$removed) { $members{$old_user}->set_groups({ remove => [$group] }); $members{$old_user}->update; } foreach my $new_user (@$added) { $groups{$group}->{$new_user}->set_groups({ add => [$group] }); $groups{$group}->{$new_user}->update; } } elsif (@$removed || @$added) { &$status('ldap_sync_group_mismatch_alert', { group => $group, old_users => $removed, new_users => $added }, 'alert'); $ldap_sync_needs_repair = 1; } } # Restore the original user. Bugzilla->set_user($current_user) if $repair; &$status('ldap_sync_repair_link') if $ldap_sync_needs_repair; } 1;