confcrypt-0.2.0.0: app/ConfCrypt/CLI/API.hs
module ConfCrypt.CLI.API (
KeyAndConf(..),
Conf(..),
KeyProvider(..),
AnyCommand(..),
ParsedKey(..),
cliParser
) where
import ConfCrypt.Types (SchemaType(..))
import ConfCrypt.Commands (GetConfCrypt(..), AddConfCrypt(..), EditConfCrypt(..), DeleteConfCrypt(..))
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 :: ParsedKey, provider :: KeyProvider, conf :: FilePath}
deriving (Eq, Show)
newtype Conf = Conf FilePath
deriving (Eq, Show)
data AnyCommand
= RC KeyAndConf
| GC KeyAndConf GetConfCrypt
| AC KeyAndConf AddConfCrypt
| EC KeyAndConf EditConfCrypt
| DC Conf DeleteConfCrypt
| VC KeyAndConf
| NC
deriving (Eq, Show)
data KeyProvider
= AWS
| 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 https://github.com/collegevine/confcrypt")
commandParser :: Parser AnyCommand
commandParser = hsubparser
(
command "aws" awsCmds
<>
command "rsa" rsaCmds
<>
command "new" new
<>
command "delete" delete
)
awsCmds :: ParserInfo AnyCommand
awsCmds = info (
hsubparser (
command "add" (add AWS)
<>
command "edit" (edit AWS)
<>
command "read" (readConf AWS)
<>
command "get" (get AWS)
<>
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.")
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 ::
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)
delete :: ParserInfo AnyCommand
delete = info ( DC <$> getConf <*> (DeleteConfCrypt <$> onlyName))
(progDesc "Removes an existing parameter from the configuration." <>
fullDesc)
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 ::
KeyProvider
-> ParserInfo AnyCommand
get provider = info ( GC <$> keyAndConf provider False <*> (GetConfCrypt <$> onlyName))
(progDesc "Get a single parameter value from the configuration file." <>
fullDesc)
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)
new :: ParserInfo AnyCommand
new = info (pure NC)
(progDesc "Produce a new boilerplate confcrypt file. This should be piped into your desired config." <>
fullDesc)
keyAndConf ::
KeyProvider
-> Bool -- This is an ugly bit of overloading
-> Parser KeyAndConf
keyAndConf AWS True =
KeyAndConf <$>
(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
onlyConf :: Parser FilePath
onlyConf = strArgument (metavar "CONFIG_FILE")
onlyName :: Parser T.Text
onlyName = strOption (
long "name" <>
short 'n' <>
metavar "PARAMETER_NAME" <>
help "The name of the configuration parameter. This must consist of consist solely of uppercase letters, digits, and the '_' (underscore) ASCII characters."
)
onlyValue :: Parser T.Text
onlyValue = strOption (
long "value" <>
short 'v' <>
metavar "PARAMETER_VALUE" <>
help "The value to set in the config file. This can be any set of characters you want, but there may be issues with multi-byte code points in UTF-8."
)
onlyType :: Parser SchemaType
onlyType =
fromString <$> strOption (
long "type" <>
short 't' <>
metavar "PARAMETER_TYPE" <>
help "The associated type for the variable"
)
where
fromString = read . (:) 'C'