File Coverage

blib/lib/Net/Squid/Auth/Engine.pm
Criterion Covered Total %
statement 9 43 20.9
branch 0 18 0.0
condition n/a
subroutine 3 8 37.5
pod 2 2 100.0
total 14 71 19.7


line stmt bran cond sub pod time code
1             package Net::Squid::Auth::Engine;
2              
3 1     1   66076 use warnings;
  1         3  
  1         36  
4 1     1   7 use strict;
  1         2  
  1         41  
5 1     1   1236 use Config::General qw(ParseConfig);
  1         65581  
  1         758  
6              
7             =head1 NAME
8              
9             Net::Squid::Auth::Engine - External Credentials Authentication for Squid HTTP Cache
10              
11             =head1 VERSION
12              
13             Version 0.01
14              
15             =cut
16              
17             our $VERSION = '0.04';
18              
19             =head1 SYNOPSIS
20              
21             Squid authentication using an external credentials repository, now implemented
22             in Perl. If you're a sysadmin trying to use this engine to authenticate your
23             Squid users, please read the documentation provided with the script
24             $Config{InstallScript}/squid-auth-engine, shipped with this module.
25              
26             #!/usr/bin/perl
27             use warnings;
28             use strict;
29             use Net::Squid::Auth::Engine;
30             use IO::Handle;
31             BEGIN { STDOUT->autoflush(1); }
32             my $engine = Net::Squid::Auth::Engine->new( $ARGV[0] );
33             $engine->run;
34              
35             =head1 CONFIGURATION FILE SPECIFICATION
36              
37             The configuration file currently supports two keywords, at the moment. Only one
38             of them is useful and required:
39              
40             =head2 C
41              
42             The C keyword indicates which module name should be loaded as
43             authentication back-end to this interface. The string
44             I will be automatically appended to the module name
45             pointed here before it's loaded.
46              
47             =head2 C
48              
49             The C keyword can be used to import and process another configuration
50             file (a part of one) if required. The configuration file name should be passed
51             as argument to the keyword.
52              
53             =head2 Configuration File Example
54              
55             plugin 'UserList'
56              
57             =head1 WRITING PLUGINS
58              
59             A plugin for L is a module under
60             L that implements a well-stablished interface.
61              
62             It is expected to keep it's own internal state and initialize only once (and
63             not at every request, this is not a stateless protocol!).
64              
65             It is also expected to implement the following methods:
66              
67             =over 4
68              
69             =item B>
70              
71             New is a constructor. It receives a hash reference containing all the keywords
72             and values passed in the main configuration file which section is the "last"
73             plugin module name (e.g.: for Net::Squid::Auth::Plugin::UserList, the section
74             is named "UserList". That's case sensitive, pay attention!).
75              
76             The constructor must return a blessed reference for the plugin module, that is
77             able to keep it's internal state and implements the following methods.
78              
79             =item B>
80              
81             Initialization method called upon instantiation. This provides an opportunity
82             for the plugin initialize itself, stablish database connections and ensure it
83             have all the necessary resources to verify the credentials presented. It
84             receives no parameters and expect no return values.
85              
86             =item B>
87              
88             Credential verification method. This method does the real work for the
89             credentials verification, and must return a boolean value without raising any
90             exceptions. A true value means that the pair (username, password) is a valid
91             credentials, and a false value means that the credentials aren't valid.
92             Undefined values will be passed as-is to the method, which means that it's free
93             to validate any credentials the way it sees fit.
94              
95             =back
96              
97             =head1 EXAMPLE PLUGIN
98              
99             As a plugin implementation example, this module depends on the
100             L, the most basic plugin possible: it loads
101             a username and password list from the configuration file and uses it to
102             authenticate users against it. For more information, please read the
103             L documentation.
104              
105             =head1 OTHER IMPLEMENTATIONS
106              
107             =head2 L
108              
109             A simple LDAP-based credentials validation plugin for L.
110              
111             =head1 FUNCTIONS
112              
113             =head2 new
114              
115             Constructor. Receives a configuration file name, opens and reads it,
116             initializes the module and returns the authentication engine instance.
117              
118             =cut
119              
120             sub new {
121 0     0 1   my ( $class, $config ) = @_;
122 0           my $self = bless {}, $class;
123 0 0         die "Net::Squid::Auth::Engine requires a config file" unless $config;
124 0           $self->_read_config_file( $config );
125 0           $self->_initialize;
126 0           return $self;
127             }
128              
129              
130             =head2 run
131              
132             Runs the engine, that is: load and parse the configuration file; identifies the
133             authentication module to be loaded; load and instantiate the authentication
134             module to be used; give the authentication module a chance to initialize itself
135             (stablishing database connections, etc.); waits for a Squid-standard
136             credentials line in the standard input, reads it, feeds it to the
137             authentication module instance, collects the answer and prints "OK" or "ERR" in
138             the stdout file handle, as the Squid external authentication protocol commands.
139             Then, waits for the next credential line to show up... you got the idea, right?
140              
141             =cut
142              
143             sub run {
144 0     0 1   my $self = shift;
145 0           while (1) {
146 0           my ( $username, $password ) = $self->_read_credentials;
147 0 0         print STDOUT $self->{_plugin}->is_valid( $username, $password )
148             ? "OK\n"
149             : "ERR\n";
150             }
151             }
152              
153             =head2 _read_config_file
154              
155             Reads a configuration file, parses it, and makes it available. The underling
156             configuration parser is L.
157              
158             =cut
159              
160             sub _read_config_file {
161 0     0     my $self = shift;
162 0           $self->{_CONF}{filename} = shift;
163 0 0         die q{Can't read the configuration file "}
164             . $self->{_CONF}{filename} . q{".}
165             unless -r $self->{_CONF}{filename};
166 0           my %conf = ParseConfig(
167             -ConfigFile => $self->{_CONF}{filename},
168             -AllowMultiOptions => 'no',
169             -UseApacheInclude => 1,
170             -MergeDuplicateBlocks => 1,
171             -AutoTrue => 1,
172             -CComments => 0,
173             );
174 0           $self->{_CONFIG} = \%conf;
175              
176             # Mandatory Config File Options Verification
177 0 0         die q{Missing mandatory 'plugin' keyword in the configuration file.}
178             unless $self->{_CONFIG}{plugin};
179 0           my $section = $self->{_CONFIG}{plugin};
180 0 0         die "Missing mandatory section '$section' in the config file."
181             unless UNIVERSAL::isa( $self->{_CONFIG}{$section}, 'HASH' );
182             }
183              
184             =head2 _initialize
185              
186             Internal engine initialization. Happens once, mainly instanciates the plugin
187             and tries to initialize it properly. Die for errors, as usual.
188              
189             =cut
190              
191             sub _initialize {
192 0     0     my $self = shift;
193 0           my $tag = $self->{_CONFIG}{plugin};
194 0           my $module = 'Net::Squid::Auth::Plugin::' . $self->{_CONFIG}{plugin};
195 0           eval "use $module";
196 0 0         die qq{Can't load "$module": $@.} if $@;
197 0           $self->{_plugin} = eval { $module->new( $self->{_CONFIG}{$tag} ); };
  0            
198 0 0         die qq{Can't instantiate $module: $@.} if $@;
199 0           eval { $self->{_plugin}->initialize; };
  0            
200 0 0         die qq{$module\:\:initialize() triggered an error: $@.} if $@;
201             }
202              
203             =head2 _read_credentials
204              
205             This method tryies, waits for, and reads a line from STDIN, splits it at the
206             first whitespace found from left to right, and returns the username (to the
207             left of the splitting whitespace) and the password (to the right of the
208             splitting whitespace), as described by the Squid HTTP Cache documentation.
209              
210             =cut
211              
212             sub _read_credentials {
213 0     0     my $self = shift;
214 0           my $credentials = ;
215             # TODO: is this the right thing to do? Check it
216 0 0         die q{Got a EOF from Squid?!?} unless $credentials;
217 0           my ( $username, $password ) = $credentials =~ m{^(\S+)(?:\s+(.+))?$};
218 0           return ( $username, $password );
219             }
220              
221             =head1 AUTHOR
222              
223             Luis Motta Campos, C<< >>
224              
225             =head1 BUGS
226              
227             Please report any bugs or feature requests to
228             C, or through the web interface at
229             L. I
230             will be notified, and then you'll automatically be notified of progress on your
231             bug as I make changes.
232              
233             =over 4
234              
235             =item * There are no working tests for this module (yet);
236              
237             =back
238              
239             =head1 SUPPORT
240              
241             You can find documentation for this module with the perldoc command.
242              
243             perldoc Net::Squid::Auth::Engine
244              
245              
246             You can also look for information at:
247              
248             =over 4
249              
250             =item * RT: CPAN's request tracker
251              
252             L
253              
254             =item * AnnoCPAN: Annotated CPAN documentation
255              
256             L
257              
258             =item * CPAN Ratings
259              
260             L
261              
262             =item * Search CPAN
263              
264             L
265              
266             =back
267              
268             =head1 ACKNOWLEDGEMENTS
269              
270             To William A. Knob, for the initial idea;
271              
272             To Otavio Fernandes, for the documentation links;
273              
274             To Lucas Mateus, for the inner loop implementation, all comments and improvements;
275              
276             To Fernando Oliveira, for comments and questioning the prototype;
277              
278             To Alexei Znamensky, Gabriel Viera, and Mike Tesliuk, for pointing me a design
279             bug and helping me re-design the responsibility chain.
280              
281             To Alexei Znamensky, for trying to use the module, reporting bugs, submiting
282             patches and implementing L.
283              
284             =head1 COPYRIGHT & LICENSE
285              
286             Copyright 2008 Luis Motta Campos, all rights reserved.
287              
288             This program is free software; you can redistribute it and/or modify it
289             under the same terms as Perl itself.
290              
291             =cut
292              
293             1; # End of Net::Squid::Auth::Engine