File Coverage

blib/lib/Catalyst/Plugin/EncryptID.pm
Criterion Covered Total %
statement 53 53 100.0
branch 12 20 60.0
condition 2 4 50.0
subroutine 10 10 100.0
pod 3 3 100.0
total 80 90 88.8


line stmt bran cond sub pod time code
1             package Catalyst::Plugin::EncryptID;
2              
3 2     2   713843 use 5.006;
  2         5  
4 2     2   7 use strict;
  2         2  
  2         44  
5 2     2   7 use warnings FATAL => 'all';
  2         4  
  2         73  
6              
7 2     2   813 use Crypt::Blowfish;
  2         1370  
  2         908  
8              
9             =head1 NAME
10              
11             Catalyst::Plugin::EncryptID - Obfuscate IDs/string in URLs
12              
13             =head1 VERSION
14              
15             Version 0.01
16              
17             =cut
18              
19             our $VERSION = '0.02';
20              
21             =head1 DESCRIPTION
22              
23             This module makes easy to obfuscate internal IDs when using them in a URL given to users.
24             Instead of seeing L<http://example.com/item/42>
25             users will see L<http://example.com/item/c98ea08a8e8ad715> .
26             This will prevent nosy users from trying to iterate all items based on a simple ID in the URL.
27              
28             =head1 CONFIGURATION
29              
30             Configuration requires a secret key at a minimum.
31              
32             Or set the secret key at run time, with:
33              
34             BEGIN {
35             TestApp->config(
36             name => 'TestApp',
37             EncryptID => {
38             secret => 'abc123xyz',
39             padding_character => '!'
40             }
41             );
42             }
43              
44             =cut
45              
46             sub _padding_character {
47 9     9   11 my ( $c ) = @_;
48 9         24 my $config = $c->config->{EncryptID};
49 9   50     494 return $config->{padding_character} || '!'
50             }
51              
52             sub _secret {
53 14     14   13 my ( $c ) = @_;
54 14         28 my $config = $c->config->{EncryptID};
55 14   50     614 my $secret = $config->{secret} || ':-) - :-)';
56 14 50       24 die "Key must be 8 byte long" if length($secret) < 8;
57 14         18 return $secret;
58             }
59              
60             sub _cipher {
61 14     14   12 my ( $c ) = @_;
62 14         19 my $secret = _secret($c);
63 14         57 return Crypt::Blowfish->new($secret);
64             }
65              
66             =head1 SYNOPSIS
67              
68             package TestApp;
69              
70             use strict;
71             use warnings;
72              
73             use Catalyst qw/EncryptID/;
74              
75             TestApp->config(
76             name => 'TestApp',
77             EncryptID => {
78             secret => 'abc123xyz',
79             padding_character => '!'
80             }
81             );
82              
83             1;
84              
85             In Controller
86              
87             package TestApp::Controller::Root;
88             use base 'Catalyst::Controller';
89              
90             __PACKAGE__->config->{namespace} = '';
91              
92             sub index : Private {
93             my ( $self, $c ) = @_;
94             $c->res->body('root index');
95             }
96              
97             sub encrypt : Global Args(1) {
98             my ( $self, $c, $id ) = @_;
99             my $encripted_hash = $c->encrypt_data($id);
100             ...
101             }
102              
103             sub decrypt : Global Args(1) {
104             my ( $self, $c, $hashid ) = @_;
105             my $decrypted_string = $c->decrypt_data($hashid);
106             ...
107             }
108              
109             sub validhash : Global Args(1) {
110             my ( $self, $c, $hashid ) = @_;
111             my $status = $c->is_valid_encrypt_hash($hashid);
112             ...
113             }
114              
115             1;
116              
117             =head1 EXPORT
118              
119             A list of functions that can be exported. You can delete this section
120             if you don't export anything, such as for a purely object-oriented module.
121              
122             =head1 SUBROUTINES/METHODS
123              
124             =head2 encrypt_data
125              
126             C<encrypt_data(ID [,PREFIX])> - Encrypt the given ID, returns the encoded hash value.
127             If "PREFIX" is given, it will be added to the ID before encoding.
128             It can be used when decoding to verify the decoded value is valid.
129              
130             =cut
131              
132             sub encrypt_data {
133 5     5 1 357555 my( $c, $text, $prefix ) = @_;
134              
135 5 50       19 warn "Missing Clear text ID parameter" unless defined $text;
136 5 50       12 return unless defined $text;
137              
138             ## Prefix is optional, can be undef
139 5 50       12 $text = $prefix . $text if defined $prefix;
140              
141 5         7 my $min_length = 8;
142 5         5 my $ciphertext_hash = '';
143              
144             #encode an empty string
145 5 50       16 $text = _padding_character($c) x $min_length if length($text) < 1;
146              
147 5         13 while ( length($text) > 0 ) {
148 7         22 my $sub_text = substr($text,0,$min_length,'');
149 7 100       18 if ( length($sub_text) < 8 ) {
150 4         9 my $left = $min_length - length($sub_text);
151 4         8 $sub_text = ( _padding_character($c) x ($left % $min_length) ). $sub_text;
152             };
153              
154 7         18 my $ciphertext = _cipher($c)->encrypt($sub_text);
155 7         568 $ciphertext_hash .= unpack('H16', $ciphertext ) ;
156             }
157              
158 5         13 return $ciphertext_hash;
159             }
160              
161             =head2 decrypt_data
162              
163             C<decrypt_data(ID)> - Decrypt the given ID, returns the original (text) ID value.
164              
165             =cut
166              
167             sub decrypt_data {
168 5     5 1 75309 my( $c, $encrypted_hash ) = @_;
169              
170 5 50       13 return unless is_valid_encrypt_hash( $c, $encrypted_hash );
171              
172 5         12 my $padding_character = _padding_character($c);
173 5         9 my $ciphertext = '';
174              
175 5         16 while ( length($encrypted_hash) > 0 ) {
176 7         23 my $sub_text = substr($encrypted_hash,0,16,'');
177 7         33 my $cipherhash = pack('H16', $sub_text );
178 7         14 my $text = _cipher($c)->decrypt($cipherhash);
179              
180 7         553 $text =~ s/^$padding_character+//;
181 7         21 $ciphertext .= $text;
182             };
183 5         14 return $ciphertext
184             }
185              
186             =head2 is_valid_encrypt_hash
187              
188             C<is_valid_encrypt_hash(HASH)> - Return true if given encrypt has is valid
189              
190             =cut
191              
192             sub is_valid_encrypt_hash {
193 7     7 1 29820 my( $c, $encrypted_hash ) = @_;
194 7 50       27 return 0 unless length($encrypted_hash);
195 7 100       19 return 0 unless length($encrypted_hash)%16 == 0;
196 6 50       30 return 0 unless $encrypted_hash =~ /^[0-9A-F]+$/i;
197 6         13 return 1;
198             }
199              
200             =head1 AUTHOR
201              
202             Rakesh Kumar Shardiwal, C<< <rakesh.shardiwal at gmail.com> >>
203              
204             =head1 BUGS
205              
206             Please report any bugs or feature requests to C<bug-catalyst-plugin-encryptid at rt.cpan.org>, or through
207             the web interface at L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Catalyst-Plugin-EncryptID>. I will be notified, and then you'll
208             automatically be notified of progress on your bug as I make changes.
209              
210              
211              
212              
213             =head1 SUPPORT
214              
215             You can find documentation for this module with the perldoc command.
216              
217             perldoc Catalyst::Plugin::EncryptID
218              
219              
220             You can also look for information at:
221              
222             =over 4
223              
224             =item * RT: CPAN's request tracker (report bugs here)
225              
226             L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Catalyst-Plugin-EncryptID>
227              
228             =item * AnnoCPAN: Annotated CPAN documentation
229              
230             L<http://annocpan.org/dist/Catalyst-Plugin-EncryptID>
231              
232             =item * CPAN Ratings
233              
234             L<http://cpanratings.perl.org/d/Catalyst-Plugin-EncryptID>
235              
236             =item * Search CPAN
237              
238             L<http://search.cpan.org/dist/Catalyst-Plugin-EncryptID/>
239              
240             =back
241              
242              
243             =head1 ACKNOWLEDGEMENTS
244              
245              
246             =head1 LICENSE AND COPYRIGHT
247              
248             Copyright 2016 Rakesh Kumar Shardiwal.
249              
250             This program is free software; you can redistribute it and/or modify it
251             under the terms of the the Artistic License (2.0). You may obtain a
252             copy of the full license at:
253              
254             L<http://www.perlfoundation.org/artistic_license_2_0>
255              
256             Any use, modification, and distribution of the Standard or Modified
257             Versions is governed by this Artistic License. By using, modifying or
258             distributing the Package, you accept this license. Do not use, modify,
259             or distribute the Package, if you do not accept this license.
260              
261             If your Modified Version has been derived from a Modified Version made
262             by someone other than you, you are nevertheless required to ensure that
263             your Modified Version complies with the requirements of this license.
264              
265             This license does not grant you the right to use any trademark, service
266             mark, tradename, or logo of the Copyright Holder.
267              
268             This license includes the non-exclusive, worldwide, free-of-charge
269             patent license to make, have made, use, offer to sell, sell, import and
270             otherwise transfer the Package with respect to any patent claims
271             licensable by the Copyright Holder that are necessarily infringed by the
272             Package. If you institute patent litigation (including a cross-claim or
273             counterclaim) against any party alleging that the Package constitutes
274             direct or contributory patent infringement, then this Artistic License
275             to you shall terminate on the date that such litigation is filed.
276              
277             Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER
278             AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
279             THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
280             PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY
281             YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR
282             CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR
283             CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE,
284             EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
285              
286              
287             =cut
288              
289             1; # End of Catalyst::Plugin::EncryptID