import torrus 1.0.9
[freeside.git] / rt / t / web / gnupg-outgoing.t
1 #!/usr/bin/perl -w
2 use strict;
3 use warnings;
4
5 use RT::Test tests => 492;
6
7 plan skip_all => 'GnuPG required.'
8     unless eval 'use GnuPG::Interface; 1';
9 plan skip_all => 'gpg executable is required.'
10     unless RT::Test->find_executable('gpg');
11
12
13 use RT::Action::SendEmail;
14 use File::Temp qw(tempdir);
15
16 RT::Test->set_mail_catcher;
17
18 use_ok('RT::Crypt::GnuPG');
19
20 RT->Config->Set( GnuPG =>
21     Enable => 1,
22     OutgoingMessagesFormat => 'RFC',
23 );
24
25 RT->Config->Set( GnuPGOptions =>
26     homedir => scalar tempdir( CLEANUP => 1 ),
27     passphrase => 'rt-test',
28     'no-permission-warning' => undef,
29     'trust-model' => 'always',
30 );
31 RT->Config->Set( 'MailPlugins' => 'Auth::MailFrom', 'Auth::GnuPG' );
32
33 RT::Test->import_gnupg_key('rt-recipient@example.com');
34 RT::Test->import_gnupg_key('rt-test@example.com', 'public');
35
36 my $queue = RT::Test->load_or_create_queue(
37     Name              => 'Regression',
38     CorrespondAddress => 'rt-recipient@example.com',
39     CommentAddress    => 'rt-recipient@example.com',
40 );
41 ok $queue && $queue->id, 'loaded or created queue';
42
43 RT::Test->set_rights(
44     Principal => 'Everyone',
45     Right => ['CreateTicket', 'ShowTicket', 'SeeQueue', 'ReplyToTicket', 'ModifyTicket'],
46 );
47
48 my ($baseurl, $m) = RT::Test->started_ok;
49 ok $m->login, 'logged in';
50
51 my @variants = (
52     {},
53     { Sign => 1 },
54     { Encrypt => 1 },
55     { Sign => 1, Encrypt => 1 },
56 );
57
58 # collect emails
59 my %mail = (
60     plain            => [],
61     signed           => [],
62     encrypted        => [],
63     signed_encrypted => [],
64 );
65
66 diag "check in read-only mode that queue's props influence create/update ticket pages" if $ENV{TEST_VERBOSE};
67 {
68     foreach my $variant ( @variants ) {
69         set_queue_crypt_options( %$variant );
70         $m->goto_create_ticket( $queue );
71         $m->form_name('TicketCreate');
72         if ( $variant->{'Encrypt'} ) {
73             ok $m->value('Encrypt', 2), "encrypt tick box is checked";
74         } else {
75             ok !$m->value('Encrypt', 2), "encrypt tick box is unchecked";
76         }
77         if ( $variant->{'Sign'} ) {
78             ok $m->value('Sign', 2), "sign tick box is checked";
79         } else {
80             ok !$m->value('Sign', 2), "sign tick box is unchecked";
81         }
82     }
83
84     # to avoid encryption/signing during create
85     set_queue_crypt_options();
86
87     my $ticket = RT::Ticket->new( $RT::SystemUser );
88     my ($id) = $ticket->Create(
89         Subject   => 'test',
90         Queue     => $queue->id,
91         Requestor => 'rt-test@example.com',
92     );
93     ok $id, 'ticket created';
94
95     foreach my $variant ( @variants ) {
96         set_queue_crypt_options( %$variant );
97         $m->goto_ticket( $id );
98         $m->follow_link_ok({text => 'Reply'}, '-> reply');
99         $m->form_number(3);
100         if ( $variant->{'Encrypt'} ) {
101             ok $m->value('Encrypt', 2), "encrypt tick box is checked";
102         } else {
103             ok !$m->value('Encrypt', 2), "encrypt tick box is unchecked";
104         }
105         if ( $variant->{'Sign'} ) {
106             ok $m->value('Sign', 2), "sign tick box is checked";
107         } else {
108             ok !$m->value('Sign', 2), "sign tick box is unchecked";
109         }
110     }
111 }
112
113 # create a ticket for each combination
114 foreach my $queue_set ( @variants ) {
115     set_queue_crypt_options( %$queue_set );
116     foreach my $ticket_set ( @variants ) {
117         create_a_ticket( %$ticket_set );
118     }
119 }
120
121 my $tid;
122 {
123     my $ticket = RT::Ticket->new( $RT::SystemUser );
124     ($tid) = $ticket->Create(
125         Subject   => 'test',
126         Queue     => $queue->id,
127         Requestor => 'rt-test@example.com',
128     );
129     ok $tid, 'ticket created';
130 }
131
132 # again for each combination add a reply message
133 foreach my $queue_set ( @variants ) {
134     set_queue_crypt_options( %$queue_set );
135     foreach my $ticket_set ( @variants ) {
136         update_ticket( $tid, %$ticket_set );
137     }
138 }
139
140
141 # ------------------------------------------------------------------------------
142 # now delete all keys from the keyring and put back secret/pub pair for rt-test@
143 # and only public key for rt-recipient@ so we can verify signatures and decrypt
144 # like we are on another side recieve emails
145 # ------------------------------------------------------------------------------
146
147 unlink $_ foreach glob( RT->Config->Get('GnuPGOptions')->{'homedir'} ."/*" );
148 RT::Test->import_gnupg_key('rt-recipient@example.com', 'public');
149 RT::Test->import_gnupg_key('rt-test@example.com');
150
151 $queue = RT::Test->load_or_create_queue(
152     Name              => 'Regression',
153     CorrespondAddress => 'rt-test@example.com',
154     CommentAddress    => 'rt-test@example.com',
155 );
156 ok $queue && $queue->id, 'changed props of the queue';
157
158 foreach my $mail ( map cleanup_headers($_), @{ $mail{'plain'} } ) {
159     my ($status, $id) = RT::Test->send_via_mailgate($mail);
160     is ($status >> 8, 0, "The mail gateway exited normally");
161     ok ($id, "got id of a newly created ticket - $id");
162
163     my $tick = RT::Ticket->new( $RT::SystemUser );
164     $tick->Load( $id );
165     ok ($tick->id, "loaded ticket #$id");
166
167     my $txn = $tick->Transactions->First;
168     my ($msg, @attachments) = @{$txn->Attachments->ItemsArrayRef};
169
170     ok !$msg->GetHeader('X-RT-Privacy'), "RT's outgoing mail has no crypto";
171     is $msg->GetHeader('X-RT-Incoming-Encryption'), 'Not encrypted',
172         "RT's outgoing mail looks not encrypted";
173     ok !$msg->GetHeader('X-RT-Incoming-Signature'),
174         "RT's outgoing mail looks not signed";
175
176     like $msg->Content, qr/Some content/, "RT's mail includes copy of ticket text";
177 }
178
179 foreach my $mail ( map cleanup_headers($_), @{ $mail{'signed'} } ) {
180     my ($status, $id) = RT::Test->send_via_mailgate($mail);
181     is ($status >> 8, 0, "The mail gateway exited normally");
182     ok ($id, "got id of a newly created ticket - $id");
183
184     my $tick = RT::Ticket->new( $RT::SystemUser );
185     $tick->Load( $id );
186     ok ($tick->id, "loaded ticket #$id");
187
188     my $txn = $tick->Transactions->First;
189     my ($msg, @attachments) = @{$txn->Attachments->ItemsArrayRef};
190
191     is $msg->GetHeader('X-RT-Privacy'), 'PGP',
192         "RT's outgoing mail has crypto";
193     is $msg->GetHeader('X-RT-Incoming-Encryption'), 'Not encrypted',
194         "RT's outgoing mail looks not encrypted";
195     like $msg->GetHeader('X-RT-Incoming-Signature'),
196         qr/<rt-recipient\@example.com>/,
197         "RT's outgoing mail looks signed";
198
199     like $attachments[0]->Content, qr/Some content/,
200         "RT's mail includes copy of ticket text";
201 }
202
203 foreach my $mail ( map cleanup_headers($_), @{ $mail{'encrypted'} } ) {
204     my ($status, $id) = RT::Test->send_via_mailgate($mail);
205     is ($status >> 8, 0, "The mail gateway exited normally");
206     ok ($id, "got id of a newly created ticket - $id");
207
208     my $tick = RT::Ticket->new( $RT::SystemUser );
209     $tick->Load( $id );
210     ok ($tick->id, "loaded ticket #$id");
211
212     my $txn = $tick->Transactions->First;
213     my ($msg, @attachments) = @{$txn->Attachments->ItemsArrayRef};
214
215     is $msg->GetHeader('X-RT-Privacy'), 'PGP',
216         "RT's outgoing mail has crypto";
217     is $msg->GetHeader('X-RT-Incoming-Encryption'), 'Success',
218         "RT's outgoing mail looks encrypted";
219     ok !$msg->GetHeader('X-RT-Incoming-Signature'),
220         "RT's outgoing mail looks not signed";
221
222     like $attachments[0]->Content, qr/Some content/,
223         "RT's mail includes copy of ticket text";
224 }
225
226 foreach my $mail ( map cleanup_headers($_), @{ $mail{'signed_encrypted'} } ) {
227     my ($status, $id) = RT::Test->send_via_mailgate($mail);
228     is ($status >> 8, 0, "The mail gateway exited normally");
229     ok ($id, "got id of a newly created ticket - $id");
230
231     my $tick = RT::Ticket->new( $RT::SystemUser );
232     $tick->Load( $id );
233     ok ($tick->id, "loaded ticket #$id");
234
235     my $txn = $tick->Transactions->First;
236     my ($msg, @attachments) = @{$txn->Attachments->ItemsArrayRef};
237
238     is $msg->GetHeader('X-RT-Privacy'), 'PGP',
239         "RT's outgoing mail has crypto";
240     is $msg->GetHeader('X-RT-Incoming-Encryption'), 'Success',
241         "RT's outgoing mail looks encrypted";
242     like $msg->GetHeader('X-RT-Incoming-Signature'),
243         qr/<rt-recipient\@example.com>/,
244         "RT's outgoing mail looks signed";
245
246     like $attachments[0]->Content, qr/Some content/,
247         "RT's mail includes copy of ticket text";
248 }
249
250 sub create_a_ticket {
251     my %args = (@_);
252
253     RT::Test->clean_caught_mails;
254
255     $m->goto_create_ticket( $queue );
256     $m->form_name('TicketCreate');
257     $m->field( Subject    => 'test' );
258     $m->field( Requestors => 'rt-test@example.com' );
259     $m->field( Content    => 'Some content' );
260
261     foreach ( qw(Sign Encrypt) ) {
262         if ( $args{ $_ } ) {
263             $m->tick( $_ => 1 );
264         } else {
265             $m->untick( $_ => 1 );
266         }
267     }
268
269     $m->submit;
270     is $m->status, 200, "request successful";
271
272     unlike($m->content, qr/unable to sign outgoing email messages/);
273
274     $m->get_ok('/'); # ensure that the mail has been processed
275
276     my @mail = RT::Test->fetch_caught_mails;
277     check_text_emails( \%args, @mail );
278 }
279
280 sub update_ticket {
281     my $tid = shift;
282     my %args = (@_);
283
284     RT::Test->clean_caught_mails;
285
286     ok $m->goto_ticket( $tid ), "UI -> ticket #$tid";
287     $m->follow_link_ok( { text => 'Reply' }, 'ticket -> reply' );
288     $m->form_number(3);
289     $m->field( UpdateContent => 'Some content' );
290
291     foreach ( qw(Sign Encrypt) ) {
292         if ( $args{ $_ } ) {
293             $m->tick( $_ => 1 );
294         } else {
295             $m->untick( $_ => 1 );
296         }
297     }
298
299     $m->click('SubmitTicket');
300     is $m->status, 200, "request successful";
301     $m->content_like(qr/Message recorded/, 'Message recorded') or diag $m->content;
302
303     $m->get_ok('/'); # ensure that the mail has been processed
304
305     my @mail = RT::Test->fetch_caught_mails;
306     check_text_emails( \%args, @mail );
307 }
308
309 sub check_text_emails {
310     my %args = %{ shift @_ };
311     my @mail = @_;
312
313     ok scalar @mail, "got some mail";
314     for my $mail (@mail) {
315         if ( $args{'Encrypt'} ) {
316             unlike $mail, qr/Some content/, "outgoing email was encrypted";
317         } else {
318             like $mail, qr/Some content/, "outgoing email was not encrypted";
319         } 
320         if ( $args{'Sign'} && $args{'Encrypt'} ) {
321             like $mail, qr/BEGIN PGP MESSAGE/, 'outgoing email was signed';
322         } elsif ( $args{'Sign'} ) {
323             like $mail, qr/SIGNATURE/, 'outgoing email was signed';
324         } else {
325             unlike $mail, qr/SIGNATURE/, 'outgoing email was not signed';
326         }
327     }
328     if ( $args{'Sign'} && $args{'Encrypt'} ) {
329         push @{ $mail{'signed_encrypted'} }, @mail;
330     } elsif ( $args{'Sign'} ) {
331         push @{ $mail{'signed'} }, @mail;
332     } elsif ( $args{'Encrypt'} ) {
333         push @{ $mail{'encrypted'} }, @mail;
334     } else {
335         push @{ $mail{'plain'} }, @mail;
336     }
337 }
338
339 sub cleanup_headers {
340     my $mail = shift;
341     # strip id from subject to create new ticket
342     $mail =~ s/^(Subject:)\s*\[.*?\s+#\d+\]\s*/$1 /m;
343     # strip several headers
344     foreach my $field ( qw(Message-ID X-RT-Original-Encoding RT-Originator RT-Ticket X-RT-Loop-Prevention) ) {
345         $mail =~ s/^$field:.*?\n(?! |\t)//gmsi;
346     }
347     return $mail;
348 }
349
350 sub set_queue_crypt_options {
351     my %args = @_;
352     $m->get_ok("/Admin/Queues/Modify.html?id=". $queue->id);
353     $m->form_with_fields('Sign', 'Encrypt');
354     foreach my $opt ('Sign', 'Encrypt') {
355         if ( $args{$opt} ) {
356             $m->tick($opt => 1);
357         } else {
358             $m->untick($opt => 1);
359         }
360     }
361     $m->submit;
362 }
363