Add auxilliary CGI for account recovery
authorChip Black <bytex64@bytex64.net>
Thu, 26 Feb 2015 09:52:03 +0000 (03:52 -0600)
committerChip Black <bytex64@bytex64.net>
Thu, 26 Feb 2015 09:52:03 +0000 (03:52 -0600)
aux/cgi/recovery.cgi [new file with mode: 0755]

diff --git a/aux/cgi/recovery.cgi b/aux/cgi/recovery.cgi
new file mode 100755 (executable)
index 0000000..d0742b6
--- /dev/null
@@ -0,0 +1,122 @@
+#!/usr/bin/perl
+use CGI::Fast qw/:cgi/;
+use Digest::SHA qw/hmac_sha256_base64/;
+use Blerg::Database;
+use strict;
+use v5.10;
+
+my $hmac_key;
+open HMAC_KEY, "$ENV{BLERG_HOME}/etc/hmac_key"
+    or die "Could not open $ENV{BLERG_HOME}/etc/hmac_key";
+read(HMAC_KEY, $hmac_key, 256);
+close HMAC_KEY;
+
+sub print_404 {
+    print header(-type => 'text/html',
+                 -status => '404 Not Found');
+    print <<DOC;
+<!DOCTYPE html>
+<h1>404 Not Found</h1>
+DOC
+}
+
+sub print_403 {
+    print header(-type => 'text/html',
+                 -status => '403 Forbidden');
+    print <<DOC;
+<!DOCTYPE html>
+<h1>403 Forbidden</h1>
+Please log in first.
+DOC
+}
+
+sub generate_reset_url {
+    my ($username, $validity) = @_;
+
+    # generate reset data
+    my $expiry = time + $validity;
+    my $counter = Blerg::Database::auth_get_counter($username)
+        or return undef;
+    my $data = "$username;$expiry;$counter";
+        
+    # HMAC-SHA256 it
+    my $hmac = hmac_sha256_base64($data, $hmac_key);
+
+    return Blerg::Database::BASEURL . "#/recovery/$data;$hmac";
+}
+
+sub validate_reset_data {
+    my ($data) = @_;
+    my ($payload, $hmac);
+
+    if ($data =~ /^(.*);([^;]+)$/) {
+        $payload = $1;
+        $hmac = $2;
+    } else {
+        return undef;
+    }
+
+    my $computed_hmac = hmac_sha256_base64($payload, $hmac_key);
+    if ($hmac ne $computed_hmac) {
+        return undef;
+    }
+
+    my ($username, $expiry, $counter) = split(';', $payload);
+    if (time > $expiry
+        || $counter != Blerg::Database::auth_get_counter($username)) {
+        return undef;
+    }
+
+    return $username;
+}
+
+REQUEST:
+while (my $q = new CGI::Fast) {
+    my (undef, $cmd, $args) = split '/', $ENV{PATH_INFO}, 3;
+
+    given ($cmd) {
+        when ('new') {
+            # determine that authentication is valid.
+            my $auth = $q->cookie('auth');
+            if (!defined $auth) {
+                print_403;
+                next REQUEST;
+            }
+            my ($username, $token) = split('/', $auth);
+            if (!Blerg::Database::auth_check_token($username, $token)) {
+                print_403;
+                next REQUEST;
+            }
+
+            my $validity = 365 * 86400;     # One year
+            print header(-type => 'text/plain');
+            print generate_reset_url($username, $validity);
+        }
+        when ('mail') {
+            # check that the user has a validated mail address
+            # generate reset message
+            # send mail
+        }
+        when ('validate') {
+            print header(-type => 'application/json');
+
+            my $username = validate_reset_data($q->param('data'));
+
+            if (!defined $username) {
+                say '{"status": "failure"}';
+                next REQUEST;
+            }
+
+            my $password = $q->param('password');
+            if (Blerg::Database::auth_set_password($username, $password)) {
+                say '{"status": "success"}';
+            } else {
+                say '{"status": "failure"}';
+            }
+        }
+        default {
+            print_404;
+            print "$cmd $args";
+        }
+    }
+}