File Coverage

lib/Mojolicious/Plugin/SPNEGO.pm
Criterion Covered Total %
statement 12 70 17.1
branch 0 44 0.0
condition 0 22 0.0
subroutine 4 6 66.6
pod 1 1 100.0
total 17 143 11.8


line stmt bran cond sub pod time code
1             package Mojolicious::Plugin::SPNEGO;
2 1     1   1008 use Mojo::Base 'Mojolicious::Plugin';
  1         159871  
  1         5  
3 1     1   1255 use Net::LDAP::SPNEGO;
  1         154784  
  1         10  
4 1     1   502 use IO::Socket::Timeout;
  1         3777  
  1         7  
5 1     1   27 use Mojo::Util qw(b64_decode);
  1         2  
  1         1003  
6             our $VERSION = '0.5.4';
7              
8             my %cCache;
9              
10             sub register {
11 0     0 1   my $self = shift;
12 0           my $app = shift;
13 0           my $plugin_cfg = shift;
14              
15             $app->helper(
16             ntlm_auth => sub {
17 0     0     my $c = shift;
18 0 0         my $helper_cfg = ref ${_}[0] eq 'HASH' ? ${_}[0] : { @_ };
19 0           my $cfg = { %$plugin_cfg, %$helper_cfg };
20 0           my $cId = $c->tx->connection;
21              
22 0 0 0       my $authorization = $c->req->headers->header(($cfg->{web_proxy_mode} ? 'Proxy-' : '' ) .'Authorization') // '';
23 0           my ($AuthBase64) = ($authorization =~ /^NTLM\s(.+)$/);
24             # $c->app->log->debug("AuthBase64: $AuthBase64") if $AuthBase64;
25              
26 0 0 0       my $cCache = $cCache{$cId} //= {
27             status => $AuthBase64 ? 'expectType1' : 'init'
28             };
29 0 0         return 1 if $cCache->{status} eq 'authenticated';
30              
31 0           my ($status) = ($cCache->{status} =~ /^expect(Type[13])/);
32             # $c->app->log->debug("status: $status") if $status;
33              
34 0 0 0       if ($AuthBase64 and $status){
35 0           for ($status){
36 0   0       my $timeout = $cfg->{timeout} // 10;
37 0           my $ldap = $cCache->{ldapObj};
38 0 0         if (not $ldap) {
39             $ldap = $cCache->{ldapObj} = Net::LDAP::SPNEGO->new(
40             $cfg->{ad_server},
41             debug=>($cfg->{ldap_debug}//$ENV{SPNEGO_LDAP_DEBUG}//0),
42 0           onerror=> sub { my $msg = shift;
43 0           $c->log->error("LDAP ERROR: " . $msg->error);
44 0           return $msg
45             },
46             timeout=>$timeout,
47 0 0 0       verify => $cfg->{verify} // 'none'
      0        
      0        
48             ) or return 0;
49 0 0         if ($ldap->uri !~ m{^ldaps://}){
50 0           my $msg;
51 0 0         if ($cfg->{verify}){
    0          
52 0           $msg = $ldap->start_tls(verify => $cfg->{verify});
53             }
54             elsif ($cfg->{start_tls}){
55 0           $msg = $ldap->start_tls($cfg->{start_tls});
56             }
57 0 0 0       if ($msg and $msg->is_error()){
58 0           $c->log->error($msg->error);
59 0           return 0;
60             }
61             }
62             # Read/Write timeouts via setsockopt
63 0           my $socket = $ldap->socket(sasl_layer=>0);
64 0           IO::Socket::Timeout->enable_timeouts_on($socket);
65 0           $socket->read_timeout($timeout);
66 0           $socket->write_timeout($timeout);
67             }
68 0 0         /^Type1/ && do {
69 0           $c->app->log->debug("Bind Type1 ...");
70 0           my $mesg = $ldap->bind_type1($AuthBase64);
71 0 0         if ($mesg->{ntlm_type2_base64}){
72 0 0         $c->res->headers->header( ($cfg->{web_proxy_mode} ? 'Proxy' : 'WWW' ) . '-Authenticate' => 'NTLM '.$mesg->{ntlm_type2_base64});
73 0 0         $c->render( text => 'Waiting for Type3 NTLM Token', status => $cfg->{web_proxy_mode} ? 407 : 401);
74 0           $cCache->{status} = 'expectType3';
75 0           return 0;
76             }
77             # lets try with a new connection
78 0           $ldap->unbind;
79 0           delete $cCache->{ldapObj};
80             };
81 0 0         /^Type3/ && do {
82 0           $c->app->log->debug("Bind Type3 as user '".$ldap->_get_user_from_ntlm_type3(b64_decode($AuthBase64))."'");
83 0           my $mesg = $ldap->bind_type3($AuthBase64);
84 0 0         if (my $user = $mesg->{ldap_user_entry}){
85 0 0         if (my $cb = $cfg->{auth_success_cb}){
86 0 0 0       if (not $cb or $cb->($c,$user,$ldap)){
87 0           $cCache->{status} = 'authenticated';
88             }
89             }
90             }
91 0           $ldap->unbind;
92 0           delete $cCache->{ldapObj};
93 0 0         return 1 if $cCache->{status} eq 'authenticated';
94             };
95             }
96             }
97 0 0         $c->res->headers->header( ($cfg->{web_proxy_mode} ? 'Proxy' : 'WWW') . '-Authenticate' => 'NTLM' );
98 0 0         $c->render( text => 'Waiting for Type 1 NTLM Token', status => $cfg->{web_proxy_mode} ? 407 : 401 );
99 0           $cCache->{status} = 'expectType1';
100 0           return 0;
101             }
102 0           );
103             }
104              
105             1;
106              
107             __END__