File Coverage

lib/OAuthomatic/SecretStorage/Keyring.pm
Criterion Covered Total %
statement 1 3 33.3
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 2 4 50.0


line stmt bran cond sub pod time code
1             package OAuthomatic::SecretStorage::Keyring;
2             # ABSTRACT: Save tokens in secure storage, using L<Passwd::Keyring::Auto>
3              
4 1     1   691 use Moose;
  0            
  0            
5             use namespace::sweep;
6             use MooseX::AttributeShortcuts;
7             use Passwd::Keyring::Auto qw(get_keyring);
8             use Const::Fast;
9              
10              
11             has 'config' => (
12             is => 'ro', isa => 'OAuthomatic::Config', required => 1,
13             handles => [
14             'app_name',
15             'password_group',
16             'debug',
17             ]);
18              
19              
20             has 'server' => (
21             is => 'ro', isa => 'OAuthomatic::Server', required => 1,
22             handles => [
23             'site_name',
24             ]);
25              
26              
27             has 'keyring' => (
28             is => 'lazy', trigger => sub {
29             my ($self, $keyring, $old_val) = @_;
30             unless($keyring->is_persistent) {
31             OAuthomatic::Error::Generic->throw(
32             ident => "Attempt to use non-persistent keyring",
33             extra => "Suggested backend is non-persistent: ") . ref($keyring) . ".\n";
34             }
35             });
36              
37             sub _build_keyring {
38             my $self = shift;
39             my $password_group = $self->password_group;
40             my $app_name = $self->app_name;
41             OAuthomatic::Error::Generic->throw(
42             ident => "Bad parameter",
43             extra => "You must specify password_group and app_name (or specify keyring)")
44             unless ($password_group && $app_name);
45             my $keyring = get_keyring(group => $password_group,
46             app => $app_name);
47             unless($keyring->is_persistent) {
48             OAuthomatic::Error::Generic->throw(
49             ident => "Bad keyring configuration",
50             extra => "Got non-persistent keyring backend (") . ref($keyring) . "). Reconfigure Passwd::Keyring::Auto (and maybe install some Passwd::Keyring::<backend>) to use something non-volatile.\n";
51             }
52             if($self->debug) {
53             print "[OAuthomatic] Constructed keyring. Type: ", ref($keyring), ", app: $app_name, group: $password_group\n";
54             }
55             return $keyring;
56             }
57              
58             const my $CLIENT_MAP_KEY => "oauthomatic_client_key";
59              
60             sub get_client_cred {
61             my ($self) = @_;
62              
63             my $client_key = $self->keyring->get_password($CLIENT_MAP_KEY, $self->site_name);
64             return unless $client_key;
65             my $client_secret = $self->keyring->get_password($client_key, $self->site_name);
66             return unless $client_secret;
67             return OAuthomatic::Types::ClientCred->new(
68             key => $client_key,
69             secret => $client_secret,
70             );
71             }
72              
73             sub save_client_cred {
74             my ($self, $client) = @_;
75              
76             $self->keyring->set_password($client->key, $client->secret, $self->site_name);
77             $self->keyring->set_password($CLIENT_MAP_KEY, $client->key, $self->site_name);
78              
79             return;
80             }
81              
82             sub clear_client_cred {
83             my ($self) = @_;
84              
85             # FIXME: give an option of clearing only mapping but keeping key in storage?
86              
87             my $client_key = $self->keyring->get_password($CLIENT_MAP_KEY, $self->site_name);
88             return unless $client_key;
89              
90             $self->keyring->clear_password($client_key, $self->site_name);
91             $self->keyring->clear_password($CLIENT_MAP_KEY, $self->site_name);
92              
93             return;
94             }
95              
96             const my $TOKEN_MAP_KEY => "oauthomatic_token";
97              
98             sub get_token_cred {
99             my ($self) = @_;
100              
101             my $token = $self->keyring->get_password($TOKEN_MAP_KEY, $self->site_name);
102             return unless $token;
103             my $token_secret = $self->keyring->get_password($token, $self->site_name);
104             return unless $token_secret;
105             return OAuthomatic::Types::TokenCred->new(
106             token => $token,
107             secret => $token_secret,
108             );
109             }
110              
111             sub save_token_cred {
112             my ($self, $access) = @_;
113              
114             $self->keyring->set_password($access->token, $access->secret, $self->site_name);
115             $self->keyring->set_password($TOKEN_MAP_KEY, $access->token, $self->site_name);
116              
117             return;
118             }
119              
120             sub clear_token_cred {
121             my ($self) = @_;
122              
123             my $token = $self->keyring->get_password($TOKEN_MAP_KEY, $self->site_name);
124             return unless $token;
125              
126             $self->keyring->clear_password($token, $self->site_name);
127             $self->keyring->clear_password($TOKEN_MAP_KEY, $self->site_name);
128              
129             return;
130             }
131              
132             with 'OAuthomatic::SecretStorage';
133              
134             1;
135              
136             __END__
137              
138             =pod
139              
140             =encoding UTF-8
141              
142             =head1 NAME
143              
144             OAuthomatic::SecretStorage::Keyring - Save tokens in secure storage, using L<Passwd::Keyring::Auto>
145              
146             =head1 VERSION
147              
148             version 0.0201
149              
150             =head1 DESCRIPTION
151              
152             Implements L<OAuthomatic::SecretStorage> interface using
153             L<Passwd::Keyring::Auto> (what usually means saving data in Gnome
154             Keyring, KDE Wallet, Windows Vault or similar secure storage).
155              
156             Note that tokens are saved in slightly specific way. Whenever client
157             key is saved, we create two entries in keyring:
158              
159             username: client_key
160             password: client_secret
161              
162             and
163              
164             username: "oauthomatic_client_key"
165             password: client_key
166              
167             The former is natural. The latter helps find what current key is
168             (this record is not very secret but it is easier to keep it too
169             than invent second configuration backend).
170              
171             Access tokens are treated similarly:
172              
173             username: token
174             password: token_secret
175              
176             and
177              
178             username: "oauthomatic_token"
179             password: token
180              
181             =head1 ATTRIBUTES
182              
183             =head2 config
184              
185             L<OAuthomatic::Config> object used to bundle various configuration params.
186              
187             =head2 server
188              
189             L<OAuthomatic::Server> object used to bundle server-related configuration params.
190              
191             =head2 keyring
192              
193             Actual password backend in use.
194              
195             Usually initialized automatically, guessing best possible password
196             backend (according to L<Passwd::Keyring::Auto> selection and using
197             it's C<app_name> and C<password_group> attributes), but can be
198             specified if application prefers to use sth. specific:
199              
200             keyring => Passwd::Keyring::KDEWallet->new(...))
201              
202             =head1 AUTHOR
203              
204             Marcin Kasperski <Marcin.Kasperski@mekk.waw.pl>
205              
206             =head1 COPYRIGHT AND LICENSE
207              
208             This software is copyright (c) 2015 by Marcin Kasperski.
209              
210             This is free software; you can redistribute it and/or modify it under
211             the same terms as the Perl 5 programming language system itself.
212              
213             =cut