diff options
author | Byron Jones <bjones@mozilla.com> | 2013-01-23 09:27:03 +0100 |
---|---|---|
committer | Byron Jones <bjones@mozilla.com> | 2013-01-23 09:27:03 +0100 |
commit | f2a29007f96f33da77440cd10f475c2af946573e (patch) | |
tree | cc8d4772bf7ab8eac52cfe5f5646c125c983e7c1 | |
parent | 3c50104166023a136d0e4cee22322904521eae87 (diff) | |
download | bugzilla-f2a29007f96f33da77440cd10f475c2af946573e.tar.gz bugzilla-f2a29007f96f33da77440cd10f475c2af946573e.tar.xz |
Bug 812433: create a report for auditing bugzilla security group membership
10 files changed, 248 insertions, 16 deletions
diff --git a/extensions/BMO/Extension.pm b/extensions/BMO/Extension.pm index c35e5688f..2681b9a41 100644 --- a/extensions/BMO/Extension.pm +++ b/extensions/BMO/Extension.pm @@ -62,7 +62,8 @@ use Bugzilla::Extension::BMO::Reports qw(user_activity_report group_admins_report email_queue_report release_tracking_report - group_membership_report); + group_membership_report + group_members_report); our $VERSION = '0.1'; @@ -180,6 +181,9 @@ sub page_before_template { elsif ($page eq 'group_membership.html' or $page eq 'group_membership.txt') { group_membership_report($page, $vars); } + elsif ($page eq 'group_members.html' or $page eq 'group_members.json') { + group_members_report($vars); + } elsif ($page eq 'email_queue.html') { email_queue_report($vars); } diff --git a/extensions/BMO/lib/Reports.pm b/extensions/BMO/lib/Reports.pm index ce307d9ff..cf663dcd9 100644 --- a/extensions/BMO/lib/Reports.pm +++ b/extensions/BMO/lib/Reports.pm @@ -13,6 +13,7 @@ use Bugzilla::Extension::BMO::Data qw($cf_disabled_flags); use Bugzilla::Constants; use Bugzilla::Error; use Bugzilla::Field; +use Bugzilla::Group; use Bugzilla::User; use Bugzilla::Util qw(trim detaint_natural trick_taint correct_urlbase); @@ -28,7 +29,8 @@ our @EXPORT_OK = qw(user_activity_report group_admins_report email_queue_report release_tracking_report - group_membership_report); + group_membership_report + group_members_report); sub user_activity_report { my ($vars) = @_; @@ -559,9 +561,9 @@ sub group_admins_report { my $dbh = Bugzilla->dbh; my $user = Bugzilla->user; - $user->in_group('editusers') - || ThrowUserError('auth_failure', { group => 'editusers', - action => 'run', + ($user->in_group('editusers') || $user->in_group('infrasec')) + || ThrowUserError('auth_failure', { group => 'editusers', + action => 'run', object => 'group_admins' }); my $query = " @@ -598,8 +600,8 @@ sub group_membership_report { my $cgi = Bugzilla->cgi; ($user->in_group('editusers') || $user->in_group('infrasec')) - || ThrowUserError('auth_failure', { group => 'editusers', - action => 'run', + || ThrowUserError('auth_failure', { group => 'editusers', + action => 'run', object => 'group_admins' }); my $who = $cgi->param('who'); @@ -693,6 +695,92 @@ sub group_membership_report { $vars->{'users'} = \@users; } +sub group_members_report { + my ($vars) = @_; + my $dbh = Bugzilla->dbh; + my $user = Bugzilla->user; + my $cgi = Bugzilla->cgi; + + ($user->in_group('editusers') || $user->in_group('infrasec')) + || ThrowUserError('auth_failure', { group => 'editusers', + action => 'run', + object => 'group_admins' }); + + my $include_disabled = $cgi->param('include_disabled') ? 1 : 0; + $vars->{'include_disabled'} = $include_disabled; + + # don't allow all groups, to avoid putting pain on the servers + my @group_names = + sort + grep { !/^(?:bz_.+|canconfirm|editbugs|everyone)$/ } + map { lc($_->name) } + Bugzilla::Group->get_all; + unshift(@group_names, ''); + $vars->{'groups'} = \@group_names; + + # load selected group + my $group = lc(trim($cgi->param('group') // '')); + $group = '' unless grep { $_ eq $group } @group_names; + return if $group eq ''; + my $group_obj = Bugzilla::Group->new({ name => $group }); + $vars->{'group'} = $group; + + # direct members + my @types = ( + { + name => 'direct', + members => _filter_userlist($group_obj->members_direct, $include_disabled), + }, + ); + + # indirect members, by group + foreach my $member_group (sort @{ $group_obj->grant_direct(GROUP_MEMBERSHIP) }) { + push @types, { + name => $member_group->name, + members => _filter_userlist($member_group->members_direct, $include_disabled), + }, + } + + # make it easy for the template to detect an empty group + my $has_members = 0; + foreach my $type (@types) { + $has_members += scalar(@{ $type->{members} }); + last if $has_members; + } + @types = () unless $has_members; + + if (@types) { + # add last-login + my $user_ids = join(',', map { map { $_->id } @{ $_->{members} } } @types); + my $tokens = $dbh->selectall_hashref(" + SELECT profiles.userid, + (SELECT DATEDIFF(curdate(), logincookies.lastused) lastseen + FROM logincookies + WHERE logincookies.userid = profiles.userid + ORDER BY lastused DESC + LIMIT 1) lastseen + FROM profiles + WHERE userid IN ($user_ids)", + 'userid'); + foreach my $type (@types) { + foreach my $member (@{ $type->{members} }) { + $member->{lastseen} = + defined $tokens->{$member->id}->{lastseen} + ? $tokens->{$member->id}->{lastseen} + : '>' . MAX_LOGINCOOKIE_AGE; + } + } + } + + $vars->{'types'} = \@types; +} + +sub _filter_userlist { + my ($list, $include_disabled) = @_; + $list = [ grep { $_->is_enabled } @$list ] unless $include_disabled; + return [ sort { lc($a->identity) cmp lc($b->identity) } @$list ]; +} + sub email_queue_report { my ($vars, $filter) = @_; my $dbh = Bugzilla->dbh; diff --git a/extensions/BMO/template/en/default/hook/reports/menu-end.html.tmpl b/extensions/BMO/template/en/default/hook/reports/menu-end.html.tmpl index dae7f9108..35644c1e4 100644 --- a/extensions/BMO/template/en/default/hook/reports/menu-end.html.tmpl +++ b/extensions/BMO/template/en/default/hook/reports/menu-end.html.tmpl @@ -24,19 +24,22 @@ <a href="[% urlbase FILTER none %]page.cgi?id=release_tracking_report.html">Release Tracking Report</a> </strong> - For triaging release-train flag information. </li> - [% IF user.in_group('editusers') %] + [% IF user.in_group('editusers') || user.in_group('infrasec') %] <li> <strong> <a href="[% urlbase FILTER none %]page.cgi?id=group_admins.html">Group Admins</a> - </strong> - Group Admins Report + </strong> - Lists the administrators of each group. </li> - [% END %] - [% IF user.in_group('editusers') || user.in_group('infrasec') %] <li> <strong> <a href="[% urlbase FILTER none %]page.cgi?id=group_membership.html">Group Membership Report</a> </strong> - Lists the groups a user is a member of. </li> + <li> + <strong> + <a href="[% urlbase FILTER none %]page.cgi?id=group_members.html">Group Members Report</a> + </strong> - Lists the users of groups. + </li> [% END %] [% IF user.in_group('admin') || user.in_group('infra') %] <li> diff --git a/extensions/BMO/template/en/default/pages/email_queue.html.tmpl b/extensions/BMO/template/en/default/pages/email_queue.html.tmpl index 0e4a37551..5c7970506 100644 --- a/extensions/BMO/template/en/default/pages/email_queue.html.tmpl +++ b/extensions/BMO/template/en/default/pages/email_queue.html.tmpl @@ -15,7 +15,7 @@ <p><i>[% jobs.size FILTER none %] email(s) in the queue.</i></p> - <table id="report" cellspacing="0" border="0"> + <table id="report" class="hover" cellspacing="0" border="0"> <tr id="report-header"> <th>Insert Time</th> <th>Run Time</th> diff --git a/extensions/BMO/template/en/default/pages/group_admins.html.tmpl b/extensions/BMO/template/en/default/pages/group_admins.html.tmpl index 1afcdb0b8..01bb744c4 100644 --- a/extensions/BMO/template/en/default/pages/group_admins.html.tmpl +++ b/extensions/BMO/template/en/default/pages/group_admins.html.tmpl @@ -25,7 +25,7 @@ %] [% IF groups.size > 0 %] - <table border="0" cellspacing="0" id="report" width="100%"> + <table border="0" cellspacing="0" id="report" class="hover" width="100%"> <tr id="report-header"> <th align="left">Name</th> <th align="left">Admins</th> diff --git a/extensions/BMO/template/en/default/pages/group_members.html.tmpl b/extensions/BMO/template/en/default/pages/group_members.html.tmpl new file mode 100644 index 000000000..daf4d5b0d --- /dev/null +++ b/extensions/BMO/template/en/default/pages/group_members.html.tmpl @@ -0,0 +1,97 @@ +[%# 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. + #%] + +[% INCLUDE global/header.html.tmpl + title = "Group Members Report" + style_urls = [ "extensions/BMO/web/styles/reports.css" ] +%] + +<form method="GET" action="page.cgi"> + <input type="hidden" name="id" value="group_members.html"> + + <table id="parameters"> + <tr> + <th>Group</th> + <td> + <select name="group"> + [% FOREACH group_name = groups %] + <option value="[% group_name FILTER html %]" + [% "selected" IF group_name == group %]> + [% group_name FILTER html %]</option> + [% END %] + </select> + <input type="checkbox" name="include_disabled" id="include_disabled" + value="1" [% "checked" IF include_disabled %]> + <label for="include_disabled"> + Include disabled users + </label> + <input type="submit" value="Generate"> + </td> + </tr> + </table> +</form> + +[% IF group != '' %] + + <p> + Members of the <b>[% group FILTER html %]</b> group: + </p> + + [% IF types.size > 0 %] + <table border="0" cellspacing="0" id="report" class="nohover" width="100%"> + <tr id="report-header"> + <th>Type</th> + <th>Count</th> + <th>Members</th> + <th class="right">Last Seen (days ago)</th> + </tr> + + [% FOREACH type = types %] + [% count = loop.count() %] + <tr class="report_item [% count % 2 == 1 ? "report_row_odd" : "report_row_even" %]"> + <td valign="top"> + [% "via " UNLESS type.name == 'direct' %] + [% type.name FILTER html %] + </td> + <td valign="top" align="right"> + [% type.members.size FILTER html %] + </td> + <td valign="top" width="100%" colspan="2"> + <table cellspacing="0" class="hoverrow"> + [% FOREACH member = type.members %] + <tr> + <td width="100%"> + <a href="editusers.cgi?action=edit&userid=[% member.id FILTER none %]" + target="_blank"> + <span [% 'class="bz_inactive"' UNLESS member.is_enabled %]> + [% member.name FILTER html %] <[% member.email FILTER email FILTER html %]> + </span> + </a> + </td> + <td align="right" nowrap> + [% member.lastseen FILTER html %] + </td> + </tr> + [% END %] + </table> + </td> + </tr> + [% END %] + </table> + + <a href="page.cgi?id=group_members.json&group=[% group FILTER uri %] + [% IF include_disabled %]&include_disabled=1[% END %]">JSON</a> + [% ELSE %] + <p> + <i>This group is empty.</i> + </p> + [% END %] + +[% END %] + +[% INCLUDE global/footer.html.tmpl %] diff --git a/extensions/BMO/template/en/default/pages/group_members.json.tmpl b/extensions/BMO/template/en/default/pages/group_members.json.tmpl new file mode 100644 index 000000000..f80fc8c5f --- /dev/null +++ b/extensions/BMO/template/en/default/pages/group_members.json.tmpl @@ -0,0 +1,32 @@ +[%# 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. + #%] + +[ + [% SET count = 0 %] + [% FOREACH type = types %] + [% SET count = count + type.members.size %] + [% END %] + [% SET i = 0 %] + [% FOREACH type = types %] + [% FOREACH member = type.members %] + [% SET i = i + 1 %] + { "login": "[% member.login FILTER email FILTER js %]", + [% IF type.name == "direct" %] + "membership": "direct", + [% ELSE %] + "membership": "indirect", + "group": [% type.name FILTER js %]", + [% END %] + [% IF include_disabled %] + "disabled": "[% member.is_enabled ? "false" : "true" %]", + [% END %] + "lastseen": "[% member.lastseen FILTER js %]" + }[% "," UNLESS i == count %] + [% END %] + [% END %] +] diff --git a/extensions/BMO/template/en/default/pages/group_membership.html.tmpl b/extensions/BMO/template/en/default/pages/group_membership.html.tmpl index 2680c7da2..32484b13f 100644 --- a/extensions/BMO/template/en/default/pages/group_membership.html.tmpl +++ b/extensions/BMO/template/en/default/pages/group_membership.html.tmpl @@ -48,7 +48,7 @@ [% IF users.size %] - <table border="0" cellspacing="0" id="report" width="100%"> + <table border="0" cellspacing="0" id="report" class="hover" width="100%"> [% FOREACH u = users %] <tr> <th colspan="3">[% u.user.identity FILTER html %]</th> diff --git a/extensions/BMO/template/en/default/pages/user_activity.html.tmpl b/extensions/BMO/template/en/default/pages/user_activity.html.tmpl index 377d7c244..f299b862b 100644 --- a/extensions/BMO/template/en/default/pages/user_activity.html.tmpl +++ b/extensions/BMO/template/en/default/pages/user_activity.html.tmpl @@ -106,7 +106,7 @@ [% IF operations.size > 0 %] <br> - <table border="1" cellpadding="4" cellspacing="0" id="report"> + <table border="1" cellpadding="4" cellspacing="0" id="report" class="hover"> <tr id="report-header"> [% IF who_count > 1 %] <th>Who</th> diff --git a/extensions/BMO/web/styles/reports.css b/extensions/BMO/web/styles/reports.css index 2a6bc54fc..7ad0df241 100644 --- a/extensions/BMO/web/styles/reports.css +++ b/extensions/BMO/web/styles/reports.css @@ -23,6 +23,10 @@ text-align: left; } +#report th.right { + text-align: right; +} + #report th.sorted { text-decoration: underline; } @@ -41,7 +45,7 @@ color: #000000; } -#report tr:hover { +#report.hover tr:hover { background-color: #ccccff; } @@ -56,3 +60,7 @@ .disabled { color: #888888; } + +.hoverrow tr:hover { + background-color: #ccccff; +} |