From 80a6317ac55de020f606a5ca114466d3f0100511 Mon Sep 17 00:00:00 2001
From: Daniel Friesel <derf@finalrewind.org>
Date: Sat, 13 Apr 2019 12:17:19 +0200
Subject: [PATCH] Use travelynx.conf for configuration and secrets

This avoids having to specify secrets in the environment, where they can leak
easily.
---
 .gitignore                        |  1 +
 README.md                         |  5 ++-
 examples/travelynx.conf           | 23 ++++++++++
 examples/travelynx.service        | 13 ------
 lib/Travelynx.pm                  | 72 ++++++++++++++++---------------
 lib/Travelynx/Helper/Sendmail.pm  |  7 +--
 templates/layouts/default.html.ep |  2 +-
 7 files changed, 70 insertions(+), 53 deletions(-)
 create mode 100644 .gitignore
 create mode 100644 examples/travelynx.conf

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..f93666be
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+/travelynx.conf
diff --git a/README.md b/README.md
index 317ced82..78ced86b 100644
--- a/README.md
+++ b/README.md
@@ -36,8 +36,9 @@ Debian 9 system, though setup on other distribution should be similar.
 * Create a postgres user for travelynx: `sudo -u postgres createuser -P travelynx`
   (enter password when prompted)
 * Create the database: `sudo -u postgres createdb -O travelynx travelynx`
-* Initialize the database: `TRAVELYNX_DB_HOST=... TRAVELYNX_DB_NAME=... `
-  `TRAVELYNX_DB_USER=... TRAVELYNX_DB_PASSWORD=... perl index.pl setup`
+* Copy `examples/travelynx.conf` to the application root directory
+  (the one in which `index.pl` resides) and configure it
+* Initialize the database: `perl index.pl database setup`
 
 Your server also needs to be able to send mail. Set up your MTA of choice and
 make sure that the sendmail binary can be used for outgoing mails. Mail
diff --git a/examples/travelynx.conf b/examples/travelynx.conf
new file mode 100644
index 00000000..ae61f7bd
--- /dev/null
+++ b/examples/travelynx.conf
@@ -0,0 +1,23 @@
+{
+	cache => {
+		schedule => '/var/cache/travelynx/iris',
+		realtime => '/var/cache/travelynx/iris-rt',
+	},
+	db => {
+		host => 'localhost',
+		database => 'travelynx',
+		user => 'travelynx',
+		password => die("Changeme!"),
+	},
+	hypnotoad => {
+		accepts  => 100,
+		clients  => 10,
+		listen   => [ 'http://127.0.0.1:8093' ],
+		pid_file => '/tmp/travelynx.pid',
+		workers  => 2,
+		spare    => 2,
+	},
+	secrets => [
+		die("Changeme!"),
+	],
+};
diff --git a/examples/travelynx.service b/examples/travelynx.service
index a9469849..0a2b2d5a 100644
--- a/examples/travelynx.service
+++ b/examples/travelynx.service
@@ -16,19 +16,6 @@ WorkingDirectory=/srv/www/travelynx
 
 Environment=LANG=en_US.UTF-8
 
-Environment=TRAVELYNX_DB_NAME=travelynx
-Environment=TRAVELYNX_DB_HOST=localhost
-Environment=TRAVELYNX_DB_PORT=5432
-Environment=TRAVELYNX_DB_USER=travelynx
-Environment=TRAVELYNX_DB_PASSWORD= ! CHANGEME !
-
-Environment=TRAVELYNX_SECRETS= ! CHANGEME !
-
-Environment=TRAVELYNX_WORKERS=2
-Environment=TRAVELYNX_LISTEN=http://127.0.0.1:8093
-
-Environment=TRAVELYNX_IRIS_CACHE=/var/cache/dbf/iris
-Environment=TRAVELYNX_IRISRT_CACHE=/var/cache/dbf/iris-rt
 
 [Install]
 WantedBy=multi-user.target
diff --git a/lib/Travelynx.pm b/lib/Travelynx.pm
index 1965da47..02f5f1b4 100755
--- a/lib/Travelynx.pm
+++ b/lib/Travelynx.pm
@@ -17,18 +17,6 @@ use Travelynx::Helper::Sendmail;
 
 our $VERSION = qx{git describe --dirty} || 'experimental';
 
