git: 7758d0b88dd4 - main - www/rt50: Update to 5.0.5
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Tue, 07 Nov 2023 12:13:56 UTC
The branch main has been updated by mikael: URL: https://cgit.FreeBSD.org/ports/commit/?id=7758d0b88dd4874449e3e708a41867c0f43bb3fd commit 7758d0b88dd4874449e3e708a41867c0f43bb3fd Author: Mikael Urankar <mikael@FreeBSD.org> AuthorDate: 2023-10-20 09:31:06 +0000 Commit: Mikael Urankar <mikael@FreeBSD.org> CommitDate: 2023-11-07 12:13:51 +0000 www/rt50: Update to 5.0.5 Changes: https://github.com/bestpractical/rt/releases/tag/rt-5.0.5 PR: 274607 --- www/rt50/Makefile | 3 +- www/rt50/Makefile.cpan | 4 +- www/rt50/distinfo | 6 +- www/rt50/files/patch-vuln-2023-09-26 | 1118 ---------------------------------- www/rt50/pkg-plist | 14 + 5 files changed, 20 insertions(+), 1125 deletions(-) diff --git a/www/rt50/Makefile b/www/rt50/Makefile index 09b2f7c61d04..f9744f86b531 100644 --- a/www/rt50/Makefile +++ b/www/rt50/Makefile @@ -1,6 +1,5 @@ PORTNAME= rt -DISTVERSION= 5.0.4 -PORTREVISION= 1 +DISTVERSION= 5.0.5 CATEGORIES= www MASTER_SITES= http://download.bestpractical.com/pub/rt/release/ PKGNAMESUFFIX= 50 diff --git a/www/rt50/Makefile.cpan b/www/rt50/Makefile.cpan index 6ef8033d17a9..74a087466bb9 100644 --- a/www/rt50/Makefile.cpan +++ b/www/rt50/Makefile.cpan @@ -22,7 +22,7 @@ ### DateTime::Format::Natural 0.67 ### DateTime::Locale 0.40 ### DBI 1.37 -### DBIx::SearchBuilder 1.76 +### DBIx::SearchBuilder 1.77 ### Devel::GlobalDestruction ### Devel::StackTrace 1.19 ### Digest::base perl std @@ -104,7 +104,7 @@ CORE_DEPS= p5-Apache-Session>=1.53:www/p5-Apache-Session \ p5-Convert-Color>0:graphics/p5-Convert-Color \ p5-Crypt-Eksblowfish>0:security/p5-Crypt-Eksblowfish \ p5-DBI>=1.37:databases/p5-DBI \ - p5-DBIx-SearchBuilder>=1.76:databases/p5-DBIx-SearchBuilder \ + p5-DBIx-SearchBuilder>=1.77:databases/p5-DBIx-SearchBuilder \ p5-Data-GUID>0:devel/p5-Data-GUID \ p5-Data-ICal>0:deskutils/p5-Data-ICal \ p5-Data-Page>0:databases/p5-Data-Page \ diff --git a/www/rt50/distinfo b/www/rt50/distinfo index 0bfa784c4625..e0d4d8f8282b 100644 --- a/www/rt50/distinfo +++ b/www/rt50/distinfo @@ -1,3 +1,3 @@ -TIMESTAMP = 1683267561 -SHA256 (rt-5.0.4.tar.gz) = 916d870d22d92027f843798be6f880aaf1517aebc3f6ab25f456f4e772f4834d -SIZE (rt-5.0.4.tar.gz) = 18949618 +TIMESTAMP = 1697786825 +SHA256 (rt-5.0.5.tar.gz) = 90f845daaa436198c334b6e9cf5afb1df9f4445dcc165d0bcae35de9eb9be8ef +SIZE (rt-5.0.5.tar.gz) = 19055361 diff --git a/www/rt50/files/patch-vuln-2023-09-26 b/www/rt50/files/patch-vuln-2023-09-26 deleted file mode 100644 index 83e7ddf5b63b..000000000000 --- a/www/rt50/files/patch-vuln-2023-09-26 +++ /dev/null @@ -1,1118 +0,0 @@ -The following issues are addressed with these security updates: - - RT is vulnerable to unvalidated email headers in incoming email and the mail-gateway REST interface. This vulnerability is assigned CVE-2023-41259. - RT is vulnerable to information leakage via response messages returned from requests sent via the mail-gateway REST interface. This vulnerability is assigned CVE-2023-41260. - RT 5.0 is vulnerable to information leakage via transaction searches made by authenticated users in the transaction query builder. This vulnerability is assigned CVE-2023-45024. - RT 5.0 can reveal information about data on various RT objects in errors and other response messages to REST 2 requests. - -diff --git a/docs/web_deployment.pod b/docs/web_deployment.pod -index 6ee03d48d8..c22001103a 100644 ---- docs/web_deployment.pod -+++ docs/web_deployment.pod -@@ -127,6 +127,31 @@ RT to access the Authorization header. - - More information is available in L<RT::Authen::Token>. - -+=head3 Restricting the REST 1.0 mail-gateway -+ -+RT processes email via a REST 1.0 endpoint. If you accept email on the same -+server as your running RT, you can restrict this endpoint to localhost only -+with a configuration like the following: -+ -+ # Accept requests only from localhost -+ <Location /REST/1.0/NoAuth/mail-gateway> -+ Require local -+ </Location> -+ -+If you run C<bin/rt-mailgate> on a separate server, you can update -+the above to allow additional IP addresses. -+ -+ <Location /REST/1.0/NoAuth/mail-gateway> -+ Require ip 127.0.0.1 ::1 192.0.2.0 # Add you actual IPs -+ </Location> -+ -+See the L<Apache documentation|https://httpd.apache.org/docs/2.4/mod/mod_authz_host.html> -+for additional configuration options. -+ -+After adding this configuration, test receiving email and confirm -+your C<bin/rt-mailgate> utility and C</etc/aliases> configurations -+can successfully submit email to RT. -+ - =head2 nginx - - C<nginx> requires that you start RT's fastcgi process externally, for -diff --git a/lib/RT/Articles.pm b/lib/RT/Articles.pm -index 79e771884e..787924ffff 100644 ---- lib/RT/Articles.pm -+++ lib/RT/Articles.pm -@@ -970,6 +970,11 @@ sub SimpleSearch { - return $self; - } - -+sub CurrentUserCanSeeAll { -+ my $self = shift; -+ return $self->CurrentUser->HasRight( Right => 'ShowArticle', Object => RT->System ) ? 1 : 0; -+} -+ - RT::Base->_ImportOverlays(); - - 1; -diff --git a/lib/RT/Assets.pm b/lib/RT/Assets.pm -index f6cf8ee647..516869f9cc 100644 ---- lib/RT/Assets.pm -+++ lib/RT/Assets.pm -@@ -1932,6 +1932,11 @@ sub _ProcessRestrictions { - - } - -+sub CurrentUserCanSeeAll { -+ my $self = shift; -+ return $self->CurrentUser->HasRight( Right => 'ShowAsset', Object => RT->System ) ? 1 : 0; -+} -+ - 1; - - RT::Base->_ImportOverlays(); -diff --git a/lib/RT/Attachment.pm b/lib/RT/Attachment.pm -index 2c50447a91..3fed08a360 100644 ---- lib/RT/Attachment.pm -+++ lib/RT/Attachment.pm -@@ -1090,6 +1090,17 @@ sub _CacheConfig { - } - - -+=head2 CurrentUserCanSee -+ -+Returns true if the current user can see the attachment, via corresponding -+transaction's rights check. -+ -+=cut -+ -+sub CurrentUserCanSee { -+ my $self = shift; -+ return $self->TransactionObj->CurrentUserCanSee; -+} - - - =head2 id -diff --git a/lib/RT/Catalog.pm b/lib/RT/Catalog.pm -index 1354207523..31946435b1 100644 ---- lib/RT/Catalog.pm -+++ lib/RT/Catalog.pm -@@ -297,6 +297,28 @@ sub CurrentUserCanSee { - || $self->CurrentUserHasRight('AdminCatalog'); - } - -+=head2 CurrentUserCanCreate -+ -+Returns true if the current user can create a new catalog, using I<AdminCatalog>. -+ -+=cut -+ -+sub CurrentUserCanCreate { -+ my $self = shift; -+ return $self->CurrentUserHasRight('AdminCatalog'); -+} -+ -+=head2 CurrentUserCanModify -+ -+Returns true if the current user can modify the catalog, using I<AdminCatalog>. -+ -+=cut -+ -+sub CurrentUserCanModify { -+ my $self = shift; -+ return $self->CurrentUserHasRight('AdminCatalog'); -+} -+ - =head2 Owner - - Returns an L<RT::User> object for this catalog's I<Owner> role group. On error, -diff --git a/lib/RT/Catalogs.pm b/lib/RT/Catalogs.pm -index 250793c47b..6d67a47720 100644 ---- lib/RT/Catalogs.pm -+++ lib/RT/Catalogs.pm -@@ -114,6 +114,11 @@ sub _Init { - - sub Table { "Catalogs" } - -+sub CurrentUserCanSeeAll { -+ my $self = shift; -+ return $self->CurrentUser->HasRight( Right => 'ShowCatalog', Object => RT->System ) ? 1 : 0; -+} -+ - RT::Base->_ImportOverlays(); - - 1; -diff --git a/lib/RT/Class.pm b/lib/RT/Class.pm -index 01079040c0..03a7926cc2 100644 ---- lib/RT/Class.pm -+++ lib/RT/Class.pm -@@ -507,6 +507,39 @@ sub IncludeArticleCFValue { - return $self->{'_cf_include_hash'}{"Value-".$cfobj->Id}; - } - -+=head2 CurrentUserCanSee -+ -+Returns true if the current user can see the class, using I<SeeClass>. -+ -+=cut -+ -+sub CurrentUserCanSee { -+ my $self = shift; -+ return $self->CurrentUserHasRight('SeeClass'); -+} -+ -+=head2 CurrentUserCanCreate -+ -+Returns true if the current user can create a new class, using I<AdminClass>. -+ -+=cut -+ -+sub CurrentUserCanCreate { -+ my $self = shift; -+ return $self->CurrentUserHasRight('AdminClass'); -+} -+ -+=head2 CurrentUserCanModify -+ -+Returns true if the current user can modify the class, using I<AdminClass>. -+ -+=cut -+ -+sub CurrentUserCanModify { -+ my $self = shift; -+ return $self->CurrentUserHasRight('AdminClass'); -+} -+ - =head2 id - - Returns the current value of id. -diff --git a/lib/RT/Classes.pm b/lib/RT/Classes.pm -index ff446dddfb..9cac032d48 100644 ---- lib/RT/Classes.pm -+++ lib/RT/Classes.pm -@@ -81,6 +81,11 @@ sub AddRecord { - - sub _SingularClass { "RT::Class" } - -+sub CurrentUserCanSeeAll { -+ my $self = shift; -+ return $self->CurrentUser->HasRight( Right => 'SeeClass', Object => RT->System ) ? 1 : 0; -+} -+ - RT::Base->_ImportOverlays(); - - 1; -diff --git a/lib/RT/CustomField.pm b/lib/RT/CustomField.pm -index 79c1f1f5a5..e261eb6f8e 100644 ---- lib/RT/CustomField.pm -+++ lib/RT/CustomField.pm -@@ -2072,6 +2072,28 @@ sub CurrentUserCanSee { - return 0; - } - -+=head2 CurrentUserCanCreate -+ -+If the user has I<AdminCustomField> they can create a new custom field. -+ -+=cut -+ -+sub CurrentUserCanCreate { -+ my $self = shift; -+ return $self->CurrentUserHasRight('AdminCustomField'); -+} -+ -+=head2 CurrentUserCanModify -+ -+If the user has I<AdminCustomField> they can modify the custom field. -+ -+=cut -+ -+sub CurrentUserCanModify { -+ my $self = shift; -+ return $self->CurrentUserHasRight('AdminCustomField'); -+} -+ - =head2 IncludeContentForValue [VALUE] (and SetIncludeContentForValue) - - Gets or sets the C<IncludeContentForValue> for this custom field. RT -diff --git a/lib/RT/CustomFields.pm b/lib/RT/CustomFields.pm -index 253513d0f9..0fc5569adb 100644 ---- lib/RT/CustomFields.pm -+++ lib/RT/CustomFields.pm -@@ -475,6 +475,11 @@ sub LimitToCatalog { - } - } - -+sub CurrentUserCanSeeAll { -+ my $self = shift; -+ return $self->CurrentUser->HasRight( Right => 'SeeCustomField', Object => RT->System ) ? 1 : 0; -+} -+ - RT::Base->_ImportOverlays(); - - 1; -diff --git a/lib/RT/CustomRole.pm b/lib/RT/CustomRole.pm -index ec37402925..750aaf2575 100644 ---- lib/RT/CustomRole.pm -+++ lib/RT/CustomRole.pm -@@ -544,6 +544,28 @@ sub GroupType { - return 'RT::CustomRole-' . $self->id; - } - -+=head2 CurrentUserCanCreate -+ -+Returns true if the current user can create a new custom role, using I<AdminCustomRoles>. -+ -+=cut -+ -+sub CurrentUserCanCreate { -+ my $self = shift; -+ return $self->CurrentUserHasRight('AdminClass'); -+} -+ -+=head2 CurrentUserCanModify -+ -+Returns true if the current user can modify the custom role, using I<AdminCustomRoles>. -+ -+=cut -+ -+sub CurrentUserCanModify { -+ my $self = shift; -+ return $self->CurrentUserHasRight('AdminClass'); -+} -+ - =head2 id - - Returns the current value of id. -diff --git a/lib/RT/CustomRoles.pm b/lib/RT/CustomRoles.pm -index 6cac676858..75587c71ed 100644 ---- lib/RT/CustomRoles.pm -+++ lib/RT/CustomRoles.pm -@@ -213,6 +213,12 @@ sub LimitToAdded { - return RT::ObjectCustomRoles->new( $self->CurrentUser )->LimitTargetToAdded( $self => @_ ); - } - -+sub CurrentUserCanSeeAll { -+ my $self = shift; -+ # Not typo, user needs SeeQueue to see CustomRoles -+ return $self->CurrentUser->HasRight( Right => 'SeeQueue', Object => RT->System ) ? 1 : 0; -+} -+ - RT::Base->_ImportOverlays(); - - 1; -diff --git a/lib/RT/Group.pm b/lib/RT/Group.pm -index ccc92fe7c1..03e20eb707 100644 ---- lib/RT/Group.pm -+++ lib/RT/Group.pm -@@ -1311,17 +1311,43 @@ sub _Set { - - =head2 CurrentUserCanSee - --Always returns 1; unfortunately, for historical reasons, users have --always been able to examine groups they have indirect access to, even if --they do not have SeeGroup explicitly. -+Unfortunately, for historical reasons, users have always been able to -+examine groups they have indirect access to, even if they do not have -+SeeGroup explicitly. -+ -+We do require "SeeGroup" to see transactions of current group. - - =cut - - sub CurrentUserCanSee { - my $self = shift; -- return 1; -+ my ($what, $txn) = @_; -+ -+ return 1 if ( $what // '' ) ne 'Transaction'; -+ return $self->CurrentUserHasRight('SeeGroup'); -+} -+ -+=head2 CurrentUserCanCreate -+ -+Returns true if the current user can create a new group, using I<AdminGroup>. -+ -+=cut -+ -+sub CurrentUserCanCreate { -+ my $self = shift; -+ return $self->CurrentUserHasRight('AdminGroup'); - } - -+=head2 CurrentUserCanModify -+ -+Returns true if the current user can modify the group, using I<AdminGroup>. -+ -+=cut -+ -+sub CurrentUserCanModify { -+ my $self = shift; -+ return $self->CurrentUserHasRight('AdminGroup'); -+} - - =head2 PrincipalObj - -diff --git a/lib/RT/Groups.pm b/lib/RT/Groups.pm -index 2f341bbba3..51a43f95e3 100644 ---- lib/RT/Groups.pm -+++ lib/RT/Groups.pm -@@ -537,6 +537,11 @@ sub SimpleSearch { - return $self; - } - -+sub CurrentUserCanSeeAll { -+ my $self = shift; -+ return $self->CurrentUser->HasRight( Right => 'SeeGroup', Object => RT->System ) ? 1 : 0; -+} -+ - RT::Base->_ImportOverlays(); - - 1; -diff --git a/lib/RT/Interface/Email.pm b/lib/RT/Interface/Email.pm -index 39bd5c4169..648e0e7f61 100644 ---- lib/RT/Interface/Email.pm -+++ lib/RT/Interface/Email.pm -@@ -161,6 +161,10 @@ sub Gateway { - ); - } - -+ # Clean up sensitive headers. Crypt related headers are cleaned up in RT::Interface::Email::Crypt::VerifyDecrypt -+ my @headers = qw( RT-Attach RT-Send-Cc RT-Send-Bcc RT-Message-ID RT-DetectedAutoGenerated RT-Squelch-Replies-To ); -+ $Message->head->delete($_) for @headers; -+ - #Set up a queue object - my $SystemQueueObj = RT::Queue->new( RT->SystemUser ); - $SystemQueueObj->Load( $args{'queue'} ); -diff --git a/lib/RT/Interface/Email/Crypt.pm b/lib/RT/Interface/Email/Crypt.pm -index bc3427ca49..9d4d4fe584 100644 ---- lib/RT/Interface/Email/Crypt.pm -+++ lib/RT/Interface/Email/Crypt.pm -@@ -73,13 +73,14 @@ sub VerifyDecrypt { - ); - - # we clean all possible headers -- my @headers = -+ my @headers = ( - qw( - X-RT-Incoming-Encryption - X-RT-Incoming-Signature X-RT-Privacy - X-RT-Sign X-RT-Encrypt - ), -- map "X-RT-$_-Status", RT::Crypt->Protocols; -+ map "X-RT-$_-Status", RT::Crypt->Protocols -+ ); - foreach my $p ( $args{'Message'}->parts_DFS ) { - $p->head->delete($_) for @headers; - } -diff --git a/lib/RT/Interface/Web.pm b/lib/RT/Interface/Web.pm -index 444eb1c24e..da0ec92bf8 100644 ---- lib/RT/Interface/Web.pm -+++ lib/RT/Interface/Web.pm -@@ -5792,6 +5792,28 @@ sub ParseCalendarData { - return undef; - } - -+sub PreprocessTransactionSearchQuery { -+ my %args = ( -+ Query => undef, -+ ObjectType => 'RT::Ticket', -+ @_ -+ ); -+ -+ my @limits; -+ if ( $args{ObjectType} eq 'RT::Ticket' ) { -+ @limits = ( -+ q{TicketType = 'ticket'}, -+ qq{ObjectType = '$args{ObjectType}'}, -+ $args{Query} =~ /^\s*\(.*\)$/ ? $args{Query} : "($args{Query})" -+ ); -+ } -+ else { -+ # Other ObjectTypes are not supported for now -+ @limits = 'id = 0'; -+ } -+ return join ' AND ', @limits; -+} -+ - package RT::Interface::Web; - RT::Base->_ImportOverlays(); - -diff --git a/lib/RT/ObjectCustomFieldValue.pm b/lib/RT/ObjectCustomFieldValue.pm -index 53e62c2334..4815f76406 100644 ---- lib/RT/ObjectCustomFieldValue.pm -+++ lib/RT/ObjectCustomFieldValue.pm -@@ -815,6 +815,7 @@ object, otherwise false. - - sub CurrentUserCanSee { - my $self = shift; -+ return undef unless $self->Id; - return $self->CustomFieldObj->CurrentUserHasRight('SeeCustomField'); - } - -diff --git a/lib/RT/Queue.pm b/lib/RT/Queue.pm -index 29ba7db3ad..609a7ebe5b 100644 ---- lib/RT/Queue.pm -+++ lib/RT/Queue.pm -@@ -810,6 +810,29 @@ sub CurrentUserCanSee { - return $self->CurrentUserHasRight('SeeQueue'); - } - -+ -+=head2 CurrentUserCanCreate -+ -+Returns true if the current user can create a new queue, using I<AdminQueue>. -+ -+=cut -+ -+sub CurrentUserCanCreate { -+ my $self = shift; -+ return $self->CurrentUserHasRight('AdminQueue'); -+} -+ -+=head2 CurrentUserCanModify -+ -+Returns true if the current user can modify the queue, using I<AdminQueue>. -+ -+=cut -+ -+sub CurrentUserCanModify { -+ my $self = shift; -+ return $self->CurrentUserHasRight('AdminQueue'); -+} -+ - =head2 id - - Returns the current value of id. -diff --git a/lib/RT/Queues.pm b/lib/RT/Queues.pm -index 5a916d70a0..0d031b8ebe 100644 ---- lib/RT/Queues.pm -+++ lib/RT/Queues.pm -@@ -128,6 +128,11 @@ sub ItemsOrderBy { - return $items; - } - -+sub CurrentUserCanSeeAll { -+ my $self = shift; -+ return $self->CurrentUser->HasRight( Right => 'SeeQueue', Object => RT->System ) ? 1 : 0; -+} -+ - RT::Base->_ImportOverlays(); - - 1; -diff --git a/lib/RT/REST2/Resource/Article.pm b/lib/RT/REST2/Resource/Article.pm -index f3a0eb32d7..d52fff739d 100644 ---- lib/RT/REST2/Resource/Article.pm -+++ lib/RT/REST2/Resource/Article.pm -@@ -80,17 +80,11 @@ sub create_record { - - return (\400, "Invalid Class") if !$data->{Class}; - -- my $class = RT::Class->new(RT->SystemUser); -+ my $class = RT::Class->new($self->current_user); - $class->Load($data->{Class}); - -- return (\400, "Invalid Class") if !$class->Id; -- - return ( \403, $self->record->loc("Permission Denied") ) -- unless $self->record->CurrentUser->HasRight( -- Right => 'CreateArticle', -- Object => $class, -- ) -- and $class->Disabled != 1; -+ unless $class->Id and !$class->__Value('Disabled') and $class->CurrentUserHasRight('CreateArticle'); - - my ($ok, $txn, $msg) = $self->_create_record($data); - return ($ok, $msg); -diff --git a/lib/RT/REST2/Resource/Asset.pm b/lib/RT/REST2/Resource/Asset.pm -index a29f637b7c..70e66464ba 100644 ---- lib/RT/REST2/Resource/Asset.pm -+++ lib/RT/REST2/Resource/Asset.pm -@@ -83,10 +83,8 @@ sub create_record { - my $catalog = RT::Catalog->new($self->record->CurrentUser); - $catalog->Load($data->{Catalog}); - -- return (\400, "Invalid Catalog") if !$catalog->Id; -- -- return (\403, $self->record->loc("Permission Denied", $catalog->Name)) -- unless $catalog->CurrentUserHasRight('CreateAsset'); -+ return (\403, $self->record->loc("Permission Denied")) -+ unless $catalog->Id and !$catalog->__Value('Disabled') and $catalog->CurrentUserHasRight('CreateAsset'); - - return $self->_create_record($data); - } -diff --git a/lib/RT/REST2/Resource/Collection.pm b/lib/RT/REST2/Resource/Collection.pm -index 2f0f16eab2..7653d5695c 100644 ---- lib/RT/REST2/Resource/Collection.pm -+++ lib/RT/REST2/Resource/Collection.pm -@@ -189,7 +189,7 @@ sub serialize { - - my %results = ( - count => scalar(@results) + 0, -- total => $collection->CountAll + 0, -+ total => $collection->CurrentUserCanSeeAll ? ( $collection->CountAll + 0 ) : undef, - per_page => $collection->RowsPerPage + 0, - page => ($collection->FirstRow / $collection->RowsPerPage) + 1, - items => \@results, -@@ -205,18 +205,20 @@ sub serialize { - } - } - -- $results{pages} = ceil($results{total} / $results{per_page}); -- if ($results{page} < $results{pages}) { -- my $page = $results{page} + 1; -- $uri->query_form( @query_form, page => $results{page} + 1 ); -- $results{next_page} = $uri->as_string; -- }; -- if ($results{page} > 1) { -- # If we're beyond the last page, set prev_page as the last page -- # available, otherwise, the previous page. -- $uri->query_form( @query_form, page => ($results{page} > $results{pages} ? $results{pages} : $results{page} - 1) ); -- $results{prev_page} = $uri->as_string; -- }; -+ $results{pages} = defined $results{total} ? ceil($results{total} / $results{per_page}) : undef; -+ if ( $results{pages} ) { -+ if ($results{page} < $results{pages}) { -+ my $page = $results{page} + 1; -+ $uri->query_form( @query_form, page => $results{page} + 1 ); -+ $results{next_page} = $uri->as_string; -+ } -+ if ($results{page} > 1) { -+ # If we're beyond the last page, set prev_page as the last page -+ # available, otherwise, the previous page. -+ $uri->query_form( @query_form, page => ($results{page} > $results{pages} ? $results{pages} : $results{page} - 1) ); -+ $results{prev_page} = $uri->as_string; -+ } -+ } - - return \%results; - } -diff --git a/lib/RT/REST2/Resource/CustomField.pm b/lib/RT/REST2/Resource/CustomField.pm -index 62e2d737b7..1924a95f2f 100644 ---- lib/RT/REST2/Resource/CustomField.pm -+++ lib/RT/REST2/Resource/CustomField.pm -@@ -87,21 +87,6 @@ sub serialize { - return $data; - } - --sub forbidden { -- my $self = shift; -- my $method = $self->request->method; -- if ($self->record->id) { -- if ($method eq 'GET') { -- return !$self->record->CurrentUserHasRight('SeeCustomField'); -- } else { -- return !($self->record->CurrentUserHasRight('SeeCustomField') && $self->record->CurrentUserHasRight('AdminCustomField')); -- } -- } else { -- return !$self->current_user->HasRight(Right => "AdminCustomField", Object => RT->System); -- } -- return 0; --} -- - sub hypermedia_links { - my $self = shift; - my $links = $self->_default_hypermedia_links(@_); -diff --git a/lib/RT/REST2/Resource/Group.pm b/lib/RT/REST2/Resource/Group.pm -index 58f46c1255..c955d1558f 100644 ---- lib/RT/REST2/Resource/Group.pm -+++ lib/RT/REST2/Resource/Group.pm -@@ -58,9 +58,7 @@ extends 'RT::REST2::Resource::Record'; - with 'RT::REST2::Resource::Record::Readable' - => { -alias => { serialize => '_default_serialize' } }, - 'RT::REST2::Resource::Record::DeletableByDisabling', -- => { -alias => { delete_resource => '_delete_resource' } }, - 'RT::REST2::Resource::Record::Writable', -- => { -alias => { create_record => '_create_record' } }, - 'RT::REST2::Resource::Record::Hypermedia' - => { -alias => { hypermedia_links => '_default_hypermedia_links' } }; - -@@ -102,33 +100,20 @@ sub hypermedia_links { - return $links; - } - --sub create_record { -+override forbidden => sub { - my $self = shift; -- my $data = shift; - -- return (\403, $self->record->loc("Permission Denied")) -- unless $self->current_user->HasRight( -- Right => "AdminGroup", -- Object => RT->System, -- ); -- -- return $self->_create_record($data); --} -- --sub delete_resource { -- my $self = shift; -- -- return (\403, $self->record->loc("Permission Denied")) -- unless $self->record->CurrentUserHasRight('AdminGroup'); -- -- return $self->_delete_resource; --} -- --sub forbidden { -- my $self = shift; -- return 0 unless $self->record->id; -- return !$self->record->CurrentUserHasRight('SeeGroup'); --} -+ # For historical reasons, RT::Group::CurrentUserCanSee always returns true. -+ # For REST2, we want to check SeeGroup. -+ no warnings 'redefine'; -+ my $original_can_see = \&RT::Group::CurrentUserCanSee; -+ local *RT::Group::CurrentUserCanSee = sub { -+ my $self = shift; -+ return 0 unless $original_can_see->($self, @_); -+ return $self->CurrentUserHasRight('SeeGroup'); -+ }; -+ return super(); -+}; - - __PACKAGE__->meta->make_immutable; - -diff --git a/lib/RT/REST2/Resource/GroupMembers.pm b/lib/RT/REST2/Resource/GroupMembers.pm -index 4c0bb0a49a..df555babec 100644 ---- lib/RT/REST2/Resource/GroupMembers.pm -+++ lib/RT/REST2/Resource/GroupMembers.pm -@@ -114,9 +114,7 @@ sub dispatch_rules { - - sub forbidden { - my $self = shift; -- return 0 unless $self->group->id; - return !$self->group->CurrentUserHasRight('AdminGroupMembership'); -- return 1; - } - - sub serialize { -diff --git a/lib/RT/REST2/Resource/ObjectCustomFieldValue.pm b/lib/RT/REST2/Resource/ObjectCustomFieldValue.pm -index b9d6fa1cb3..4dbc9cb7a8 100644 ---- lib/RT/REST2/Resource/ObjectCustomFieldValue.pm -+++ lib/RT/REST2/Resource/ObjectCustomFieldValue.pm -@@ -71,12 +71,6 @@ sub content_types_provided { - { [ {$self->record->ContentType || 'text/plain; charset=utf-8' => 'to_binary'} ] }; - } - --sub forbidden { -- my $self = shift; -- return 0 unless $self->record->id; -- return !$self->record->CurrentUserHasRight('SeeCustomField'); --} -- - sub to_binary { - my $self = shift; - unless ($self->record->CustomFieldObj->Type =~ /^(?:Image|Binary)$/) { -diff --git a/lib/RT/REST2/Resource/RT.pm b/lib/RT/REST2/Resource/RT.pm -index 6a31ba3a71..724880af72 100644 ---- lib/RT/REST2/Resource/RT.pm -+++ lib/RT/REST2/Resource/RT.pm -@@ -71,7 +71,9 @@ sub to_json { - my $self = shift; - return JSON::to_json({ - Version => $RT::VERSION, -- Plugins => [ RT->Config->Get('Plugins') ], -+ $self->current_user->HasRight( Object => RT->System, Right => 'SuperUser' ) -+ ? ( Plugins => [ RT->Config->Get('Plugins') ] ) -+ : (), - }, { pretty => 1 }); - } - __PACKAGE__->meta->make_immutable; -diff --git a/lib/RT/REST2/Resource/Record.pm b/lib/RT/REST2/Resource/Record.pm -index b335dab09e..df1223e993 100644 ---- lib/RT/REST2/Resource/Record.pm -+++ lib/RT/REST2/Resource/Record.pm -@@ -100,10 +100,21 @@ sub resource_exists { - - sub forbidden { - my $self = shift; -- return 0 unless $self->record->id; -+ my $method = $self->request->method; -+ -+ my $right_method; -+ if ( $self->record->id ) { -+ $right_method = $method =~ /^(?:GET|HEAD)$/ ? 'CurrentUserCanSee' : 'CurrentUserCanModify'; -+ } -+ else { -+ # Even without id, the method can be GET, e.g. to access a not-exsting record. -+ $right_method = $method =~ /^(?:GET|HEAD)$/ ? 'CurrentUserCanSee' : 'CurrentUserCanCreate'; -+ } -+ -+ if ( $self->record->can($right_method) ) { -+ return !$self->record->$right_method; -+ } - -- my $can_see = $self->record->can("CurrentUserCanSee"); -- return 1 if $can_see and not $self->record->$can_see(); - return 0; - } - -diff --git a/lib/RT/REST2/Resource/Ticket.pm b/lib/RT/REST2/Resource/Ticket.pm -index 1873f0a636..64b4d67647 100644 ---- lib/RT/REST2/Resource/Ticket.pm -+++ lib/RT/REST2/Resource/Ticket.pm -@@ -225,16 +225,11 @@ sub validate_input { - if ( $args{'Action'} eq 'create' ) { - return (0, "Could not create ticket. Queue not set", 400) if !$data->{Queue}; - -- my $queue = RT::Queue->new(RT->SystemUser); -+ my $queue = RT::Queue->new($self->current_user); - $queue->Load($data->{Queue}); - -- return (0, "Unable to find queue", 400) if !$queue->Id; -- -- return (0, $self->record->loc("No permission to create tickets in the queue '[_1]'", $queue->Name), 403) -- unless $self->record->CurrentUser->HasRight( -- Right => 'CreateTicket', -- Object => $queue, -- ) and $queue->Disabled != 1; -+ return (0, $self->record->loc("No permission to create tickets in the queue '[_1]'", $data->{Queue}), 403) -+ unless $queue->Id and $queue->__Value('Disabled') != 1 and $queue->CurrentUserHasRight('CreateTicket'); - } - - if ( $args{'Action'} eq 'update' ) { -diff --git a/lib/RT/REST2/Resource/User.pm b/lib/RT/REST2/Resource/User.pm -index 510a8c7740..af4c8c0c2b 100644 ---- lib/RT/REST2/Resource/User.pm -+++ lib/RT/REST2/Resource/User.pm -@@ -105,9 +105,8 @@ around 'serialize' => sub { - - sub forbidden { - my $self = shift; -- return 0 if not $self->record->id; -- return 0 if $self->record->id == $self->current_user->id; - return 0 if $self->current_user->Privileged; -+ return 0 if ( $self->record->id || 0 ) == $self->current_user->id; - return 1; - } - -diff --git a/lib/RT/SearchBuilder.pm b/lib/RT/SearchBuilder.pm -index adb1e4e953..03088d9394 100644 ---- lib/RT/SearchBuilder.pm -+++ lib/RT/SearchBuilder.pm -@@ -1150,6 +1150,11 @@ sub DistinctFieldValues { - return @values; - } - -+sub CurrentUserCanSeeAll { -+ my $self = shift; -+ return $self->CurrentUser->HasRight( Right => 'SuperUser', Object => RT->System ) ? 1 : 0; -+} -+ - RT::Base->_ImportOverlays(); - - 1; -diff --git a/lib/RT/SearchBuilder/Role/Roles.pm b/lib/RT/SearchBuilder/Role/Roles.pm -index ac36e8538c..06462d3e88 100644 ---- lib/RT/SearchBuilder/Role/Roles.pm -+++ lib/RT/SearchBuilder/Role/Roles.pm -@@ -97,7 +97,7 @@ sub _RoleGroupClass { - - sub _RoleGroupsJoin { - my $self = shift; -- my %args = (New => 0, Class => '', Name => '', @_); -+ my %args = (New => 0, Class => '', Name => '', Alias => 'main', @_); - - $args{'Class'} ||= $self->_RoleGroupClass; - -@@ -118,7 +118,7 @@ sub _RoleGroupsJoin { - # Previously (before 4.4) this used an inner join. - my $groups = $self->Join( - TYPE => 'left', -- ALIAS1 => 'main', -+ ALIAS1 => $args{Alias}, - FIELD1 => $instance, - TABLE2 => 'Groups', - FIELD2 => 'Instance', -diff --git a/lib/RT/Ticket.pm b/lib/RT/Ticket.pm -index 741d2e3007..ec7f6922fb 100644 ---- lib/RT/Ticket.pm -+++ lib/RT/Ticket.pm -@@ -3788,6 +3788,10 @@ sub Serialize { - $obj->Load( $store{EffectiveId} ); - $store{EffectiveId} = \($obj->UID); - -+ unless ( $self->CurrentUserCanSeeTime ) { -+ delete $store{$_} for qw/TimeEstimated TimeLeft TimeWorked/; -+ } -+ - return %store; - } - -diff --git a/lib/RT/Tickets.pm b/lib/RT/Tickets.pm -index fbaa2d602a..99fd3db13f 100644 ---- lib/RT/Tickets.pm -+++ lib/RT/Tickets.pm -@@ -2856,7 +2856,8 @@ sub CurrentUserCanSee { - return unless @queues; - $self->Limit( - SUBCLAUSE => 'ACL', -- ALIAS => 'main', -+ # RT::Transactions::CurrentUserCanSee reuses RT::Tickets::CurrentUserCanSee -+ ALIAS => $self->isa('RT::Transactions') ? $self->_JoinTickets : 'main', - FIELD => 'Queue', - OPERATOR => 'IN', - VALUE => [ @queues ], -@@ -2912,6 +2913,8 @@ sub CurrentUserCanSee { - FIELD => 'Owner', - VALUE => $id, - ENTRYAGGREGATOR => $ea, -+ # RT::Transactions::CurrentUserCanSee reuses RT::Tickets::CurrentUserCanSee -+ ALIAS => $self->isa('RT::Transactions') ? $self->_JoinTickets : 'main', - ); - } - else { -@@ -3563,6 +3566,12 @@ sub Query { - return $self->{_sql_query}; - } - -+sub CurrentUserCanSeeAll { -+ my $self = shift; -+ return 1 if RT->Config->Get('UseSQLForACLChecks'); -+ return $self->CurrentUser->HasRight( Right => 'ShowTicket', Object => RT->System ) ? 1 : 0; -+} -+ - RT::Base->_ImportOverlays(); - - 1; -diff --git a/lib/RT/Transactions.pm b/lib/RT/Transactions.pm -index 21ca3cd86e..e03d2995a8 100644 ---- lib/RT/Transactions.pm -+++ lib/RT/Transactions.pm -@@ -142,7 +142,28 @@ sub AddRecord { - my $self = shift; - my ($record) = @_; - -- return unless $record->CurrentUserCanSee; -+ if ( $self->{_is_ticket_only_search} && RT->Config->Get('UseSQLForACLChecks') ) { -+ # UseSQLForACLChecks implies ShowTicket only, need to check out extra rights here. -+ my $type = $record->__Value('Type'); -+ if ( $type eq 'Comment' ) { -+ return unless $record->CurrentUserHasRight('ShowTicketComments'); -+ } -+ elsif ( $type eq 'CommentEmailRecord' ) { -+ return -+ unless $record->CurrentUserHasRight('ShowTicketComments') -+ && $record->CurrentUserHasRight('ShowOutgoingEmail'); -+ } -+ elsif ( $type eq 'EmailRecord' ) { -+ return unless $record->CurrentUserHasRight('ShowOutgoingEmail'); -+ } -+ elsif ( $type eq 'CustomField' ) { -+ return unless $record->CurrentUserCanSee; -+ } -+ } -+ else { -+ return unless $record->CurrentUserCanSee; -+ } -+ - return $self->SUPER::AddRecord($record); *** 280 LINES SKIPPED ***