File Coverage

blib/lib/App/PasswordManager.pm
Criterion Covered Total %
statement 58 65 89.2
branch 10 18 55.5
condition 1 3 33.3
subroutine 13 14 92.8
pod 0 6 0.0
total 82 106 77.3


line stmt bran cond sub pod time code
1             package App::PasswordManager;
2              
3 2     2   386413 use strict;
  2         5  
  2         91  
4 2     2   13 use warnings;
  2         4  
  2         146  
5              
6 2     2   1160 use Crypt::PBKDF2;
  2         515228  
  2         117  
7 2     2   1365 use File::HomeDir;
  2         15946  
  2         198  
8 2     2   21 use File::Spec;
  2         4  
  2         57  
9 2     2   1749 use JSON;
  2         28507  
  2         18  
10              
11             our $VERSION = '1.0.0';
12              
13             sub new {
14 8     8 0 398449 my ( $class, %args ) = @_;
15              
16 8         83 my $home_dir = File::HomeDir->my_home;
17 8         587 my $file = File::Spec->catfile( $home_dir, '.password_manager.json' );
18              
19             my $self = {
20             pbkdf2 => Crypt::PBKDF2->new(
21             hash_class => 'HMACSHA1',
22             iterations => 10000,
23             salt_len => 10,
24             ),
25 8   33     320 data_file => $args{file} || $file,
26             salt => 42,
27             passwords => {},
28             };
29              
30 8         41444 bless $self, $class;
31              
32 8         66 $self->_load_passwords();
33 8         42 return $self;
34             }
35              
36             sub add_password {
37 7     7 0 70 my ( $self, $login, $password ) = @_;
38 7 100       44 die "Login '$login' already exists!\n" if exists $self->{passwords}{$login};
39              
40 6         44 my $hashed_password = $self->{pbkdf2}->generate( $password, $self->{salt} );
41             $self->{passwords}{$login} = {
42             password => $hashed_password,
43             salt => $self->{salt},
44 6         462241 };
45 6         50 $self->_save_passwords();
46 6         21 return scalar keys %{ $self->{passwords} };
  6         42  
47             }
48              
49             sub list_passwords {
50 1     1 0 7 my ($self) = @_;
51 1         13 return [ keys %{ $self->{passwords} } ];
  1         5  
52             }
53              
54             sub remove_password {
55 2     2 0 22 my ( $self, $login ) = @_;
56 2 100       19 die "Login '$login' not found!\n" unless exists $self->{passwords}{$login};
57 1         5 delete $self->{passwords}{$login};
58 1         5 $self->_save_passwords();
59 1         8 return 1;
60             }
61              
62             sub edit_password {
63 2     2 0 19 my ( $self, $login, $new_password ) = @_;
64 2 100       17 die "Login '$login' not found!\n" unless exists $self->{passwords}{$login};
65              
66 1         7 my $hashed_password = $self->{pbkdf2}->generate( $new_password, $self->{salt} );
67 1         60396 $self->{passwords}{$login}{password} = $hashed_password;
68 1         3 $self->{passwords}{$login}{salt} = $self->{salt};
69 1         6 $self->_save_passwords();
70 1         5 return 1;
71             }
72              
73             sub copy_to_clipboard {
74 0     0 0 0 my ( $self, $login ) = @_;
75 0 0       0 die "Login '$login' not found!\n" unless exists $self->{passwords}{$login};
76 0         0 my $password = $self->{passwords}{$login}{password};
77 0 0       0 open my $clip, '|-', 'xclip -selection clipboard'
78             or die "Could not copy: $!";
79 0         0 print $clip $password;
80 0         0 close $clip;
81 0         0 return 1;
82             }
83              
84             sub _load_passwords {
85 8     8   26 my ($self) = @_;
86 8 50       216 if ( -e $self->{data_file} ) {
87 8 50       344 open my $fh, '<', $self->{data_file} or die "Could not open file '$self->{data_file}': $!";
88 8         45 local $/;
89 8         254 my $json = <$fh>;
90 8         101 close $fh;
91 8 50       78 $self->{passwords} = decode_json($json) if $json;
92             }
93             }
94              
95             sub _save_passwords {
96 8     8   21 my ($self) = @_;
97 8 50       940 open my $fh, '>', $self->{data_file} or die "Could not open file '$self->{data_file}': $!";
98 8         126 print $fh encode_json($self->{passwords});
99 8         1169 close $fh;
100             }
101              
102             1;
103             __END__