From e772c4b6d52627c463e70b4284e3794aa0bd0634 Mon Sep 17 00:00:00 2001
From: Ryan Lue <hello@ryanlue.com>
Date: Thu, 30 Jun 2022 23:04:35 -0700
Subject: calcurse-caldav: Support PasswordCommand option

This commit adds a new `Auth/PasswordCommand` option
to support security best practices re: handling secrets
in CLI program configuration.

Prior to this commit, the two available options
for specifying a password were:

  1. via the `Auth/Password` config parameter, or
  2. via a `$CALCURSE_CALDAV_PASSWORD` environment variable.

The former is unsafe for obvious reasons;
the latter is unsafe because as long as the script is running,
its environment can be accessed via

    $ cat /proc/<pid>/environ

and is thus visible to anyone with access to the system.

This commit preserves preexisting behavior (for backward compatibility)
but removes all mention of option 2 from the README.
Since the README example for option 2 used a password command anyway,
there is little reason to continue its use,
and this commit recommends it be deprecated.

Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
---
 contrib/caldav/README.md          | 13 ++++++-------
 contrib/caldav/calcurse-caldav.py | 17 +++++++++++++----
 contrib/caldav/config.sample      |  8 +++++---
 3 files changed, 24 insertions(+), 14 deletions(-)

(limited to 'contrib')

diff --git a/contrib/caldav/README.md b/contrib/caldav/README.md
index a842081..b464dc9 100644
--- a/contrib/caldav/README.md
+++ b/contrib/caldav/README.md
@@ -34,13 +34,12 @@ argument. You can choose between the following initialization modes:
 For subsequent calcurse-caldav invocations, you don't need to specify any
 additional parameters.
 
-You can specify a username and password for basic authentication in the
-config file. Alternatively, the password can be passed securely from another
-program (such as *pass*) via the `CALCURSE_CALDAV_PASSWORD` environment variable like
-so:
-```
-CALCURSE_CALDAV_PASSWORD=$(pass show calcurse) calcurse-caldav
-```
+Specify your HTTP Basic authentication credentials under the config file's
+`Auth` section. The most secure approach is to save your password in a CLI
+encrypted password store (_e.g.,_ [pass](https://www.passwordstore.org/)), and
+then set `PasswordCommand` to the shell command used to retrieve it.
+If security is not a priority, you may store your password in plain text
+instead.
 
 Hooks
 -----
diff --git a/contrib/caldav/calcurse-caldav.py b/contrib/caldav/calcurse-caldav.py
index 99e2e6a..f9488e6 100755
--- a/contrib/caldav/calcurse-caldav.py
+++ b/contrib/caldav/calcurse-caldav.py
@@ -6,6 +6,7 @@ import configparser
 import os
 import pathlib
 import re
+import shlex
 import subprocess
 import sys
 import textwrap
@@ -30,6 +31,7 @@ class Config:
         self._map = {
             'Auth': {
                 'Password': None,
+                'PasswordCommand': None,
                 'Username': None,
             },
             'CustomHeaders': {},
@@ -657,9 +659,6 @@ verbose = args.verbose
 debug = args.debug
 debug_raw = args.debug_raw
 
-# Read environment variables
-password = os.getenv('CALCURSE_CALDAV_PASSWORD')
-
 # Read configuration.
 config = Config(configfn)
 
@@ -674,7 +673,17 @@ path = config.get('General', 'Path')
 sync_filter = config.get('General', 'SyncFilter')
 verbose = verbose or config.get('General', 'Verbose')
 
-password = password or config.get('Auth', 'Password')
+if os.getenv('CALCURSE_CALDAV_PASSWORD'):
+    # This approach is deprecated, but preserved for backwards compatibility
+    password = os.getenv('CALCURSE_CALDAV_PASSWORD')
+elif config.get('Auth', 'Password'):
+    password = config.get('Auth', 'Password')
+elif config.get('Auth', 'PasswordCommand'):
+    tokenized_cmd = shlex.split(config.get('Auth', 'PasswordCommand'))
+    password = subprocess.run(tokenized_cmd, capture_output=True).stdout.decode('UTF-8')
+else:
+    password = None
+
 username = config.get('Auth', 'Username')
 
 client_id = config.get('OAuth2', 'ClientID')
diff --git a/contrib/caldav/config.sample b/contrib/caldav/config.sample
index e2c6c2d..0ba8fa8 100644
--- a/contrib/caldav/config.sample
+++ b/contrib/caldav/config.sample
@@ -48,11 +48,13 @@ DryRun = Yes
 # Enable this if you want detailed logs written to stdout.
 Verbose = Yes
 
-# Credentials for HTTP Basic Authentication. Leave this commented out if you do
-# not want to use authentication.
+# Credentials for HTTP Basic Authentication (if required).
+# Set `Password` to your password in plaintext (unsafe),
+# or `PasswordCommand` to a shell command that retrieves it (recommended).
 #[Auth]
 #Username = user
-#Password = pass
+#Password = password
+#PasswordCommand = pass baikal
 
 # Optionally specify additional HTTP headers here.
 #[CustomHeaders]
-- 
cgit v1.2.3-70-g09d2