File Coverage

blib/lib/Passwd/Keyring/KDEWallet.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             package Passwd::Keyring::KDEWallet;
2              
3 1     1   18748 use warnings;
  1         3  
  1         33  
4 1     1   4 use strict;
  1         2  
  1         38  
5             #use parent 'Keyring';
6 1     1   269 use Net::DBus;
  0            
  0            
7             use Carp;
8              
9             =head1 NAME
10              
11             Passwd::Keyring::KDEWallet - Password storage implementation based on KDE Wallet.
12              
13             =head1 VERSION
14              
15             Version 0.2008
16              
17             =cut
18              
19             our $VERSION = '0.2008';
20              
21             our $APP_NAME = "Passwd::Keyring";
22             our $FOLDER_NAME = "Perl-Passwd-Keyring";
23              
24             =head1 SYNOPSIS
25              
26             KDE Wallet based implementation of L.
27              
28             use Passwd::Keyring::KDEWallet;
29              
30             my $keyring = Passwd::Keyring::KDEWallet->new(
31             app=>"blahblah scraper",
32             group=>"Johnny web scrapers",
33             );
34              
35             my $username = "John"; # or get from .ini, or from .argv...
36              
37             my $password = $keyring->get_password($username, "blahblah.com");
38             unless( $password ) {
39             $password = ;
40              
41             # securely save password for future use
42             $keyring->set_password($username, $password, "blahblah.com");
43             }
44              
45             login_somewhere_using($username, $password);
46             if( password_was_wrong ) {
47             $keyring->clear_password($username, "blahblah.com");
48             }
49              
50             Note: see L for detailed comments
51             on keyring method semantics (this document is installed with
52             C package).
53              
54             =head1 SUBROUTINES/METHODS
55              
56             =head2 new(app=>'app name', group=>'passwords folder')
57              
58             Initializes the processing. Croaks if kwallet (or d-bus, or anything needed) does not
59             seem to be available.
60              
61             Handled named parameters:
62              
63             - app - symbolic application name (used in "Application .... is asking to open the wallet"
64             KDE Wallet prompt)
65              
66             - group - name for the password group (used as KDE Wallet folder name)
67              
68             =cut
69              
70             sub new {
71             my ($cls, %args) = @_;
72              
73             my $self = {};
74             $self->{app} = $args{app} || 'Passwd::Keyring::KDEWallet';
75             $self->{group} = $args{group} || 'Passwd::Keyring::default';
76             bless $self;
77              
78             #$self->{bus} = Net::DBus->find()
79             $self->{bus} = Net::DBus->session()
80             or croak("KWallet not available (can't access DBus)");
81             # get_service also may fail by itself, I got cpantesters reports with message
82             # "org.freedesktop.DBus.Error.ServiceUnknown: The name org.kde.kwalletd was not provided by any .service files"
83             # Let's rewrite this slightly
84             my$kwallet_svc;
85             eval {
86             $kwallet_svc = $self->{bus}->get_service('org.kde.kwalletd');
87             };
88             if($@) {
89             croak("KWallet not available (not installed?). Details:\n$@");
90             } elsif (! $kwallet_svc) {
91             croak("KWallet not available (can't access KWallet, likely kwalletd not running)");
92             }
93             $self->{kwallet} = $kwallet_svc->get_object('/modules/kwalletd', 'org.kde.KWallet')
94             or croak("Kwallet not available (can't find wallet)");
95             $self->_open_if_not_open();
96              
97             unless($self->{kwallet}->hasFolder($self->{handle}, $self->{group}, $self->{app})) {
98             $self->{kwallet}->createFolder($self->{handle}, $self->{group}, $self->{app})
99             or croak("Failed to create $self->{group} folder (app $self->{app})");
100             }
101              
102             return $self;
103             }
104              
105             sub _open_if_not_open {
106             my $self = shift;
107              
108             if($self->{handle}) {
109             if($self->{kwallet}->isOpen($self->{handle})) {
110             return;
111             }
112             }
113             my $net_wallet = $self->{kwallet}->networkWallet()
114             or croak("Kwallet not available (can't access network wallet");
115             $self->{handle} = $self->{kwallet}->open($net_wallet, 0, $self->{app})
116             or croak("Failed to open the KDE wallet");
117             }
118              
119             =head2 set_password(username, password, realm)
120              
121             Sets (stores) password identified by given realm for given user
122              
123             =cut
124              
125             sub set_password {
126             my ($self, $user_name, $user_password, $realm) = @_;
127             $self->_open_if_not_open();
128             my $status = $self->{kwallet}->writePassword(
129             $self->{handle}, $self->{group}, "$realm || $user_name", $user_password, $self->{app});
130             if($status) { # non-zero means failure
131             croak("Failed to save the password (status $status, user name $user_name, realm $realm, handle $self->{handle}, group $self->{group})");
132             }
133             }
134              
135             =head2 get_password($user_name, $realm)
136              
137             Reads previously stored password for given user in given app.
138             If such password can not be found, returns undef.
139              
140             =cut
141              
142             sub get_password {
143             my ($self, $user_name, $realm) = @_;
144             $self->_open_if_not_open();
145             my $reply = $self->{kwallet}->readPassword(
146             $self->{handle}, $self->{group}, "$realm || $user_name", $self->{app});
147             # In case of missing passsword we get empty string. I do not know
148             # whether it is possible to distinguish missing password from empty password,
149             # but empty passwords are exotic enough to ignore.
150             return undef if ! defined($reply) or $reply eq '';
151             return $reply;
152             }
153              
154             =head2 clear_password($user_name, $realm)
155              
156             Removes given password (if present)
157              
158             =cut
159              
160             sub clear_password {
161             my ($self, $user_name, $realm) = @_;
162             $self->_open_if_not_open();
163             my $status = $self->{kwallet}->removeEntry(
164             $self->{handle}, $self->{group}, "$realm || $user_name", $self->{app});
165             if($status == 0) {
166             return 1;
167             } else {
168             # TODO: classify failures
169             return 0;
170             }
171             }
172              
173             =head2 is_persistent
174              
175             Returns info, whether this keyring actually saves passwords persistently.
176              
177             (true in this case)
178              
179             =cut
180              
181             sub is_persistent {
182             my ($self) = @_;
183             return 1;
184             }
185              
186             =head1 AUTHOR
187              
188             Marcin Kasperski
189              
190             Approach inspired by L.
191              
192             =head1 BUGS
193              
194             Please report any bugs or feature requests to
195             issue tracker at L.
196              
197             =head1 SUPPORT
198              
199             You can find documentation for this module with the perldoc command.
200              
201             perldoc Passwd::Keyring::KDEWallet
202              
203             You can also look for information at:
204              
205             L
206              
207             Source code is tracked at:
208              
209             L
210              
211             =head1 LICENSE AND COPYRIGHT
212              
213             Copyright 2012 Marcin Kasperski.
214              
215             This program is free software; you can redistribute it and/or modify it
216             under the terms of either: the GNU General Public License as published
217             by the Free Software Foundation; or the Artistic License.
218              
219             See http://dev.perl.org/licenses/ for more information.
220              
221             =cut
222              
223              
224             1; # End of Passwd::Keyring::KDEWallet
225              
226