File Coverage

lib/Mozilla/Persona/Setup.pm
Criterion Covered Total %
statement 39 111 35.1
branch 0 40 0.0
condition 0 32 0.0
subroutine 13 18 72.2
pod 1 5 20.0
total 53 206 25.7


line stmt bran cond sub pod time code
1             # Copyrights 2012 by [Mark Overmeer].
2             # For other contributors see ChangeLog.
3             # See the manual pages for details on the licensing terms.
4             # Pod stripped from pm file by OODoc 2.00.
5 1     1   895 use warnings;
  1         2  
  1         23  
6 1     1   4 use strict;
  1         1  
  1         29  
7              
8             package Mozilla::Persona::Setup;
9 1     1   5 use vars '$VERSION';
  1         1  
  1         43  
10             $VERSION = '0.12';
11              
12 1     1   4 use base 'Exporter';
  1         1  
  1         95  
13              
14             our @EXPORT = qw/setup_persona/;
15              
16 1     1   4 use open 'utf8';
  1         2  
  1         4  
17 1     1   57 use Log::Report qw/persona/;
  1         2  
  1         5  
18              
19 1     1   209 use File::Slurp qw/read_file write_file/;
  1         1  
  1         1173  
20 1     1   7 use JSON qw/encode_json/;
  1         1  
  1         46  
21 1     1   203 use File::Basename qw/basename/;
  1         3  
  1         93  
22              
23 1     1   390 use Mozilla::Persona::Server ();
  1         3  
  1         21  
24 1     1   7 use Crypt::OpenSSL::Bignum ();
  1         2  
  1         12  
25 1     1   4 use Crypt::OpenSSL::RSA ();
  1         2  
  1         13  
26 1     1   5 use LWP::UserAgent ();
  1         1  
  1         1953  
