File Coverage

blib/lib/Finance/Bank/Postbank_de/APIv1.pm
Criterion Covered Total %
statement 96 115 83.4
branch 4 12 33.3
condition n/a
subroutine 22 23 95.6
pod 0 6 0.0
total 122 156 78.2


line stmt bran cond sub pod time code
1             package Finance::Bank::Postbank_de::APIv1;
2 9     9   66 use Moo;
  9         23  
  9         57  
3 9     9   8287 use JSON 'decode_json';
  9         83511  
  9         49  
4 9     9   5526 use Filter::signatures;
  9         200335  
  9         62  
5 9     9   386 no warnings 'experimental::signatures';
  9         21  
  9         306  
6 9     9   54 use feature 'signatures';
  9         20  
  9         293  
7 9     9   52 use Carp 'croak';
  9         18  
  9         421  
8 9     9   5693 use WWW::Mechanize;
  9         1030466  
  9         387  
9 9     9   4462 use Mozilla::CA;
  9         2306  
  9         284  
10 9     9   3953 use HTTP::CookieJar::LWP;
  9         225581  
  9         397  
11 9     9   8240 use IO::Socket::SSL qw(SSL_VERIFY_PEER SSL_VERIFY_NONE);
  9         583249  
  9         84  
12              
13 9     9   4905 use HAL::Resource;
  9         31  
  9         375  
14 9     9   4482 use Finance::Bank::Postbank_de::APIv1::Finanzstatus;
  9         32  
  9         330  
15 9     9   82 use Finance::Bank::Postbank_de::APIv1::Message;
  9         156  
  9         245  
16 9     9   4250 use Finance::Bank::Postbank_de::APIv1::Transaction;
  9         33  
  9         311  
17 9     9   68 use Finance::Bank::Postbank_de::APIv1::Account;
  9         21  
  9         212  
18 9     9   4236 use Finance::Bank::Postbank_de::APIv1::Depot;
  9         27  
  9         516  
19 9     9   4112 use Finance::Bank::Postbank_de::APIv1::Position;
  9         29  
  9         11299  
