-- | -- Module: ConfCrypt.Types -- Copyright: (c) 2018 Chris Coffey -- (c) 2018 CollegeVine -- License: MIT -- Maintainer: Chris Coffey -- Stability: experimental -- Portability: portable -- -- Core types and some small helper functions used to construct ConfCrypt. module ConfCrypt.Types ( -- * Core types ConfCryptM, -- ** Errors ConfCryptError(..), -- ** Runtime Environment ConfCryptFile(..), Parameter(..), -- ** File Format ConfCryptElement(..), LineNumber(..), SchemaType(..), ParamLine(..), Schema(..), -- ** Key constraints LocalKey, KMSKey, -- * Helpers unWrapSchema, isParameter, typeToOutputString, parameterToLines ) where import Conduit (ResourceT) import Control.Monad.Reader (MonadReader, ReaderT, runReaderT) import Control.Monad.Except (MonadError, ExceptT, runExceptT) import Control.DeepSeq (NFData) import qualified Crypto.PubKey.RSA.Types as RSA import GHC.Generics (Generic) import qualified Data.Text as T import qualified Data.Map.Strict as M -- | The core transformer stack for ConfCrypt. The most important parts are the 'ReaderT' and -- 'ResourceT', as 'ExceptT' can be replaced with explicit return type. type ConfCryptM m ctx = ReaderT (ConfCryptFile, ctx) ( ExceptT ConfCryptError ( ResourceT m ) ) -- | The possible errors produced during a confcrypt operation. data ConfCryptError = ParserError T.Text | NonRSAKey | KeyUnpackingError T.Text | DecryptionError T.Text | AWSDecryptionError T.Text | AWSEncryptionError T.Text | EncryptionError RSA.Error | MissingLine T.Text | UnknownParameter T.Text | WrongFileAction T.Text | CleanupError T.Text deriving (Show, Generic, Eq, Ord) instance Ord RSA.Error where (<=) l r = show l <= show r -- | As indicated in the Readme, a ConfCrypt file data ConfCryptFile = ConfCryptFile { fileName :: T.Text, fileContents :: M.Map ConfCryptElement LineNumber, parameters :: [Parameter] } deriving (Show, Generic, NFData) -- | The syntax used to describe a confcrypt file. A line in a confcrypt file may be one of 'Schema', -- 'ParamLine', or comment. The grammar itself is described in the readme and 'Confcrypt.Parser'. data ConfCryptElement = SchemaLine Schema | CommentLine {cText ::T.Text} | ParameterLine ParamLine deriving (Show, Generic, NFData) -- | this implementation means that there can only be a single parameter or schema with the same name. -- Attempting to add multiple with the same name is undefined behavior and will result in missing data. instance Eq ConfCryptElement where (==) (SchemaLine l) (SchemaLine r) = sName l == sName r (==) (ParameterLine l) (ParameterLine r) = pName l == pName r (==) (CommentLine l) (CommentLine r) = l == r (==) _ _ = False -- | In order to instance Ord ConfCryptElement where (<=) (SchemaLine l) (SchemaLine r) = sName l <= sName r (<=) (SchemaLine l) (CommentLine _) = False (<=) (SchemaLine l) (ParameterLine _) = True (<=) (ParameterLine l) (ParameterLine r) = pName l <= pName r (<=) (ParameterLine l) (CommentLine _) = False (<=) (ParameterLine l) (SchemaLine _) = False (<=) (CommentLine l) (CommentLine r) = l <= r (<=) (CommentLine l) (ParameterLine _) = True (<=) (CommentLine l) (SchemaLine _) = True -- | A parameter consists of both a 'ParamLine' and 'Schema' line from the confcr data Parameter = Parameter {paramName :: T.Text, paramValue :: T.Text, paramType :: Maybe SchemaType} deriving (Eq, Ord, Show, Generic, NFData) -- | A parsed parameter line from a confcrypt file data ParamLine = ParamLine {pName :: T.Text, pValue :: T.Text} deriving (Eq, Ord, Show, Generic, NFData) -- | A parsed schema line from a confcrypt file data Schema = Schema {sName :: T.Text, sType :: SchemaType} deriving (Eq, Ord, Show, Generic, NFData) -- | Self explanitory newtype LineNumber = LineNumber Int deriving (Eq, Ord, Show, Generic, NFData) -- | Indicates which types a data SchemaType = CString -- ^ Maps to 'String' | CInt -- ^ Maps to 'Int' | CBoolean -- ^ Maps to 'Bool' deriving (Eq, Ord, Show, Generic, NFData, Read) -- | A special purpose 'Show' function for convert typeToOutputString :: SchemaType -> T.Text typeToOutputString CString = "String" typeToOutputString CInt = "Int" typeToOutputString CBoolean = "Boolean" -- | Convert a parameter into a 'ParameterLine' and 'SchemaLine' if possible. parameterToLines :: Parameter -> (ParamLine, Maybe Schema) parameterToLines Parameter {paramName, paramValue, paramType} = (ParamLine paramName paramValue, Schema paramName <$> paramType) -- | Checks whether the provided line from a confcrypt file is a 'Parameter' isParameter :: ConfCryptElement -> Bool isParameter (ParameterLine _) = True isParameter _ = False -- | Attempts to unwrap a line from a confcrypt file into a 'Schema' unWrapSchema :: ConfCryptElement -> Maybe Schema unWrapSchema (SchemaLine s) = Just s unWrapSchema _ = Nothing -- | This constraint provides a type-level check that the wrapped key type is local to the -- current machine. For use with things like RSA keys. class LocalKey key -- | This constraint provides a type-level check that the wrapped key type exists off-system inside -- an externally provided Key Management System (KMS). For use with AWS KMS or Azure KMS. class KMSKey key