-- Copyright (C) 2018 Red Hat, Inc. -- -- This file is part of bdcs-api. -- -- bdcs-api is free software: you can redistribute it and/or modify -- it under the terms of the GNU General Public License as published by -- the Free Software Foundation, either version 3 of the License, or -- (at your option) any later version. -- -- bdcs-api is distributed in the hope that it will be useful, -- but WITHOUT ANY WARRANTY; without even the implied warranty of -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -- GNU General Public License for more details. -- -- You should have received a copy of the GNU General Public License -- along with bdcs-api. If not, see . {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} {-| Customizations applies to the content of an export -} module BDCS.API.Customization(RecipeCustomization(..), RecipeSshKey(..), emptyCustomization, processCustomization) where import BDCS.DB import BDCS.Export.Customize(Customization(..)) import Control.Monad.IO.Class(MonadIO, liftIO) import Data.Aeson import Data.Bits((.|.)) import qualified Data.ByteString.Char8 as C8 import Data.Maybe(catMaybes) import qualified Data.Text as T import Data.Time.Clock(getCurrentTime) import Data.Time.Clock.POSIX(POSIXTime, utcTimeToPOSIXSeconds) import System.FilePath.Posix(()) import System.Posix.Files(directoryMode, regularFileMode) -- | Recipe customization commands data RecipeCustomization = RecipeCustomization { rcHostName :: Maybe String -- ^ System hostname , rcSshKeys :: [RecipeSshKey] -- ^ Ssh keys to install } deriving (Eq, Show) instance FromJSON RecipeCustomization where parseJSON = withObject "recipe customization" $ \o -> do rcHostName <- o .:? "hostname" rcSshKeys <- o .:? "sshkey".!= [] return RecipeCustomization{..} instance ToJSON RecipeCustomization where toJSON RecipeCustomization{..} = let maybeHostname = ("hostname" .=) <$> rcHostName maybeSshKeys = if null rcSshKeys then Nothing else Just ("sshkey" .= toJSONList rcSshKeys) in object $ catMaybes [maybeHostname, maybeSshKeys] emptyCustomization :: RecipeCustomization emptyCustomization = RecipeCustomization Nothing [] -- | A sshkey customization data RecipeSshKey = RecipeSshKey { rcSshUser :: String -- ^ User to which to apply the key , rcSshKey :: String -- ^ Key to install } deriving (Eq, Show) instance FromJSON RecipeSshKey where parseJSON = withObject "ssh key" $ \o -> do rcSshUser <- o .: "user" rcSshKey <- o .: "key" return RecipeSshKey{..} instance ToJSON RecipeSshKey where toJSON RecipeSshKey{..} = object [ "user" .= rcSshUser , "key" .= rcSshKey ] -- | Convert a 'RecipeCustomization' block into a list of bdcs 'Customization' directives processCustomization :: MonadIO m => RecipeCustomization -> m [Customization] processCustomization RecipeCustomization{..} = do currentTime <- utcTimeToPOSIXSeconds <$> liftIO getCurrentTime let hostnameCustomization = maybe [] (\h -> [processHostname currentTime h]) rcHostName sshKeyCustomizations = concatMap (processSshKey currentTime) rcSshKeys return $ hostnameCustomization ++ sshKeyCustomizations where processHostname :: POSIXTime -> String -> Customization processHostname currentTime hostname = let hostnameData = C8.pack $ hostname ++ "\n" hostnameFile = Files "/etc/hostname" "root" "root" (floor currentTime) Nothing (fromIntegral $ regularFileMode .|. 0o0644) (C8.length hostnameData) Nothing in WriteFile hostnameFile (Just hostnameData) processSshKey :: POSIXTime -> RecipeSshKey -> [Customization] processSshKey currentTime RecipeSshKey{..} = let keyData = C8.pack $ rcSshKey ++ "\n" keyDir = Files (T.pack $ "/home" rcSshUser ".ssh") (T.pack rcSshUser) (T.pack rcSshUser) (floor currentTime) Nothing (fromIntegral $ directoryMode .|. 0o0700) 0 Nothing keyFile = Files (T.pack $ "/home" rcSshUser ".ssh" "authorized_keys") (T.pack rcSshUser) (T.pack rcSshUser) (floor currentTime) Nothing (fromIntegral $ regularFileMode .|. 0o0644) (C8.length keyData) Nothing in [WriteFile keyDir Nothing, WriteFile keyFile (Just keyData)]