File Coverage

blib/lib/Mail/TLSRPT/Report.pm
Criterion Covered Total %
statement 110 115 95.6
branch 10 16 62.5
condition n/a
subroutine 22 22 100.0
pod 8 8 100.0
total 150 161 93.1


line stmt bran cond sub pod time code
1             package Mail::TLSRPT::Report;
2             # ABSTRACT: TLSRPT report object
3             our $VERSION = '1.20200413.1'; # VERSION
4 7     7   4712 use 5.20.0;
  7         27  
5 7     7   41 use Moo;
  7         16  
  7         40  
6 7     7   2249 use Mail::TLSRPT::Pragmas;
  7         25  
  7         60  
7 7     7   4586 use Mail::TLSRPT::Policy;
  7         26  
  7         259  
8 7     7   61 use Mail::TLSRPT::Failure;
  7         16  
  7         147  
9 7     7   7582 use DateTime;
  7         3512522  
  7         412  
10 7     7   4883 use Date::Parse qw{ str2time };
  7         52353  
  7         672  
11 7     7   4441 use IO::Uncompress::Gunzip;
  7         328030  
  7         439  
12 7     7   5630 use Text::CSV;
  7         100148  
  7         11335  
13             has organization_name => (is => 'rw', isa => Str, required => 1);
14             has start_datetime => (is => 'rw', isa => class_type('DateTime'), required => 1, coerce => sub{&_coerce_datetime});
15             has end_datetime => (is => 'rw', isa => class_type('DateTime'), required => 1, coerce => sub{&_coerce_datetime});
16             has contact_info => (is => 'rw', isa => Str, required => 1);
17             has report_id => (is => 'rw', isa => Str, required => 1);
18 1     1   4037 has policies => (is => 'rw', isa => ArrayRef, required => 0, lazy => 1, builder => sub{return []} );
19              
20             sub _coerce_datetime {
21 18     18   44 my $time = shift;
22 18 100       68 $time = DateTime->from_epoch( epoch=>$time ) unless ref $time eq 'DateTime';
23 18         610 return $time;
24             }
25              
26              
27 7     7 1 28688 sub new_from_json($class,$json) {
  7         20  
  7         17  
  7         16  
28 7 50       45 if ( $json =~ /^\037\213/ ) {
29 0         0 return $class->new_from_json_gz($json);
30             }
31 7         122 my $j = JSON->new;
32 7         208 my $data = $j->decode($json);
33 7         36 return $class->new_from_data($data);
34             }
35              
36              
37 1     1 1 4275 sub new_from_json_gz($class,$compressed_json) {
  1         2  
  1         3  
  1         2  
38 1         2 my $json;
39 1         8 IO::Uncompress::Gunzip::gunzip(\$compressed_json,\$json);
40 1         2498 return $class->new_from_json($json);
41             }
42              
43              
44 8     8 1 4864 sub new_from_data($class,$data) {
  8         20  
  8         57  
  8         17  
45 8         15 my @policies;
46 8         39 foreach my $policy ( $data->{policies}->@* ) {
47 6         55 push @policies, Mail::TLSRPT::Policy->new_from_data($policy);
48             }
49             my $self = $class->new(
50             organization_name => $data->{'organization-name'},
51             start_datetime => DateTime->from_epoch(epoch => str2time($data->{'date-range'}->{'start-datetime'})),
52             end_datetime => DateTime->from_epoch(epoch => str2time($data->{'date-range'}->{'end-datetime'})),
53             contact_info => $data->{'contact-info'},
54 8         71 report_id => $data->{'report-id'},
55             policies => \@policies,
56             );
57 8         348 return $self;
58             }
59              
60              
61 3     3 1 2756 sub as_json($self) {
  3         8  
  3         5  
62 3         16 my $j = JSON->new;
63 3         38 $j->canonical;
64 3         10 return $j->encode( $self->as_struct );
65             }
66              
67              
68 5     5 1 1407 sub as_struct($self) {
  5         9  
  5         8  
69 5         126 my @policies = map {$_->as_struct} $self->policies->@*;
  3         37  
70             return {
71 5 100       493 'organization-name' => $self->organization_name,
72             'date-range' => {
73             'start-datetime' => $self->start_datetime->datetime.'Z',
74             'end-datetime' => $self->end_datetime->datetime.'Z',
75             },
76             'contact-info' => $self->contact_info,
77             'report-id' => $self->report_id,
78             scalar $self->policies->@* ? ( policies => \@policies ) : (),
79             };
80             }
81              
82              
83 4     4 1 10674 sub as_string($self) {
  4         10  
  4         8  
84             return join( "\n",
85             'Report-ID: <'.$self->report_id.'>',
86             'From: "'.$self->organization_name.'" <'.$self->contact_info.'>',
87             'Dates: '.$self->start_datetime->datetime.'Z to '.$self->end_datetime->datetime.'Z',
88 4         101 map { $_->as_string } $self->policies->@*,
  1         199  
89             );
90             }
91              
92 1     1   2 sub _register_prometheus($self,$prometheus) {
  1         2  
  1         2  
  1         2  
93 1         4 $prometheus->declare('tlsrpt_reports_processed_total', help=>'TLSRPT reports processed', type=>'counter' );
94             }
95              
96              
97 1     1 1 1453 sub process_prometheus($self,$prometheus) {
  1         3  
  1         2  
  1         2  
98 1         5 $self->_register_prometheus($prometheus);
99 1         262 $prometheus->add('tlsrpt_reports_processed_total',1,{
100             organization_name=>$self->organization_name,
101             policies=>scalar $self->policies->@*,
102             });
103 1 50       116 Mail::TLSRPT::Policy->_register_prometheus($prometheus) if ! scalar $self->policies->@*;
104 1 50       25 Mail::TLSRPT::Failure->_register_prometheus($prometheus) if ! scalar $self->policies->@*;
105 1         23 foreach my $policy ( $self->policies->@* ) {
106 1         11 $policy->process_prometheus($self,$prometheus);
107             }
108             }
109              
110 1     1   2 sub _csv_headers($self) {
  1         2  
  1         2  
111             return (
112 1         7 'report id',
113             'organization name',
114             'start date time',
115             'end date time',
116             'contact info',
117             );
118             }
119              
120 3     3   5 sub _csv_fragment($self) {
  3         6  
  3         4  
121             return (
122 3         53 $self->report_id,
123             $self->organization_name,
124             $self->start_datetime->datetime.'Z',
125             $self->end_datetime->datetime.'Z',
126             $self->contact_info,
127             );
128             }
129              
130              
131 1     1 1 664 sub as_csv($self,$args) {
  1         3  
  1         3  
  1         2  
132 1         2 my @output;
133 1         11 my $csv = Text::CSV->new;
134 1 50       162 if ( $args->{add_header} ) {
135 1         5 $csv->combine($self->_csv_headers,Mail::TLSRPT::Policy->_csv_headers,Mail::TLSRPT::Failure->_csv_headers);
136 1         44 push @output, $csv->string;
137             }
138 1 50       36 if ( scalar $self->policies->@* ) {
139 1         27 foreach my $policy ( $self->policies->@* ) {
140 1 50       24 if ( scalar $policy->failures->@* ) {
141 1         24 foreach my $failure ( $policy->failures->@* ) {
142 3         27 $csv->combine($self->_csv_fragment,$policy->_csv_fragment,$failure->_csv_fragment);
143 3         557 push @output, $csv->string;
144             }
145             }
146             else {
147 0         0 $csv->combine($self->_csv_fragment,$policy->_csv_fragment);
148 0         0 push @output, $csv->string;
149             }
150             }
151             }
152             else {
153 0         0 $csv->combine($self->_csv_fragment);
154 0         0 push @output, $csv->string;
155             }
156 1         43 return join( "\n", @output );
157             }
158              
159             1;
160              
161             __END__
162              
163             =pod
164              
165             =encoding UTF-8
166              
167             =head1 NAME
168              
169             Mail::TLSRPT::Report - TLSRPT report object
170              
171             =head1 VERSION
172              
173             version 1.20200413.1
174              
175             =head1 SYNOPSIS
176              
177             my $report = Mail::TLSRPT::Report->new(
178             organization_name => 'My Corp',
179             start_datetime => $date,
180             end_datetime => $enddate,
181             contact_info => 'reports@example.com',
182             report_id => '123abc',
183             policies => $policies,
184             };
185              
186             =head1 DESCRIPTION
187              
188             Classes to process tlsrpt report
189              
190             =head1 CONSTRUCTOR
191              
192             =head2 I<new($class)>
193              
194             Create a new object
195              
196             =head2 I<new_from_json($json)>
197              
198             Create a new object using a JSON string, this will create sub-objects as required.
199              
200             Will detect and handle a gzipped string.
201              
202             =head2 I<new_from_json_gz($json)>
203              
204             Create a new object using a gzipped JSON string, this will create sub-objects as required.
205              
206             =head2 I<new_from_data($data)>
207              
208             Create a new object using a data structure, this will create sub-objects as required.
209              
210             =head1 METHODS
211              
212             =head2 I<as_json>
213              
214             Return the current object and sub-objects as a json string
215              
216             =head2 I<as_struct>
217              
218             Return the current object and sub-objects as a data structure
219              
220             =head2 I<as_string>
221              
222             Return a textual human readable representation of the current object and its sub-objects
223              
224             =head2 I<process_prometheus($prometheus)>
225              
226             Generate metrics using the given Prometheus::Tiny object
227              
228             =head2 I<as_csv($args)>
229              
230             Return a csv representation of the current object and its sub-objects
231              
232             If the argument add_header is true then a csv header will be included in the output.
233              
234             =head1 AUTHOR
235              
236             Marc Bradshaw <marc@marcbradshaw.net>
237              
238             =head1 COPYRIGHT AND LICENSE
239              
240             This software is copyright (c) 2020 by Marc Bradshaw.
241              
242             This is free software; you can redistribute it and/or modify it under
243             the same terms as the Perl 5 programming language system itself.
244              
245             =cut