packages feed

salmon-ops-0.1.0.0: src/Salmon/Builtin/Nodes/Secrets.hs

module Salmon.Builtin.Nodes.Secrets where

import Salmon.Actions.UpDown (skipIfFileExists)
import Salmon.Builtin.Extension
import Salmon.Builtin.Nodes.Binary (Binary, Command (..), withBinary)
import qualified Salmon.Builtin.Nodes.Binary as Binary
import Salmon.Builtin.Nodes.Filesystem as FS
import Salmon.Op.Ref
import Salmon.Op.Track
import Salmon.Reporter

import Control.Monad (void)
import qualified Data.ByteString.Char8 as ByteString
import Data.Text (Text)
import qualified Data.Text as Text

import System.FilePath (takeDirectory, (</>))
import System.Process.ListLike (CreateProcess, proc)

-------------------------------------------------------------------------------
data Report
    = Generate !Secret !Binary.Report
    deriving (Show)

-------------------------------------------------------------------------------

data SecretType
    = Base64
    | Base64SafeUrl
    | Hex
    deriving (Show)

data Secret
    = Secret
    { secret_type :: SecretType
    , secret_bytes :: Int
    , secret_path :: FilePath
    }
    deriving (Show)

sharedSecretFile :: Reporter Report -> Track' (Binary "openssl") -> Secret -> Op
sharedSecretFile r bin sec =
    withBinary bin openssl (GenRandom sec) $ \up -> do
        op "secret:gen" (deps [enclosingdir]) $ \actions ->
            actions
                { help = "generates a secret file for shared-secret"
                , ref = mkRef "gen-secret" sec.secret_path
                , check = skipIfFileExists sec.secret_path
                , up = up r' >> modifyInPlace sec.secret_path
                }
  where
    r' = contramap (Generate sec) r
    enclosingdir :: Op
    enclosingdir = FS.dir (FS.Directory $ takeDirectory sec.secret_path)
    modifyInPlace path =
        case sec.secret_type of
            Base64SafeUrl -> chompNewLines path >> safeUrlizeB64 path
            Base64 -> chompNewLines path
            Hex -> chompNewLines path

data GenRandom
    = GenRandom Secret

openssl :: Command "openssl" GenRandom
openssl = Command $ \(GenRandom s) ->
    case s.secret_type of
        Hex -> proc "openssl" ["rand", "-hex", "-out", s.secret_path, show s.secret_bytes]
        Base64 -> proc "openssl" ["rand", "-base64", "-out", s.secret_path, show s.secret_bytes]
        Base64SafeUrl -> proc "openssl" ["rand", "-base64", "-out", s.secret_path, show s.secret_bytes]

chompNewLines :: FilePath -> IO ()
chompNewLines path =
    ByteString.readFile path >>= ByteString.writeFile path . chomp
  where
    chomp = ByteString.filter ((/=) '\n')

safeUrlizeB64 :: FilePath -> IO ()
safeUrlizeB64 path =
    ByteString.readFile path >>= ByteString.writeFile path . tr
  where
    tr = ByteString.map f
    f '+' = '-'
    f '/' = '_'
    f x = x