-my $cache_iris_main = Cache::File->new(
-	cache_root      => $ENV{TRAVELYNX_IRIS_CACHE} // '/tmp/dbf-iris-main',
-	default_expires => '6 hours',
-	lock_level      => Cache::File::LOCK_LOCAL(),
-);
-
-my $cache_iris_rt = Cache::File->new(
-	cache_root      => $ENV{TRAVELYNX_IRISRT_CACHE} // '/tmp/dbf-iris-realtime',
-	default_expires => '70 seconds',
-	lock_level      => Cache::File::LOCK_LOCAL(),
-);
-
 sub check_password {
 	my ( $password, $hash ) = @_;
 
@@ -66,27 +54,18 @@ sub get_station {
 sub startup {
 	my ($self) = @_;
 
-	if ( $ENV{TRAVELYNX_SECRETS} ) {
-		$self->secrets( [ split( qr{:}, $ENV{TRAVELYNX_SECRETS} ) ] );
-	}
-
 	push( @{ $self->commands->namespaces }, 'Travelynx::Command' );
 
 	$self->defaults( layout => 'default' );
 
-	$self->config(
-		hypnotoad => {
-			accepts  => $ENV{TRAVELYNX_ACCEPTS} // 100,
-			clients  => $ENV{TRAVELYNX_CLIENS} // 10,
-			listen   => [ $ENV{TRAVELYNX_LISTEN} // 'http://*:8093' ],
-			pid_file => $ENV{TRAVELYNX_PID_FILE} // '/tmp/travelynx.pid',
-			workers  => $ENV{TRAVELYNX_WORKERS} // 2,
-			spare    => $ENV{TRAVELYNX_SPARE} // 2,
-		},
-	);
-
 	$self->types->type( json => 'application/json; charset=utf-8' );
 
+	$self->plugin('Config');
+
+	if ( $self->config->{secrets} ) {
+		$self->secrets( $self->config->{secrets} );
+	}
+
 	$self->plugin(
 		authentication => {
 			autoload_user => 1,
@@ -116,6 +95,30 @@ sub startup {
 
 	$self->defaults( layout => 'default' );
 
+	$self->attr(
+		cache_iris_main => sub {
+			my ($self) = @_;
+
+			return Cache::File->new(
+				cache_root      => $self->app->config->{cache}->{schedule},
+				default_expires => '6 hours',
+				lock_level      => Cache::File::LOCK_LOCAL(),
+			);
+		}
+	);
+
+	$self->attr(
+		cache_iris_rt => sub {
+			my ($self) = @_;
+
+			return Cache::File->new(
+				cache_root      => $self->app->config->{cache}->{realtime},
+				default_expires => '70 seconds',
+				lock_level      => Cache::File::LOCK_LOCAL(),
+			);
+		}
+	);
+
 	$self->attr(
 		action_type => sub {
 			return {
@@ -321,12 +324,13 @@ sub startup {
 	$self->attr(
 		dbh => sub {
 			my ($self) = @_;
+			my $config = $self->app->config;
 
-			my $dbname = $ENV{TRAVELYNX_DB_NAME} // 'travelynx_dev';
-			my $host   = $ENV{TRAVELYNX_DB_HOST} // 'localhost';
-			my $port   = $ENV{TRAVELYNX_DB_PORT} // '5432';
-			my $user   = $ENV{TRAVELYNX_DB_USER};
-			my $pw     = $ENV{TRAVELYNX_DB_PASSWORD};
+			my $dbname = $config->{db}->{database};
+			my $host   = $config->{db}->{host} // 'localhost';
+			my $port   = $config->{db}->{port} // 5432;
+			my $user   = $config->{db}->{user};
+			my $pw     = $config->{db}->{password};
 
 			return DBI->connect(
 				"dbi:Pg:dbname=${dbname};host=${host};port=${port}",
@@ -593,8 +597,8 @@ qq{select * from pending_mails where email = ? and num_tries > 1;}
 				$station = $station_matches[0][0];
 				my $status = Travel::Status::DE::IRIS->new(
 					station        => $station,
-					main_cache     => $cache_iris_main,
-					realtime_cache => $cache_iris_rt,
+					main_cache     => $self->app->cache_iris_main,
+					realtime_cache => $self->app->cache_iris_rt,
 					lookbehind     => 20,
 					datetime => DateTime->now( time_zone => 'Europe/Berlin' )
 					  ->subtract( minutes => $lookbehind ),
diff --git a/lib/Travelynx/Helper/Sendmail.pm b/lib/Travelynx/Helper/Sendmail.pm
index 61938844..09c8a0d2 100644
--- a/lib/Travelynx/Helper/Sendmail.pm
+++ b/lib/Travelynx/Helper/Sendmail.pm
@@ -12,11 +12,11 @@ use Email::Simple;
 sub new {
 	my ($class) = @_;
 
-	return bless({}, $class);
+	return bless( {}, $class );
 }
 
 sub custom {
-	my ($self, $to, $subject, $body) = @_;
+	my ( $self, $to, $subject, $body ) = @_;
 
 	my $reg_mail = Email::Simple->create(
 		header => [
@@ -28,7 +28,8 @@ sub custom {
 		body => encode( 'utf-8', $body ),
 	);
 
-	if ($ENV{TRAVELYNX_DB_NAME} =~ m{travelynx_dev}) {
+	if ( $self->app->config->{db}->{database} =~ m{travelynx_dev} ) {
+
 		# Do not send mail in dev mode
 		say "sendmail to ${to}: ${subject}\n\n${body}";
 		return 1;
diff --git a/templates/layouts/default.html.ep b/templates/layouts/default.html.ep
index b76f1abe..1f82a2d9 100644
--- a/templates/layouts/default.html.ep
+++ b/templates/layouts/default.html.ep
@@ -23,7 +23,7 @@
 
 <nav class="deep-purple">
 	<div class="nav-wrapper container">
-		<a href="/" class="brand-logo left"><%= $ENV{TRAVELYNX_DB_NAME} =~ m{travelynx_dev} ? 'develynx' : 'travelynx' %></a>
+		<a href="/" class="brand-logo left"><%= app->config->{db}->{database} =~ m{travelynx_dev} ? 'develynx' : 'travelynx' %></a>
 		<ul id="nav-mobile" class="right">
 			% if (is_user_authenticated()) {
 				<li class="<%= navbar_class('/history') %>"><a href='/history' title="History"><i class="material-icons">history</i></a></li>
-- 
GitLab