From ceefacd41df24ddb2c41e235a01938f479e40a22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Buclin?= Date: Mon, 9 Oct 2017 20:42:12 +0200 Subject: Bug 899: Sync Bugzilla groups with LDAP groups --- extensions/Mageia/Config.pm | 6 ++ extensions/Mageia/Extension.pm | 17 +++- extensions/Mageia/lib/Util.pm | 100 ++++++++++++++++++++- extensions/Mageia/sync_LDAP_groups.pl | 27 ++++++ .../admin/sanitycheck/messages-statuses.html.tmpl | 27 ++++++ 5 files changed, 175 insertions(+), 2 deletions(-) create mode 100755 extensions/Mageia/sync_LDAP_groups.pl create mode 100644 extensions/Mageia/template/en/default/hook/admin/sanitycheck/messages-statuses.html.tmpl diff --git a/extensions/Mageia/Config.pm b/extensions/Mageia/Config.pm index ce0eae39d..492bdab11 100644 --- a/extensions/Mageia/Config.pm +++ b/extensions/Mageia/Config.pm @@ -14,6 +14,12 @@ use warnings; use constant NAME => 'Mageia'; use constant REQUIRED_MODULES => [ + # Required to sync LDAP groups with Bugzilla groups. + { + package => 'perl-ldap', + module => 'Net::LDAP', + version => 0 + } ]; use constant OPTIONAL_MODULES => [ diff --git a/extensions/Mageia/Extension.pm b/extensions/Mageia/Extension.pm index 982a3bdb7..0c5efb02c 100644 --- a/extensions/Mageia/Extension.pm +++ b/extensions/Mageia/Extension.pm @@ -18,7 +18,7 @@ use Bugzilla::Bug qw(LogActivityEntry); use Bugzilla::Field qw(get_field_id); use Bugzilla::User qw(); use Bugzilla::User::Setting qw(add_setting); -use Bugzilla::Extension::Mageia::Util qw(compare_datetimes); +use Bugzilla::Extension::Mageia::Util qw(compare_datetimes sync_ldap_groups_check); use Email::Address; use Encode qw(encode); @@ -136,6 +136,21 @@ sub mailer_before_send { } } +sub sanitycheck_check { + my ($self, $args) = @_; + &{$args->{status}}('ldap_check_group_membership'); + sync_ldap_groups_check($args->{status}); +} + +sub sanitycheck_repair { + my ($self, $args) = @_; + if (Bugzilla->cgi->param('sync_ldap_groups')) { + &{$args->{status}}('ldap_repair_start'); + sync_ldap_groups_check($args->{status}, 1); + &{$args->{status}}('ldap_repair_end'); + } +} + sub template_before_process { my ($self, $args) = @_; _inline_history($args) if $args->{file} eq 'bug/comments.html.tmpl'; diff --git a/extensions/Mageia/lib/Util.pm b/extensions/Mageia/lib/Util.pm index 60447fa28..11f4fcaf6 100644 --- a/extensions/Mageia/lib/Util.pm +++ b/extensions/Mageia/lib/Util.pm @@ -11,9 +11,17 @@ use 5.10.1; 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); +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 @@ -41,4 +49,94 @@ sub compare_datetimes { 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. + my $bz_user = Bugzilla::User->new({ extern_id => $uid }) or next; + $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; diff --git a/extensions/Mageia/sync_LDAP_groups.pl b/extensions/Mageia/sync_LDAP_groups.pl new file mode 100755 index 000000000..77a99ee9a --- /dev/null +++ b/extensions/Mageia/sync_LDAP_groups.pl @@ -0,0 +1,27 @@ +#!/usr/bin/perl -T +# 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. + +use 5.14.0; +use strict; +use warnings; + +use lib qw(. lib); + +use Bugzilla; +BEGIN { Bugzilla->extensions() } +use Bugzilla::Extension::Mageia::Util qw(sync_ldap_groups_check); + +# See Status() in sanitycheck.cgi. +sub status { + my ($san_tag, $vars, $alert) = @_; + return unless $alert && $san_tag eq 'ldap_sync_alert'; + + say 'LDAP error: ' . $vars->{ldap_error}; +} + +sync_ldap_groups_check(\&status, 1); diff --git a/extensions/Mageia/template/en/default/hook/admin/sanitycheck/messages-statuses.html.tmpl b/extensions/Mageia/template/en/default/hook/admin/sanitycheck/messages-statuses.html.tmpl new file mode 100644 index 000000000..19155048b --- /dev/null +++ b/extensions/Mageia/template/en/default/hook/admin/sanitycheck/messages-statuses.html.tmpl @@ -0,0 +1,27 @@ +[%# 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. + #%] + +[% IF san_tag == "ldap_check_group_membership" %] + Checking group membership for LDAP groups. +[% ELSIF san_tag == "ldap_repair_start" %] + OK, now fixing Bugzilla group memberships based on LDAP groups. +[% ELSIF san_tag == "ldap_repair_end" %] + Bugzilla group memberships synchronization completed. +[% ELSIF san_tag == "ldap_sync_alert" %] + LDAP error: [% ldap_error FILTER html %] +[% ELSIF san_tag == "ldap_sync_group_mismatch_alert" %] + Group [% group FILTER html %] is out of sync:
+ - [% old_users.size %] users are no longer in this group and should be removed: + [%+ old_users.join(", ") FILTER html %].
+ - [% new_users.size %] users are not yet in this group and should be added: + [%+ new_users.join(", ") FILTER html %]. +[% ELSIF san_tag == "ldap_sync_repair_link" %] + Synchronize Bugzilla groups + with LDAP groups (LDAP -> Bugzilla). +[% END %] -- cgit v1.2.1