line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
#!perl |
2
|
|
|
|
|
|
|
# vim: softtabstop=4 tabstop=4 shiftwidth=4 ft=perl expandtab smarttab |
3
|
|
|
|
|
|
|
# ABSTRACT: Perl API for HashiCorp's Vault (Base) |
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
# See also https://github.com/hashicorp/vault-ruby |
6
|
|
|
|
|
|
|
# And https://github.com/ianunruh/hvac |
7
|
|
|
|
|
|
|
# And https://www.vaultproject.io/api/index.html |
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
package WebService::HashiCorp::Vault::Base; |
10
|
|
|
|
|
|
|
|
11
|
2
|
|
|
2
|
|
1495
|
use Moo; |
|
2
|
|
|
|
|
6
|
|
|
2
|
|
|
|
|
10
|
|
12
|
|
|
|
|
|
|
our $VERSION = '0.02'; # VERSION |
13
|
2
|
|
|
2
|
|
673
|
use namespace::clean; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
20
|
|
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
with 'WebService::Client'; |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
has '+base_url' => ( default => 'http://127.0.0.1:8200' ); |
18
|
|
|
|
|
|
|
has token => ( is => 'rw' ); |
19
|
|
|
|
|
|
|
has _token_expires => ( is => 'rw' ); |
20
|
|
|
|
|
|
|
has approle => ( is => 'ro' ); |
21
|
|
|
|
|
|
|
has version => ( is => 'ro', default => 'v1' ); |
22
|
|
|
|
|
|
|
has mount => ( is => 'ro' ); |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
before 'get' => sub { |
25
|
|
|
|
|
|
|
$_[0]->_check_token(); |
26
|
|
|
|
|
|
|
$_[0]->_set_headers(); |
27
|
|
|
|
|
|
|
}; |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
before 'post' => sub { |
30
|
|
|
|
|
|
|
# Skip checking the token on a token request |
31
|
|
|
|
|
|
|
if ($_[1] !~ m#auth/approle/login$#) { |
32
|
|
|
|
|
|
|
$_[0]->_check_token(); |
33
|
|
|
|
|
|
|
} |
34
|
|
|
|
|
|
|
$_[0]->_set_headers(); |
35
|
|
|
|
|
|
|
}; |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
before 'put' => sub { |
38
|
|
|
|
|
|
|
$_[0]->_check_token(); |
39
|
|
|
|
|
|
|
$_[0]->_set_headers(); |
40
|
|
|
|
|
|
|
}; |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
before 'delete' => sub { |
43
|
|
|
|
|
|
|
$_[0]->_check_token(); |
44
|
|
|
|
|
|
|
$_[0]->_set_headers(); |
45
|
|
|
|
|
|
|
}; |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
sub _check_token { |
48
|
0
|
|
|
0
|
|
|
my $self = shift; |
49
|
|
|
|
|
|
|
|
50
|
0
|
0
|
|
|
|
|
$self->_request_token() |
51
|
|
|
|
|
|
|
unless defined $self->token; |
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
## Check the token and get a new one if required |
54
|
0
|
0
|
0
|
|
|
|
if (defined $self->_token_expires && (time > $self->_token_expires)) { |
55
|
0
|
|
|
|
|
|
$self->_request_token() |
56
|
|
|
|
|
|
|
} |
57
|
|
|
|
|
|
|
} |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
sub _set_headers { |
60
|
0
|
|
|
0
|
|
|
my $self = shift; |
61
|
0
|
|
|
|
|
|
$self->ua->default_header( |
62
|
|
|
|
|
|
|
'X-Vault-Token' => $self->token, |
63
|
|
|
|
|
|
|
'User_Agent' => sprintf( |
64
|
|
|
|
|
|
|
'WebService::HashiCorp::Vault %s (perl %s; %s)', |
65
|
|
|
|
|
|
|
__PACKAGE__->VERSION, |
66
|
|
|
|
|
|
|
$^V, $^O), |
67
|
|
|
|
|
|
|
); |
68
|
|
|
|
|
|
|
} |
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
sub _mkuri { |
71
|
0
|
|
|
0
|
|
|
my $self = shift; |
72
|
0
|
|
|
|
|
|
my @paths = @_; |
73
|
0
|
|
|
|
|
|
return join '/', |
74
|
|
|
|
|
|
|
$self->base_url, |
75
|
|
|
|
|
|
|
$self->version, |
76
|
|
|
|
|
|
|
$self->mount, |
77
|
|
|
|
|
|
|
@paths |
78
|
|
|
|
|
|
|
} |
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
sub _request_token { |
82
|
0
|
|
|
0
|
|
|
my $self = shift; |
83
|
|
|
|
|
|
|
|
84
|
0
|
0
|
|
|
|
|
die 'Must provide either token or approle' |
85
|
|
|
|
|
|
|
unless defined $self->approle; |
86
|
|
|
|
|
|
|
die 'role_id missing in approle' |
87
|
0
|
0
|
|
|
|
|
unless defined $self->approle->{role_id}; |
88
|
|
|
|
|
|
|
die 'secret_id missing in approle' |
89
|
0
|
0
|
|
|
|
|
unless defined $self->approle->{secret_id}; |
90
|
|
|
|
|
|
|
|
91
|
0
|
|
|
|
|
|
my $url = join('/', $self->base_url, $self->version, 'auth/approle/login'); |
92
|
0
|
|
|
|
|
|
my $resp = $self->post( $url , $self->approle ); |
93
|
0
|
|
|
|
|
|
$self->{token} = $resp->{auth}->{client_token}; |
94
|
|
|
|
|
|
|
## Set the expiry to 1 second before acutal expiry |
95
|
0
|
|
|
|
|
|
$self->_token_expires(time + $resp->{auth}->{lease_duration} - 1); |
96
|
|
|
|
|
|
|
} |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
sub list { |
100
|
0
|
|
|
0
|
1
|
|
my ($self, $path) = @_; |
101
|
|
|
|
|
|
|
|
102
|
0
|
|
|
|
|
|
$self->_check_token; |
103
|
0
|
|
|
|
|
|
$self->_set_headers; |
104
|
|
|
|
|
|
|
|
105
|
0
|
|
|
|
|
|
my $headers = $self->_headers(); |
106
|
0
|
|
|
|
|
|
my $url = $self->_url($path); |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
# HashiCorp have decided that 'LIST' is a http verb, so we must hack it in |
109
|
0
|
|
|
|
|
|
my $req = HTTP::Request->new( |
110
|
|
|
|
|
|
|
'LIST' => $url, |
111
|
|
|
|
|
|
|
HTTP::Headers->new(%$headers) |
112
|
|
|
|
|
|
|
); |
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
# this is a WebService::Client internal function. I said hack! |
115
|
0
|
|
|
|
|
|
return $self->req( $req ); |
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
} |
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
1; |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
__END__ |