]>
Commit | Line | Data |
---|---|---|
7c87e995 MG |
1 | package Apache2::Authen::Passphrase; |
2 | ||
f4cc782b MG |
3 | use 5.014000; |
4 | use strict; | |
5 | use warnings; | |
6 | use parent qw/Exporter/; | |
788a3738 | 7 | use subs qw/OK HTTP_UNAUTHORIZED/; |
f4cc782b | 8 | |
8443f44e MG |
9 | our $VERSION = 0.002001; |
10 | ||
11 | use constant USER_REGEX => qr/^\w{2,20}$/pas; | |
12 | use constant PASSPHRASE_VERSION => 1; | |
13 | use constant INVALID_USER => "invalid-user\n"; | |
14 | use constant BAD_PASSWORD => "bad-password\n"; | |
f4cc782b | 15 | |
788a3738 | 16 | use if $ENV{MOD_PERL}, 'Apache2::RequestRec'; |
de5adf1e | 17 | use if $ENV{MOD_PERL}, 'Apache2::RequestUtil'; |
788a3738 MG |
18 | use if $ENV{MOD_PERL}, 'Apache2::Access'; |
19 | use if $ENV{MOD_PERL}, 'Apache2::Const' => qw/OK HTTP_UNAUTHORIZED/; | |
f4cc782b MG |
20 | use Authen::Passphrase; |
21 | use Authen::Passphrase::BlowfishCrypt; | |
22 | use YAML::Any qw/LoadFile DumpFile/; | |
23 | ||
24 | our @EXPORT_OK = qw/pwset pwcheck pwhash USER_REGEX PASSPHRASE_VERSION INVALID_USER BAD_PASSWORD/; | |
25 | ||
26 | ################################################## | |
27 | ||
8443f44e MG |
28 | our $rootdir; |
29 | $rootdir //= $ENV{AAP_ROOTDIR}; | |
f4cc782b MG |
30 | |
31 | sub pwhash{ | |
6cc05078 | 32 | my ($pass)=@_; |
f4cc782b | 33 | |
6cc05078 MG |
34 | my $ppr=Authen::Passphrase::BlowfishCrypt->new( |
35 | cost => 10, | |
36 | passphrase => $pass, | |
37 | salt_random => 1, | |
38 | ); | |
f4cc782b | 39 | |
6cc05078 | 40 | $ppr->as_rfc2307 |
f4cc782b MG |
41 | } |
42 | ||
43 | sub pwset{ | |
6cc05078 | 44 | my ($user, $pass)=@_; |
f4cc782b | 45 | |
6cc05078 MG |
46 | my $file = "$rootdir/$user.yml"; |
47 | my $conf = eval { LoadFile $file } // undef; | |
48 | $conf->{passphrase}=pwhash $pass; | |
49 | $conf->{passphrase_version}=PASSPHRASE_VERSION; | |
50 | DumpFile $file, $conf; | |
f4cc782b | 51 | |
6cc05078 | 52 | chmod 0660, $file; |
f4cc782b MG |
53 | } |
54 | ||
55 | sub pwcheck{ | |
6cc05078 MG |
56 | my ($user, $pass)=@_; |
57 | die INVALID_USER unless $user =~ USER_REGEX; ## no critic (RequireCarping) | |
58 | $user=${^MATCH}; # Make taint shut up | |
59 | my $conf=LoadFile "$rootdir/$user.yml"; | |
60 | ||
61 | ## no critic (RequireCarping) | |
804310f6 | 62 | die BAD_PASSWORD unless keys %$conf; # Empty hash means no such user |
6cc05078 MG |
63 | die BAD_PASSWORD unless Authen::Passphrase->from_rfc2307($conf->{passphrase})->match($pass); |
64 | ## use critic | |
65 | pwset $user, $pass if $conf->{passphrase_version} < PASSPHRASE_VERSION | |
f4cc782b MG |
66 | } |
67 | ||
68 | sub handler{ | |
6cc05078 MG |
69 | my $r=shift; |
70 | local $rootdir = $r->dir_config('AuthenPassphraseRootdir'); | |
f4cc782b | 71 | |
6cc05078 MG |
72 | my ($rc, $pass) = $r->get_basic_auth_pw; |
73 | return $rc unless $rc == OK; | |
f4cc782b | 74 | |
6cc05078 MG |
75 | my $user=$r->user; |
76 | unless (eval { pwcheck $user, $pass; 1 }) { | |
77 | $r->note_basic_auth_failure; | |
78 | return HTTP_UNAUTHORIZED | |
79 | } | |
f4cc782b | 80 | |
6cc05078 | 81 | OK |
f4cc782b MG |
82 | } |
83 | ||
84 | 1; | |
85 | __END__ | |
86 | ||
87 | =head1 NAME | |
88 | ||
89 | Apache2::Authen::Passphrase - basic authentication with Authen::Passphrase | |
90 | ||
91 | =head1 SYNOPSIS | |
92 | ||
93 | use Apache2::Authen::Passphrase qw/pwcheck pwset pwhash/; | |
f7fdbaad | 94 | $Apache2::Authen::Passphrase::rootdir = "/path/to/user/directory" |
f4cc782b MG |
95 | my $hash = pwhash $username, $password; |
96 | pwset $username, "pass123"; | |
97 | eval { pwcheck $username, "pass123" }; | |
98 | ||
99 | # In Apache2 config | |
100 | <Location /secret> | |
101 | PerlAuthenHandler Apache2::Authen::Passphrase | |
f7fdbaad | 102 | PerlSetVar AuthenPassphraseRootdir /path/to/user/directory |
f4cc782b MG |
103 | AuthName MyAuth |
104 | Require valid-user | |
105 | </Location> | |
106 | ||
107 | =head1 DESCRIPTION | |
108 | ||
109 | Apache2::Authen::Passphrase is a perl module which provides easy-to-use Apache2 authentication. It exports some utility functions and it contains a PerlAuthenHandler. | |
110 | ||
7c87e995 MG |
111 | The password hashes are stored in YAML files in an directory (called the C<rootdir>), one file per user. |
112 | ||
113 | Set the C<rootdir> like this: | |
114 | ||
115 | $Apache2::Authen::Passphrase::rootdir = '/path/to/rootdir'; | |
116 | ||
117 | or by setting the C<AAP_ROOTDIR> enviroment variable to the desired value. | |
118 | ||
f4cc782b MG |
119 | =head1 FUNCTIONS |
120 | ||
121 | =over | |
122 | ||
123 | =item B<pwhash>() | |
124 | ||
125 | Takes the password as a single argument and returns the password hash. | |
126 | ||
127 | =item B<pwset>(I<$username>, I<$password>) | |
128 | ||
129 | Sets the password of $username to $password. | |
130 | ||
131 | =item B<pwcheck>(I<$username>, I<$password>) | |
132 | ||
133 | Checks the given username and password, throwing an exception if the username is invalid or the password is incorrect. | |
134 | ||
135 | =item B<handler> | |
136 | ||
137 | The PerlAuthenHandler for use in apache2. It uses Basic Access Authentication. | |
138 | ||
139 | =item B<USER_REGEX> | |
140 | ||
141 | A regex that matches valid usernames. Usernames must be at least 2 characters, at most 20 characters, and they may only contain word characters (C<[A-Za-z0-9_]>). | |
142 | ||
143 | =item B<INVALID_USER> | |
144 | ||
145 | Exception thrown if the username does not match C<USER_REGEX>. | |
146 | ||
147 | =item B<BAD_PASSWORD> | |
148 | ||
149 | Exception thrown if the password is different from the one stored in the user's yml file. | |
150 | ||
151 | =item B<PASSPHRASE_VERSION> | |
152 | ||
153 | The version of the passphrase. It is incremented each time the passphrase hashing scheme is changed. Versions so far: | |
154 | ||
155 | =over | |
156 | ||
157 | =item Version 1 B<(current)> | |
158 | ||
159 | Uses C<Authen::Passphrase::BlowfishCrypt> with a cost factor of 10 | |
160 | ||
161 | =back | |
162 | ||
163 | =back | |
164 | ||
7c87e995 MG |
165 | =head1 ENVIRONMENT |
166 | ||
167 | =over | |
168 | ||
169 | =item AAP_ROOTDIR | |
170 | ||
171 | If the C<rootdir> is not explicitly set, it is taken from this environment variable. | |
172 | ||
173 | =back | |
174 | ||
f4cc782b MG |
175 | =head1 AUTHOR |
176 | ||
177 | Marius Gavrilescu, E<lt>marius@ieval.roE<gt> | |
178 | ||
179 | =head1 COPYRIGHT AND LICENSE | |
180 | ||
0fc1719a | 181 | Copyright (C) 2013-2015 by Marius Gavrilescu |
f4cc782b MG |
182 | |
183 | This library is free software; you can redistribute it and/or modify | |
184 | it under the same terms as Perl itself, either Perl version 5.14.2 or, | |
185 | at your option, any later version of Perl 5 you may have available. | |
186 | ||
187 | ||
188 | =cut |