File Coverage

blib/lib/Auth/Kokolores.pm
Criterion Covered Total %
statement 27 139 19.4
branch 0 36 0.0
condition 0 9 0.0
subroutine 9 23 39.1
pod 6 13 46.1
total 42 220 19.0


line stmt bran cond sub pod time code
1             package Auth::Kokolores;
2            
3 1     1   551 use strict;
  1         1  
  1         29  
4 1     1   3 use base qw(Net::Server::PreFork);
  1         0  
  1         531  
5              
6             # ABSTRACT: an alternative saslauthd
7             our $VERSION = '1.01'; # VERSION
8              
9 1     1   41703 use Auth::Kokolores::Config;
  1         2  
  1         51  
10 1     1   380 use Auth::Kokolores::Request;
  1         2  
  1         31  
11 1     1   382 use Auth::Kokolores::Response;
  1         1  
  1         25  
12 1     1   619 use Auth::Kokolores::Plugins;
  1         2  
  1         37  
13 1     1   365 use Auth::Kokolores::Protocol::CyrusSaslauthd;
  1         2  
  1         38  
14 1     1   407 use Auth::Kokolores::Protocol::DovecotAuth;
  1         3  
  1         30  
15              
16 1     1   704 use Getopt::Long;
  1         7356  
  1         3  
