From 6fac08b94b6cd67d8dd7ae8f7eeb5de5233c59d7 Mon Sep 17 00:00:00 2001 From: Frédéric Buclin Date: Sat, 4 Dec 2010 02:22:49 +0100 Subject: Bug 529974: Let users with local editcomponents privs manage flags for products they can administer a=LpSolit (module owner) --- editflagtypes.cgi | 215 ++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 161 insertions(+), 54 deletions(-) (limited to 'editflagtypes.cgi') diff --git a/editflagtypes.cgi b/editflagtypes.cgi index ecfa32ca8..4d11eecdb 100755 --- a/editflagtypes.cgi +++ b/editflagtypes.cgi @@ -38,7 +38,6 @@ use Bugzilla::Group; use Bugzilla::Util; use Bugzilla::Error; use Bugzilla::Product; -use Bugzilla::Component; use Bugzilla::Token; # Make sure the user is logged in and has the right privileges. @@ -46,16 +45,18 @@ my $user = Bugzilla->login(LOGIN_REQUIRED); my $cgi = Bugzilla->cgi; my $template = Bugzilla->template; -# We need this everywhere. -my $vars = get_products_and_components(); - print $cgi->header(); $user->in_group('editcomponents') + || scalar(@{$user->get_products_by_permission('editcomponents')}) || ThrowUserError("auth_failure", {group => "editcomponents", action => "edit", object => "flagtypes"}); +# We need this everywhere. +my $vars = get_products_and_components(); +my @products = @{$vars->{products}}; + my $action = $cgi->param('action') || 'list'; my $token = $cgi->param('token'); my $product = $cgi->param('product'); @@ -63,13 +64,18 @@ my $component = $cgi->param('component'); my $flag_id = $cgi->param('id'); if ($product) { - $product = Bugzilla::Product->check({ name => $product, allow_inaccessible => 1 }); + # Make sure the user is allowed to view this product name. + # Users with global editcomponents privs can see all product names. + ($product) = grep { lc($_->name) eq lc($product) } @products; + $product || ThrowUserError('product_access_denied', { name => $cgi->param('product') }); } if ($component) { ($product && $product->id) || ThrowUserError('flag_type_component_without_product'); - $component = Bugzilla::Component->check({ product => $product, name => $component }); + ($component) = grep { lc($_->name) eq lc($component) } @{$product->components}; + $component || ThrowUserError('product_unknown_component', { product => $product->name, + comp => $cgi->param('component') }); } # If 'categoryAction' is set, it has priority over 'action'. @@ -78,15 +84,30 @@ if (my ($category_action) = grep { $_ =~ /^categoryAction-(?:\w+)$/ } $cgi->para my @inclusions = $cgi->param('inclusions'); my @exclusions = $cgi->param('exclusions'); + my @categories; + if ($category_action =~ /^(in|ex)clude$/) { + if (!$user->in_group('editcomponents') && !$product) { + # The user can only add the flag type to products he can administrate. + foreach my $prod (@products) { + push(@categories, $prod->id . ':0') + } + } + else { + my $category = ($product ? $product->id : 0) . ':' . + ($component ? $component->id : 0); + push(@categories, $category); + } + } + if ($category_action eq 'include') { - my $category = ($product ? $product->id : 0) . ":" . - ($component ? $component->id : 0); - push(@inclusions, $category) unless grep($_ eq $category, @inclusions); + foreach my $category (@categories) { + push(@inclusions, $category) unless grep($_ eq $category, @inclusions); + } } elsif ($category_action eq 'exclude') { - my $category = ($product ? $product->id : 0) . ":" . - ($component ? $component->id : 0); - push(@exclusions, $category) unless grep($_ eq $category, @exclusions); + foreach my $category (@categories) { + push(@exclusions, $category) unless grep($_ eq $category, @exclusions); + } } elsif ($category_action eq 'removeInclusion') { my @inclusion_to_remove = $cgi->param('inclusion_to_remove'); @@ -101,11 +122,6 @@ if (my ($category_action) = grep { $_ =~ /^categoryAction-(?:\w+)$/ } $cgi->para } } - # Convert the array @clusions('prod_ID:comp_ID') back to a hash of - # the form %clusions{'prod_name:comp_name'} = 'prod_ID:comp_ID' - my %inclusions = clusion_array_to_hash(\@inclusions); - my %exclusions = clusion_array_to_hash(\@exclusions); - $vars->{'groups'} = [Bugzilla::Group->get_all]; $vars->{'action'} = $action; @@ -122,11 +138,13 @@ if (my ($category_action) = grep { $_ =~ /^categoryAction-(?:\w+)$/ } $cgi->para $type->{'request_group'} = {}; $type->{'request_group'}->{'name'} = $cgi->param('request_group'); - $type->{'inclusions'} = \%inclusions; - $type->{'exclusions'} = \%exclusions; + $vars->{'inclusions'} = clusion_array_to_hash(\@inclusions, \@products); + $vars->{'exclusions'} = clusion_array_to_hash(\@exclusions, \@products); + $vars->{'type'} = $type; $vars->{'token'} = $token; $vars->{'check_clusions'} = 1; + $vars->{'can_fully_edit'} = $cgi->param('can_fully_edit'); $template->process("admin/flag-type/edit.html.tmpl", $vars) || ThrowTemplateError($template->error()); @@ -165,8 +183,7 @@ if ($action eq 'list') { } # If no product is given, then show all flag types available. else { - my $flagtypes = Bugzilla::FlagType::match({ group => $group_id }); - + my $flagtypes = get_editable_flagtypes(\@products, $group_id); $bug_flagtypes = [grep { $_->target_type eq 'bug' } @$flagtypes]; $attach_flagtypes = [grep { $_->target_type eq 'attachment' } @$flagtypes]; } @@ -202,8 +219,12 @@ if ($action eq 'enter') { $vars->{'action'} = 'insert'; $vars->{'token'} = issue_session_token('add_flagtype'); - $vars->{'type'} = { 'target_type' => $type, - 'inclusions' => { '__Any__:__Any__' => '0:0' } }; + $vars->{'type'} = { 'target_type' => $type }; + # Only users with global editcomponents privs can add a flagtype + # to all products. + $vars->{'inclusions'} = { '__Any__:__Any__' => '0:0' } + if $user->in_group('editcomponents'); + $vars->{'can_fully_edit'} = 1; # Get a list of groups available to restrict this flag type against. $vars->{'groups'} = [Bugzilla::Group->get_all]; @@ -213,7 +234,19 @@ if ($action eq 'enter') { } if ($action eq 'edit' || $action eq 'copy') { - $vars->{'type'} = Bugzilla::FlagType->check({ id => $flag_id }); + my ($flagtype, $can_fully_edit) = $user->check_can_admin_flagtype($flag_id); + $vars->{'type'} = $flagtype; + $vars->{'can_fully_edit'} = $can_fully_edit; + + if ($user->in_group('editcomponents')) { + $vars->{'inclusions'} = $flagtype->inclusions; + $vars->{'exclusions'} = $flagtype->exclusions; + } + else { + # Filter products the user shouldn't know about. + $vars->{'inclusions'} = clusion_array_to_hash([values %{$flagtype->inclusions}], \@products); + $vars->{'exclusions'} = clusion_array_to_hash([values %{$flagtype->exclusions}], \@products); + } if ($action eq 'copy') { $vars->{'action'} = "insert"; @@ -249,6 +282,12 @@ if ($action eq 'insert') { my @inclusions = $cgi->param('inclusions'); my @exclusions = $cgi->param('exclusions'); + # Filter inclusion and exclusion lists to products the user can see. + unless ($user->in_group('editcomponents')) { + @inclusions = values %{clusion_array_to_hash(\@inclusions, \@products)}; + @exclusions = values %{clusion_array_to_hash(\@exclusions, \@products)}; + } + my $flagtype = Bugzilla::FlagType->create({ name => $name, description => $description, @@ -270,9 +309,9 @@ if ($action eq 'insert') { $vars->{'name'} = $flagtype->name; $vars->{'message'} = "flag_type_created"; - my @flagtypes = Bugzilla::FlagType->get_all; - $vars->{'bug_types'} = [grep { $_->target_type eq 'bug' } @flagtypes]; - $vars->{'attachment_types'} = [grep { $_->target_type eq 'attachment' } @flagtypes]; + my $flagtypes = get_editable_flagtypes(\@products); + $vars->{'bug_types'} = [grep { $_->target_type eq 'bug' } @$flagtypes]; + $vars->{'attachment_types'} = [grep { $_->target_type eq 'attachment' } @$flagtypes]; $template->process("admin/flag-type/list.html.tmpl", $vars) || ThrowTemplateError($template->error()); @@ -295,17 +334,34 @@ if ($action eq 'update') { my @inclusions = $cgi->param('inclusions'); my @exclusions = $cgi->param('exclusions'); - my $flagtype = Bugzilla::FlagType->check({ id => $flag_id }); - $flagtype->set_name($name); - $flagtype->set_description($description); - $flagtype->set_cc_list($cc_list); - $flagtype->set_sortkey($sortkey); - $flagtype->set_is_active($is_active); - $flagtype->set_is_requestable($is_requestable); - $flagtype->set_is_specifically_requestable($is_specifically); - $flagtype->set_is_multiplicable($is_multiplicable); - $flagtype->set_grant_group($grant_group); - $flagtype->set_request_group($request_group); + my ($flagtype, $can_fully_edit) = $user->check_can_admin_flagtype($flag_id); + if ($cgi->param('check_clusions') && !$user->in_group('editcomponents')) { + # Filter inclusion and exclusion lists to products the user can edit. + @inclusions = values %{clusion_array_to_hash(\@inclusions, \@products)}; + @exclusions = values %{clusion_array_to_hash(\@exclusions, \@products)}; + # Bring back the products the user cannot edit. + foreach my $item (values %{$flagtype->inclusions}) { + my ($prod_id, $comp_id) = split(':', $item); + push(@inclusions, $item) unless grep { $_->id == $prod_id } @products; + } + foreach my $item (values %{$flagtype->exclusions}) { + my ($prod_id, $comp_id) = split(':', $item); + push(@exclusions, $item) unless grep { $_->id == $prod_id } @products; + } + } + + if ($can_fully_edit) { + $flagtype->set_name($name); + $flagtype->set_description($description); + $flagtype->set_cc_list($cc_list); + $flagtype->set_sortkey($sortkey); + $flagtype->set_is_active($is_active); + $flagtype->set_is_requestable($is_requestable); + $flagtype->set_is_specifically_requestable($is_specifically); + $flagtype->set_is_multiplicable($is_multiplicable); + $flagtype->set_grant_group($grant_group); + $flagtype->set_request_group($request_group); + } $flagtype->set_clusions({ inclusions => \@inclusions, exclusions => \@exclusions}) if $cgi->param('check_clusions'); my $changes = $flagtype->update(); @@ -316,9 +372,9 @@ if ($action eq 'update') { $vars->{'changes'} = $changes; $vars->{'message'} = 'flag_type_updated'; - my @flagtypes = Bugzilla::FlagType->get_all; - $vars->{'bug_types'} = [grep { $_->target_type eq 'bug' } @flagtypes]; - $vars->{'attachment_types'} = [grep { $_->target_type eq 'attachment' } @flagtypes]; + my $flagtypes = get_editable_flagtypes(\@products); + $vars->{'bug_types'} = [grep { $_->target_type eq 'bug' } @$flagtypes]; + $vars->{'attachment_types'} = [grep { $_->target_type eq 'attachment' } @$flagtypes]; $template->process("admin/flag-type/list.html.tmpl", $vars) || ThrowTemplateError($template->error()); @@ -326,7 +382,10 @@ if ($action eq 'update') { } if ($action eq 'confirmdelete') { - $vars->{'flag_type'} = Bugzilla::FlagType->check({ id => $flag_id }); + my ($flagtype, $can_fully_edit) = $user->check_can_admin_flagtype($flag_id); + ThrowUserError('flag_type_cannot_delete', { flagtype => $flagtype }) unless $can_fully_edit; + + $vars->{'flag_type'} = $flagtype; $vars->{'token'} = issue_session_token('delete_flagtype'); $template->process("admin/flag-type/confirm-delete.html.tmpl", $vars) @@ -337,7 +396,9 @@ if ($action eq 'confirmdelete') { if ($action eq 'delete') { check_token_data($token, 'delete_flagtype'); - my $flagtype = Bugzilla::FlagType->check({ id => $flag_id }); + my ($flagtype, $can_fully_edit) = $user->check_can_admin_flagtype($flag_id); + ThrowUserError('flag_type_cannot_delete', { flagtype => $flagtype }) unless $can_fully_edit; + $flagtype->remove_from_db(); delete_token($token); @@ -357,7 +418,9 @@ if ($action eq 'delete') { if ($action eq 'deactivate') { check_token_data($token, 'delete_flagtype'); - my $flagtype = Bugzilla::FlagType->check({ id => $flag_id }); + my ($flagtype, $can_fully_edit) = $user->check_can_admin_flagtype($flag_id); + ThrowUserError('flag_type_cannot_deactivate', { flagtype => $flagtype }) unless $can_fully_edit; + $flagtype->set_is_active(0); $flagtype->update(); @@ -383,8 +446,15 @@ ThrowUserError('unknown_action', {action => $action}); sub get_products_and_components { my $vars = {}; + my $user = Bugzilla->user; - my @products = Bugzilla::Product->get_all; + my @products; + if ($user->in_group('editcomponents')) { + @products = Bugzilla::Product->get_all; + } + else { + @products = @{$user->get_products_by_permission('editcomponents')}; + } # We require all unique component names. my %components; foreach my $product (@products) { @@ -397,6 +467,29 @@ sub get_products_and_components { return $vars; } +sub get_editable_flagtypes { + my ($products, $group_id) = @_; + my $flagtypes; + + if (Bugzilla->user->in_group('editcomponents')) { + $flagtypes = Bugzilla::FlagType::match({ group => $group_id }); + return $flagtypes; + } + + my %visible_flagtypes; + foreach my $product (@$products) { + foreach my $target ('bug', 'attachment') { + my $prod_flagtypes = $product->flag_types->{$target}; + $visible_flagtypes{$_->id} ||= $_ foreach @$prod_flagtypes; + } + } + @$flagtypes = sort { $a->sortkey <=> $b->sortkey || $a->name cmp $b->name } + values %visible_flagtypes; + # Filter flag types if a group ID is given. + $flagtypes = filter_group($flagtypes, $group_id); + return $flagtypes; +} + sub filter_group { my ($flag_types, $gid) = @_; return $flag_types unless $gid; @@ -410,24 +503,38 @@ sub filter_group { # Convert the array @clusions('prod_ID:comp_ID') back to a hash of # the form %clusions{'prod_name:comp_name'} = 'prod_ID:comp_ID' sub clusion_array_to_hash { - my $array = shift; + my ($array, $visible_products) = @_; + my $user = Bugzilla->user; + my $has_privs = $user->in_group('editcomponents'); + my %hash; my %products; my %components; + foreach my $ids (@$array) { - trick_taint($ids); my ($product_id, $component_id) = split(":", $ids); my $product_name = "__Any__"; + my $component_name = "__Any__"; + if ($product_id) { - $products{$product_id} ||= new Bugzilla::Product($product_id); - $product_name = $products{$product_id}->name if $products{$product_id}; + ($products{$product_id}) = grep { $_->id == $product_id } @$visible_products; + next unless $products{$product_id}; + $product_name = $products{$product_id}->name; + + if ($component_id) { + ($components{$component_id}) = + grep { $_->id == $component_id } @{$products{$product_id}->components}; + next unless $components{$component_id}; + $component_name = $components{$component_id}->name; + } } - my $component_name = "__Any__"; - if ($component_id) { - $components{$component_id} ||= new Bugzilla::Component($component_id); - $component_name = $components{$component_id}->name if $components{$component_id}; + else { + # Users with local editcomponents privs cannot use __Any__:__Any__. + next unless $has_privs; + # It's illegal to select a component without a product. + next if $component_id; } $hash{"$product_name:$component_name"} = $ids; } - return %hash; + return \%hash; } -- cgit v1.2.3-24-g4f1b