diff options
Diffstat (limited to 'extensions/Mageia/lib/Util.pm')
-rw-r--r-- | extensions/Mageia/lib/Util.pm | 146 |
1 files changed, 146 insertions, 0 deletions
diff --git a/extensions/Mageia/lib/Util.pm b/extensions/Mageia/lib/Util.pm new file mode 100644 index 000000000..b70d39818 --- /dev/null +++ b/extensions/Mageia/lib/Util.pm @@ -0,0 +1,146 @@ +# 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; |