17              
18             sub print_usage {
19 0     0 0   print "$0 [-h|--help] [-c|--config=<file>] [-f|--foreground] [-l|--loglevel=<level>]\n";
20 0           return;
21             }
22            
23             sub configure {
24 0     0 1   my $self = shift;
25 0           my $server = $self->{'server'};
26              
27 0 0         return if(@_);
28              
29 0 0         if( ! defined $server->{'config_file'} ) {
30 0           $server->{'config_file'} = '/etc/kokolores/kokolores.conf';
31             }
32 0           $self->{'program_name'} = $0;
33              
34 0           $server->{'background'} = 1;
35 0           $server->{'setsid'} = 1;
36 0           $server->{'no_close_by_child'} = 1;
37              
38             # commandline options
39 0           my $cmdline = {};
40 0           GetOptions( $cmdline,
41             "help|h",
42             "config|c:s",
43             "foreground|f",
44             "loglevel|l:i",
45             );
46 0 0         if ($cmdline->{'help'}) {
47 0           $self->print_usage;
48 0           exit 0;
49             }
50 0 0 0       if (defined($cmdline->{'config'}) && $cmdline->{'config'} ne "") {
51 0           $server->{'config_file'} = $cmdline->{'config'};
52             }
53              
54             # read and apply configuration file
55 0           my $config = Auth::Kokolores::Config->new_from_file( $server->{'config_file'} );
56 0           $config->apply_config( $self );
57              
58 0           $server->{'port'} = $self->{'socket_path'}.'|unix';
59              
60 0           $self->{'plugins'} = Auth::Kokolores::Plugins->new_from_config( $self, $config->Plugin );
61              
62             # cmdline values which overwrite config/defaults
63 0 0         if ($cmdline->{'foreground'}) {
64 0           $server->{'background'} = undef;
65 0           $server->{'setsid'} = undef;
66 0           $server->{'log_file'} = undef;
67             }
68 0 0         if( $cmdline->{'loglevel'} ) {
69 0           $server->{'log_level'} = $cmdline->{'loglevel'};
70             }
71              
72 0           return;
73             }
74              
75             sub post_configure_hook {
76 0     0 1   my $self = shift;
77 0           $self->{'plugins'}->init( $self );
78 0           return;
79             }
80              
81             sub post_bind_hook {
82 0     0 1   my $self = shift;
83 0           $self->_set_process_stat('master');
84 0           $self->set_socket_permissions;
85 0           return;
86             }
87              
88             sub set_socket_permissions {
89 0     0 0   my $self = shift;
90 0 0         if( ! defined $self->{'socket_mode'} ) {
91 0           return;
92             }
93 0           my $mode = oct($self->{'socket_mode'});
94              
95 0           $self->log(2, sprintf('setting socket mode to: %o', $mode));
96 0 0         chmod( $mode, $self->{'socket_path'} )
97             or $self->log(1, 'could not change mode of socket: '.$!);
98            
99 0           return;
100             }
101              
102             sub child_init_hook {
103 0     0 1   my $self = shift;
104 0           $self->{'plugins'}->child_init( $self );
105 0           $self->_set_process_stat('virgin child');
106 0           return;
107             }
108              
109             sub child_finish_hook {
110 0     0 1   my $self = shift;
111 0           $self->{'plugins'}->shutdown( $self );
112 0           return;
113             }
114              
115             sub authenticate {
116 0     0 0   my ( $self, $r ) = @_;
117 0           my $failed = 0;
118              
119 0           foreach my $plugin ( $self->{'plugins'}->all_plugins ) {
120 0           my $ok = $plugin->authenticate( $r );
121 0 0 0       if( ! defined $ok ) {
    0 0        
    0          
    0          
122 0           $self->log(3, 'plugin '.$plugin->name.': next');
123 0           next;
124             } elsif( $ok && $self->{'satisfy'} eq 'any' ) {
125 0           $self->log(3, 'plugin '.$plugin->name.': success (any)');
126 0           return Auth::Kokolores::Response->new_success;
127             } elsif( !$ok && $self->{'satisfy'} ne 'any' ) {
128 0           $self->log(3, 'plugin '.$plugin->name.': failed (all)');
129 0           return Auth::Kokolores::Response->new_fail;
130             } elsif( $ok ) {
131 0           $self->log(3, 'plugin '.$plugin->name.': success');
132             } else {
133 0           $self->log(3, 'plugin '.$plugin->name.': failed');
134 0           $failed++;
135             }
136             }
137              
138 0 0         if( $failed == 0 ) {
139 0           return Auth::Kokolores::Response->new_success;
140             }
141 0           return Auth::Kokolores::Response->new_fail;
142             }
143              
144             sub pre_process_kokolores_request {
145 0     0 0   my ( $self, $r ) = @_;
146              
147 0           foreach my $plugin ( $self->{'plugins'}->all_plugins ) {
148 0           my $pre_response = $plugin->pre_process( $r );
149 0 0         if( defined $pre_response ) {
150 0           return $pre_response;
151             }
152             }
153              
154 0           return;
155             }
156              
157             sub post_process_kokolores_request {
158 0     0 0   my ( $self, $r, $response ) = @_;
159              
160 0           foreach my $plugin ( $self->{'plugins'}->all_plugins ) {
161 0           my $post_response = $plugin->post_process( $r, $response );
162 0 0         if( defined $post_response ) {
163 0           return $post_response;
164             }
165             }
166              
167 0           return;
168             }
169            
170             sub process_kokolores_request {
171 0     0 0   my ( $self, $r ) = @_;
172              
173 0           my $pre_response = $self->pre_process_kokolores_request( $r );
174 0 0         if( defined $pre_response ) {
175             # if there is a response from a pre_process object
176             # return with this response
177 0           return $pre_response;
178             }
179              
180 0           my $response = $self->authenticate( $r );
181              
182 0           my $post_response = $self->post_process_kokolores_request( $r, $response );
183 0 0         if( defined $post_response ) {
184 0           return $post_response;
185             }
186              
187 0           return $response;
188             }
189              
190             sub get_protocol_handler {
191 0     0 0   my ( $self, $conn, $port ) = @_;
192             # TODO: make this configurable per socket
193 0           my $protocol = $self->{'protocol'};
194 0           my $protocol_class = "Auth::Kokolores::Protocol::".$protocol;
195 0           return $protocol_class->new(
196             server => $self,
197             handle => $conn,
198             );
199             }
200              
201             sub process_request {
202 0     0 1   my ( $self, $conn ) = @_;
203 0           my $port = $conn->NS_port;
204 0           $self->log(4, "accepted new client on port $port");
205              
206 0           my $protocol = $self->get_protocol_handler( $conn, $port );
207 0           $protocol->init_connection;
208              
209 0           $self->_set_process_stat('waiting request');
210 0           my $r = $protocol->read_request;
211              
212 0           $self->_set_process_stat('processing request');
213              
214 0           my $response;
215 0           eval { $response = $self->process_kokolores_request( $r ); };
  0            
216 0 0         if( $@ ) {
217 0           $self->log(1, 'processing request failed: '.$@);
218 0           return;
219             }
220 0           $protocol->write_response( $response );
221              
222 0           $protocol->shutdown_connection;
223 0           $self->_set_process_stat('idle');
224 0           return;
225             }
226              
227             sub _set_process_stat {
228 0     0     my ( $self, $stat ) = @_;
229 0           $0 = $self->{'program_name'}.' ('.$stat.')';
230             }
231              
232             1;
233              
234             __END__
235              
236             =pod
237              
238             =encoding UTF-8
239              
240             =head1 NAME
241              
242             Auth::Kokolores - an alternative saslauthd
243              
244             =head1 VERSION
245              
246             version 1.01
247              
248             =head1 AUTHOR
249              
250             Markus Benning <ich@markusbenning.de>
251              
252             =head1 COPYRIGHT AND LICENSE
253              
254             This software is Copyright (c) 2016 by Markus Benning <ich@markusbenning.de>.
255              
256             This is free software, licensed under:
257              
258             The GNU General Public License, Version 2, June 1991
259              
260             =cut