aboutsummaryrefslogtreecommitdiffstats
path: root/extensions/Mageia/lib/Util.pm
diff options
context:
space:
mode:
Diffstat (limited to 'extensions/Mageia/lib/Util.pm')
-rw-r--r--extensions/Mageia/lib/Util.pm146
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;