File Coverage

blib/lib/Dancer2/Plugin/EncryptID.pm
Criterion Covered Total %
statement 42 45 93.3
branch 9 18 50.0
condition n/a
subroutine 8 8 100.0
pod 3 3 100.0
total 62 74 83.7


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