diff options
author | Ivan Kohler <ivan@freeside.biz> | 2012-06-07 16:58:33 -0700 |
---|---|---|
committer | Ivan Kohler <ivan@freeside.biz> | 2012-06-07 16:58:33 -0700 |
commit | 21a232b78413718d8a68867ba7eb4f52a287f9b6 (patch) | |
tree | 988115f9363144a2afdac9e3d9914964a7725105 /rt/t/web | |
parent | c24d6e2242ae0e026684b8f95decf156aba6e75e (diff) |
rt 4.0.6
Diffstat (limited to 'rt/t/web')
-rw-r--r-- | rt/t/web/command_line_link_to_articles.t | 48 | ||||
-rw-r--r-- | rt/t/web/csrf-rest.t | 77 | ||||
-rw-r--r-- | rt/t/web/csrf.t | 181 | ||||
-rw-r--r-- | rt/t/web/installer.t | 95 | ||||
-rw-r--r-- | rt/t/web/owner_disabled_group_19221.t | 190 | ||||
-rw-r--r-- | rt/t/web/query_builder_queue_limits.t | 180 | ||||
-rw-r--r-- | rt/t/web/rest_cfs_with_same_name.t | 88 |
7 files changed, 859 insertions, 0 deletions
diff --git a/rt/t/web/command_line_link_to_articles.t b/rt/t/web/command_line_link_to_articles.t new file mode 100644 index 000000000..9a49145fd --- /dev/null +++ b/rt/t/web/command_line_link_to_articles.t @@ -0,0 +1,48 @@ +use strict; +use warnings; +use Test::Expect; +use RT::Test tests => 12, actual_server => 1; + +my $class = RT::Class->new( RT->SystemUser ); +my ( $class_id, $msg ) = $class->Create( Name => 'foo' ); +ok( $class_id, $msg ); + +my $article = RT::Article->new( RT->SystemUser ); +( my $article_id, $msg ) = + $article->Create( Class => 'foo', Summary => 'article summary' ); +ok( $article_id, $msg ); + +my ( $baseurl, $m ) = RT::Test->started_ok; +my $rt_tool_path = "$RT::BinPath/rt"; + +$ENV{'RTUSER'} = 'root'; +$ENV{'RTPASSWD'} = 'password'; +$RT::Logger->debug( + "Connecting to server at " . RT->Config->Get('WebBaseURL') ); +$ENV{'RTSERVER'} = RT->Config->Get('WebBaseURL'); +$ENV{'RTDEBUG'} = '1'; +$ENV{'RTCONFIG'} = '/dev/null'; + +expect_run( + command => "$rt_tool_path shell", + prompt => 'rt> ', + quit => 'quit', +); +expect_send( q{create -t ticket set subject='new ticket'}, + "creating a ticket..." ); + +expect_like( qr/Ticket \d+ created/, "created the ticket" ); +expect_handle->before() =~ /Ticket (\d+) created/; +my $ticket_id = $1; +expect_send( + "link $ticket_id RefersTo a:$article_id", + "link $ticket_id RefersTo a:$article_id" +); +expect_like( qr/Created link $ticket_id RefersTo a:$article_id/, + 'created link' ); +expect_send( "show -s ticket/$ticket_id/links", "show ticket links" ); +expect_like( qr|RefersTo: fsck\.com-article://example\.com/article/$article_id|, + "found new created link" ); + +expect_quit(); + diff --git a/rt/t/web/csrf-rest.t b/rt/t/web/csrf-rest.t new file mode 100644 index 000000000..5bb908165 --- /dev/null +++ b/rt/t/web/csrf-rest.t @@ -0,0 +1,77 @@ +#!/usr/bin/perl +use strict; +use warnings; + +use RT::Test tests => undef; + +my ($baseurl, $m) = RT::Test->started_ok; + +# Get a non-REST session +diag "Standard web session"; +ok $m->login, 'logged in'; +$m->content_contains("RT at a glance", "Get full UI content"); + +# Requesting a REST page should be fine, as we have a Referer +$m->post("$baseurl/REST/1.0/ticket/new", [ + format => 'l', +]); +$m->content_like(qr{^id: ticket/new}m, "REST request with referrer"); + +# Removing the Referer header gets us an interstitial +$m->add_header(Referer => undef); +$m->post("$baseurl/REST/1.0/ticket/new", [ + format => 'l', + foo => 'bar', +]); +$m->content_contains("Possible cross-site request forgery", + "REST request without referrer is blocked"); + +# But passing username and password lets us though +$m->post("$baseurl/REST/1.0/ticket/new", [ + user => 'root', + pass => 'password', + format => 'l', +]); +$m->content_like(qr{^id: ticket/new}m, "REST request without referrer, but username/password supplied, is OK"); + +# And we can still access non-REST urls +$m->get("$baseurl"); +$m->content_contains("RT at a glance", "Full UI is still available"); + + +# Now go get a REST session +diag "REST session"; +$m = RT::Test::Web->new; +$m->post("$baseurl/REST/1.0/ticket/new", [ + user => 'root', + pass => 'password', + format => 'l', +]); +$m->content_like(qr{^id: ticket/new}m, "REST request to log in"); + +# Requesting that page again, with a username/password but no referrer, +# is fine +$m->add_header(Referer => undef); +$m->post("$baseurl/REST/1.0/ticket/new", [ + user => 'root', + pass => 'password', + format => 'l', +]); +$m->content_like(qr{^id: ticket/new}m, "REST request with no referrer, but username/pass"); + +# And it's still fine without both referer and username and password, +# because REST is special-cased +$m->post("$baseurl/REST/1.0/ticket/new", [ + format => 'l', +]); +$m->content_like(qr{^id: ticket/new}m, "REST request with no referrer or username/pass is special-cased for REST sessions"); + +# But the REST page can't request normal pages +$m->get("$baseurl"); +$m->content_lacks("RT at a glance", "Full UI is denied for REST sessions"); +$m->content_contains("This login session belongs to a REST client", "Tells you why"); +$m->warning_like(qr/This login session belongs to a REST client/, "Logs a warning"); + +undef $m; +done_testing; + diff --git a/rt/t/web/csrf.t b/rt/t/web/csrf.t new file mode 100644 index 000000000..d99b4ce22 --- /dev/null +++ b/rt/t/web/csrf.t @@ -0,0 +1,181 @@ +#!/usr/bin/perl +use strict; +use warnings; + +use RT::Test tests => undef; + +my $ticket = RT::Ticket->new(RT::CurrentUser->new('root')); +my ($ok, $msg) = $ticket->Create(Queue => 1, Owner => 'nobody', Subject => 'bad music'); +ok($ok); +my $other = RT::Test->load_or_create_queue(Name => "Other queue", Disabled => 0); +my $other_queue_id = $other->id; + +my ($baseurl, $m) = RT::Test->started_ok; + +my $test_page = "/Ticket/Create.html?Queue=1"; +my $test_path = "/Ticket/Create.html"; + +ok $m->login, 'logged in'; + +# valid referer +$m->add_header(Referer => $baseurl); +$m->get_ok($test_page); +$m->content_lacks("Possible cross-site request forgery"); +$m->title_is('Create a new ticket'); + +# off-site referer BUT provides auth +$m->add_header(Referer => 'http://example.net'); +$m->get_ok("$test_page&user=root&pass=password"); +$m->content_lacks("Possible cross-site request forgery"); +$m->title_is('Create a new ticket'); + +# explicitly no referer BUT provides auth +$m->add_header(Referer => undef); +$m->get_ok("$test_page&user=root&pass=password"); +$m->content_lacks("Possible cross-site request forgery"); +$m->title_is('Create a new ticket'); + +# now send a referer from an attacker +$m->add_header(Referer => 'http://example.net'); +$m->get_ok($test_page); +$m->content_contains("Possible cross-site request forgery"); +$m->content_contains("If you really intended to visit <tt>/Ticket/Create.html</tt>"); +$m->content_contains("the Referrer header supplied by your browser (example.net:80) is not allowed"); +$m->title_is('Possible cross-site request forgery'); + +# reinstate mech's usual header policy +$m->delete_header('Referer'); + +# clicking the resume request button gets us to the test page +$m->follow_link(text_regex => qr{resume your request}); +$m->content_lacks("Possible cross-site request forgery"); +like($m->response->request->uri, qr{^http://[^/]+\Q$test_path\E\?CSRF_Token=\w+$}); +$m->title_is('Create a new ticket'); + +# try a whitelisted argument from an attacker +$m->add_header(Referer => 'http://example.net'); +$m->get_ok("/Ticket/Display.html?id=1"); +$m->content_lacks("Possible cross-site request forgery"); +$m->title_is('#1: bad music'); + +# now a non-whitelisted argument +$m->get_ok("/Ticket/Display.html?id=1&Action=Take"); +$m->content_contains("Possible cross-site request forgery"); +$m->content_contains("If you really intended to visit <tt>/Ticket/Display.html</tt>"); +$m->content_contains("the Referrer header supplied by your browser (example.net:80) is not allowed"); +$m->title_is('Possible cross-site request forgery'); + +$m->delete_header('Referer'); +$m->follow_link(text_regex => qr{resume your request}); +$m->content_lacks("Possible cross-site request forgery"); +like($m->response->request->uri, qr{^http://[^/]+\Q/Ticket/Display.html}); +$m->title_is('#1: bad music'); +$m->content_contains('Owner changed from Nobody to root'); + +# force mech to never set referer +$m->add_header(Referer => undef); +$m->get_ok($test_page); +$m->content_contains("Possible cross-site request forgery"); +$m->content_contains("If you really intended to visit <tt>/Ticket/Create.html</tt>"); +$m->content_contains("your browser did not supply a Referrer header"); +$m->title_is('Possible cross-site request forgery'); + +$m->follow_link(text_regex => qr{resume your request}); +$m->content_lacks("Possible cross-site request forgery"); +is($m->response->redirects, 0, "no redirection"); +like($m->response->request->uri, qr{^http://[^/]+\Q$test_path\E\?CSRF_Token=\w+$}); +$m->title_is('Create a new ticket'); + +# try sending the wrong csrf token, then the right one +$m->add_header(Referer => undef); +$m->get_ok($test_page); +$m->content_contains("Possible cross-site request forgery"); +$m->content_contains("If you really intended to visit <tt>/Ticket/Create.html</tt>"); +$m->content_contains("your browser did not supply a Referrer header"); +$m->title_is('Possible cross-site request forgery'); + +# Sending a wrong CSRF is just a normal request. We'll make a request +# with just an invalid token, which means no Queue=, which means +# Create.html errors out. +my $link = $m->find_link(text_regex => qr{resume your request}); +(my $broken_url = $link->url) =~ s/(CSRF_Token)=\w+/$1=crud/; +$m->get_ok($broken_url); +$m->content_contains("Queue could not be loaded"); +$m->title_is('RT Error'); +$m->warning_like(qr/Queue could not be loaded/); + +# The token doesn't work for other pages, or other arguments to the same page. +$m->add_header(Referer => undef); +$m->get_ok($test_page); +$m->content_contains("Possible cross-site request forgery"); +my ($token) = $m->content =~ m{CSRF_Token=(\w+)}; + +$m->add_header(Referer => undef); +$m->get_ok("/Admin/Queues/Modify.html?id=new&Name=test&CSRF_Token=$token"); +$m->content_contains("Possible cross-site request forgery"); +$m->content_contains("If you really intended to visit <tt>/Admin/Queues/Modify.html</tt>"); +$m->content_contains("your browser did not supply a Referrer header"); +$m->title_is('Possible cross-site request forgery'); + +$m->follow_link(text_regex => qr{resume your request}); +$m->content_lacks("Possible cross-site request forgery"); +$m->title_is('Configuration for queue test'); + +# Try the same page, but different query parameters, which are blatted by the token +$m->get_ok("/Ticket/Create.html?Queue=$other_queue_id&CSRF_Token=$token"); +$m->content_lacks("Possible cross-site request forgery"); +$m->title_is('Create a new ticket'); +$m->text_unlike(qr/Queue:\s*Other queue/); +$m->text_like(qr/Queue:\s*General/); + +# Ensure that file uploads work across the interstitial +$m->delete_header('Referer'); +$m->get_ok($test_page); +$m->content_contains("Create a new ticket", 'ticket create page'); +$m->form_name('TicketCreate'); +$m->field('Subject', 'Attachments test'); + +my $logofile = "$RT::MasonComponentRoot/NoAuth/images/bpslogo.png"; +open LOGO, "<", $logofile or die "Can't open logo file: $!"; +binmode LOGO; +my $logo_contents = do {local $/; <LOGO>}; +close LOGO; +$m->field('Attach', $logofile); + +# Lose the referer before the POST +$m->add_header(Referer => undef); +$m->submit; +$m->content_contains("Possible cross-site request forgery"); +$m->content_contains("If you really intended to visit <tt>/Ticket/Create.html</tt>"); +$m->follow_link(text_regex => qr{resume your request}); +$m->content_contains('Download bpslogo.png', 'page has file name'); +$m->follow_link_ok({text => "Download bpslogo.png"}); +is($m->content, $logo_contents, "Binary content matches"); + + +# now try self-service with CSRF +my $user = RT::User->new(RT->SystemUser); +$user->Create(Name => "SelfService", Password => "chops", Privileged => 0); + +$m = RT::Test::Web->new; +$m->get_ok("$baseurl/index.html?user=SelfService&pass=chops"); +$m->title_is("Open tickets", "got self-service interface"); +$m->content_contains("My open tickets", "got self-service interface"); + +# post without referer +$m->add_header(Referer => undef); +$m->get_ok("/SelfService/Create.html?Queue=1"); +$m->content_contains("Possible cross-site request forgery"); +$m->content_contains("If you really intended to visit <tt>/SelfService/Create.html</tt>"); +$m->content_contains("your browser did not supply a Referrer header"); +$m->title_is('Possible cross-site request forgery'); + +$m->follow_link(text_regex => qr{resume your request}); +$m->content_lacks("Possible cross-site request forgery"); +is($m->response->redirects, 0, "no redirection"); +like($m->response->request->uri, qr{^http://[^/]+\Q/SelfService/Create.html\E\?CSRF_Token=\w+$}); +$m->title_is('Create a ticket'); +$m->content_contains('Describe the issue below:'); + +undef $m; +done_testing; diff --git a/rt/t/web/installer.t b/rt/t/web/installer.t new file mode 100644 index 000000000..4dc82df47 --- /dev/null +++ b/rt/t/web/installer.t @@ -0,0 +1,95 @@ +#!/usr/bin/perl +use strict; +use warnings; + +$ENV{RT_TEST_WEB_HANDLER} = 'plack+rt-server'; +use RT::Test + tests => undef, + nodb => 1, + server_ok => 1; + +my ($base, $m) = RT::Test->started_ok; + +$m->warning_like(qr/If this is a new installation of RT/, + "Got startup warning"); + +$m->get_ok($base); +like $m->uri, qr/Install/, 'at installer'; + +diag "Testing language change"; +{ + $m->submit_form_ok( + { + with_fields => { + Lang => 'fr', + }, + button => 'ChangeLang', + }, + 'change language to french' + ); + $m->content_like(qr/RT\s+pour\s+example\.com/i); + $m->submit_form_ok( + { + with_fields => { + Lang => 'en', + }, + button => 'ChangeLang', + }, + 'change language to english' + ); + $m->content_like(qr/RT\s+for\s+example\.com/i); +} + +diag "Walking through install screens setting defaults"; +{ + $m->click_ok('Run'); + + # Database type + $m->content_contains('DatabaseType'); + $m->content_contains($_, "found database $_") + for qw(MySQL PostgreSQL Oracle SQLite); + $m->submit(); + + # Database details + $m->content_contains('DatabaseName'); + $m->submit(); + $m->content_contains('Connection succeeded'); + $m->submit_form_ok({ button => 'Next' }); + + # Basic options + $m->submit_form_ok({ + with_fields => { + Password => 'password', + } + }, 'set root password'); + + # Mail options + $m->submit_form_ok({ + with_fields => { + OwnerEmail => 'admin@example.com', + }, + }, 'set admin email'); + + # Mail addresses + $m->submit_form_ok({ + with_fields => { + CorrespondAddress => 'rt@example.com', + CommentAddress => 'rt-comment@example.com', + }, + }, 'set addresses'); + + # Initialize database + $m->content_contains('database'); + $m->submit(); + + # Finish + $m->content_contains('/RT_SiteConfig.pm'); + $m->content_contains('Finish'); + $m->submit(); + + $m->content_contains('Login'); + ok $m->login(), 'logged in'; +} + +undef $m; +done_testing; diff --git a/rt/t/web/owner_disabled_group_19221.t b/rt/t/web/owner_disabled_group_19221.t new file mode 100644 index 000000000..2664c5bc2 --- /dev/null +++ b/rt/t/web/owner_disabled_group_19221.t @@ -0,0 +1,190 @@ +#!/usr/bin/env perl +use strict; +use warnings; + +use RT::Test tests => undef; + +my $queue = RT::Test->load_or_create_queue( Name => 'Test' ); +ok $queue && $queue->id, 'loaded or created queue'; + +my $user = RT::Test->load_or_create_user( + Name => 'ausername', + Privileged => 1, +); +ok $user && $user->id, 'loaded or created user'; + +my $group = RT::Group->new(RT->SystemUser); +my ($ok, $msg) = $group->CreateUserDefinedGroup(Name => 'Disabled Group'); +ok($ok, $msg); + +($ok, $msg) = $group->AddMember( $user->PrincipalId ); +ok($ok, $msg); + +ok( RT::Test->set_rights({ + Principal => $group, + Object => $queue, + Right => [qw(OwnTicket)] +}), 'set rights'); + +RT->Config->Set( AutocompleteOwners => 0 ); +my ($base, $m) = RT::Test->started_ok; +ok $m->login, 'logged in'; + +diag "user from group shows up in create form"; +{ + $m->get_ok('/', 'open home page'); + $m->form_name('CreateTicketInQueue'); + $m->select( 'Queue', $queue->id ); + $m->submit; + + $m->content_contains('Create a new ticket', 'opened create ticket page'); + my $form = $m->form_name('TicketCreate'); + my $input = $form->find_input('Owner'); + is $input->value, RT->Nobody->Id, 'correct owner selected'; + ok((scalar grep { $_ == $user->Id } $input->possible_values), 'user from group is in dropdown'); +} + +diag "user from disabled group DOESN'T shows up in create form"; +{ + ($ok, $msg) = $group->SetDisabled(1); + ok($ok, $msg); + + $m->get_ok('/', 'open home page'); + $m->form_name('CreateTicketInQueue'); + $m->select( 'Queue', $queue->id ); + $m->submit; + + $m->content_contains('Create a new ticket', 'opened create ticket page'); + my $form = $m->form_name('TicketCreate'); + my $input = $form->find_input('Owner'); + is $input->value, RT->Nobody->Id, 'correct owner selected'; + ok((not scalar grep { $_ == $user->Id } $input->possible_values), 'user from disabled group is NOT in dropdown'); + ($ok, $msg) = $group->SetDisabled(0); + ok($ok, $msg); +} + + + +diag "Put us in a nested group"; +my $super = RT::Group->new(RT->SystemUser); +($ok, $msg) = $super->CreateUserDefinedGroup(Name => 'Supergroup'); +ok($ok, $msg); + +($ok, $msg) = $super->AddMember( $group->PrincipalId ); +ok($ok, $msg); + +ok( RT::Test->set_rights({ + Principal => $super, + Object => $queue, + Right => [qw(OwnTicket)] +}), 'set rights'); + + +diag "Disable the middle group"; +{ + ($ok, $msg) = $group->SetDisabled(1); + ok($ok, "Disabled group: $msg"); + + $m->get_ok('/', 'open home page'); + $m->form_name('CreateTicketInQueue'); + $m->select( 'Queue', $queue->id ); + $m->submit; + + $m->content_contains('Create a new ticket', 'opened create ticket page'); + my $form = $m->form_name('TicketCreate'); + my $input = $form->find_input('Owner'); + is $input->value, RT->Nobody->Id, 'correct owner selected'; + ok((not scalar grep { $_ == $user->Id } $input->possible_values), 'user from disabled group is NOT in dropdown'); + ($ok, $msg) = $group->SetDisabled(0); + ok($ok, "Re-enabled group: $msg"); +} + +diag "Disable the top group"; +{ + ($ok, $msg) = $super->SetDisabled(1); + ok($ok, "Disabled supergroup: $msg"); + + $m->get_ok('/', 'open home page'); + $m->form_name('CreateTicketInQueue'); + $m->select( 'Queue', $queue->id ); + $m->submit; + + $m->content_contains('Create a new ticket', 'opened create ticket page'); + my $form = $m->form_name('TicketCreate'); + my $input = $form->find_input('Owner'); + is $input->value, RT->Nobody->Id, 'correct owner selected'; + ok((not scalar grep { $_ == $user->Id } $input->possible_values), 'user from disabled group is NOT in dropdown'); + ($ok, $msg) = $super->SetDisabled(0); + ok($ok, "Re-enabled supergroup: $msg"); +} + + +diag "Check WithMember and WithoutMember recursively"; +{ + my $with = RT::Groups->new( RT->SystemUser ); + $with->WithMember( PrincipalId => $user->PrincipalObj->Id, Recursively => 1 ); + $with->Limit( FIELD => 'domain', OPERATOR => '=', VALUE => 'UserDefined' ); + is_deeply( + [map {$_->Name} @{$with->ItemsArrayRef}], + ['Disabled Group','Supergroup'], + "Get expected recursive memberships", + ); + + my $without = RT::Groups->new( RT->SystemUser ); + $without->WithoutMember( PrincipalId => $user->PrincipalObj->Id, Recursively => 1 ); + $without->Limit( FIELD => 'domain', OPERATOR => '=', VALUE => 'UserDefined' ); + is_deeply( + [map {$_->Name} @{$without->ItemsArrayRef}], + [], + "And not a member of no groups", + ); + + ($ok, $msg) = $super->SetDisabled(1); + ok($ok, "Disabled supergroup: $msg"); + $with->RedoSearch; + $without->RedoSearch; + is_deeply( + [map {$_->Name} @{$with->ItemsArrayRef}], + ['Disabled Group'], + "Recursive check only contains subgroup", + ); + is_deeply( + [map {$_->Name} @{$without->ItemsArrayRef}], + [], + "Doesn't find the currently disabled group", + ); + ($ok, $msg) = $super->SetDisabled(0); + ok($ok, "Re-enabled supergroup: $msg"); + + ($ok, $msg) = $group->SetDisabled(1); + ok($ok, "Disabled intermediate group: $msg"); + $with->RedoSearch; + $without->RedoSearch; + is_deeply( + [map {$_->Name} @{$with->ItemsArrayRef}], + [], + "Recursive check finds no groups", + ); + is_deeply( + [map {$_->Name} @{$without->ItemsArrayRef}], + ['Supergroup'], + "Now not a member of the supergroup", + ); + ($ok, $msg) = $group->SetDisabled(0); + ok($ok, "Re-enabled intermediate group: $msg"); +} + +diag "Check MemberOfGroup"; +{ + ($ok, $msg) = $group->SetDisabled(1); + ok($ok, "Disabled intermediate group: $msg"); + my $users = RT::Users->new(RT->SystemUser); + $users->MemberOfGroup($super->PrincipalObj->id); + is($users->Count, 0, "Supergroup claims no members"); + ($ok, $msg) = $group->SetDisabled(0); + ok($ok, "Re-enabled intermediate group: $msg"); +} + + +undef $m; +done_testing; diff --git a/rt/t/web/query_builder_queue_limits.t b/rt/t/web/query_builder_queue_limits.t new file mode 100644 index 000000000..a3b976524 --- /dev/null +++ b/rt/t/web/query_builder_queue_limits.t @@ -0,0 +1,180 @@ +use strict; +use warnings; + +use RT::Test tests => 34; + +my $lifecycles = RT->Config->Get('Lifecycles'); +$lifecycles->{foo} = { + initial => ['initial'], + active => ['open'], + inactive => ['resolved'], + +}; + +RT::Lifecycle->FillCache(); + +my $general = RT::Test->load_or_create_queue( Name => 'General' ); +my $foo = RT::Test->load_or_create_queue( Name => 'foo', Lifecycle => 'foo' ); + +my $global_cf = RT::Test->load_or_create_custom_field( + Name => 'global_cf', + Queue => 0, + Type => 'FreeformSingle', +); + +my $general_cf = RT::Test->load_or_create_custom_field( + Name => 'general_cf', + Queue => 'General', + Type => 'FreeformSingle', +); + +my $foo_cf = RT::Test->load_or_create_custom_field( + Name => 'foo_cf', + Queue => 'foo', + Type => 'FreeformSingle' +); + +my $root = RT::Test->load_or_create_user( Name => 'root', ); +my $user_a = RT::Test->load_or_create_user( + Name => 'user_a', + Password => 'password', +); +my $user_b = RT::Test->load_or_create_user( + Name => 'user_b', + Password => 'password', +); + +ok( + RT::Test->set_rights( + { + Principal => $user_a, + Object => $general, + Right => ['OwnTicket'], + }, + { + Principal => $user_b, + Object => $foo, + Right => ['OwnTicket'], + }, + ), + 'granted OwnTicket right for user_a and user_b' +); + +my ( $url, $m ) = RT::Test->started_ok; +ok( $m->login, 'logged in' ); + +$m->get_ok( $url . '/Search/Build.html' ); + +diag "check default statuses, cf and owners"; +my $form = $m->form_name('BuildQuery'); +ok( $form, 'found BuildQuery form' ); +ok( $form->find_input("ValueOf'CF.{global_cf}'"), 'found global_cf by default' ); +ok( !$form->find_input("ValueOf'CF.{general_cf}'"), 'no general_cf by default' ); +ok( !$form->find_input("ValueOf'CF.{foo_cf}'"), 'no foo_cf by default' ); + +my $status_input = $form->find_input('ValueOfStatus'); +my @statuses = sort $status_input->possible_values; +is_deeply( + \@statuses, [ '', qw/initial new open rejected resolved stalled/], 'found all statuses' +); + +my $owner_input = $form->find_input('ValueOfActor'); +my @owners = sort $owner_input->possible_values; +is_deeply( + \@owners, [ '', qw/Nobody root user_a user_b/], 'found all users' +); + +diag "limit queue to foo"; +$m->submit_form( + fields => { ValueOfQueue => 'foo' }, + button => 'AddClause', +); + +$form = $m->form_name('BuildQuery'); +ok( $form->find_input("ValueOf'CF.{foo_cf}'"), 'found foo_cf' ); +ok( $form->find_input("ValueOf'CF.{global_cf}'"), 'found global_cf' ); +ok( !$form->find_input("ValueOf'CF.{general_cf}'"), 'still no general_cf' ); +$status_input = $form->find_input('ValueOfStatus'); +@statuses = sort $status_input->possible_values; +is_deeply( + \@statuses, + [ '', qw/initial open resolved/ ], + 'found statuses from foo only' +); + +$owner_input = $form->find_input('ValueOfActor'); +@owners = sort $owner_input->possible_values; +is_deeply( + \@owners, [ '', qw/Nobody root user_b/], 'no user_a' +); + +diag "limit queue to general too"; + +$m->submit_form( + fields => { ValueOfQueue => 'General' }, + button => 'AddClause', +); + +$form = $m->form_name('BuildQuery'); +ok( $form->find_input("ValueOf'CF.{general_cf}'"), 'found general_cf' ); +ok( $form->find_input("ValueOf'CF.{foo_cf}'"), 'found foo_cf' ); +ok( $form->find_input("ValueOf'CF.{global_cf}'"), 'found global_cf' ); +$status_input = $form->find_input('ValueOfStatus'); +@statuses = sort $status_input->possible_values; +is_deeply( + \@statuses, + [ '', qw/initial new open rejected resolved stalled/ ], + 'found all statuses again' +); +$owner_input = $form->find_input('ValueOfActor'); +@owners = sort $owner_input->possible_values; +is_deeply( + \@owners, [ '', qw/Nobody root user_a user_b/], 'found all users again' +); + +diag "limit queue to != foo"; +$m->get_ok( $url . '/Search/Build.html?NewQuery=1' ); +$m->submit_form( + form_name => 'BuildQuery', + fields => { ValueOfQueue => 'foo', QueueOp => '!=' }, + button => 'AddClause', +); + +$form = $m->form_name('BuildQuery'); +ok( $form->find_input("ValueOf'CF.{global_cf}'"), 'found global_cf' ); +ok( !$form->find_input("ValueOf'CF.{foo_cf}'"), 'no foo_cf' ); +ok( !$form->find_input("ValueOf'CF.{general_cf}'"), 'no general_cf' ); +$status_input = $form->find_input('ValueOfStatus'); +@statuses = sort $status_input->possible_values; +is_deeply( + \@statuses, [ '', qw/initial new open rejected resolved stalled/], + 'found all statuses' +); +$owner_input = $form->find_input('ValueOfActor'); +@owners = sort $owner_input->possible_values; +is_deeply( + \@owners, [ '', qw/Nobody root user_a user_b/], 'found all users' +); + +diag "limit queue to General OR foo"; +$m->get_ok( $url . '/Search/Edit.html' ); +$m->submit_form( + form_name => 'BuildQueryAdvanced', + fields => { Query => q{Queue = 'General' OR Queue = 'foo'} }, +); +$form = $m->form_name('BuildQuery'); +ok( $form->find_input("ValueOf'CF.{general_cf}'"), 'found general_cf' ); +ok( $form->find_input("ValueOf'CF.{foo_cf}'"), 'found foo_cf' ); +ok( $form->find_input("ValueOf'CF.{global_cf}'"), 'found global_cf' ); +$status_input = $form->find_input('ValueOfStatus'); +@statuses = sort $status_input->possible_values; +is_deeply( + \@statuses, + [ '', qw/initial new open rejected resolved stalled/ ], + 'found all statuses' +); +$owner_input = $form->find_input('ValueOfActor'); +@owners = sort $owner_input->possible_values; +is_deeply( + \@owners, [ '', qw/Nobody root user_a user_b/], 'found all users' +); diff --git a/rt/t/web/rest_cfs_with_same_name.t b/rt/t/web/rest_cfs_with_same_name.t new file mode 100644 index 000000000..958f67177 --- /dev/null +++ b/rt/t/web/rest_cfs_with_same_name.t @@ -0,0 +1,88 @@ +use strict; +use warnings; +use RT::Interface::REST; + +use RT::Test tests => 25; + +my ( $baseurl, $m ) = RT::Test->started_ok; +for my $queue_name (qw/foo bar/) { + + my $queue = RT::Test->load_or_create_queue( Name => $queue_name ); + ok( $queue, "created queue $queue_name" ); + my $cf = RT::Test->load_or_create_custom_field( + Name => 'test', + Type => 'Freeform', + Queue => $queue_name, + ); + ok( $cf->id, "created cf test for queue $queue_name " . $cf->id ); + + $m->post( + "$baseurl/REST/1.0/ticket/new", + [ + user => 'root', + pass => 'password', + format => 'l', + ] + ); + + my $text = $m->content; + my @lines = $text =~ m{.*}g; + shift @lines; # header + + # cfs aren't in the default ticket form + push @lines, "CF.{test}: baz"; + + $text = join "\n", @lines; + + ok( $text =~ s/Subject:\s*$/Subject: test cf/m, + "successfully replaced subject" ); + ok( $text =~ s/Queue: General\s*$/Queue: $queue_name/m, + "successfully replaced Queue" ); + + $m->post( + "$baseurl/REST/1.0/ticket/edit", + [ + user => 'root', + pass => 'password', + content => $text, + ], + Content_Type => 'form-data' + ); + + my ($id) = $m->content =~ /Ticket (\d+) created/; + ok( $id, "got ticket #$id" ); + + my $ticket = RT::Ticket->new( RT->SystemUser ); + $ticket->Load($id); + is( $ticket->id, $id, "loaded the REST-created ticket" ); + is( $ticket->Subject, "test cf", "subject successfully set" ); + is( $ticket->Queue, $queue->id, "queue successfully set" ); + is( $ticket->FirstCustomFieldValue("test"), "baz", "cf successfully set" ); + + $m->post( + "$baseurl/REST/1.0/ticket/show", + [ + user => 'root', + pass => 'password', + format => 'l', + id => "ticket/$id", + ] + ); + $text = $m->content; + like( $text, qr/^CF\.{test}: baz\s*$/m, 'cf value in rest show' ); + + $text =~ s{.*}{}; # remove header + $text =~ s!CF\.{test}: baz!CF.{test}: newbaz!; + $m->post( + "$baseurl/REST/1.0/ticket/edit", + [ + user => 'root', + pass => 'password', + content => $text, + ], + Content_Type => 'form-data' + ); + $m->content =~ /Ticket ($id) updated/; + is( $ticket->FirstCustomFieldValue("test"), "newbaz", "cf successfully updated" ); +} + |