aboutsummaryrefslogtreecommitdiff
path: root/lib/Net/MulkyID/Setup.pm
blob: 2709c874d7224eaced6e0288c04356855580a57b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
#! /usr/bin/env perl
# Copyright 2012, 2014, Matthias Andreas Benkard <code@mail.matthias.benkard.de>.

package Net::MulkyID::Setup;

use common::sense;
use Modern::Perl;
use JSON;
use Crypt::OpenSSL::RSA;
use File::Slurp qw(write_file read_file);
use File::Path qw(make_path);
use File::Copy;
use LWP::Simple qw(getstore);
use Data::Dumper;
#use autodie;

use base 'Exporter';
our @EXPORT = qw(configure build);

our $conffile = "www/config.pl";

sub prompt($$) {
  my ($question, $default) = @_;
  $|++;
  print "${question} \[${default}\] ";
  $_ = <STDIN>;
  chomp;
  if ($_) {
    return $_;
  } else {
    return $default;
  }
}

sub makespec($$) {
  my ($key, $basepath) = @_;
  my ($n, $e, @stuff) = $key->get_key_parameters;
  return
    encode_json({"public-key"     => {e => $e->to_decimal, n => $n->to_decimal, algorithm => "RS"},
                 "authentication" => "$basepath/authenticate.html",
                 "provisioning"   => "$basepath/provision.html"});
};

sub load_configuration() {
  $::MULKONF = { };
  if (stat($conffile)) {
    say "Found existing configuration ($conffile).";
    do $conffile;
  }
}

sub write_configuration() {
  write_file($conffile, <<EOF
#! /usr/bin/env perl
# NB. Do not edit this file directly.  It is overwritten with each run of setup.pl.
@{[Data::Dumper->Dump([$::MULKONF], ["::MULKONF"])]}
1;
EOF
  ) or die "Could not write configuration to $conffile: $!";
  say "Configuration saved to: $conffile";

  say "";
  say "******************************************************************";
  say "* FINISHED.                                                      *";
  say "*                                                                *";
  say "* Please put browserid.json where it will be served as           *";
  say "*     https://<whatever>/.well-known/browserid                   *";
  say "* with a content type of:                                        *";
  say "*     application/json                                           *";
  say "*                                                                *";
  say "* In addition, please ensure that the private key file can be    *";
  say "* read by the web server by assigning the file to the            *";
  say "* appropriate owner.                                             *";
  say "******************************************************************";
}

sub build_deps() {
  # Download jQuery.
  make_path("www/jquery");
  if (stat("www/jquery/jquery.js")) {
    say "Using existing copy of jQuery (www/jquery/jquery.js).";
  } else {
    say "Fetching jQuery...";
    getstore("http://code.jquery.com/jquery-1.7.2.min.js", "www/jquery/jquery.js")
      or die "Could not fetch jQuery";
    say "jQuery saved to: www/jquery/jquery.js";
  }
}

sub build_spec() {
  # Generate the private key and generate the BrowserID spec file.
  my $pemfile      = $::MULKONF->{pemfile}  // die "pemfile not defined";
  my $basepath     = $::MULKONF->{basepath} // die "basepath not defined";
  my $configpath   = $::MULKONF->{configpath} // die "configpath not defined";

  my $key;
  if (stat($pemfile)) {
    say "Using existing private key ($pemfile).";
    $key = Crypt::OpenSSL::RSA->new_private_key(scalar read_file($pemfile));
  } else {
    say "Generating private key...";
    $key = Crypt::OpenSSL::RSA->generate_key(2048);
    make_path($configpath);
    write_file($pemfile, $key->get_private_key_string())
      or die "Could not write private key to $pemfile: $!";
    say "Private key saved to: $pemfile";
    chmod 0440, $pemfile;
  }

  write_file("browserid.json", makespec($key, $basepath))
    or die "Could not write spec to browserid.json: $!";
  say "Persona spec file saved to: browserid.json";
}

sub build() {
  load_configuration;
  build_deps;
  build_spec;
}

sub configure() {
  load_configuration;

  my $configpath   = $::MULKONF->{configpath}   // "/etc/mulkyid";
  my $pemfile      = $::MULKONF->{pemfile}      // "$configpath/rsa2048.pem";
  my $auth_type    = $::MULKONF->{auth_type}    // "imap";
  my $aliases_file = $::MULKONF->{aliases_file} // "/etc/aliases";
  my $imap_server  = $::MULKONF->{imap_server}  // "localhost";
  my $imap_port    = $::MULKONF->{imap_port}    // 143;
  my $basepath     = $::MULKONF->{basepath}     // "/browserid";
  my $fake_domain  = $::MULKONF->{fake_domain}  // "";
  my $real_domain  = $::MULKONF->{real_domain}  // "";
  my $google_oauth2_client_secret = $::MULKONF->{google_oauth2_client_secret}  // "";
  my $google_oauth2_client_id     = $::MULKONF->{google_oauth2_client_id}      // "";
  $configpath = prompt("Where shall I put configuration files?", $configpath);
  $pemfile    = prompt("Where shall I put the private key?", $pemfile);
  $auth_type  = prompt("How will users authenticate? (imap, google)", $auth_type);
  $basepath   = prompt("What will be the web-facing base path for IdP files and scripts?", $basepath);
  given (my $_ = $auth_type) {
    when ("imap") {
      $aliases_file = prompt("Where is the aliases file?  Type a single dot for none.", $aliases_file);
      $imap_server  = prompt("What is the IMAP server's address?", $imap_server);
      $imap_port    = int(prompt("What is the IMAP server's port?", $imap_port));
    }
    when ("google") {
      $fake_domain = prompt("Fake domain name for email addresses?  Type a single dot for none. (FOR DEVELOPMENT)", $fake_domain);
      if ($fake_domain eq '.' or $fake_domain eq '') {
        $fake_domain = '';
      } else {
        $real_domain = prompt("Real domain name?", $real_domain);
        $real_domain = '' if ($real_domain eq '.');
      }
      $google_oauth2_client_id = prompt("Google OAuth2 client ID?", $google_oauth2_client_id);
      $google_oauth2_client_secret = prompt("Google OAuth2 client secret?", $google_oauth2_client_secret);
    }
    default {
      die "Invalid authentication type";
    }
  }

  say "OK.";

  $::MULKONF = {
    configpath   => $configpath,
    pemfile      => $pemfile,
    aliases_file => $aliases_file,
    imap_server  => $imap_server,
    imap_port    => $imap_port,
    basepath     => $basepath,
    auth_type    => $auth_type,
    fake_domain  => $fake_domain,
    real_domain  => $real_domain,
    google_oauth2_client_secret => $google_oauth2_client_secret,
    google_oauth2_client_id     => $google_oauth2_client_id
  };

  build_deps;
  build_spec;
  write_configuration;
}

1;