File Coverage

blib/lib/Authen/PhoneChallenge.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             package Authen::PhoneChallenge;
2 1     1   1289 use warnings;
  1         2  
  1         36  
3 1     1   5 use strict;
  1         2  
  1         36  
4 1     1   15 use Carp;
  1         2  
  1         75  
5 1     1   392 use XML::Simple;
  0            
  0            
6              
7             our $VERSION = "0.02";
8              
9             =head1 NAME
10              
11             PhoneChallenge - Module that does simple challenge/response using only numbers, for use in phone systems.
12              
13             =head1 DESCRIPTION
14              
15             This module is a simple challenge/response system for use over the phone.
16             The scheme is that a user is issued a list of indexes and values. When they
17             need to authenticate the system prompts them with an index and they respond with
18             the associated value.
19              
20              
21             =head1 SYNOPSIS
22              
23             use Authen::PhoneChallenge;
24             my $auth = new Authen::PhoneChallenge($authFile);
25             $auth->set_user($userId);
26             $auth->get_challenge();
27             ...
28             $auth->check_response($resp);
29              
30              
31             =head1 FILE FORMAT
32              
33             The authentication file is a simple XML document in the following format:
34              
35            
36            
37            
38            
39            
40            
41              
42             =head1 FUNCTIONS
43              
44             =cut
45              
46             =head2 new
47              
48             Create a new challenge object. Must pass a authentication file name (See FILE FORMAT above)
49              
50             =cut
51              
52             sub new
53             {
54             my $class = shift;
55             my $self = {
56             user => undef,
57             authFile => shift,
58             authData => undef,
59             maxTokenUse => undef,
60             challenge => undef,
61             };
62              
63             return bless $self, $class;
64             }
65              
66              
67             =head2 set_user
68              
69             Set the user ID for all future operations.
70              
71             =cut
72              
73             sub set_user
74             {
75             my $self = shift;
76              
77             $self->{user} = shift || carp('Must pass user ID');
78            
79             # Load the data
80             $self->parse_authen_file();
81              
82             return defined $self->{authData};
83             }
84              
85             =head2 get_challenge
86              
87             Get a challenge for the user.
88             Calling get_challenge will invalidate any outstanding challenges.
89              
90             =cut
91              
92             sub get_challenge
93             {
94             my $self = shift;
95             return unless defined $self->{authData};
96              
97             my @challenges = keys %{$self->{authData}};
98              
99             my $index = int(rand(@challenges));
100             $self->{challenge} = $challenges[$index];
101              
102             return $self->{challenge};
103             }
104              
105              
106             =head2 check_response
107              
108             Check a response for validity.
109              
110             =cut
111              
112             sub check_response
113             {
114             my $self = shift;
115             return unless defined $self->{authData} && defined $self->{challenge};
116              
117             my $resp = shift || croak('No response passed');
118              
119             if ($self->{authData}{$self->{challenge}}{response} eq $resp)
120             {
121             $self->{challenge} = undef;
122             return 1;
123             }
124              
125             return;
126             }
127              
128              
129             sub parse_authen_file
130             {
131             my $self = shift;
132             $self->{authData} = undef;
133              
134             return if(!-e $self->{authFile});
135              
136             my $parser = XML::Simple->new();
137             my $doc = $parser->XMLin($self->{authFile});
138              
139             return if(!$doc->{user}{$self->{user}});
140              
141             foreach my $token (@{$doc->{user}{$self->{user}}{token}})
142             {
143             $self->{authData}{$token->{challenge}} = {
144             response => $token->{response},
145             used => $token->{used}
146             };
147             }
148             }
149              
150             1;
151             __END__