From dbfd6207290d1eee53fddec4c7c3b4aac0b2d47a Mon Sep 17 00:00:00 2001 From: David Lawrence Date: Wed, 8 Apr 2015 18:48:36 +0100 Subject: Bug 1051056: The REST API needs to be versioned so that new changes can be made that do not break compatibility r=dylan,a=glob --- Bugzilla/API/1_0/Resource/Group.pm | 636 +++++++++++++++++++++++++++++++++++++ 1 file changed, 636 insertions(+) create mode 100644 Bugzilla/API/1_0/Resource/Group.pm (limited to 'Bugzilla/API/1_0/Resource/Group.pm') diff --git a/Bugzilla/API/1_0/Resource/Group.pm b/Bugzilla/API/1_0/Resource/Group.pm new file mode 100644 index 000000000..aee8a7492 --- /dev/null +++ b/Bugzilla/API/1_0/Resource/Group.pm @@ -0,0 +1,636 @@ +# 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::API::1_0::Resource::Group; + +use 5.10.1; +use strict; +use warnings; + +use Bugzilla::API::1_0::Constants; +use Bugzilla::API::1_0::Util; + +use Bugzilla::Constants; +use Bugzilla::Error; + +use Moo; + +extends 'Bugzilla::API::1_0::Resource'; + +############## +# Constants # +############## + +use constant PUBLIC_METHODS => qw( + create + get + update +); + +use constant MAPPED_RETURNS => { + userregexp => 'user_regexp', + isactive => 'is_active' +}; + +sub REST_RESOURCES { + my $rest_resources = [ + qr{^/group$}, { + GET => { + method => 'get' + }, + POST => { + method => 'create', + success_code => STATUS_CREATED + } + }, + qr{^/group/([^/]+)$}, { + PUT => { + method => 'update', + params => sub { + my $param = $_[0] =~ /^\d+$/ ? 'ids' : 'names'; + return { $param => [ $_[0] ] }; + } + } + } + ]; + return $rest_resources; +} + +############ +# Methods # +############ + +sub create { + my ($self, $params) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + Bugzilla->user->in_group('creategroups') + || ThrowUserError("auth_failure", { group => "creategroups", + action => "add", + object => "group"}); + # Create group + my $group = Bugzilla::Group->create({ + name => $params->{name}, + description => $params->{description}, + userregexp => $params->{user_regexp}, + isactive => $params->{is_active}, + isbuggroup => 1, + icon_url => $params->{icon_url} + }); + return { id => as_int($group->id) }; +} + +sub update { + my ($self, $params) = @_; + + my $dbh = Bugzilla->dbh; + + Bugzilla->login(LOGIN_REQUIRED); + Bugzilla->user->in_group('creategroups') + || ThrowUserError("auth_failure", { group => "creategroups", + action => "edit", + object => "group" }); + + defined($params->{names}) || defined($params->{ids}) + || ThrowCodeError('params_required', + { function => 'Group.update', params => ['ids', 'names'] }); + + my $group_objects = params_to_objects($params, 'Bugzilla::Group'); + + my %values = %$params; + + # We delete names and ids to keep only new values to set. + delete $values{names}; + delete $values{ids}; + + $dbh->bz_start_transaction(); + foreach my $group (@$group_objects) { + $group->set_all(\%values); + } + + my %changes; + foreach my $group (@$group_objects) { + my $returned_changes = $group->update(); + $changes{$group->id} = translate($returned_changes, MAPPED_RETURNS); + } + $dbh->bz_commit_transaction(); + + my @result; + foreach my $group (@$group_objects) { + my %hash = ( + id => $group->id, + changes => {}, + ); + foreach my $field (keys %{ $changes{$group->id} }) { + my $change = $changes{$group->id}->{$field}; + $hash{changes}{$field} = { + removed => as_string($change->[0]), + added => as_string($change->[1]) + }; + } + push(@result, \%hash); + } + + return { groups => \@result }; +} + +sub get { + my ($self, $params) = validate(@_, 'ids', 'names', 'type'); + + Bugzilla->login(LOGIN_REQUIRED); + + # Reject access if there is no sense in continuing. + my $user = Bugzilla->user; + my $all_groups = $user->in_group('editusers') || $user->in_group('creategroups'); + if (!$all_groups && !$user->can_bless) { + ThrowUserError('group_cannot_view'); + } + + Bugzilla->switch_to_shadow_db(); + + my $groups = []; + + if (defined $params->{ids}) { + # Get the groups by id + $groups = Bugzilla::Group->new_from_list($params->{ids}); + } + + if (defined $params->{names}) { + # Get the groups by name. Check will throw an error if a bad name is given + foreach my $name (@{$params->{names}}) { + # Skip if we got this from params->{id} + next if grep { $_->name eq $name } @$groups; + + push @$groups, Bugzilla::Group->check({ name => $name }); + } + } + + if (!defined $params->{ids} && !defined $params->{names}) { + if ($all_groups) { + @$groups = Bugzilla::Group->get_all; + } + else { + # Get only groups the user has bless groups too + $groups = $user->bless_groups; + } + } + + # Now create a result entry for each. + my @groups = map { $self->_group_to_hash($params, $_) } @$groups; + return { groups => \@groups }; +} + +sub _group_to_hash { + my ($self, $params, $group) = @_; + my $user = Bugzilla->user; + + my $field_data = { + id => as_int($group->id), + name => as_string($group->name), + description => as_string($group->description), + }; + + if ($user->in_group('creategroups')) { + $field_data->{is_active} = as_boolean($group->is_active); + $field_data->{is_bug_group} = as_boolean($group->is_bug_group); + $field_data->{user_regexp} = as_string($group->user_regexp); + } + + if ($params->{membership}) { + $field_data->{membership} = $self->_get_group_membership($group, $params); + } + return $field_data; +} + +sub _get_group_membership { + my ($self, $group, $params) = @_; + my $user = Bugzilla->user; + + my %users_only; + my $dbh = Bugzilla->dbh; + my $editusers = $user->in_group('editusers'); + + my $query = 'SELECT userid FROM profiles'; + my $visibleGroups; + + if (!$editusers && Bugzilla->params->{'usevisibilitygroups'}) { + # Show only users in visible groups. + $visibleGroups = $user->visible_groups_inherited; + + if (scalar @$visibleGroups) { + $query .= qq{, user_group_map AS ugm + WHERE ugm.user_id = profiles.userid + AND ugm.isbless = 0 + AND } . $dbh->sql_in('ugm.group_id', $visibleGroups); + } + } elsif ($editusers || $user->can_bless($group->id) || $user->in_group('creategroups')) { + $visibleGroups = 1; + $query .= qq{, user_group_map AS ugm + WHERE ugm.user_id = profiles.userid + AND ugm.isbless = 0 + }; + } + if (!$visibleGroups) { + ThrowUserError('group_not_visible', { group => $group }); + } + + my $grouplist = Bugzilla::Group->flatten_group_membership($group->id); + $query .= ' AND ' . $dbh->sql_in('ugm.group_id', $grouplist); + + my $userids = $dbh->selectcol_arrayref($query); + my $user_objects = Bugzilla::User->new_from_list($userids); + my @users = + map {{ + id => as_int($_->id), + real_name => as_string($_->name), + name => as_string($_->login), + email => as_string($_->email), + can_login => as_boolean($_->is_enabled), + email_enabled => as_boolean($_->email_enabled), + login_denied_text => as_string($_->disabledtext), + }} @$user_objects; + + return \@users; +} + +1; + +__END__ + +=head1 NAME + +Bugzilla::API::1_0::Resource::Group - The API for creating, changing, and getting +information about Groups. + +=head1 DESCRIPTION + +This part of the Bugzilla API allows you to create Groups and +get information about them. + +=head1 METHODS + +=head2 create + +=over + +=item B + +This allows you to create a new group in Bugzilla. + +=item B + +POST /rest/group + +The params to include in the POST body as well as the returned data format, +are the same as below. + +=item B + +Some params must be set, or an error will be thrown. These params are +marked B. + +=over + +=item C + +B C A short name for this group. Must be unique. This +is not usually displayed in the user interface, except in a few places. + +=item C + +B C A human-readable name for this group. Should be +relatively short. This is what will normally appear in the UI as the +name of the group. + +=item C + +C A regular expression. Any user whose Bugzilla username matches +this regular expression will automatically be granted membership in this group. + +=item C + +C C if new group can be used for bugs, C if this +is a group that will only contain users and no bugs will be restricted +to it. + +=item C + +C A URL pointing to a small icon used to identify the group. +This icon will show up next to users' names in various parts of Bugzilla +if they are in this group. + +=back + +=item B + +A hash with one element, C. This is the id of the newly-created group. + +=item B + +=over + +=item 800 (Empty Group Name) + +You must specify a value for the C field. + +=item 801 (Group Exists) + +There is already another group with the same C. + +=item 802 (Group Missing Description) + +You must specify a value for the C field. + +=item 803 (Group Regexp Invalid) + +You specified an invalid regular expression in the C field. + +=back + +=item B + +=over + +=item REST API call added in Bugzilla B<5.0>. + +=back + +=back + +=head2 update + +=over + +=item B + +This allows you to update a group in Bugzilla. + +=item B + +PUT /rest/group/ + +The params to include in the PUT body as well as the returned data format, +are the same as below. The C param will be overridden as it is pulled +from the URL path. + +=item B + +At least C or C must be set, or an error will be thrown. + +=over + +=item C + +B C Contain ids of groups to update. + +=item C + +B C Contain names of groups to update. + +=item C + +C A new name for group. + +=item C + +C A new description for groups. This is what will appear in the UI +as the name of the groups. + +=item C + +C A new regular expression for email. Will automatically grant +membership to these groups to anyone with an email address that matches +this perl regular expression. + +=item C + +C Set if groups are active and eligible to be used for bugs. +True if bugs can be restricted to this group, false otherwise. + +=item C + +C A URL pointing to an icon that will appear next to the name of +users who are in this group. + +=back + +=item B + +A C with a single field "groups". This points to an array of hashes +with the following fields: + +=over + +=item C + +C The id of the group that was updated. + +=item C + +C The changes that were actually done on this group. The keys are +the names of the fields that were changed, and the values are a hash +with two keys: + +=over + +=item C + +C The values that were added to this field, +possibly a comma-and-space-separated list if multiple values were added. + +=item C + +C The values that were removed from this field, possibly a +comma-and-space-separated list if multiple values were removed. + +=back + +=back + +=item B + +The same as L. + +=item B + +=over + +=item REST API call added in Bugzilla B<5.0>. + +=back + +=back + +=head1 Group Information + +=head2 get + +=over + +=item B + +Returns information about L. + +=item B + +To return information about a specific group by C or C: + +GET /rest/group/ + +You can also return information about more than one specific group +by using the following in your query string: + +GET /rest/group?ids=1&ids=2&ids=3 or GET /group?names=ProductOne&names=Product2 + +the returned data format is same as below. + +=item B + +If neither ids or names is passed, and you are in the creategroups or +editusers group, then all groups will be retrieved. Otherwise, only groups +that you have bless privileges for will be returned. + +=over + +=item C + +C Contain ids of groups to update. + +=item C + +C Contain names of groups to update. + +=item C + +C Set to 1 then a list of members of the passed groups' names and +ids will be returned. + +=back + +=item B + +If the user is a member of the "creategroups" group they will receive +information about all groups or groups matching the criteria that they passed. +You have to be in the creategroups group unless you're requesting membership +information. + +If the user is not a member of the "creategroups" group, but they are in the +"editusers" group or have bless privileges to the groups they require +membership information for, the is_active, is_bug_group and user_regexp values +are not supplied. + +The return value will be a hash containing group names as the keys, each group +name will point to a hash that describes the group and has the following items: + +=over + +=item id + +C The unique integer ID that Bugzilla uses to identify this group. +Even if the name of the group changes, this ID will stay the same. + +=item name + +C The name of the group. + +=item description + +C The description of the group. + +=item is_bug_group + +C Whether this groups is to be used for bug reports or is only administrative specific. + +=item user_regexp + +C A regular expression that allows users to be added to this group if their login matches. + +=item is_active + +C Whether this group is currently active or not. + +=item users + +C An array of hashes, each hash contains a user object for one of the +members of this group, only returned if the user sets the C +parameter to 1, the user hash has the following items: + +=over + +=item id + +C The id of the user. + +=item real_name + +C The actual name of the user. + +=item email + +C The email address of the user. + +=item name + +C The login name of the user. Note that in some situations this is +different than their email. + +=item can_login + +C A boolean value to indicate if the user can login into bugzilla. + +=item email_enabled + +C A boolean value to indicate if bug-related mail will be sent +to the user or not. + +=item disabled_text + +C A text field that holds the reason for disabling a user from logging +into bugzilla, if empty then the user account is enabled otherwise it is +disabled/closed. + +=back + +=back + +=item B + +=over + +=item 51 (Invalid Object) + +A non existing group name was passed to the function, as a result no +group object existed for that invalid name. + +=item 805 (Cannot view groups) + +Logged-in users are not authorized to edit bugzilla groups as they are not +members of the creategroups group in bugzilla, or they are not authorized to +access group member's information as they are not members of the "editusers" +group or can bless the group. + +=back + +=item B + +=over + +=item This function was added in Bugzilla B<5.0>. + +=back + +=back + +=cut + +=head1 B + +=over + +=item REST_RESOURCES + +=back -- cgit v1.2.3-24-g4f1b