{-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} module Main where import Data.Aeson import Data.List (sort) import GHC.Generics import System.Environment import System.IO.Temp (withSystemTempDirectory) import Test.Tasty.HUnit import Network.VaultTool import Network.VaultTool.VaultServerProcess withTempVaultBackend :: (VaultBackendConfig -> IO a) -> IO a withTempVaultBackend action = withSystemTempDirectory "hs_vault" $ \tmpDir -> do let backendConfig = object [ "file" .= object [ "path" .= tmpDir ] ] action backendConfig main :: IO () main = withTempVaultBackend $ \vaultBackendConfig -> do putStrLn "Running tests..." vaultExe <- lookupEnv "VAULT_EXE" let cfg = vaultConfigDefaultAddress vaultBackendConfig addr = vaultAddress cfg withVaultConfigFile cfg $ \vaultConfigFile -> withVaultServerProcess vaultExe vaultConfigFile addr $ talkToVault addr putStrLn "Ok" -- | The vault must be a newly created, non-initialized vault -- -- TODO It would be better to break this into lots of individual unit tests -- instead of this one big-ass test talkToVault :: VaultAddress -> IO () talkToVault addr = do health <- vaultHealth addr _VaultHealth_Initialized health @?= False (unsealKeys, rootToken) <- vaultInit addr 4 2 length unsealKeys @?= 4 status0 <- vaultSealStatus addr status0 @?= VaultSealStatus { _VaultSealStatus_Sealed = True , _VaultSealStatus_T = 2 , _VaultSealStatus_N = 4 , _VaultSealStatus_Progress = 0 } status1 <- vaultUnseal addr (VaultUnseal_Key (unsealKeys !! 0)) status1 @?= VaultSealStatus { _VaultSealStatus_Sealed = True , _VaultSealStatus_T = 2 , _VaultSealStatus_N = 4 , _VaultSealStatus_Progress = 1 } status2 <- vaultUnseal addr VaultUnseal_Reset status2 @?= VaultSealStatus { _VaultSealStatus_Sealed = True , _VaultSealStatus_T = 2 , _VaultSealStatus_N = 4 , _VaultSealStatus_Progress = 0 } status3 <- vaultUnseal addr (VaultUnseal_Key (unsealKeys !! 1)) status3 @?= VaultSealStatus { _VaultSealStatus_Sealed = True , _VaultSealStatus_T = 2 , _VaultSealStatus_N = 4 , _VaultSealStatus_Progress = 1 } status4 <- vaultUnseal addr (VaultUnseal_Key (unsealKeys !! 2)) status4 @?= VaultSealStatus { _VaultSealStatus_Sealed = False , _VaultSealStatus_T = 2 , _VaultSealStatus_N = 4 , _VaultSealStatus_Progress = 0 } conn <- connectToVault addr rootToken allMounts <- vaultMounts conn fmap _VaultMount_Type (lookup "cubbyhole/" allMounts) @?= Just "cubbyhole" fmap _VaultMount_Type (lookup "secret/" allMounts) @?= Just "kv" fmap _VaultMount_Type (lookup "sys/" allMounts) @?= Just "system" _ <- vaultMountTune conn "cubbyhole" _ <- vaultMountTune conn "secret" _ <- vaultMountTune conn "sys" vaultNewMount conn "mymount" VaultMount { _VaultMount_Type = "generic" , _VaultMount_Description = Just "blah blah blah" , _VaultMount_Config = Just VaultMountConfig { _VaultMountConfig_DefaultLeaseTtl = Just 42 , _VaultMountConfig_MaxLeaseTtl = Nothing } } mounts2 <- vaultMounts conn fmap _VaultMount_Description (lookup "mymount/" mounts2) @?= Just "blah blah blah" t <- vaultMountTune conn "mymount" _VaultMountConfig_DefaultLeaseTtl t @?= 42 vaultMountSetTune conn "mymount" VaultMountConfig { _VaultMountConfig_DefaultLeaseTtl = Just 52 , _VaultMountConfig_MaxLeaseTtl = Nothing } t2 <- vaultMountTune conn "mymount" _VaultMountConfig_DefaultLeaseTtl t2 @?= 52 vaultUnmount conn "mymount" mounts3 <- vaultMounts conn lookup "mymount/" mounts3 @?= Nothing vaultWrite conn (VaultSecretPath "secret/big") (object ["A" .= 'a', "B" .= 'b']) (_, r) <- vaultRead conn (VaultSecretPath "secret/big") case r of Left err -> assertFailure $ "Failed to parse secret/big: " ++ (show err) Right x -> x @?= object ["A" .= 'a', "B" .= 'b'] vaultWrite conn (VaultSecretPath "secret/fun") (FunStuff "fun" [1, 2, 3]) (_, r2) <- vaultRead conn (VaultSecretPath "secret/fun") case r2 of Left err -> assertFailure $ "Failed to parse secret/big: " ++ (show err) Right x -> x @?= (FunStuff "fun" [1, 2, 3]) (_, r3) <- vaultRead conn (VaultSecretPath "secret/big") case r3 of Left (v, _) -> v @?= object ["A" .= 'a', "B" .= 'b'] Right (x :: FunStuff) -> assertFailure $ "Somehow parsed an impossible value" ++ show x vaultWrite conn (VaultSecretPath "secret/foo/bar/a") (object ["X" .= 'x']) vaultWrite conn (VaultSecretPath "secret/foo/bar/b") (object ["X" .= 'x']) vaultWrite conn (VaultSecretPath "secret/foo/bar/a/b/c/d/e/f/g") (object ["X" .= 'x']) vaultWrite conn (VaultSecretPath "secret/foo/quack/duck") (object ["X" .= 'x']) keys <- vaultList conn (VaultSecretPath "secret/") assertBool "Secret in list" $ VaultSecretPath "secret/big" `elem` keys vaultDelete conn (VaultSecretPath "secret/big") keys2 <- vaultList conn (VaultSecretPath "secret") assertBool "Secret not in list" $ not (VaultSecretPath "secret/big" `elem` keys2) keys3 <- vaultListRecursive conn (VaultSecretPath "secret/foo/") sort keys3 @?= sort [ VaultSecretPath "secret/foo/bar/a" , VaultSecretPath "secret/foo/bar/b" , VaultSecretPath "secret/foo/bar/a/b/c/d/e/f/g" , VaultSecretPath "secret/foo/quack/duck" ] vaultAuthEnable conn "approle" vaultWrite conn (VaultSecretPath "secret/small") (object ["X" .= 'x']) vaultPolicyCreate conn "foo" "path \"secret/small\" { capabilities = [\"read\"] }" vaultAppRoleCreate conn "foo-role" defaultVaultAppRoleParameters{_VaultAppRoleParameters_Policies=["foo"]} roleId <- vaultAppRoleRoleIdRead conn "foo-role" secretId <- _VaultAppRoleSecretIdGenerateResponse_SecretId <$> vaultAppRoleSecretIdGenerate conn "foo-role" "" arConn <- connectToVaultAppRole addr roleId secretId (_, ar1) <- vaultRead conn (VaultSecretPath "secret/small") case ar1 of Left (v, _) -> v @?= object ["X" .= 'x'] Right (x :: FunStuff) -> assertFailure $ "Somehow parsed an impossible value" ++ show x vaultSeal conn status5 <- vaultSealStatus addr status5 @?= VaultSealStatus { _VaultSealStatus_Sealed = True , _VaultSealStatus_T = 2 , _VaultSealStatus_N = 4 , _VaultSealStatus_Progress = 0 } health2 <- vaultHealth addr _VaultHealth_Initialized health2 @?= True _VaultHealth_Sealed health2 @?= True data FunStuff = FunStuff { funString :: String , funNumbers :: [Int] } deriving (Show, Eq, Generic) instance FromJSON FunStuff instance ToJSON FunStuff