27              
28             my $ua;
29             my $latest_jquery = 'http://code.jquery.com/jquery.min.js';
30             my $restart;
31              
32             sub get_jquery($$);
33             sub create_private_key($$);
34             sub publish_config($$);
35             sub publish_helpers($$$);
36              
37              
38             sub setup_persona(%)
39 0     0 1   { my %args = @_;
40              
41             ### Configuration
42              
43 0 0         my $docroot = $args{docroot} or panic;
44 0 0         my $secrets = $args{secrets} or panic;
45 0 0         my $domain = $args{domain} or panic;
46 0 0         my $group = $args{group} or panic;
47 0   0       $restart = $args{restart} || 0;
48              
49 0 0         -d $docroot
50             or fault __x"website doc-root {dir} missing", dir => $docroot;
51              
52 0           my $servdir = "$docroot/persona";
53 0           my $jquery = "$servdir/jquery.js";
54 0           my $config = "$secrets/$domain.json";
55 0           my $privkey = "$secrets/$domain.pem";
56              
57 0 0 0       -d $secrets || mkdir $secrets
58             or fault __x"cannot create directory {dir} for secrets", dir => $secrets;
59              
60 0 0 0       -d $servdir || mkdir $servdir
61             or fault __x"cannot create directory {dir} for service", dir => $servdir;
62              
63 0           my $wk = "$docroot/.well-known";
64 0 0 0       -d $wk || mkdir $wk
65             or fault __x"cannot create directory {dir} for publish", dir => $wk;
66              
67 0           my $publish = "$wk/browserid";
68              
69 0           (my $setup_src = __FILE__) =~ s!Setup.pm$!setup!;
70              
71             ### Work
72              
73 0           get_jquery $latest_jquery, $jquery;
74 0           create_private_key $privkey, $group;
75 0           publish_config $publish, $privkey;
76              
77 0           my $persona = Mozilla::Persona::Server->new
78             ( private_pem => $privkey
79             , cookie_name => 'persona'
80             , domain => $domain
81             , validator =>
82             { class => 'Mozilla::Persona::Validate::Table'
83             , pwfile => "$secrets/passwords"
84             , domain => $domain
85             }
86             );
87              
88 0           $persona->writeConfig($config);
89              
90 0           publish_helpers $setup_src, "$docroot/persona", $config;
91              
92 0           print __x"now you probably want to modify {fn}", fn => $config;
93             }
94              
95             #### HELPERS
96              
97             sub get_jquery($$)
98 0     0 0   { my ($from_url, $to_fn) = @_;
99              
100 0 0 0       if(-f $to_fn && !$restart)
101 0           { info __x"reusing jquery from {fn}", fn => $to_fn;
102 0           return;
103             }
104              
105 0           info __x"downloading latest jquery stable into {fn}", fn => $to_fn;
106              
107 0   0       $ua ||= LWP::UserAgent->new;
108 0           my $resp = $ua->get($from_url);
109 0 0         $resp->is_success
110             or error __x"failed downloading jquery from {url}: {err}"
111             , url => $from_url. err => $resp->status_line;
112              
113 0   0       write_file $to_fn, $resp->decoded_content || $resp->content;
114             }
115              
116             sub create_private_key($$)
117 0     0 0   { my ($outfn, $group) = @_;
118              
119 0 0         my $gid = getpwnam $group
120             or error __x"unknown group {name}", name => $group;
121              
122 0 0 0       if(-f $outfn && !$restart)
123 0           { info __x"reusing private key in {fn}", fn => $outfn;
124              
125 0           my $has_gid = (stat $outfn)[5];
126 0 0         $gid == $has_gid
127             or warning __x"please set group on {fn} to {group}"
128             , fn => $outfn, group => $group;
129              
130 0           return;
131             }
132              
133 0           info __x"generating new private key at {fn}", fn => $outfn;
134              
135 0 0 0       ! -f $outfn || unlink $outfn
136             or fault __x"cannot replace existing pem file in {fn}", fn => $outfn;
137              
138 0           my $key = Crypt::OpenSSL::RSA->generate_key(2048);
139 0           write_file $outfn, $key->get_private_key_string;
140              
141 0           chmod 0440, $outfn;
142 0 0         chown -1, $gid, $outfn
143             or warning __x"please set group on {fn} to {group}"
144             , fn => $outfn, group => $group;
145              
146 0           $key;
147             }
148              
149             sub publish_config($$)
150 0     0 0   { my ($outfn, $keyfn) = @_;
151              
152 0           my $pem = read_file $keyfn;
153 0           my $key = Crypt::OpenSSL::RSA->new_private_key($pem);
154              
155 0           my ($n, $e, @stuff) = $key->get_key_parameters;
156 0           write_file $outfn, encode_json
157             { 'public-key' =>
158             { e => $e->to_decimal
159             , n => $n->to_decimal
160             , algorithm => 'RS'
161             }
162             , authentication => '/persona/authenticate.html'
163             , provisioning => '/persona/provision.html'
164             };
165              
166 0           info __x"public configuration written to {fn}", fn => $outfn;
167 0           $outfn;
168             }
169              
170             sub publish_helpers($$$)
171 0     0 0   { my ($indir, $outdir, $config) = @_;
172 0           local(*FROM, *TO);
173 0 0 0       -d $outdir or mkdir $outdir
174             or fault __x"cannot create directory {dir}", dir => $outdir;
175              
176 0           foreach my $fn (glob "$indir/*")
177 0           { my $outfn = $outdir.'/'.basename $fn;
178 0 0 0       if(-f $outfn && !$restart)
179 0           { info __x"keeping file {fn}", fn => $outfn;
180 0           next;
181             }
182              
183 0 0         open FROM, '<:encoding(utf8)', $fn
184             or fault __x"cannot read {filename}", filename => $fn;
185              
186 0 0         open TO, '>:encoding(utf8)', $outfn
187             or fault __x"cannot write to {filename}", filename => $outfn;
188              
189 0           while()
190 0           { s/__CONFIG__/$config/;
191 0           print TO $_;
192             }
193              
194 0           close TO;
195 0           close FROM;
196              
197 0 0         my $mode = $outfn =~ m/\.pl$/i ? 0755 : 0644;
198 0           chmod $mode, $outfn;
199              
200 0           info __x"created file {fn} more 0{mode%o}", fn => $outfn, mode => $mode;
201             }
202             }
203              
204             1;