packages feed

confcrypt-0.1.0.3: app/ConfCrypt/CLI/API.hs

module ConfCrypt.CLI.API (
    KeyAndConf(..),
    Conf(..),
    KeyProvider(..),
    AnyCommand(..),
    cliParser
) where

import ConfCrypt.Types (SchemaType(..))
import ConfCrypt.Commands (GetConfCrypt(..), AddConfCrypt(..), EditConfCrypt(..), DeleteConfCrypt(..))

import Options.Applicative
import qualified Data.Text as T

data KeyAndConf = KeyAndConf {key :: FilePath, 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)

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>")


commandParser :: Parser AnyCommand
commandParser = hsubparser
    (
        command "add" add
        <>
        command "edit" edit
        <>
        command "delete" delete
        <>
        command "read" readConf
        <>
        command "get" get
        <>
        command "validate" validate
        <>
        command "new" new
    )

add :: ParserInfo AnyCommand
add = info ( AC <$> keyAndConf <*> (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))
           (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 :: ParserInfo AnyCommand
readConf = info ( RC <$> keyAndConf )
           (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))
            (progDesc "Get a single parameter value from the configuration file." <>
            fullDesc)

validate :: ParserInfo AnyCommand
validate = info ( VC <$> keyAndConf)
           (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 :: Parser KeyAndConf
keyAndConf =
    KeyAndConf <$>
        strOption (
            long "key" <>
            short 'k' <>
            metavar "KEY" <>
            help "The path to the private RSA key used to encrypt this file."
            ) <*>
        getProvider <*>
        onlyConf

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'

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"
    )