packages feed

confcrypt 0.1.0.4 → 0.2.0.0

raw patch · 5 files changed

+162/−101 lines, 5 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

Files

README.md view
@@ -1,5 +1,5 @@ # confcrypt-As soon as an application is deployed or built on more than a single machine, you tend to start worrying about managing the configuration. There are a number of ways to approach this problem, but ultimately there's a need to protect sentisive inforamtion like database password and api tokens. While you can always store those directly in a config management system like AWS' Parameter Store, doing so means you can't track configuration changes in source control. This application provides yet another simple and straightforward means of hiding config information within source control.+As soon as an application is deployed or built on more than a single machine, you tend to start worrying about managing the configuration. There are a number of ways to approach this problem, but ultimately there's a need to protect sensitive information like database passwords and api tokens. While you can always store those directly in a config management system like AWS' Parameter Store, doing so means you can't track configuration changes in source control. This application provides yet another simple and straightforward means of hiding config information within source control.  [![CircleCI](https://circleci.com/gh/collegevine/confcrypt/tree/master.svg?style=svg)](https://circleci.com/gh/collegevine/confcrypt/tree/master) @@ -30,23 +30,23 @@     # TIMEOUT_MS = 300     ``` - read a config-    `confcrypt read --key <filename> <filename>`+    `confcrypt rsa read --key <filename> <filename>`     This command reads in the provided file, decrypts the configuration variables using the provided key, then prints them to stdout. This allows you to pipe the results to other utilities. Returns 0 on success. - add a parameter-    `confcrypt add --key <filename> --name <String> --type <SchemaType> --vaue <String> <filename>+    `confcrypt rsa add --key <filename> --name <String> --type <SchemaType> --vaue <String> <filename>     Adds a new confguration parameter to the file. `--name` and `--value` are required, while `--type` is optional. If `--type` is provided, the schema record will be added immediately before the config variable. In total this adds two lines to the file. Returns 0 on sccess. - remove a parameter-    `confcrypt delete --name <filename>+    `confcrypt delete --name <filename>`     Removes an existing config parameter & associated schema. Returns 0 on success or 1 if the parameter is not found in the file. - edit a parameter in-place-    `confcrypt edit --key <filename> --name <String> --value <String> --type <SchemaType> <filename>`+    `confcrypt rsa edit --key <filename> --name <String> --value <String> --type <SchemaType> <filename>`     Modifies an existing configuration parameter in place, leaving all other lines unchanged. While this isn't how it's actually implemented, this operation is equivalent to piping `confcrypt read` to a new file, editing the parameter, then reencrypting it. - validate a config-    `confcrypt validate --key <filename> <filename>`+    `confcrypt rsa validate --key <filename> <filename>`     Checks that each config parameter matches the type of its schema. All errors are accumulated and returned at the end, with a response code equal to the number of failures.  - Using Amazon KMS instead of a local key-    The `--use-aws` flag changes the behavior of the `--key` parameter to represent a KMS key id rather than an on-disk rsa key file.+    The `rsa` command tree exists under `aws`, which changes the behavior of the `--key` parameter to represent a KMS key id rather than an on-disk rsa key file. The otherwise the semantics of the commands are identical between `rsa` and `kms` branches.  ## The confcrypt file format     ```
app/ConfCrypt/CLI/API.hs view
@@ -3,16 +3,18 @@     Conf(..),     KeyProvider(..),     AnyCommand(..),+    ParsedKey(..),     cliParser ) where  import ConfCrypt.Types (SchemaType(..)) import ConfCrypt.Commands (GetConfCrypt(..), AddConfCrypt(..), EditConfCrypt(..), DeleteConfCrypt(..)) -import Options.Applicative+import Options.Applicative (ParserInfo, Parser, progDesc, command, fullDesc, long, flag, metavar,+    help, strOption, short, info, header, footer, strArgument, hsubparser, helper, (<**>)) import qualified Data.Text as T -data KeyAndConf = KeyAndConf {key :: FilePath, provider :: KeyProvider, conf :: FilePath}+data KeyAndConf = KeyAndConf {key :: ParsedKey, provider :: KeyProvider, conf :: FilePath}     deriving (Eq, Show) newtype Conf = Conf FilePath     deriving (Eq, Show)@@ -32,38 +34,77 @@     | LocalRSA     deriving (Eq, Show) +data ParsedKey+    = OnDisk FilePath+    | KmsId T.Text+    | UnNecessary+    deriving (Eq, Show)+ cliParser :: ParserInfo AnyCommand cliParser = info (commandParser <**> helper) $         fullDesc <>         (header "confcrypt: a tool for sane configuration management") <>-        (footer "confcrypt's documentation and source is avaiable at <TODO fill me in>")+        (footer "confcrypt's documentation and source is avaiable at https://github.com/collegevine/confcrypt")   commandParser :: Parser AnyCommand commandParser = hsubparser     (-        command "add" add+        command "aws" awsCmds         <>-        command "edit" edit+        command "rsa" rsaCmds         <>+        command "new" new+        <>         command "delete" delete+    )++awsCmds :: ParserInfo AnyCommand+awsCmds = info (+    hsubparser (+        command "add" (add AWS)         <>-        command "read" readConf+        command "edit" (edit AWS)         <>-        command "get" get+        command "read" (readConf AWS)         <>-        command "validate" validate+        command "get" (get AWS)         <>-        command "new" new-    )+        command "validate" (validate AWS)+        )+    ) $+    fullDesc <>+    (header "Run using a local RSA key. This overloads the --key option to accept a file path to the public key.") -add :: ParserInfo AnyCommand-add = info ( AC <$> keyAndConf <*> (AddConfCrypt <$> onlyName <*> onlyValue <*> onlyType))+rsaCmds :: ParserInfo AnyCommand+rsaCmds = info (+    hsubparser (+        command "add" (add LocalRSA)+        <>+        command "edit" (edit LocalRSA)+        <>+        command "read" (readConf LocalRSA)+        <>+        command "get" (get LocalRSA)+        <>+        command "validate" (validate LocalRSA)+        )+    ) $+    fullDesc <>+    (header "Run using a local RSA key. This overloads the --key option to accept a file path to the public key.")+++add ::+    KeyProvider+    -> ParserInfo AnyCommand+add provider = info ( AC <$> keyAndConf provider True <*> (AddConfCrypt <$> onlyName <*> onlyValue <*> onlyType))            (progDesc "Add a new parameter to the configuration file. New parameters are added to the end of the file." <>             fullDesc) -edit :: ParserInfo AnyCommand-edit = info ( EC <$> keyAndConf <*> (EditConfCrypt <$> onlyName <*> onlyValue <*> onlyType))+edit ::+    KeyProvider+    -> ParserInfo AnyCommand+edit provider = info ( EC <$> keyAndConf provider True <*> (EditConfCrypt <$> onlyName <*> onlyValue <*> onlyType))            (progDesc "Modify an existing parameter in-place. This should preserve a clean diff." <>             fullDesc) @@ -72,18 +113,24 @@            (progDesc "Removes an existing parameter from the configuration." <>             fullDesc) -readConf :: ParserInfo AnyCommand-readConf = info ( RC <$> keyAndConf )+readConf ::+    KeyProvider+    -> ParserInfo AnyCommand+readConf provider = info ( RC <$> keyAndConf provider False)            (progDesc "Read in the provided config and decrypt it with the key. Results are printed to StdOut." <>             fullDesc) -get :: ParserInfo AnyCommand-get = info ( GC <$> keyAndConf <*> (GetConfCrypt <$> onlyName))+get ::+    KeyProvider+    -> ParserInfo AnyCommand+get provider = info ( GC <$> keyAndConf provider False <*> (GetConfCrypt <$> onlyName))             (progDesc "Get a single parameter value from the configuration file." <>             fullDesc) -validate :: ParserInfo AnyCommand-validate = info ( VC <$> keyAndConf)+validate ::+    KeyProvider+    -> ParserInfo AnyCommand+validate provider = info ( VC <$> keyAndConf provider False)            (progDesc "Check that the configuration is self-consistent and obeys the confcrypt rules." <>             fullDesc) @@ -92,18 +139,34 @@             (progDesc "Produce a new boilerplate confcrypt file. This should be piped into your desired config." <>              fullDesc) -keyAndConf :: Parser KeyAndConf-keyAndConf =+keyAndConf ::+    KeyProvider+    -> Bool -- This is an ugly bit of overloading+    -> Parser KeyAndConf+keyAndConf AWS True =     KeyAndConf <$>-        strOption (-            long "key" <>-            short 'k' <>-            metavar "KEY" <>-            help "The path to the private RSA key used to encrypt this file."-            ) <*>-        getProvider <*>+        (KmsId . T.pack <$> parseKey) <*>+        pure AWS <*>         onlyConf+keyAndConf AWS False =+    KeyAndConf <$>+        pure UnNecessary <*>+        pure AWS <*>+        onlyConf+keyAndConf LocalRSA _ =+    KeyAndConf <$>+        (OnDisk <$> parseKey) <*>+        pure LocalRSA <*>+        onlyConf +parseKey :: Parser String+parseKey =+    strOption (+        long "key" <>+        short 'k' <>+        metavar "KEY" <>+        help "The path to the key. May be a KMS id or RSA file path, depending on command"+        ) getConf :: Parser Conf getConf = Conf <$> onlyConf @@ -136,11 +199,3 @@         )     where         fromString = read . (:) 'C'--getProvider :: Parser KeyProvider-getProvider = flag LocalRSA AWS (-    long "use-aws" <>-    help "Toggles whether the --key indicates an RSA keyfile or an AWS KMS key identifer"-    )--
app/ConfCrypt/CLI/Engine.hs view
@@ -2,13 +2,14 @@     run     ) where -import ConfCrypt.Types-import ConfCrypt.Commands-import ConfCrypt.Parser+import ConfCrypt.Types (ConfCryptError(..), ConfCryptFile(..), ConfCryptM)+import ConfCrypt.Commands (NewConfCrypt(..), ReadConfCrypt(..), ValidateConfCrypt(..),+    AddConfCrypt(..), DeleteConfCrypt(..), evaluate)+import ConfCrypt.Parser (parseConfCrypt) import ConfCrypt.Encryption import ConfCrypt.Default (emptyConfCryptFile)-import ConfCrypt.Providers.AWS-import ConfCrypt.CLI.API+import ConfCrypt.Providers.AWS (AWSCtx, loadAwsCtx, KMSKeyId(..))+import ConfCrypt.CLI.API (AnyCommand(..), Conf(..), KeyAndConf(..), KeyProvider(..), ParsedKey(..))  import Conduit (ResourceT, runResourceT) import Control.Exception (catch)@@ -66,19 +67,25 @@          -- Inject an encryption context into the currently loaded environment. This varies dependng         -- on whether its a local RSA key or a KMS key-        runWithEncrypt k AWS cmd = do-            ctx <- loadAwsCtx (KMSKeyId $ T.pack k)+        runWithEncrypt (KmsId k) AWS cmd = do+            ctx <- loadAwsCtx (KMSKeyId k)             withReaderT (injectAWSCtx ctx) $ evaluate cmd-        runWithEncrypt k LocalRSA cmd = do+        runWithEncrypt UnNecessary AWS cmd = do+            ctx <- loadAwsCtx (KMSKeyId "dummy")+            withReaderT (injectAWSCtx ctx) $ evaluate cmd+        runWithEncrypt (OnDisk k) LocalRSA cmd = do             rsaKey <- loadRSAKey k             withReaderT (injectPubKey rsaKey) $ evaluate cmd          -- Inject a decryption context into the currently loaded environment. This varies dependng         -- on whether its a local RSA key or a KMS key-        runWithDecrypt k AWS cmd = do-            ctx <- loadAwsCtx (KMSKeyId $ T.pack k)+        runWithDecrypt (KmsId k) AWS cmd = do+            ctx <- loadAwsCtx (KMSKeyId k)             withReaderT (injectAWSCtx ctx) $ evaluate cmd-        runWithDecrypt k LocalRSA cmd = do+        runWithDecrypt UnNecessary AWS cmd = do+            ctx <- loadAwsCtx (KMSKeyId "dummy")+            withReaderT (injectAWSCtx ctx) $ evaluate cmd+        runWithDecrypt (OnDisk k) LocalRSA cmd = do             rsaKey <- loadRSAKey k             withReaderT (injectPrivateKey rsaKey) $ evaluate cmd 
confcrypt.cabal view
@@ -1,29 +1,28 @@-cabal-version: 1.12---- This file has been generated from package.yaml by hpack version 0.31.0.+-- This file has been generated from package.yaml by hpack version 0.28.2. -- -- see: https://github.com/sol/hpack ----- hash: 3348b210d7e230af456f68d94b941adedba4a217fe592d8af9572e8159cb927c+-- hash: 78b2963f823c24783db5be9543ec39e215f9a07298d3987ac7dc95bf5249ca0b  name:           confcrypt-version:        0.1.0.4+version:        0.2.0.0 description:    Please see the README on GitHub at <https://github.com/CollegeVine/confcrypt#readme>-homepage:       https://github.com/https://github.com/collegevine/confcrypt#readme-bug-reports:    https://github.com/https://github.com/collegevine/confcrypt/issues+homepage:       https://github.com/collegevine/confcrypt#readme+bug-reports:    https://github.com/collegevine/confcrypt/issues author:         Chris Coffey maintainer:     chris@collegevine.com copyright:      2018 Chris Coffey, CollegeVine license:        MIT license-file:   LICENSE build-type:     Simple+cabal-version:  >= 1.10 extra-source-files:-    README.md     ChangeLog.md+    README.md  source-repository head   type: git-  location: https://github.com/https://github.com/collegevine/confcrypt+  location: https://github.com/collegevine/confcrypt  library   exposed-modules:
test/ConfCrypt/CLI/API/Tests.hs view
@@ -13,7 +13,7 @@ import Test.Tasty.HUnit  localTestConf :: KeyAndConf-localTestConf = KeyAndConf "testKey" LocalRSA "test.econf"+localTestConf = KeyAndConf (OnDisk "testKey") LocalRSA "test.econf"  cliAPITests :: TestTree cliAPITests = testGroup "cli api" [@@ -33,32 +33,32 @@ readCases :: TestTree readCases = testGroup "read" [     testCase "read requires a key" $ do-        let args = ["read", "test.econf"]+        let args = ["rsa", "read", "test.econf"]             res = execParserPure defaultPrefs cliParser args         case res of             Failure _ -> assertBool "can't fail" True             Success a -> assertFailure ("Incorrectly parsed: "<> show a)             CompletionInvoked _ -> assertFailure "Incorrectly triggered completion"     ,testCase "read requires a config file" $ do-        let args = ["read", "--key", "testKey"]+        let args = ["rsa", "read", "--key", "testKey"]             res = execParserPure defaultPrefs cliParser args         case res of             Failure _ -> assertBool "can't fail" True             Success a -> assertFailure ("Incorrectly parsed: "<> show a)             CompletionInvoked _ -> assertFailure "Incorrectly triggered completion"    ,testCase "read preserves the provided key file with -k" $ do-        let args = ["read", "-k", "testKey", "test.econf"]+        let args = ["rsa", "read", "-k", "testKey", "test.econf"]             res = execParserPure defaultPrefs cliParser args         case res of-            Success (RC (KeyAndConf "testKey" LocalRSA "test.econf") ) -> assertBool "can't fail" True+            Success (RC (KeyAndConf (OnDisk "testKey") LocalRSA "test.econf") ) -> assertBool "can't fail" True             Success a -> assertFailure ("Incorrectly parsed: "<> show a)             Failure _ -> assertFailure "Should have parsed an RC"             CompletionInvoked _ -> assertFailure "Incorrectly triggered completion"    ,testCase "read preserves the provided key file with --key" $ do-        let args = ["read", "--key", "testKey","test.econf"]+        let args = ["rsa", "read", "--key", "testKey","test.econf"]             res = execParserPure defaultPrefs cliParser args         case res of-            Success (RC (KeyAndConf "testKey" LocalRSA "test.econf") ) -> assertBool "can't fail" True+            Success (RC (KeyAndConf (OnDisk "testKey") LocalRSA "test.econf") ) -> assertBool "can't fail" True             Success a -> assertFailure ("Incorrectly parsed: "<> show a)             Failure _ -> assertFailure "Should have parsed an RC"             CompletionInvoked _ -> assertFailure "Incorrectly triggered completion"@@ -67,32 +67,32 @@ getCases :: TestTree getCases = testGroup "get" [     testCase "get requires a key" $ do-        let args = ["get", "--name", "Test", "test.econf"]+        let args = ["rsa", "get", "--name", "Test", "test.econf"]             res = execParserPure defaultPrefs cliParser args         case res of             Failure _ -> assertBool "can't fail" True             Success a -> assertFailure ("Incorrectly parsed: "<> show a)             CompletionInvoked _ -> assertFailure "Incorrectly triggered completion"     ,testCase "get requires a config file" $ do-        let args = ["get", "--key", "testKey", "--name", "Test"]+        let args = ["rsa", "get", "--key", "testKey", "--name", "Test"]             res = execParserPure defaultPrefs cliParser args         case res of             Failure _ -> assertBool "can't fail" True             Success a -> assertFailure ("Incorrectly parsed: "<> show a)             CompletionInvoked _ -> assertFailure "Incorrectly triggered completion"    ,testCase "get preserves the provided key file with -k" $ do-        let args = ["get", "-k", "testKey", "--name", "Test", "test.econf"]+        let args = ["rsa", "get", "-k", "testKey", "--name", "Test", "test.econf"]             res = execParserPure defaultPrefs cliParser args         case res of-            Success (GC (KeyAndConf "testKey" LocalRSA "test.econf") (GetConfCrypt "Test")) -> assertBool "can't fail" True+            Success (GC (KeyAndConf (OnDisk "testKey") LocalRSA "test.econf") (GetConfCrypt "Test")) -> assertBool "can't fail" True             Success a -> assertFailure ("Incorrectly parsed: "<> show a)             Failure _ -> assertFailure "Should have parsed a GC"             CompletionInvoked _ -> assertFailure "Incorrectly triggered completion"    ,testCase "get preserves the provided key file with --key" $ do-        let args = ["get", "--key", "testKey", "--name", "Test", "test.econf"]+        let args = ["rsa", "get", "--key", "testKey", "--name", "Test", "test.econf"]             res = execParserPure defaultPrefs cliParser args         case res of-            Success (GC (KeyAndConf "testKey" LocalRSA "test.econf") (GetConfCrypt "Test")) -> assertBool "can't fail" True+            Success (GC (KeyAndConf (OnDisk "testKey") LocalRSA "test.econf") (GetConfCrypt "Test")) -> assertBool "can't fail" True             Success a -> assertFailure ("Incorrectly parsed: "<> show a)             Failure _ -> assertFailure "Should have parsed a GC"             CompletionInvoked _ -> assertFailure "Incorrectly triggered completion"@@ -101,7 +101,7 @@ addCases :: TestTree addCases = testGroup "add" [     testCase "requires a key" $ do-        let args = ["add", "--name", "test", "--type", "String", "--value", "foo", "test.econf"]+        let args = ["rsa", "add", "--name", "test", "--type", "String", "--value", "foo", "test.econf"]             res = execParserPure defaultPrefs cliParser args         case res of             Failure _ -> assertBool "can't fail" True@@ -109,7 +109,7 @@             CompletionInvoked _ -> assertFailure "Incorrectly triggered completion"     ,testCase "requires a name" $ do-        let args = ["add", "--key", "testKey", "--type", "String", "--value", "foo", "test.econf"]+        let args = ["rsa", "add", "--key", "testKey", "--type", "String", "--value", "foo", "test.econf"]             res = execParserPure defaultPrefs cliParser args         case res of             Failure _ -> assertBool "can't fail" True@@ -117,7 +117,7 @@             CompletionInvoked _ -> assertFailure "Incorrectly triggered completion"     ,testCase "requires a type" $ do-        let args = ["add", "--key", "testKey", "--name", "test", "--value", "foo", "test.econf"]+        let args = ["rsa", "add", "--key", "testKey", "--name", "test", "--value", "foo", "test.econf"]             res = execParserPure defaultPrefs cliParser args         case res of             Failure _ -> assertBool "can't fail" True@@ -125,7 +125,7 @@             CompletionInvoked _ -> assertFailure "Incorrectly triggered completion"     ,testCase "requires a value" $ do-        let args = ["add", "--key", "testKey", "--name", "test", "--type", "String", "test.econf"]+        let args = ["rsa", "add", "--key", "testKey", "--name", "test", "--type", "String", "test.econf"]             res = execParserPure defaultPrefs cliParser args         case res of             Failure _ -> assertBool "can't fail" True@@ -133,7 +133,7 @@             CompletionInvoked _ -> assertFailure "Incorrectly triggered completion"     ,testCase "requires a config file" $ do-        let args = ["add", "--key", "testKey", "--name", "test", "--type", "String", "--value", "foo"]+        let args = ["rsa", "add", "--key", "testKey", "--name", "test", "--type", "String", "--value", "foo"]             res = execParserPure defaultPrefs cliParser args         case res of             Failure _ -> assertBool "can't fail" True@@ -141,19 +141,19 @@             CompletionInvoked _ -> assertFailure "Incorrectly triggered completion"     ,testCase "preserves the provided arguments " $ do-        let args =  ["add", "--key", "testKey", "--name", "test", "--type", "String", "--value", "foo", "test.econf"]+        let args =  ["rsa", "add", "--key", "testKey", "--name", "test", "--type", "String", "--value", "foo", "test.econf"]             res = execParserPure defaultPrefs cliParser args         case res of-            Success (AC (KeyAndConf "testKey" LocalRSA "test.econf") (AddConfCrypt "test" "foo" CString)) -> assertBool "can't fail" True+            Success (AC (KeyAndConf (OnDisk "testKey") LocalRSA "test.econf") (AddConfCrypt "test" "foo" CString)) -> assertBool "can't fail" True             Success a -> assertFailure ("Incorrectly parsed: "<> show a)             Failure _ -> assertFailure "Should have parsed an AC"             CompletionInvoked _ -> assertFailure "Incorrectly triggered completion"     ,testCase "supports alternative argument labels" $ do-        let args =  ["add", "-k", "testKey", "-n", "test", "-t", "String", "-v", "foo", "test.econf"]+        let args =  ["rsa", "add", "-k", "testKey", "-n", "test", "-t", "String", "-v", "foo", "test.econf"]             res = execParserPure defaultPrefs cliParser args         case res of-            Success (AC (KeyAndConf "testKey" LocalRSA "test.econf") (AddConfCrypt "test" "foo" CString)) -> assertBool "can't fail" True+            Success (AC (KeyAndConf (OnDisk "testKey") LocalRSA "test.econf") (AddConfCrypt "test" "foo" CString)) -> assertBool "can't fail" True             Success a -> assertFailure ("Incorrectly parsed: "<> show a)             Failure _ -> assertFailure "Should have parsed an AC"             CompletionInvoked _ -> assertFailure "Incorrectly triggered completion"@@ -162,7 +162,7 @@ editCases :: TestTree editCases = testGroup "edit" [     testCase "requires a key" $ do-        let args = ["edit", "--name", "test", "--type", "String", "--value", "foo", "test.econf"]+        let args = ["rsa", "edit", "--name", "test", "--type", "String", "--value", "foo", "test.econf"]             res = execParserPure defaultPrefs cliParser args         case res of             Failure _ -> assertBool "can't fail" True@@ -170,7 +170,7 @@             CompletionInvoked _ -> assertFailure "Incorrectly triggered completion"     ,testCase "requires a name" $ do-        let args = ["edit", "--key", "testKey", "--type", "String", "--value", "foo", "test.econf"]+        let args = ["rsa", "edit", "--key", "testKey", "--type", "String", "--value", "foo", "test.econf"]             res = execParserPure defaultPrefs cliParser args         case res of             Failure _ -> assertBool "can't fail" True@@ -178,7 +178,7 @@             CompletionInvoked _ -> assertFailure "Incorrectly triggered completion"     ,testCase "requires a type" $ do-        let args = ["edit", "--key", "testKey", "--name", "test", "--value", "foo", "test.econf"]+        let args = ["rsa", "edit", "--key", "testKey", "--name", "test", "--value", "foo", "test.econf"]             res = execParserPure defaultPrefs cliParser args         case res of             Failure _ -> assertBool "can't fail" True@@ -186,7 +186,7 @@             CompletionInvoked _ -> assertFailure "Incorrectly triggered completion"     ,testCase "requires a value" $ do-        let args = ["edit", "--key", "testKey", "--name", "test", "--type", "String", "test.econf"]+        let args = ["rsa", "edit", "--key", "testKey", "--name", "test", "--type", "String", "test.econf"]             res = execParserPure defaultPrefs cliParser args         case res of             Failure _ -> assertBool "can't fail" True@@ -194,7 +194,7 @@             CompletionInvoked _ -> assertFailure "Incorrectly triggered completion"     ,testCase "requires a config file" $ do-        let args = ["edit", "--key", "testKey", "--name", "test", "--type", "String", "--value", "foo"]+        let args = ["rsa", "edit", "--key", "testKey", "--name", "test", "--type", "String", "--value", "foo"]             res = execParserPure defaultPrefs cliParser args         case res of             Failure _ -> assertBool "can't fail" True@@ -202,19 +202,19 @@             CompletionInvoked _ -> assertFailure "Incorrectly triggered completion"     ,testCase "preserves the provided arguments " $ do-        let args =  ["edit", "--key", "testKey", "--name", "test", "--type", "String", "--value", "foo", "test.econf"]+        let args =  ["rsa", "edit", "--key", "testKey", "--name", "test", "--type", "String", "--value", "foo", "test.econf"]             res = execParserPure defaultPrefs cliParser args         case res of-            Success (EC (KeyAndConf "testKey" LocalRSA "test.econf") (EditConfCrypt "test" "foo" CString)) -> assertBool "can't fail" True+            Success (EC (KeyAndConf (OnDisk "testKey") LocalRSA "test.econf") (EditConfCrypt "test" "foo" CString)) -> assertBool "can't fail" True             Success a -> assertFailure ("Incorrectly parsed: "<> show a)             Failure _ -> assertFailure "Should have parsed an AC"             CompletionInvoked _ -> assertFailure "Incorrectly triggered completion"     ,testCase "supports alternative argument labels" $ do-        let args =  ["edit", "-k", "testKey", "-n", "test", "-t", "String", "-v", "foo", "test.econf"]+        let args =  ["rsa", "edit", "-k", "testKey", "-n", "test", "-t", "String", "-v", "foo", "test.econf"]             res = execParserPure defaultPrefs cliParser args         case res of-            Success (EC (KeyAndConf "testKey" LocalRSA "test.econf") (EditConfCrypt "test" "foo" CString)) -> assertBool "can't fail" True+            Success (EC (KeyAndConf (OnDisk "testKey") LocalRSA "test.econf") (EditConfCrypt "test" "foo" CString)) -> assertBool "can't fail" True             Success a -> assertFailure ("Incorrectly parsed: "<> show a)             Failure _ -> assertFailure "Should have parsed an AC"             CompletionInvoked _ -> assertFailure "Incorrectly triggered completion"@@ -260,32 +260,32 @@ validateCases :: TestTree validateCases = testGroup "validate" [     testCase "validate requires a key" $ do-        let args = ["validate", "test.econf"]+        let args = ["rsa", "validate", "test.econf"]             res = execParserPure defaultPrefs cliParser args         case res of             Failure _ -> assertBool "can't fail" True             Success a -> assertFailure ("Incorrectly parsed: "<> show a)             CompletionInvoked _ -> assertFailure "Incorrectly triggered completion"    ,testCase "validate requires a config file" $ do-        let args = ["validate", "--key", "testKey"]+        let args = ["rsa", "validate", "--key", "testKey"]             res = execParserPure defaultPrefs cliParser args         case res of             Failure _ -> assertBool "can't fail" True             Success a -> assertFailure ("Incorrectly parsed: "<> show a)             CompletionInvoked _ -> assertFailure "Incorrectly triggered completion"    ,testCase "validate preserves the provided key file with -k" $ do-        let args = ["validate", "-k", "testKey", "test.econf"]+        let args = ["rsa", "validate", "-k", "testKey", "test.econf"]             res = execParserPure defaultPrefs cliParser args         case res of-            Success (VC (KeyAndConf "testKey" LocalRSA "test.econf") ) -> assertBool "can't fail" True+            Success (VC (KeyAndConf (OnDisk "testKey") LocalRSA "test.econf") ) -> assertBool "can't fail" True             Success a -> assertFailure ("Incorrectly parsed: "<> show a)             Failure _ -> assertFailure "Should have parsed an VC"             CompletionInvoked _ -> assertFailure "Incorrectly triggered completion"    ,testCase "validate preserves the provided key file with --key" $ do-        let args = ["validate", "--key", "testKey","test.econf"]+        let args = ["rsa", "validate", "--key", "testKey","test.econf"]             res = execParserPure defaultPrefs cliParser args         case res of-            Success (VC (KeyAndConf "testKey" LocalRSA "test.econf") ) -> assertBool "can't fail" True+            Success (VC (KeyAndConf (OnDisk "testKey") LocalRSA "test.econf") ) -> assertBool "can't fail" True             Success a -> assertFailure ("Incorrectly parsed: "<> show a)             Failure _ -> assertFailure "Should have parsed an VC"             CompletionInvoked _ -> assertFailure "Incorrectly triggered completion"