[Box Backup-commit] COMMIT r2375 - box/trunk/contrib/bbadmin

boxbackup-dev at boxbackup.org boxbackup-dev at boxbackup.org
Sat Nov 8 13:37:45 GMT 2008


Author: chris
Date: 2008-11-08 13:37:45 +0000 (Sat, 08 Nov 2008)
New Revision: 2375

Added:
   box/trunk/contrib/bbadmin/accounts.cgi
   box/trunk/contrib/bbadmin/apache.conf
   box/trunk/contrib/bbadmin/bb.css
Log:
Add the WebManagementInterface to contribs.


Added: box/trunk/contrib/bbadmin/accounts.cgi
===================================================================
--- box/trunk/contrib/bbadmin/accounts.cgi	                        (rev 0)
+++ box/trunk/contrib/bbadmin/accounts.cgi	2008-11-08 13:37:45 UTC (rev 2375)
@@ -0,0 +1,579 @@
+#!/usr/bin/perl
+
+# Box Backup web management interface (c) Chris Wilson, 2008
+#
+# LICENSE: The Box Backup license applies to this code, with the following
+# additional conditions:
+#
+# If you make any changes to this code, except for changes to existing
+# variables in the Configuration section below, you must publish the changes
+# under the same license, whether or not you distribute copies of the 
+# changed version.
+#
+# If you use any of this code in a derivative work, you must publish the
+# source code of the derivative work under the same or compatible license,
+# whether or not you distribute copies of the derivative work.
+#
+# The terms of the Box Backup license may be viewed here:
+# https://www.boxbackup.org/license.html
+#
+# If you require access to the code under a different license, this may
+# be negotiated with the copyright holder.
+
+use strict;
+use warnings;
+
+# Variables which you may need to change to match your installation
+# Changes to existing variables are NOT required to be published.
+
+my $box_dir = "/etc/box";
+my $bbstored_dir = "$box_dir/bbstored";
+my $ca_dir = "/mnt/backup/boxbackup/ca";
+
+# You should not need to change these unless you have a non-standard installation
+
+my $bbstored_conf_file = "$box_dir/bbstored.conf";
+my $bbstoreaccounts = "/usr/local/sbin/bbstoreaccounts";
+my $accounts_db_file = undef;
+# my $accounts_db_file = "/etc/box/bbstored/accounts.txt";
+my $raidfile_conf_file = undef;
+# my $raidfile_conf_file = "/etc/box/raidfile.conf";
+my $sign_period = '5000';
+
+# install Perl module with:
+# perl -MCPAN -e 'install Config::Scoped'
+# perl -MCPAN -e 'force install P/PT/PTHOMSEN/BoxBackup/BBConfig-0.03.tar.gz'
+# perl -MCPAN -e 'install Convert::ASN1'
+# download http://search.cpan.org/CPAN/authors/id/L/LE/LEO/Convert-X509-0.1.tar.gz,
+# unpack, and move the Convert folder to /usr/lib/perl5/site_perl/X.X.X
+
+# Check that SSL is being used.
+# DO NOT DISABLE THIS unless you really know what you're doing!
+die "This script requires an SSL web server" unless $ENV{HTTPS};
+
+# Check that the script is protected by basic authentication.
+# DO NOT DISABLE THIS unless you really know what you're doing!
+die "This script requires HTTP Authentication" unless $ENV{REMOTE_USER};
+
+# You should not need to change anything below this line.
+# If you do, you must publish your changes to comply with the license.
+
+use BoxBackup::Config::Accounts;
+use BoxBackup::Config::DiskSets;
+use CGI::Carp qw(fatalsToBrowser);
+use CGI::Pretty;
+use Config::Scoped;
+use Convert::X509::Request;
+use English;
+use Fcntl;
+use File::Temp;
+use URI;
+use URI::QueryParam;
+
+sub check_access($$)
+{
+	my ($file,$desc) = @_;
+	unless (-r $file)
+	{
+		open FILE, "< $file" and die "should have failed";
+		die "Failed to access $desc ($file): $!";
+	}
+}
+
+sub check_executable($$)
+{
+	my ($file,$desc) = @_;
+	unless (-x $file)
+	{
+		open FILE, "< $file" and die "should have failed";
+		die "$desc is not executable ($file): $!";
+	}
+}
+
+
+my $cgi = new CGI;
+
+if (my $download = $cgi->param("download"))
+{
+	my ($filename, $acct_no);
+
+	if ($download eq "cert")
+	{
+		$acct_no = $cgi->param("account");
+		$acct_no =~ tr/0-9a-fA-F//cd;
+		$filename = "$acct_no-cert.pem";
+	}
+	elsif ($download eq "cacert")
+	{
+		$filename = "serverCA.pem";
+	}
+	else
+	{
+		die "No such download method $download";
+	}
+		
+	print $cgi->header(-type => "text/plain",
+		-"content-disposition" => "attachment; filename=$filename");
+	
+	my $send_file;
+
+	if ($download eq "cert")
+	{
+		$send_file = "$ca_dir/clients/$filename";
+	}
+	elsif ($download eq "cacert")
+	{
+		$send_file = "$ca_dir/roots/serverCA.pem";
+	}
+
+	die "File does not exist: $send_file"
+		unless -f $send_file;
+	die "File is not readable by user " . getpwuid($UID) .
+		": $send_file" unless -r $send_file;
+	
+	open SENDFILE, "< $send_file" or die "Failed to open file " .
+		"$send_file: $!";
+	while (my $line = <SENDFILE>)
+	{
+		print $line;
+	}
+	close SENDFILE;
+	exit 0;
+}	
+
+print $cgi->header(), $cgi->start_html(-title=>"Box Backup Certificates",
+	-style=>'bb.css');
+print $cgi->h1("Box Backup Certificates");
+
+check_access($bbstored_conf_file, "BBStoreD configuration file");
+
+my $bbstored_conf = Config::Scoped->new(file => $bbstored_conf_file)->parse();
+
+$accounts_db_file ||= $bbstored_conf->{'Server'}{'AccountDatabase'};
+die "Missing AccountDatabase in $bbstored_conf_file" unless $accounts_db_file;
+check_access($accounts_db_file, "Accounts Database");
+
+$raidfile_conf_file ||= $bbstored_conf->{'Server'}{'RaidFileConf'};
+die "Missing RaidFileConf in $bbstored_conf_file" unless $raidfile_conf_file;
+check_access($raidfile_conf_file, "RaidFile configuration file");
+
+my $accounts_db = BoxBackup::Config::Accounts->new($accounts_db_file);
+
+check_executable($bbstoreaccounts, "bbstoreaccounts program");
+
+sub error($)
+{
+	my ($message) = @_;
+	unless ($message =~ /^</)
+	{
+		$message = $cgi->p($message);
+	}
+	print $cgi->div({-class=>"error"}, $message);
+	return 0;
+}
+
+sub url
+{
+	my $cgi = shift @_;
+	my %params = @_;
+	my $uri = URI->new($cgi->url(-absolute=>1));
+	foreach my $param (keys %params)
+	{
+		$uri->query_param($param, $params{$param});
+	}
+	return $uri;
+}
+
+sub create_account($)
+{
+	my ($cgi) = @_;
+
+	my $upload = $cgi->upload('req');
+	unless ($upload)
+	{
+		return error("Please attach a certificate request file.");
+	}
+
+	my $tempfile = File::Temp->new("bbaccount-certreq-XXXXXX.pem");
+	my $csr_data = "";
+
+	while (my $line = <$upload>)
+	{
+		print $tempfile $line;
+		$csr_data .= $line;
+	}
+
+	my @accounts = $accounts_db->getAccountIDs();
+	my $new_acc_no = $cgi->param('account');
+	if (not $new_acc_no)
+	{
+		return error("Please enter an account number.");
+	}
+
+	foreach my $account_no (@accounts)
+	{
+		if ($account_no == $new_acc_no)
+		{
+			return error("The account number $new_acc_no " .
+				"already exists, please use one which " .
+				"does not.");
+		}
+	}
+
+	my $req = Convert::X509::Request->new($csr_data);
+	my $cn;
+	foreach my $part ($req->subject)
+	{
+		if ($part =~ /^cn=(.*)/i)
+		{
+			$cn = $1;
+			last;
+		}
+	}
+
+	unless ($cn)
+	{
+		return error("The certificate request does not include a " .
+			"common name, which should be BACKUP-$new_acc_no.");
+	}
+
+	unless ($cn eq "BACKUP-$new_acc_no")
+	{
+		return error("The certificate request includes the wrong " .
+			"common name. Expected " .
+			"<tt>BACKUP-$new_acc_no</tt> but found " .
+			"<tt>$cn</tt>.");
+	}
+
+	my $out_cert_dir = "$ca_dir/clients";
+	unless (-w $out_cert_dir)
+	{
+		return error("Cannot write to certificate directory " .
+			"<tt>$out_cert_dir</tt> as user " .
+			"<tt>" . getpwuid($UID) . "</tt>.");
+	}
+
+	my $out_cert = "$out_cert_dir/$new_acc_no-cert.pem";
+	if (-f $out_cert and not -w $out_cert)
+	{
+		return error("The certificate file <tt>$out_cert</tt> " .
+			"exists and is not writable as user " .
+			"<tt>$out_cert_dir</tt> as user " .
+			"<tt>" . getpwuid($UID) . "</tt>.");
+	}
+
+	my $client_ca_cert_file = "$ca_dir/roots/clientCA.pem";
+	unless (-r $client_ca_cert_file)
+	{
+		return error("The client CA certificate file " .
+			"<tt>$client_ca_cert_file</tt> " .
+			"is not readable by user " .
+			"<tt>" . getpwuid($UID) . "</tt>.");
+	}
+
+	my $client_ca_key_file = "$ca_dir/keys/clientRootKey.pem";
+	unless (-r $client_ca_key_file)
+	{
+		return error("The client CA key file " .
+			"<tt>$client_ca_key_file</tt> " .
+			"is not readable by user " .
+			"<tt>" . getpwuid($UID) . "</tt>.");
+	}
+
+	my $serial_file = "$ca_dir/roots/clientCA.srl";
+	unless (-w $serial_file)
+	{
+		return error("The certificate serial number file " .
+			"<tt>$serial_file</tt> " .
+			"is not writable by user " .
+			"<tt>" . getpwuid($UID) . "</tt>.");
+	}
+
+	my $outputfile = File::Temp->new("bbaccounts-openssl-output-XXXXXX");
+
+	if (system("openssl x509 -req -in $tempfile -sha1 " .
+		"-extensions usr_crt " .
+		"-CA $client_ca_cert_file " .
+		"-CAkey $client_ca_key_file " .
+		"-out $out_cert -days $sign_period " .
+		">$outputfile 2>&1") != 0)
+	{
+		open ERR, "< $outputfile" or die "$outputfile: $!";
+		my $errors = join("", <ERR>);
+		close ERR;
+		return error($cgi->p("Failed to sign certificate:") .
+			$cgi->pre($errors));
+	}
+	
+	my $cert_uri = url($cgi, download => "cert", account => $new_acc_no);
+	my $ca_uri   = url($cgi, download => "cacert");
+
+	print $cgi->div({-class=>"success"},
+		$cgi->p("Account created. Please download the following " .
+			"files:") .
+		$cgi->ul(
+			$cgi->li($cgi->a({href=>$cert_uri},
+				"Client Certificate")),
+			$cgi->li($cgi->a({href=>$ca_uri},
+				"CA Certificate"))
+			)
+		);
+
+	return 1;
+}
+
+if ($cgi->param("create"))
+{
+	print $cgi->h2("Account Creation");
+	create_account($cgi);
+}
+
+print $cgi->h2("Accounts");
+print $cgi->start_table({-border=>0, -class=>"numbers"});
+
+print $cgi->Tr(
+	$cgi->th("Account"),
+	$cgi->th('Used'),		$cgi->th('%'),
+	$cgi->th('Old files'),		$cgi->th('%'),
+	$cgi->th('Deleted files'),	$cgi->th('%'),
+	$cgi->th('Directories'),	$cgi->th('%'),
+	$cgi->th('Soft limit'),		$cgi->th('%'),
+	$cgi->th('Hard limit'),
+	$cgi->th('Actions')
+	);
+
+sub human_format($)
+{
+	my ($kb) = @_;
+	die unless $kb =~ /^(\d+) (kB)$/;
+
+	my $value = $1;
+	my $units = $2;
+
+	if ($value > 1024)
+	{
+		$value /= 1024;
+		$units = "MB";
+	}
+
+	if ($value > 1024)
+	{
+		$value /= 1024;
+		$units = "GB";
+	}
+
+	$value = sprintf("%.1f", $value);
+	return "$value $units";
+}
+
+sub bbstoreaccounts_format($)
+{
+	my ($kb) = @_;
+	die unless $kb =~ /^(\d+) (kB)$/;
+
+	my $value = $1;
+	my $units = "K";
+
+	unless ($value % 1024)
+	{
+		$value /= 1024;
+		$units = "M";
+	}
+
+	unless ($value % 1024)
+	{
+		$value /= 1024;
+		$units = "G";
+	}
+
+	return "$value$units";
+}
+
+sub get_account_info($)
+{
+	my ($account) = @_;
+
+	open BBSA, "$bbstoreaccounts -c $bbstored_conf_file -m info $account |"
+		or die "Failed to get account info for $account: $!";
+
+	my $account_info = {};
+
+	while (my $line = <BBSA>)
+	{
+		unless ($line =~ m/([^:]*): (.*)/)
+		{
+			die "Bad format in bbstoreaccounts info output " .
+				"for account $account: '$line'";
+		}
+
+		my $name = $1;
+		my $value = $2;
+
+		if ($value =~ /(.*), (.*)/)
+		{
+			$account_info->{$name} = [$1, $2];
+		}
+		else
+		{
+			$account_info->{$name} = $value;
+		}
+	}
+
+	return $account_info;
+}
+
+sub format_account_info($)
+{
+	my ($values) = @_;
+	my $kb = $values->[0];
+	my $pc = $values->[1];
+	return $cgi->td(human_format($kb)), $cgi->td($values->[1]);
+}
+
+my %account_numbers;
+
+my @accounts = $accounts_db->getAccountIDs();
+foreach my $i (@accounts)
+{
+	die "Duplicate account number $i" if $account_numbers{hex($i)};
+	$account_numbers{hex($i)} = 1;
+
+	# Find out what account is on what diskset.
+	my $disk = $accounts_db->getDisk($i);
+
+	# store limits
+	my $account_info = get_account_info($i);
+
+	print $cgi->Tr(
+		$cgi->td($i),
+		format_account_info($account_info->{'Used'}),
+		format_account_info($account_info->{'Old files'}),
+		format_account_info($account_info->{'Deleted files'}),
+		format_account_info($account_info->{'Directories'}),
+		format_account_info($account_info->{'Soft limit'}),
+		$cgi->td(human_format($account_info->{'Hard limit'}[0])),
+		$cgi->td($cgi->a({-href=>url($cgi, account=>$i)},
+			"Edit"))
+		);
+}
+
+print $cgi->end_table();
+
+my $account_no = $cgi->param("account");
+$account_no =~ tr/0-9a-fA-F//cd;
+
+if (not $cgi->param("showcreate"))
+{
+	print $cgi->start_form,
+		$cgi->submit(-name=>"showcreate",
+		-value=>"Create Account"),
+		$cgi->end_form();
+}
+
+if ($account_no)
+{
+	print $cgi->h2("Edit Account");
+	my $account_info = get_account_info($account_no);
+	$cgi->param("account", $account_no);
+	$cgi->param("soft_limit",
+		bbstoreaccounts_format($account_info->{'Soft limit'}[0]));
+	$cgi->param("hard_limit",
+		bbstoreaccounts_format($account_info->{'Hard limit'}[0]));
+}
+elsif ($cgi->param("showcreate"))
+{
+	print $cgi->h2("Create Account");
+}
+
+if ($account_no or $cgi->param("showcreate"))
+{
+	my $new_account_no = 1;
+	while ($account_numbers{$new_account_no})
+	{
+		$new_account_no++;
+	}
+
+	my $disksets_conf = BoxBackup::Config::DiskSets->new($raidfile_conf_file);
+	my @disk_names = $disksets_conf->getListofDisks();
+	my @disk_numbers;
+	my %disk_labels;
+
+	foreach my $name (@disk_names)
+	{
+		my $num = $disksets_conf->getParamVal($name, "SetNumber");
+		push @disk_numbers, $num;
+		$disk_labels{$num} = $name;
+	}
+
+	print $cgi->start_multipart_form(),
+		$cgi->start_table();
+
+	if ($account_no)
+	{
+		print $cgi->Tr(
+			$cgi->th("Account Number"),
+			$cgi->td($account_no . 
+				$cgi->hidden("account", $account_no))
+			);
+	}
+	else
+	{
+		print $cgi->Tr(
+			$cgi->th("Account Number"),
+			$account_no ? $account_no :
+			$cgi->td($cgi->textfield(-name => "account",
+				-default => sprintf("%x", $new_account_no))),
+			);
+	}
+
+	if (not $account_no)
+	{
+		print $cgi->Tr(
+			$cgi->th("Disk Set"),
+			$cgi->td($cgi->popup_menu(-name => "disk_set",
+				-values => \@disk_numbers,
+				-labels => \%disk_labels))
+			);
+	}
+
+	print	$cgi->Tr(
+			$cgi->th("Soft Limit"),
+			$cgi->td($cgi->textfield(-name => "soft_limit",
+				-default => "10G"))
+			),
+		$cgi->Tr(
+			$cgi->th("Hard Limit"),
+			$cgi->td($cgi->textfield(-name => "hard_limit",
+				-default => "20G"))
+			),
+		$cgi->Tr(
+			$cgi->th("Certificate Request"),
+			$cgi->td($cgi->filefield({
+				-name => "req",
+				-default => "*.crt"
+				}))
+			);
+
+	if ($account_no)
+	{
+		print $cgi->Tr(
+			$cgi->th(),
+			$cgi->td($cgi->submit(-name => "update",
+				-value => "Update Account"))
+			);
+	}
+	else
+	{
+		print $cgi->Tr(
+			$cgi->th(),
+			$cgi->td($cgi->submit(-name => "create",
+				-value => "Create Account"))
+			);
+	}
+
+	print $cgi->end_table(), $cgi->end_form();
+}
+
+print $cgi->end_html;
+
+exit 0;


