File Coverage

blib/lib/POE/Component/UserBase.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1             # $Id: UserBase.pm,v 1.10 2000/12/14 04:15:56 jgoff Exp $
2             # License and documentation are after __END__.
3              
4             package POE::Component::UserBase;
5              
6 1     1   735 use strict;
  1         1  
  1         32  
7              
8 1     1   4 use vars qw($VERSION);
  1         1  
  1         53  
9             $VERSION = '0.09';
10              
11 1     1   5 use Carp qw (croak);
  1         4  
  1         64  
12              
13 1     1   1551 use POE::Session;
  0            
  0            
14             use Storable qw(freeze thaw);
15              
16             BEGIN {
17             eval 'use Digest::MD5 qw(md5 md5_hex md5_base64)';
18             if(defined $@ and length $@) {
19             eval 'sub HAS_MD5 () { 0 }';
20             } else {
21             eval 'sub HAS_MD5 () { 1 }';
22             }
23             eval 'use Digest::SHA1 qw(sha1 sha1_hex sha1_base64)';
24             if(defined $@ and length $@) {
25             eval 'sub HAS_SHA1 () { 0 }';
26             } else {
27             eval 'sub HAS_SHA1 () { 1 }';
28             }
29             };
30              
31             sub DEBUG () { 0 }
32             sub _no_undef { (defined $_[0]) ? $_[0] : '(undef)' }
33              
34             sub _user_key {
35             my $params = shift;
36             my $domain = $params->{domain} || '';
37             return $params->{user_name} . ':' . $domain;
38             }
39              
40             # Spawn a new PoCo::UserBase session. This basically is a
41             # constructor, but it isn't named "new" because it doesn't create a
42             # usable object. Instead, it spawns the object off as a session.
43              
44             sub spawn {
45             my $type = shift;
46              
47             croak qq($type requires an even number of parameters.)
48             if @_ % 2;
49              
50             my %params = @_;
51             $params{Protocol} ||= 'file'; # Default to 'file' protocol
52             $params{Cipher} ||= 'crypt'; # Default to the 'crypt' method of encryption
53              
54             my @legal_protocols =
55             qw(file dbi);
56             my @legal_ciphers =
57             qw(crypt des md5 md5_hex md5_base64 sha1 sha1_hex sha1_base64);
58             croak qq($type does not understand Protocol '$params{Protocol}'.)
59             unless grep { /$params{Protocol}/ } @legal_protocols;
60             croak qq($type does not understand Cipher '$params{Cipher}'.)
61             unless grep { /$params{Cipher}/ } @legal_ciphers;
62             if(!HAS_MD5) {
63             croak qq($type cannot load Digest::MD5 for Cipher '$params{Cipher}'.)
64             if $params{Cipher} =~ /md5/;
65             }
66             if(!HAS_SHA1) {
67             croak qq($type cannot load Digest::SHA1 for Cipher '$params{Cipher}'.)
68             if $params{Cipher} =~ /sha1/;
69             }
70              
71             my $states = { _start => \&poco_userbase_start,
72             persist => \&poco_userbase_persist,
73             log_on => \&poco_userbase_log_on,
74             log_off => \&poco_userbase_log_off,
75              
76             create => \&poco_userbase_create,
77             delete => \&poco_userbase_delete,
78             update => \&poco_userbase_update,
79              
80             list_active => \&poco_userbase_list_active,
81             };
82              
83             $params{_type} = $type;
84              
85             for($params{Protocol}) {
86             /file/ and do {
87             croak qq($type requires a file name with the 'file' protocol.)
88             unless exists $params{File};
89             $params{Dir} = '.persist'
90             unless exists $params{Dir};
91             last;
92             };
93             /dbi/ and do {
94             croak qq($type requires a Connection handle with the 'dbi' protocol.)
95             unless exists $params{Connection};
96             croak qq($type requires a Table name whne using the 'dbi' protocol.)
97             unless exists $params{Table};
98             $params{UserColumn} = 'user_name'
99             unless exists $params{UserColumn};
100             $params{PasswordColumn} = 'password'
101             unless exists $params{PasswordColumn};
102             $params{DomainColumn} = 'domain'
103             unless exists $params{DomainColumn};
104             $params{PersistentColumn} = 'persistent'
105             unless exists $params{PersistentColumn};
106             last;
107             };
108             }
109              
110             DEBUG and do {
111             warn "\n";
112             warn "/--- spawning $type component ---" . "\n";
113             warn "| Alias : $params{Alias}" . "\n";
114             warn "| Protocol : $params{Protocol}" . "\n";
115             warn "| Cipher : $params{Cipher}" . "\n";
116             for($params{Protocol}) {
117             /file/ and do {
118             warn "| File : " . _no_undef($params{File}) . "\n";
119             warn "| Dir : " . $params{Dir} . "\n";
120             last;
121             };
122             /dbi/ and do {
123             warn "| Connection : " . _no_undef($params{Connection}) . "\n";
124             warn "| Table : " . _no_undef($params{Table}) . "\n";
125             last;
126             };
127             }
128             warn "\\--------------------------------" . "\n";
129             };
130              
131             POE::Session->create
132             ( inline_states => $states,
133             args => [%params],
134             );
135             undef;
136             }
137              
138             ###############################################################################
139             #
140             # File - Format is "user_name:password:persistent:domain"
141             #
142              
143             sub _create_file {
144             my ($heap, $href) = @_;
145             open FILE,">>$heap->{File}" or
146             croak qq($heap->{_type} could not open '>>$heap->{File}'.);
147             binmode(FILE);
148             print FILE join ':',( $href->{user_name},
149             $href->{password} || '',
150             $href->{domain} || '',
151             );
152             print FILE "\n";
153             close FILE;
154             }
155              
156             sub _read_file {
157             my ($heap, $href) = @_;
158             my ($line,$user_line);
159              
160             open FILE,"<$heap->{File}" or
161             croak qq($heap->{_type} could not open '<$heap->{File}'.);
162             binmode(FILE);
163             while(defined($line=)) {
164             next unless $line=~/^$href->{user_name}/;
165             next if defined $href->{domain} && $line!~/:$href->{domain}:/;
166             $user_line = $line;
167             last;
168             }
169             close FILE;
170             chomp $user_line;
171             return unless $user_line;
172             my %foo;
173             @foo{qw(user_name password domain)} = split ':',$user_line;
174              
175             if(-d $heap->{Dir} && open FILE,"< $heap->{Dir}/$href->{user_name}") {
176             binmode(FILE);
177             $foo{persistent} = join '',;
178             close FILE;
179             } else {
180             $foo{persistent} = undef;
181             }
182             return \%foo;
183             }
184              
185             sub __update_line {
186             my ($line,$heap,$href) = @_;
187             my @rec = split ':',$line;
188            
189             $rec[0] = $href->{new_user_name} if $href->{new_user_name};
190             $rec[1] = $href->{new_password} if $href->{new_password};
191             $rec[3] = $href->{new_domain} if $href->{new_domain};
192              
193             return join ':',@rec;
194             }
195              
196             sub _update_file {
197             my ($heap,$href) = @_;
198             my @lines;
199             open FILE,"<$heap->{File}" or
200             croak qq($heap->{_type} could not open '<$heap->{File}'.);
201             binmode(FILE);
202             @lines=;
203             close FILE;
204             open FILE,">$heap->{File}" or
205             croak qq($heap->{_type} could not open '>$heap->{File}'.);
206             binmode(FILE);
207             for(@lines) {
208             if(/^$href->{user_name}/) {
209             print FILE __update_line($_,$href);
210             } else {
211             print FILE $_;
212             }
213             }
214             close FILE;
215              
216             -d $heap->{Dir} || mkdir $heap->{Dir},0755;
217             unlink "$heap->{Dir}/$href->{user_name}" if $href->{new_user_name};
218             open FILE,">$heap->{Dir}/$href->{user_name}";
219             binmode(FILE);
220             if(defined $href->{persistent}) {
221             print FILE $href->{persistent};
222             } elsif (defined $href->{new_persistent}) {
223             print FILE $href->{new_persistent};
224             }
225             close FILE;
226             }
227              
228             sub _delete_file {
229             my ($heap,$href) = @_;
230             my @lines;
231             open FILE,"<$heap->{File}" or
232             croak qq($heap->{_type} could not open '<$heap->{File}'.);
233             binmode(FILE);
234             @lines=;
235             close FILE;
236             open FILE,">$heap->{File}" or
237             croak qq($heap->{_type} could not open '>$heap->{File}'.);
238             binmode(FILE);
239             for(@lines) {
240             print FILE $_ unless /^$href->{user_name}/;
241             print FILE $_ if defined $href->{domain} && $_!~/:$href->{domain}:/;
242             }
243             close FILE;
244             unlink "$heap->{Dir}/$href->{user_name}"
245             if -e "$href->{Dir}/$href->{user_name}";
246             }
247              
248             ###############################################################################
249             #
250             # Database - uncomment the Pg lines to handle raw Postgres drivers
251             # or for that matter hack your own database in.
252             #
253              
254             sub _create_dbi {
255             my ($heap,$href) = @_;
256             my $stm = <<_EOSTM_;
257             insert into $heap->{Table} ($heap->{UserColumn},
258             $heap->{DomainColumn},
259             $heap->{PasswordColumn},
260             $heap->{PersistentColumn}
261             )
262             values('$heap->{user_name}',
263             '$heap->{domain}',
264             '$heap->{password}',
265             '$heap->{persistent}')
266             _EOSTM_
267              
268             my $sth = $heap->{Connection}->prepare($stm);
269             my $rv = $sth->execute();
270             $sth->finish();
271             }
272              
273             sub _read_dbi {
274             my ($heap,$href) = @_;
275             my @fields = qw(user_name domain password persistent);
276             my $field_list = join ',',@fields;
277             my $stm = <<_EOSTM_;
278             select $field_list
279             from $heap->{Table}
280             where $heap->{UserColumn} like '$href->{user_name}'
281             _EOSTM_
282              
283             $stm .= qq[and $heap->{DomainColumn} like '$href->{domain}'] if
284             $href->{domain};
285              
286             my $sth = $heap->{Connection}->prepare($stm);
287             my $rv = $sth->execute();
288             my $foo = $sth->fetchrow_hashref();
289             $sth->finish();
290             return $foo;
291             }
292              
293             sub _update_dbi {
294             my ($heap,$href) = @_;
295             $href->{new_user_name} ||= $href->{user_name};
296             $href->{new_domain} ||= $href->{domain} || '';
297             $href->{new_password} ||= $href->{password} || '';
298             $href->{new_persistent} ||= $href->{persistent} || '';
299             my $stm = <<_EOSTM_;
300             update $heap->{Table}
301             set $heap->{UserColumn} = '$href->{new_user_name}',
302             $heap->{DomainColumn} = '$href->{new_domain}',
303             $heap->{PasswordColumn} = '$href->{new_password}',
304             $heap->{PersistentColumn} = '$href->{new_persistent}'
305             where user_name like '$href->{user_name}'
306             _EOSTM_
307              
308             $stm .= qq[ and $heap->{DomainColumn} like '$href->{domain}'] if
309             $href->{domain};
310             my $sth = $heap->{Connection}->prepare($stm);
311             my $rv = $sth->execute();
312             $sth->finish();
313             }
314              
315             sub delete_dbi {
316             my ($heap,$href) = @_;
317             my $stm = <<_EOSTM_;
318             delete from $heap->{Table}
319             where $heap->{UserColumn} = '$href->{user_name}'
320             _EOSTM_
321              
322             $stm .= qq[ and $heap->{DomainColumn} = '$href->{domain}'] if
323             $href->{domain};
324             my $sth = $heap->{Connection}->prepare($stm);
325             my $rv = $sth->execute();
326             $sth->finish();
327             }
328              
329             ###############################################################################
330             #
331             # The main UserBase states
332             #
333              
334             sub poco_userbase_start {
335             my ($kernel,$heap) =
336             @_[KERNEL, HEAP];
337             for(my $i=ARG0;$i<@_;$i+=2) { $heap->{$_[$i]}=$_[$i+1]; }
338             $kernel->alias_set($heap->{Alias});
339             }
340              
341             sub poco_userbase_log_on {
342             my $heap = $_[HEAP];
343             my %params = splice @_,ARG0;
344              
345             croak qq($heap->{_type} requires a user_name to log on.)
346             unless exists $params{user_name};
347             croak qq($heap->{_type} requires a response state to return to.)
348             unless exists $params{response};
349            
350             DEBUG and do {
351             warn "\n";
352             warn "/--- $heap->{_type} logging in ---" . "\n";
353             warn "| user_name : $params{user_name}" . "\n";
354             warn "| password : " . _no_undef($params{password}) . "\n";
355             warn "| persistent : " . _no_undef($params{persistent}) . "\n";
356             warn "| domain : " . _no_undef($params{domain}) . "\n";
357             warn "| response : $params{response}" . "\n";
358             warn "\\-------------------" . "\n";
359             };
360              
361             my $uref;
362             for($heap->{Protocol}) {
363             /file/ && do { $uref = _read_file($heap,\%params); last; };
364             /dbi/ && do { $uref = _read_dbi($heap,\%params); last; };
365             }
366             my $auth = 0;
367              
368             if($uref->{user_name}) {
369             warn qq(Found user_name $uref->{user_name}) if DEBUG;
370             if($uref->{password}) {
371             warn qq(Found password $uref->{password}, trying to match) if DEBUG;
372             for($heap->{Cipher}) {
373             /crypt/ && do {
374             $auth = 1 if
375             crypt($params{password},$uref->{password}) eq $uref->{password};
376             last;
377             };
378             /md5$/ && do {
379             $auth = 1 if md5($params{password}) eq $uref->{password};
380             last;
381             };
382             /md5_hex$/ && do {
383             $auth = 1 if md5_hex($params{password}) eq $uref->{password};
384             last;
385             };
386             /md5_base64$/ && do {
387             $auth = 1 if md5_base64($params{password}) eq $uref->{password};
388             last;
389             };
390             /sha1$/ && do {
391             $auth = 1 if
392             sha1($params{password}) eq $uref->{password};
393             last;
394             };
395             /sha1_hex$/ && do {
396             $auth = 1 if sha1_hex($params{password}) eq $uref->{password};
397             last;
398             };
399             /sha1_base64$/ && do {
400             $auth = 1 if sha1_base64($params{password}) eq $uref->{password};
401             last;
402             };
403             }
404             if($auth) {
405             warn qq(Found matching password) if DEBUG;
406             } else {
407             warn qq(Did not find matching password) if DEBUG;
408             }
409             } else {
410             warn qq(No password to match, assuming that it's authorized) if DEBUG;
411             $auth = 1;
412             }
413             } else {
414             warn qq(Failed to authorize $params{user_name}) if DEBUG;
415             }
416              
417             if($auth) {
418             $heap->{Users}{_user_key(\%params)} = { logged_in => 1,
419             persistent => $params{persistent},
420             };
421             $params{persistent}{_persistent} = thaw($uref->{persistent})
422             if $uref->{persistent} && $uref->{persistent} ne '';
423             }
424              
425             $_[SENDER]->postback($params{response})->($auth,
426             $params{user_name},
427             $params{domain},
428             $params{password} );
429             }
430              
431             sub poco_userbase_log_off {
432             my $heap = $_[HEAP];
433             my %params = splice @_,ARG0;
434              
435             croak qq($heap->{user_name} requires a user_name to log on.)
436             unless exists $params{user_name};
437            
438             DEBUG and do {
439             warn "\n";
440             warn "/--- $heap->{_type} logging out ---" . "\n";
441             warn "| user_name : $params{user_name}" . "\n";
442             warn "| domain : " . _no_undef($params{domain}) . "\n";
443             warn "\\--------------------" . "\n";
444             };
445              
446             my $persist_ref =
447             $heap->{Users}{_user_key(\%params)}{persistent}{_persistent};
448             $persist_ref = freeze($persist_ref) if defined $persist_ref;
449             my $rec = { user_name => $params{user_name},
450             domain => $params{domain},
451             new_persistent => $persist_ref,
452             };
453              
454             for($heap->{Protocol}) {
455             /file/ and do { _update_file($heap,$rec); last; };
456             /dbi/ and do { _update_dbi($heap,$rec); last; };
457             }
458              
459             delete $heap->{Users}{_user_key(\%params)};
460             }
461              
462             ###############################################################################
463              
464             sub poco_userbase_create {
465             my $heap = $_[HEAP];
466             my $protocol = $heap->{Protocol};
467             my %params = splice @_,ARG0;
468              
469             croak qq($heap->{_type} could not create user without valid username.)
470             unless exists $params{user_name};
471              
472             DEBUG and do {
473             warn "\n";
474             warn "/--- $heap->{_type} creating user ---" . "\n";
475             warn "| user_name : $params{user_name}" . "\n";
476             warn "| domain : " . _no_undef($params{domain}) . "\n";
477             warn "| password : " . _no_undef($params{password}) . "\n";
478             warn "\\-------------------" . "\n";
479             };
480              
481             if($params{password}) {
482             for($heap->{Cipher}) {
483             /crypt/ && do {
484             my $salt =
485             join '', ('.', '/', 0..9, 'A'..'Z', 'a'..'z')[rand 64, rand 64];
486             $params{password} = crypt($params{password},$salt);
487             last;
488             };
489             /md5$/ && do {
490             $params{password} = md5($params{password});
491             last;
492             };
493             /md5_hex$/ && do {
494             $params{password} = md5_hex($params{password});
495             last;
496             };
497             /md5_base64$/ && do {
498             $params{password} = md5_base64($params{password});
499             last;
500             };
501             /sha1$/ && do {
502             $params{password} = sha1($params{password});
503             last;
504             };
505             /sha1_hex$/ && do {
506             $params{password} = sha1_hex($params{password});
507             last;
508             };
509             /sha1_base64$/ && do {
510             $params{password} = sha1_base64($params{password});
511             last;
512             };
513             }
514             }
515              
516             for($heap->{Protocol}) {
517             /file/ and do { _create_file($heap,\%params); last; };
518             /dbi/ and do { _create_dbi($heap,\%params); last; };
519             }
520             }
521              
522             sub poco_userbase_delete {
523             my $heap = $_[HEAP];
524             my $protocol = $heap->{Protocol};
525             my %params = splice @_,ARG0;
526              
527             croak qq($heap->{_type} could not delete a user without a user_name.)
528             unless exists $params{user_name};
529            
530             DEBUG and do {
531             warn "\n";
532             warn "/--- logging in ---" . "\n";
533             warn "| user_name : $params{user_name}" . "\n";
534             warn "| domain : " . _no_undef($params{domain}) . "\n";
535             warn "| password : " . _no_undef($params{password}) . "\n";
536             warn "\\-------------------" . "\n";
537             };
538              
539             for($heap->{Protocol}) {
540             /file/ and do { _delete_file($heap,\%params); last; };
541             /dbi/ and do { _delete_dbi($heap,\%params); last; };
542             }
543             }
544              
545             sub poco_userbase_update {
546             my $heap = $_[HEAP];
547             my $protocol = $heap->{Protocol};
548             my %params = splice @_,ARG0;
549            
550             DEBUG and do {
551             warn "\n";
552             warn "/--- $heap->{_type} updating ---" . "\n";
553             warn "| user_name : $params{user_name}" . "\n";
554             warn "| domain : " . _no_undef($params{domain}) . "\n";
555             warn "| password : " . _no_undef($params{password}) . "\n";
556             warn "\\-------------------" . "\n";
557             };
558              
559             for($heap->{Cipher}) {
560             /crypt/ && do {
561             my $salt =
562             join '', ('.', '/', 0..9, 'A'..'Z', 'a'..'z')[rand 64, rand 64];
563             $params{password} = crypt($params{password},$salt);
564             last;
565             };
566             /md5$/ && do {
567             $params{password} = md5($params{password});
568             last;
569             };
570             /md5_hex$/ && do {
571             $params{password} = md5_hex($params{password});
572             last;
573             };
574             /md5_base64$/ && do {
575             $params{password} = md5_base64($params{password});
576             last;
577             };
578             /sha1$/ && do {
579             $params{password} = sha1($params{password});
580             last;
581             };
582             /sha1_hex$/ && do {
583             $params{password} = sha1_hex($params{password});
584             last;
585             };
586             /sha1_base64$/ && do {
587             $params{password} = sha1_base64($params{password});
588             last;
589             };
590             }
591            
592             for($heap->{Protocol}) {
593             /file/ and do { _update_file($heap,\%params); last; };
594             /dbi/ and do { _update_dbi($heap,\%params); last; };
595             }
596             }
597              
598             ###############################################################################
599              
600             sub poco_userbase_list_active {
601             my $heap = $_[HEAP];
602             my %params = splice @_,ARG0;
603            
604             DEBUG and do {
605             warn "\n";
606             warn "/--- $heap->{_type} listing active users ---" . "\n";
607             warn "| response : $params{response}" . "\n";
608             warn "\\-------------------" . "\n";
609             };
610              
611             my $users = [map { [split ':'] } keys %{$heap->{Users}} ];
612             $_[SENDER]->postback($params{response})->($users);
613             }
614              
615             ###############################################################################
616              
617             1;
618              
619             __END__