20              
21             our $VERSION = '0.55';
22              
23             =head1 NAME
24              
25             Finance::Bank::Postbank_de::APIv1 - Postbank connection
26              
27             =head1 SYNOPSIS
28              
29             my $api = Finance::Bank::Postbank_de::APIv1->new();
30             $api->configure_ua();
31             my $postbank = $api->login( 'Petra.Pfiffig', '11111' );
32              
33             =cut
34              
35             #my $logger;
36             has ua => (
37             is => 'ro',
38             default => sub( $class ) {
39             my $ua = WWW::Mechanize->new(
40             autocheck => 1,
41             keep_alive => 1,
42             cookie_jar => HTTP::CookieJar::LWP->new(),
43             );
44             #use LWP::ConsoleLogger::Easy qw( debug_ua );
45             #$logger = debug_ua($ua);
46             #$logger->dump_content(0);
47             #$logger->dump_text(0);
48             $ua
49             }
50             );
51              
52             has config => (
53             is => 'rw',
54             );
55              
56             has certificate_subject => (
57             is => 'ro',
58             default => sub {
59             +{
60             #/jurisdictionC=DE/jurisdictionST=Nordrhein-Westfalen/jurisdictionL=Bonn/businessCategory=Private Organization/serialNumber=HRB6793/C=DE/postalCode=53113/ST=Nordrhein-Westfalen/L=Bonn/street=Friedrich Ebert Allee 114 126/O=Deutsche Postbank AG/OU=PB Systems AG/CN=meine.postbank.de
61             #meine_postbank_de => qr{^/(?:\Q1.3.6.1.4.1.311.60.2.1.3\E|jurisdictionC|jurisdictionCountryName)=DE/(?:\Q1.3.6.1.4.1.311.60.2.1.2\E|jurisdictionST|jurisdictionStateOrProvinceName)=Nordrhein-Westfalen/(?:\Q1.3.6.1.4.1.311.60.2.1.1\E|jurisdictionL|jurisdictionLocalityName)=Bonn/businessCategory=Private Organization/serialNumber=HRB6793/C=DE/postalCode=53113/ST=Nordrhein-Westfalen/L=Bonn/street=Friedrich Ebert Allee 114 126/O=Deutsche Postbank AG/OU=PB Systems AG/CN=meine.postbank.de$},
62             api_public_postbank_de => qr{^/(?:\Q2.5.4.15\E|businessCategory)=Private Organization/(?:\Q1.3.6.1.4.1.311.60.2.1.3\E|jurisdictionC|jurisdictionCountryName)=DE/(?:\Q1.3.6.1.4.1.311.60.2.1.2\E|jurisdictionST|jurisdictionStateOrProvinceName)=Hessen/(?:\Q1.3.6.1.4.1.311.60.2.1.1\E|jurisdictionL|jurisdictionLocalityName)=Frankfurt am Main/serialNumber=HRB 47141/C=DE/ST=Nordrhein-Westfalen/L=Bonn/O=DB Privat- und Firmenkundenbank AG/OU=Postbank Systems AG/CN=bankapi-public.postbank.de$}
63             },
64             },
65             );
66              
67 0     0 0 0 sub diagnoseCertificateError( $self, $error=$@ ) {
  0         0  
  0         0  
  0         0  
68 0 0       0 my( $found, $re ) = ($error =~ m#'(.+?)' !~ /\Q(?^:\E(.+?)/ at #)
69             or die "$error"; # reraise
70 0         0 warn $found;
71 0         0 warn $re;
72 0         0 my @found_parts = split m!/!, $found;
73 0         0 my @re_parts = split m!/!, $re;
74              
75 0         0 for my $i (0..$#re_parts ) {
76 0 0       0 if( $found_parts[ $i ] =~ $re_parts[ $i ]) {
77 0         0 warn "'$found_parts[ $i ]' =~ /$re_parts[ $i ]/, OK\n";
78             } else {
79 0         0 warn "'$found_parts[ $i ]' !~ /$re_parts[ $i ]/, not OK\n";
80             };
81             };
82 0         0 die "Certificate mismatch";
83             }
84              
85 10     10 0 24 sub fetch_config( $self ) {
  10         23  
  10         20  
86             # Do an initial fetch to set up cookies
87 10         48 my $ua = $self->ua;
88 10         45 $self->configure_ua_ssl;
89             #my $re = join "|", values %{ $self->certificate_subject };
90             #$ua->add_header(
91             # "If-SSL-Cert-Subject" => qr/$re/,
92             #);
93 10         1674 eval {
94 10         70 $ua->get('https://meine.postbank.de');
95 10         1741683 $ua->get('https://meine.postbank.de/configuration.json');
96             };
97 10 50       349914 if( my $err = $@ ) {
98 0         0 $self->diagnoseCertificateError( "$@ ");
99             };
100 10         65 my $config = decode_json( $ua->content );
101 10 50       1607 if( ! exists $config->{ 'iob5-base' }) {
102 0         0 require Data::Dumper;
103 0         0 croak "Invalid config retrieved: " . Data::Dumper::Dumper($config);
104             };
105 10         111 $self->config( $config );
106 10         36 $config
107             }
108              
109 10     10 0 22 sub configure_ua_ssl( $self, $ua=$self->ua ) {
  10         25  
  10         31  
  10         24  
110             # OpenSSL 1.0.1 doesn't properly scan the certificate chain as supplied
111             # by Mozilla::CA, so we only verify the certificate directly there:
112 10         26 my @verify;
113              
114 10 50       323 if( IO::Socket::SSL->VERSION <= 1.990 ) {
    50          
115             # No OCSP support
116 0         0 @verify = ();
117             } elsif( Net::SSLeay::SSLeay() <= 0x100010bf ) { # 1.0.1k
118 0         0 @verify = (
119             SSL_fingerprint => 'sha256$C0F407E7D1562B52D8896B4A00DFF538CBC84407E95D8E0A7E5BFC6647B98967',
120             SSL_ocsp_mode => IO::Socket::SSL::SSL_OCSP_NO_STAPLE(),
121             );
122             } else {
123             # We need no special additional options to verify the certificate chain
124 10         47 @verify = (
125             SSL_ocsp_mode => IO::Socket::SSL::SSL_OCSP_FULL_CHAIN(),
126             );
127             };
128 10         75 $ua->ssl_opts(
129             SSL_ca_file => Mozilla::CA::SSL_ca_file(),
130             SSL_verify_mode => SSL_VERIFY_PEER(),
131             @verify,
132             #SSL_verify_callback => sub {
133             #use Data::Dumper;
134             #warn Dumper \@_;
135             #return 1;
136             #},
137             );
138             };
139              
140 10     10 0 28 sub configure_ua( $self, $config = $self->fetch_config ) {
  10         22  
  10         40  
  10         47  
141 10         55 my $ua = $self->ua;
142              
143             $ua->add_header(
144             'api-key' => $config->{'iob5-base'}->{apiKey},
145             #'device-signature' => '494f423500225fd9',
146             accept => ['application/hal+json', '*/*'],
147             keep_alive => 1,
148             # / businessCategory =Private Organization/ jurisdictionC =DE/ jurisdictionST =Hessen/ jurisdictionL =Frankfurt am Main/serialNumber=HRB 47141/C=DE/ST=Nordrhein-Westfalen/L=Bonn/O=DB Privat- und Firmenkundenbank AG/OU=Postbank Systems AG/CN=(?:banking|bankapi-public).postbank.de
149             "If-SSL-Cert-Subject" => $self->certificate_subject->{ api_public_postbank_de },
150 10         142 );
151             };
152              
153 7     7 0 17 sub login_url( $self ) {
  7         17  
  7         14  
154 7         22 my $config = $self->config;
155 7         26 my $loginUrl = $config->{'iob5-base'}->{loginUrl};
156 7         55 $loginUrl =~ s!%(\w+)%!$config->{'iob5-base'}->{$1}!ge;
  7         42  
157 7         29 $loginUrl
158             }
159              
160 7     7 0 20 sub login( $self, $username, $password ) {
  7         17  
  7         18  
  7         16  
  7         17  
161 7         25 my $ua = $self->ua;
162 7         30 my $loginUrl = $self->login_url();
163              
164 7         64 my $r =
165             $ua->post(
166             $loginUrl,
167             #content => sprintf 'dummy=value&password=%s&username=%s', $password, $username
168             #content => sprintf 'password=%s&username=%s', $password, $username
169             content => sprintf 'username=%s&password=%s', $username, $password
170             );
171              
172             my $postbank = HAL::Resource->new(
173             ua => $ua,
174 4         643796 %{ decode_json($ua->content)}
  4         24  
175             );
176              
177             };
178              
179             1;
180              
181             =head1 RESOURCE HIERARCHY
182              
183             This is the hierarchy of the resources in the API:
184              
185             APIv1
186             Finanzstatus
187             BusinessPartner
188             Account
189             Transaction
190             Message
191             Attachment
192             Depot
193              
194             =head1 AUTHOR
195              
196             Max Maischein, E<lt>corion@cpan.orgE<gt>
197              
198             =head1 SEE ALSO
199              
200             L<perl>, L<WWW::Mechanize>.
201              
202             =head1 REPOSITORY
203              
204             The public repository of this module is
205             L<https://github.com/Corion/Finance-Bank-Postbank_de>.
206              
207             =head1 SUPPORT
208              
209             The public support forum of this module is
210             L<https://perlmonks.org/>.
211              
212             =head1 BUG TRACKER
213              
214             Please report bugs in this module via the RT CPAN bug queue at
215             L<https://rt.cpan.org/Public/Dist/Display.html?Name=Finance-Bank-Postbank_de>
216             or via mail to L<finance-bank-postbank_de-Bugs@rt.cpan.org>.
217              
218             =head1 COPYRIGHT (c)
219              
220             Copyright 2003-2018 by Max Maischein C<corion@cpan.org>.
221              
222             =head1 LICENSE
223              
224             This module is released under the same terms as Perl itself.
225              
226             =cut
227