Property changes on: box/trunk/contrib/bbadmin/accounts.cgi
___________________________________________________________________
Added: svn:executable
   + *

Added: box/trunk/contrib/bbadmin/apache.conf
===================================================================
--- box/trunk/contrib/bbadmin/apache.conf	                        (rev 0)
+++ box/trunk/contrib/bbadmin/apache.conf	2008-11-08 13:37:45 UTC (rev 2375)
@@ -0,0 +1,14 @@
+Alias /bbadmin /var/www/localhost/bbadmin
+
+<Directory /var/www/localhost/bbadmin>
+	AuthType basic
+	AuthName "Box Backup Web Management Interface"
+	AuthUserFile /etc/apache2/bbadmin.cgi.htpasswd
+	Require valid-user
+
+	Allow from all
+
+	Options ExecCGI
+	AddHandler cgi-script .cgi
+	DirectoryIndex accounts.cgi
+</Directory>

Added: box/trunk/contrib/bbadmin/bb.css
===================================================================
--- box/trunk/contrib/bbadmin/bb.css	                        (rev 0)
+++ box/trunk/contrib/bbadmin/bb.css	2008-11-08 13:37:45 UTC (rev 2375)
@@ -0,0 +1,70 @@
+body
+{
+	background: #edeef3;
+}
+
+table
+{
+	border-spacing: 0px;
+}
+
+h1, th
+{
+	background: #e4e6ed;
+}
+
+h1
+{
+	border-top: 1px solid #c4c4d5;
+	border-bottom: 1px solid #fff;
+}
+
+td, th
+{
+	border-top: 1px solid #fff;
+	border-bottom: 1px solid #c4c4d5;
+	margin: 0px;
+	padding: 0.2em 0.5em;
+}
+
+th
+{
+	text-align: left;
+}
+
+table.numbers td
+{
+	text-align: right;
+}
+
+div.error, div.success
+{
+	margin: 1em;
+}
+
+div.error>*, div.success>*
+{
+	margin: 0.5em;
+}
+
+div.error
+{
+	background: #fdd;
+	border: 2px solid #c00;
+}
+
+div.success
+{
+	background: #dfd;
+	border: 2px solid #0c0;
+}
+
+h2, table, p
+{
+	margin: 0.5em;
+}
+
+form
+{
+	margin: 0.5em 0;
+}




More information about the Boxbackup-commit mailing list