    {-# LANGUAGE DeriveDataTypeable, TypeFamilies #-}

    module Propellor.Property.Ssh (
    	installed,
    	restarted,
    	PubKeyText,
    	SshKeyType(..),
    	-- * Daemon configuration
    	sshdConfig,
    	ConfigKeyword,
    	setSshdConfigBool,
    	setSshdConfig,
    	RootLogin(..),
    	permitRootLogin,
    	passwordAuthentication,
    	noPasswords,
    	listenPort,
    	-- * Host keys
    	randomHostKeys,
    	hostKeys,
    	hostKey,
    	hostPubKey,
    	getHostPubKey,
    	-- * User keys and configuration
    	userKeys,
    	userKeyAt,
    	knownHost,
    	unknownHost,
    	authorizedKeysFrom,
    	unauthorizedKeysFrom,
    	authorizedKeys,
    	authorizedKey,
    	hasAuthorizedKeys,
    	getUserPubKeys,
    ) where

    import Propellor.Base
    import qualified Propellor.Property.File as File
    import qualified Propellor.Property.Service as Service
    import qualified Propellor.Property.Apt as Apt
    import Propellor.Property.User
    import Propellor.Types.Info

    import System.PosixCompat
    import qualified Data.Map as M
    import qualified Data.Set as S
    import qualified Data.Semigroup as Sem
    import Data.List

    installed :: Property UnixLike
    installed :: Property UnixLike
    installed = "ssh installed" Desc -> Property UnixLike -> Property UnixLike
    forall i. IsProp (Property i) => Desc -> Property i -> Property i
    ==> (Property DebianLike
    aptinstall Property DebianLike -> Property UnixLike -> Property UnixLike
    forall k ka kb (c :: k) (a :: ka) (b :: kb).
    (HasCallStack, SingKind 'KProxy, SingKind 'KProxy,
     DemoteRep 'KProxy ~ [MetaType], DemoteRep 'KProxy ~ [MetaType],
     SingI c) =>
    Property (MetaTypes a)
    -> Property (MetaTypes b) -> Property (MetaTypes c)
    `pickOS` Property UnixLike
    unsupportedOS)
      where
    	aptinstall :: Property DebianLike
    	aptinstall :: Property DebianLike
    aptinstall = [Desc] -> Property DebianLike
    Apt.installed ["ssh"]

    restarted :: Property DebianLike
    restarted :: Property DebianLike
    restarted = Desc -> Property DebianLike
    Service.restarted "ssh"

    sshBool :: Bool -> String
    sshBool :: Bool -> Desc
    sshBool True = "yes"
    sshBool False = "no"

    sshdConfig :: FilePath
    sshdConfig :: Desc
    sshdConfig = "/etc/ssh/sshd_config"

    type ConfigKeyword = String

    setSshdConfigBool :: ConfigKeyword -> Bool -> Property DebianLike
    setSshdConfigBool :: Desc -> Bool -> Property DebianLike
    setSshdConfigBool setting :: Desc
    setting allowed :: Bool
    allowed = Desc -> Desc -> Property DebianLike
    setSshdConfig Desc
    setting (Bool -> Desc
    sshBool Bool
    allowed)

    setSshdConfig :: ConfigKeyword -> String -> Property DebianLike
    setSshdConfig :: Desc -> Desc -> Property DebianLike
    setSshdConfig setting :: Desc
    setting v :: Desc
    v = Desc -> ([Desc] -> [Desc]) -> Desc -> Property UnixLike
    forall c.
    (FileContent c, Eq c) =>
    Desc -> (c -> c) -> Desc -> Property UnixLike
    File.fileProperty Desc
    desc [Desc] -> [Desc]
    f Desc
    sshdConfig
    	Property UnixLike
    -> Property DebianLike
    -> CombinedType (Property UnixLike) (Property DebianLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `onChange` Property DebianLike
    restarted
      where
    	desc :: Desc
    desc = [Desc] -> Desc
    unwords [ "ssh config:", Desc
    setting, Desc
    v ]
    	cfgline :: Desc
    cfgline = Desc
    setting Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ " " Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc
    v
    	wantedline :: Desc -> Bool
    wantedline s :: Desc
    s
    		| Desc
    s Desc -> Desc -> Bool
    forall a. Eq a => a -> a -> Bool
    == Desc
    cfgline = Bool
    True
    		| (Desc
    setting Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ " ") Desc -> Desc -> Bool
    forall a. Eq a => [a] -> [a] -> Bool
    `isPrefixOf` Desc
    s = Bool
    False
    		| Bool
    otherwise = Bool
    True
    	f :: [Desc] -> [Desc]
    f ls :: [Desc]
    ls
    		| Desc
    cfgline Desc -> [Desc] -> Bool
    forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
    `elem` [Desc]
    ls = (Desc -> Bool) -> [Desc] -> [Desc]
    forall a. (a -> Bool) -> [a] -> [a]
    filter Desc -> Bool
    wantedline [Desc]
    ls
    		| Bool
    otherwise = (Desc -> Bool) -> [Desc] -> [Desc]
    forall a. (a -> Bool) -> [a] -> [a]
    filter Desc -> Bool
    wantedline [Desc]
    ls [Desc] -> [Desc] -> [Desc]
    forall a. [a] -> [a] -> [a]
    ++ [Desc
    cfgline]

    data RootLogin
    	= RootLogin Bool  -- ^ allow or prevent root login
    	| WithoutPassword -- ^ disable password authentication for root, while allowing other authentication methods
    	| ForcedCommandsOnly -- ^ allow root login with public-key authentication, but only if a forced command has been specified for the public key

    permitRootLogin :: RootLogin -> Property DebianLike
    permitRootLogin :: RootLogin -> Property DebianLike
    permitRootLogin (RootLogin b :: Bool
    b) = Desc -> Bool -> Property DebianLike
    setSshdConfigBool "PermitRootLogin" Bool
    b
    permitRootLogin WithoutPassword = Desc -> Desc -> Property DebianLike
    setSshdConfig "PermitRootLogin" "without-password"
    permitRootLogin ForcedCommandsOnly = Desc -> Desc -> Property DebianLike
    setSshdConfig "PermitRootLogin" "forced-commands-only"

    passwordAuthentication :: Bool -> Property DebianLike
    passwordAuthentication :: Bool -> Property DebianLike
    passwordAuthentication = Desc -> Bool -> Property DebianLike
    setSshdConfigBool "PasswordAuthentication"

    -- | Configure ssh to not allow password logins.
    --
    -- To prevent lock-out, this is done only once root's
    -- authorized_keys is in place.
    noPasswords :: Property DebianLike
    noPasswords :: Property DebianLike
    noPasswords = IO Bool -> Property DebianLike -> Property DebianLike
    forall (p :: * -> *) i (m :: * -> *).
    (Checkable p i, LiftPropellor m) =>
    m Bool -> p i -> Property i
    check (User -> IO Bool
    hasAuthorizedKeys (Desc -> User
    User "root")) (Property DebianLike -> Property DebianLike)
    -> Property DebianLike -> Property DebianLike
    forall a b. (a -> b) -> a -> b
    $
    	Bool -> Property DebianLike
    passwordAuthentication Bool
    False

    dotDir :: User -> IO FilePath
    dotDir :: User -> IO Desc
    dotDir user :: User
    user = do
    	Desc
    h <- User -> IO Desc
    homedir User
    user
    	Desc -> IO Desc
    forall (m :: * -> *) a. Monad m => a -> m a
    return (Desc -> IO Desc) -> Desc -> IO Desc
    forall a b. (a -> b) -> a -> b
    $ Desc
    h Desc -> Desc -> Desc
    </> ".ssh"

    dotFile :: FilePath -> User -> IO FilePath
    dotFile :: Desc -> User -> IO Desc
    dotFile f :: Desc
    f user :: User
    user = do
    	Desc
    d <- User -> IO Desc
    dotDir User
    user
    	Desc -> IO Desc
    forall (m :: * -> *) a. Monad m => a -> m a
    return (Desc -> IO Desc) -> Desc -> IO Desc
    forall a b. (a -> b) -> a -> b
    $ Desc
    d Desc -> Desc -> Desc
    </> Desc
    f

    -- | Makes the ssh server listen on a given port, in addition to any other
    -- ports it is configured to listen on.
    --
    -- Revert to prevent it listening on a particular port.
    listenPort :: Port -> RevertableProperty DebianLike DebianLike
    listenPort :: Port -> RevertableProperty DebianLike DebianLike
    listenPort port :: Port
    port = CombinedType (Property UnixLike) (Property DebianLike)
    Property DebianLike
    enable Property DebianLike
    -> Property DebianLike -> RevertableProperty DebianLike DebianLike
    forall setupmetatypes undometatypes.
    Property setupmetatypes
    -> Property undometatypes
    -> RevertableProperty setupmetatypes undometatypes
    <!> CombinedType (Property UnixLike) (Property DebianLike)
    Property DebianLike
    disable
      where
    	portline :: Desc
    portline = "Port " Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Port -> Desc
    forall t. ConfigurableValue t => t -> Desc
    val Port
    port
    	enable :: CombinedType (Property UnixLike) (Property DebianLike)
    enable = Desc
    sshdConfig Desc -> Desc -> Property UnixLike
    `File.containsLine` Desc
    portline
    		Property UnixLike -> Desc -> Property UnixLike
    forall p. IsProp p => p -> Desc -> p
    `describe` ("ssh listening on " Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc
    portline)
    		Property UnixLike
    -> Property DebianLike
    -> CombinedType (Property UnixLike) (Property DebianLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `onChange` Property DebianLike
    restarted
    	disable :: CombinedType (Property UnixLike) (Property DebianLike)
    disable = Desc
    sshdConfig Desc -> Desc -> Property UnixLike
    `File.lacksLine` Desc
    portline
    		Property UnixLike -> Desc -> Property UnixLike
    forall p. IsProp p => p -> Desc -> p
    `describe` ("ssh not listening on " Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc
    portline)
    		Property UnixLike
    -> Property DebianLike
    -> CombinedType (Property UnixLike) (Property DebianLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `onChange` Property DebianLike
    restarted

    hasAuthorizedKeys :: User -> IO Bool
    hasAuthorizedKeys :: User -> IO Bool
    hasAuthorizedKeys = Desc -> IO Bool
    go (Desc -> IO Bool) -> (User -> IO Desc) -> User -> IO Bool
    forall (m :: * -> *) b c a.
    Monad m =>
    (b -> m c) -> (a -> m b) -> a -> m c
    <=< Desc -> User -> IO Desc
    dotFile "authorized_keys"
      where
    	go :: Desc -> IO Bool
    go f :: Desc
    f = Bool -> Bool
    not (Bool -> Bool) -> (Desc -> Bool) -> Desc -> Bool
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Desc -> Bool
    forall (t :: * -> *) a. Foldable t => t a -> Bool
    null (Desc -> Bool) -> IO Desc -> IO Bool
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> Desc -> IO Desc -> IO Desc
    forall (m :: * -> *) a. MonadCatch m => a -> m a -> m a
    catchDefaultIO "" (Desc -> IO Desc
    readFile Desc
    f)

    -- | Blows away existing host keys and make new ones.
    -- Useful for systems installed from an image that might reuse host keys.
    -- A flag file is used to only ever do this once.
    randomHostKeys :: Property DebianLike
    randomHostKeys :: Property DebianLike
    randomHostKeys = Property UnixLike -> Desc -> Property UnixLike
    forall i. Property i -> Desc -> Property i
    flagFile Property UnixLike
    prop "/etc/ssh/.unique_host_keys"
    	Property UnixLike
    -> Property DebianLike
    -> CombinedType (Property UnixLike) (Property DebianLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `onChange` Property DebianLike
    restarted
      where
    	prop :: Property UnixLike
    	prop :: Property UnixLike
    prop = Desc
    -> (OuterMetaTypesWitness
          '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
             'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
        -> Propellor Result)
    -> Property UnixLike
    forall k (metatypes :: k).
    SingI metatypes =>
    Desc
    -> (OuterMetaTypesWitness metatypes -> Propellor Result)
    -> Property (MetaTypes metatypes)
    property' "ssh random host keys" ((OuterMetaTypesWitness
        '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
           'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
      -> Propellor Result)
     -> Property UnixLike)
    -> (OuterMetaTypesWitness
          '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
             'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
        -> Propellor Result)
    -> Property UnixLike
    forall a b. (a -> b) -> a -> b
    $ \w :: OuterMetaTypesWitness
      '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
         'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
    w -> do
    		Propellor Bool -> Propellor ()
    forall (f :: * -> *) a. Functor f => f a -> f ()
    void (Propellor Bool -> Propellor ()) -> Propellor Bool -> Propellor ()
    forall a b. (a -> b) -> a -> b
    $ IO Bool -> Propellor Bool
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO (IO Bool -> Propellor Bool) -> IO Bool -> Propellor Bool
    forall a b. (a -> b) -> a -> b
    $ Desc -> [CommandParam] -> IO Bool
    boolSystem "sh"
    			[ Desc -> CommandParam
    Param "-c"
    			, Desc -> CommandParam
    Param "rm -f /etc/ssh/ssh_host_*"
    			]
    		OuterMetaTypesWitness
      '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
         'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
    -> Property UnixLike -> Propellor Result
    forall (inner :: [MetaType]) (outer :: [MetaType]).
    EnsurePropertyAllowed inner outer =>
    OuterMetaTypesWitness outer
    -> Property (MetaTypes inner) -> Propellor Result
    ensureProperty OuterMetaTypesWitness
      '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
         'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
    w (Property UnixLike -> Propellor Result)
    -> Property UnixLike -> Propellor Result
    forall a b. (a -> b) -> a -> b
    $ [Desc] -> UncheckedProperty UnixLike
    scriptProperty [ "DPKG_MAINTSCRIPT_NAME=postinst DPKG_MAINTSCRIPT_PACKAGE=openssh-server /var/lib/dpkg/info/openssh-server.postinst configure" ]
    			UncheckedProperty UnixLike -> Result -> Property UnixLike
    forall (p :: * -> *) i.
    Checkable p i =>
    p i -> Result -> Property i
    `assume` Result
    MadeChange

    -- | The text of a ssh public key, for example, "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIB3BJ2GqZiTR2LEoDXyYFgh/BduWefjdKXAsAtzS9zeI"
    type PubKeyText = String

    -- | Installs the specified list of ssh host keys.
    --
    -- The corresponding private keys come from the privdata.
    --
    -- Any host keys that are not in the list are removed from the host.
    hostKeys :: IsContext c => c -> [(SshKeyType, PubKeyText)] -> Property (HasInfo + DebianLike)
    hostKeys :: c -> [(SshKeyType, Desc)] -> Property (HasInfo + DebianLike)
    hostKeys ctx :: c
    ctx l :: [(SshKeyType, Desc)]
    l = Property
      (Sing '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    Property (HasInfo + DebianLike)
    go Property
      (Sing '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property DebianLike
    -> CombinedType
         (Property
            (Sing '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
         (Property DebianLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `before` Property DebianLike
    cleanup
      where
    	desc :: Desc
    desc = "ssh host keys configured " Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ [SshKeyType] -> Desc
    typelist (((SshKeyType, Desc) -> SshKeyType)
    -> [(SshKeyType, Desc)] -> [SshKeyType]
    forall a b. (a -> b) -> [a] -> [b]
    map (SshKeyType, Desc) -> SshKeyType
    forall a b. (a, b) -> a
    fst [(SshKeyType, Desc)]
    l)
    	go :: Property (HasInfo + DebianLike)
    	go :: Property (HasInfo + DebianLike)
    go = Desc
    -> Props
         (Sing '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property
         (Sing '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    forall k (metatypes :: k).
    SingI metatypes =>
    Desc
    -> Props (MetaTypes metatypes) -> Property (MetaTypes metatypes)
    propertyList Desc
    desc (Props
       (Sing '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
     -> Property (HasInfo + DebianLike))
    -> Props
         (Sing '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property (HasInfo + DebianLike)
    forall a b. (a -> b) -> a -> b
    $ [Property
       (Sing '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])]
    -> Props
         (Sing '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    forall k (metatypes :: k).
    [Property (MetaTypes metatypes)] -> Props (MetaTypes metatypes)
    toProps ([Property
        (Sing '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])]
     -> Props
          (Sing '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    -> [Property
          (Sing '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])]
    -> Props
         (Sing '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    forall a b. (a -> b) -> a -> b
    $ [Maybe
       (Property
          (Sing '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))]
    -> [Property
          (Sing '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])]
    forall a. [Maybe a] -> [a]
    catMaybes ([Maybe
        (Property
           (Sing '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))]
     -> [Property
           (Sing '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])])
    -> [Maybe
          (Property
             (Sing '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))]
    -> [Property
          (Sing '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])]
    forall a b. (a -> b) -> a -> b
    $
    		((SshKeyType, Desc)
     -> Maybe
          (Property
             (Sing '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])))
    -> [(SshKeyType, Desc)]
    -> [Maybe
          (Property
             (Sing '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))]
    forall a b. (a -> b) -> [a] -> [b]
    map (\(t :: SshKeyType
    t, pub :: Desc
    pub) -> Property
      (Sing '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Maybe
         (Property
            (Sing '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a. a -> Maybe a
    Just (Property
       (Sing '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
     -> Maybe
          (Property
             (Sing '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])))
    -> Property
         (Sing '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Maybe
         (Property
            (Sing '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a b. (a -> b) -> a -> b
    $ c -> SshKeyType -> Desc -> Property (HasInfo + DebianLike)
    forall c.
    IsContext c =>
    c -> SshKeyType -> Desc -> Property (HasInfo + DebianLike)
    hostKey c
    ctx SshKeyType
    t Desc
    pub) [(SshKeyType, Desc)]
    l
    	typelist :: [SshKeyType] -> Desc
    typelist tl :: [SshKeyType]
    tl = "(" Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ [Desc] -> Desc
    unwords ((SshKeyType -> Desc) -> [SshKeyType] -> [Desc]
    forall a b. (a -> b) -> [a] -> [b]
    map SshKeyType -> Desc
    fromKeyType [SshKeyType]
    tl) Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ ")"
    	alltypes :: [SshKeyType]
    alltypes = [SshKeyType
    forall a. Bounded a => a
    minBound..SshKeyType
    forall a. Bounded a => a
    maxBound]
    	staletypes :: [SshKeyType]
    staletypes = let have :: [SshKeyType]
    have = ((SshKeyType, Desc) -> SshKeyType)
    -> [(SshKeyType, Desc)] -> [SshKeyType]
    forall a b. (a -> b) -> [a] -> [b]
    map (SshKeyType, Desc) -> SshKeyType
    forall a b. (a, b) -> a
    fst [(SshKeyType, Desc)]
    l in (SshKeyType -> Bool) -> [SshKeyType] -> [SshKeyType]
    forall a. (a -> Bool) -> [a] -> [a]
    filter (SshKeyType -> [SshKeyType] -> Bool
    forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
    `notElem` [SshKeyType]
    have) [SshKeyType]
    alltypes
    	removestale :: Bool -> [Property DebianLike]
    	removestale :: Bool -> [Property DebianLike]
    removestale b :: Bool
    b = (SshKeyType -> Property DebianLike)
    -> [SshKeyType] -> [Property DebianLike]
    forall a b. (a -> b) -> [a] -> [b]
    map (Property UnixLike -> Property DebianLike
    forall (p :: * -> *) (untightened :: [MetaType])
           (tightened :: [MetaType]).
    (TightenTargets p, TightenTargetsAllowed untightened tightened,
     SingI tightened) =>
    p (MetaTypes untightened) -> p (MetaTypes tightened)
    tightenTargets (Property UnixLike -> Property DebianLike)
    -> (SshKeyType -> Property UnixLike)
    -> SshKeyType
    -> Property DebianLike
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Desc -> Property UnixLike
    File.notPresent (Desc -> Property UnixLike)
    -> (SshKeyType -> Desc) -> SshKeyType -> Property UnixLike
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (SshKeyType -> Bool -> Desc) -> Bool -> SshKeyType -> Desc
    forall a b c. (a -> b -> c) -> b -> a -> c
    flip SshKeyType -> Bool -> Desc
    keyFile Bool
    b) [SshKeyType]
    staletypes
    	cleanup :: Property DebianLike
    	cleanup :: Property DebianLike
    cleanup
    		| [SshKeyType] -> Bool
    forall (t :: * -> *) a. Foldable t => t a -> Bool
    null [SshKeyType]
    staletypes Bool -> Bool -> Bool
    || [(SshKeyType, Desc)] -> Bool
    forall (t :: * -> *) a. Foldable t => t a -> Bool
    null [(SshKeyType, Desc)]
    l = Property DebianLike
    forall k (t :: k). SingI t => Property (MetaTypes t)
    doNothing
    		| Bool
    otherwise =
    			Desc -> Props DebianLike -> Property DebianLike
    forall k (metatypes :: k).
    SingI metatypes =>
    Desc
    -> Props (MetaTypes metatypes) -> Property (MetaTypes metatypes)
    combineProperties ("any other ssh host keys removed " Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ [SshKeyType] -> Desc
    typelist [SshKeyType]
    staletypes)
    				([Property DebianLike] -> Props DebianLike
    forall k (metatypes :: k).
    [Property (MetaTypes metatypes)] -> Props (MetaTypes metatypes)
    toProps ([Property DebianLike] -> Props DebianLike)
    -> [Property DebianLike] -> Props DebianLike
    forall a b. (a -> b) -> a -> b
    $ Bool -> [Property DebianLike]
    removestale Bool
    True [Property DebianLike]
    -> [Property DebianLike] -> [Property DebianLike]
    forall a. [a] -> [a] -> [a]
    ++ Bool -> [Property DebianLike]
    removestale Bool
    False)
    				Property DebianLike
    -> Property DebianLike
    -> CombinedType (Property DebianLike) (Property DebianLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `onChange` Property DebianLike
    restarted

    -- | Installs a single ssh host key of a particular type.
    --
    -- The public key is provided to this function;
    -- the private key comes from the privdata;
    hostKey :: IsContext c => c -> SshKeyType -> PubKeyText -> Property (HasInfo + DebianLike)
    hostKey :: c -> SshKeyType -> Desc -> Property (HasInfo + DebianLike)
    hostKey context :: c
    context keytype :: SshKeyType
    keytype pub :: Desc
    pub = Property
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
            'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    go Property
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
            'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> Property DebianLike
    -> CombinedType
         (Property
            (MetaTypes
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
         (Property DebianLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `onChange` Property DebianLike
    restarted
      where
    	go :: Property
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
            'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    go = Desc
    -> Props
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> Property
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    forall k (metatypes :: k).
    SingI metatypes =>
    Desc
    -> Props (MetaTypes metatypes) -> Property (MetaTypes metatypes)
    combineProperties Desc
    desc (Props
       (MetaTypes
          '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
             'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
     -> Property
          (MetaTypes
             '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    -> Props
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> Property
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    forall a b. (a -> b) -> a -> b
    $ Props UnixLike
    props
    		Props UnixLike
    -> Property
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & SshKeyType -> Desc -> Property (HasInfo + UnixLike)
    hostPubKey SshKeyType
    keytype Desc
    pub
    		Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
            'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> Property UnixLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Property UnixLike
    installpub
    		Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
            'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> Property
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Property
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
            'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    Property (HasInfo + UnixLike)
    installpriv
    	desc :: Desc
    desc = "ssh host key configured (" Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ SshKeyType -> Desc
    fromKeyType SshKeyType
    keytype Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ ")"
    	keysrc :: Desc -> PrivDataField -> PrivDataSource
    keysrc ext :: Desc
    ext field :: PrivDataField
    field = PrivDataField -> Desc -> Desc -> PrivDataSource
    PrivDataSourceFileFromCommand PrivDataField
    field ("sshkey"Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++Desc
    ext)
    		("ssh-keygen -t " Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ SshKeyType -> Desc
    sshKeyTypeParam SshKeyType
    keytype Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ " -f sshkey")
    	installpub :: Property UnixLike
    	installpub :: Property UnixLike
    installpub = (Desc -> [Desc] -> Property UnixLike)
    -> Bool -> [Desc] -> Property UnixLike
    keywriter Desc -> [Desc] -> Property UnixLike
    File.hasContent Bool
    True (Desc -> [Desc]
    lines Desc
    pub)
    	installpriv :: Property (HasInfo + UnixLike)
    	installpriv :: Property (HasInfo + UnixLike)
    installpriv = PrivDataSource
    -> c
    -> (((PrivData -> Propellor Result) -> Propellor Result)
        -> Property
             (MetaTypes
                '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                   'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    -> Property
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    forall c s metatypes.
    (IsContext c, IsPrivDataSource s,
     IncludesInfo metatypes ~ 'True) =>
    s
    -> c
    -> (((PrivData -> Propellor Result) -> Propellor Result)
        -> Property metatypes)
    -> Property metatypes
    withPrivData (Desc -> PrivDataField -> PrivDataSource
    keysrc "" (SshKeyType -> Desc -> PrivDataField
    SshPrivKey SshKeyType
    keytype "")) c
    context ((((PrivData -> Propellor Result) -> Propellor Result)
      -> Property
           (MetaTypes
              '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                 'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
     -> Property (HasInfo + UnixLike))
    -> (((PrivData -> Propellor Result) -> Propellor Result)
        -> Property
             (MetaTypes
                '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                   'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    -> Property (HasInfo + UnixLike)
    forall a b. (a -> b) -> a -> b
    $ \getkey :: (PrivData -> Propellor Result) -> Propellor Result
    getkey ->
    		Desc
    -> (OuterMetaTypesWitness
          '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
             'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
        -> Propellor Result)
    -> Property
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    forall k (metatypes :: k).
    SingI metatypes =>
    Desc
    -> (OuterMetaTypesWitness metatypes -> Propellor Result)
    -> Property (MetaTypes metatypes)
    property' Desc
    desc ((OuterMetaTypesWitness
        '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
           'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
      -> Propellor Result)
     -> Property
          (MetaTypes
             '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    -> (OuterMetaTypesWitness
          '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
             'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
        -> Propellor Result)
    -> Property
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    forall a b. (a -> b) -> a -> b
    $ \w :: OuterMetaTypesWitness
      '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
         'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
    w -> (PrivData -> Propellor Result) -> Propellor Result
    getkey ((PrivData -> Propellor Result) -> Propellor Result)
    -> (PrivData -> Propellor Result) -> Propellor Result
    forall a b. (a -> b) -> a -> b
    $
    			OuterMetaTypesWitness
      '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
         'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
    -> Property UnixLike -> Propellor Result
    forall (inner :: [MetaType]) (outer :: [MetaType]).
    EnsurePropertyAllowed inner outer =>
    OuterMetaTypesWitness outer
    -> Property (MetaTypes inner) -> Propellor Result
    ensureProperty OuterMetaTypesWitness
      '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
         'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
    w
    				(Property UnixLike -> Propellor Result)
    -> (PrivData -> Property UnixLike) -> PrivData -> Propellor Result
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (Desc -> [Desc] -> Property UnixLike)
    -> Bool -> [Desc] -> Property UnixLike
    keywriter Desc -> [Desc] -> Property UnixLike
    File.hasContentProtected Bool
    False
    				([Desc] -> Property UnixLike)
    -> (PrivData -> [Desc]) -> PrivData -> Property UnixLike
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . PrivData -> [Desc]
    privDataLines
    	keywriter :: (Desc -> [Desc] -> Property UnixLike)
    -> Bool -> [Desc] -> Property UnixLike
    keywriter p :: Desc -> [Desc] -> Property UnixLike
    p ispub :: Bool
    ispub keylines :: [Desc]
    keylines = do
    		let f :: Desc
    f = SshKeyType -> Bool -> Desc
    keyFile SshKeyType
    keytype Bool
    ispub
    		Desc -> [Desc] -> Property UnixLike
    p Desc
    f ([Desc] -> [Desc]
    keyFileContent [Desc]
    keylines)

    -- Make sure that there is a newline at the end;
    -- ssh requires this for some types of private keys.
    keyFileContent :: [String] -> [File.Line]
    keyFileContent :: [Desc] -> [Desc]
    keyFileContent keylines :: [Desc]
    keylines = [Desc]
    keylines [Desc] -> [Desc] -> [Desc]
    forall a. [a] -> [a] -> [a]
    ++ [""]

    keyFile :: SshKeyType -> Bool -> FilePath
    keyFile :: SshKeyType -> Bool -> Desc
    keyFile keytype :: SshKeyType
    keytype ispub :: Bool
    ispub = "/etc/ssh/ssh_host_" Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ SshKeyType -> Desc
    fromKeyType SshKeyType
    keytype Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ "_key" Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc
    ext
      where
    	ext :: Desc
    ext = if Bool
    ispub then ".pub" else ""

    -- | Indicates the host key that is used by a Host, but does not actually
    -- configure the host to use it. Normally this does not need to be used;
    -- use 'hostKey' instead.
    hostPubKey :: SshKeyType -> PubKeyText -> Property (HasInfo + UnixLike)
    hostPubKey :: SshKeyType -> Desc -> Property (HasInfo + UnixLike)
    hostPubKey t :: SshKeyType
    t = Desc -> HostKeyInfo -> Property (HasInfo + UnixLike)
    forall v. IsInfo v => Desc -> v -> Property (HasInfo + UnixLike)
    pureInfoProperty "ssh pubkey known" (HostKeyInfo
     -> Property
          (MetaTypes
             '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    -> (Desc -> HostKeyInfo)
    -> Desc
    -> Property
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Map SshKeyType Desc -> HostKeyInfo
    HostKeyInfo (Map SshKeyType Desc -> HostKeyInfo)
    -> (Desc -> Map SshKeyType Desc) -> Desc -> HostKeyInfo
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . SshKeyType -> Desc -> Map SshKeyType Desc
    forall k a. k -> a -> Map k a
    M.singleton SshKeyType
    t

    getHostPubKey :: Propellor (M.Map SshKeyType PubKeyText)
    getHostPubKey :: Propellor (Map SshKeyType Desc)
    getHostPubKey = HostKeyInfo -> Map SshKeyType Desc
    fromHostKeyInfo (HostKeyInfo -> Map SshKeyType Desc)
    -> Propellor HostKeyInfo -> Propellor (Map SshKeyType Desc)
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> Propellor HostKeyInfo
    forall v. IsInfo v => Propellor v
    askInfo

    newtype HostKeyInfo = HostKeyInfo
    	{ HostKeyInfo -> Map SshKeyType Desc
    fromHostKeyInfo :: M.Map SshKeyType PubKeyText }
    	deriving (HostKeyInfo -> HostKeyInfo -> Bool
    (HostKeyInfo -> HostKeyInfo -> Bool)
    -> (HostKeyInfo -> HostKeyInfo -> Bool) -> Eq HostKeyInfo
    forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
    /= :: HostKeyInfo -> HostKeyInfo -> Bool
    $c/= :: HostKeyInfo -> HostKeyInfo -> Bool
    == :: HostKeyInfo -> HostKeyInfo -> Bool
    $c== :: HostKeyInfo -> HostKeyInfo -> Bool
    Eq, Eq HostKeyInfo
    Eq HostKeyInfo =>
    (HostKeyInfo -> HostKeyInfo -> Ordering)
    -> (HostKeyInfo -> HostKeyInfo -> Bool)
    -> (HostKeyInfo -> HostKeyInfo -> Bool)
    -> (HostKeyInfo -> HostKeyInfo -> Bool)
    -> (HostKeyInfo -> HostKeyInfo -> Bool)
    -> (HostKeyInfo -> HostKeyInfo -> HostKeyInfo)
    -> (HostKeyInfo -> HostKeyInfo -> HostKeyInfo)
    -> Ord HostKeyInfo
    HostKeyInfo -> HostKeyInfo -> Bool
    HostKeyInfo -> HostKeyInfo -> Ordering
    HostKeyInfo -> HostKeyInfo -> HostKeyInfo
    forall a.
    Eq a =>
    (a -> a -> Ordering)
    -> (a -> a -> Bool)
    -> (a -> a -> Bool)
    -> (a -> a -> Bool)
    -> (a -> a -> Bool)
    -> (a -> a -> a)
    -> (a -> a -> a)
    -> Ord a
    min :: HostKeyInfo -> HostKeyInfo -> HostKeyInfo
    $cmin :: HostKeyInfo -> HostKeyInfo -> HostKeyInfo
    max :: HostKeyInfo -> HostKeyInfo -> HostKeyInfo
    $cmax :: HostKeyInfo -> HostKeyInfo -> HostKeyInfo
    >= :: HostKeyInfo -> HostKeyInfo -> Bool
    $c>= :: HostKeyInfo -> HostKeyInfo -> Bool
    > :: HostKeyInfo -> HostKeyInfo -> Bool
    $c> :: HostKeyInfo -> HostKeyInfo -> Bool
    <= :: HostKeyInfo -> HostKeyInfo -> Bool
    $c<= :: HostKeyInfo -> HostKeyInfo -> Bool
    < :: HostKeyInfo -> HostKeyInfo -> Bool
    $c< :: HostKeyInfo -> HostKeyInfo -> Bool
    compare :: HostKeyInfo -> HostKeyInfo -> Ordering
    $ccompare :: HostKeyInfo -> HostKeyInfo -> Ordering
    $cp1Ord :: Eq HostKeyInfo
    Ord, Typeable, Int -> HostKeyInfo -> Desc -> Desc
    [HostKeyInfo] -> Desc -> Desc
    HostKeyInfo -> Desc
    (Int -> HostKeyInfo -> Desc -> Desc)
    -> (HostKeyInfo -> Desc)
    -> ([HostKeyInfo] -> Desc -> Desc)
    -> Show HostKeyInfo
    forall a.
    (Int -> a -> Desc -> Desc)
    -> (a -> Desc) -> ([a] -> Desc -> Desc) -> Show a
    showList :: [HostKeyInfo] -> Desc -> Desc
    $cshowList :: [HostKeyInfo] -> Desc -> Desc
    show :: HostKeyInfo -> Desc
    $cshow :: HostKeyInfo -> Desc
    showsPrec :: Int -> HostKeyInfo -> Desc -> Desc
    $cshowsPrec :: Int -> HostKeyInfo -> Desc -> Desc
    Show)

    instance IsInfo HostKeyInfo where
    	propagateInfo :: HostKeyInfo -> PropagateInfo
    propagateInfo _ = Bool -> PropagateInfo
    PropagateInfo Bool
    False

    instance Sem.Semigroup HostKeyInfo where
    	HostKeyInfo old :: Map SshKeyType Desc
    old <> :: HostKeyInfo -> HostKeyInfo -> HostKeyInfo
    <> HostKeyInfo new :: Map SshKeyType Desc
    new =
    		-- new first because union prefers values from the first
    		-- parameter when there is a duplicate key
    		Map SshKeyType Desc -> HostKeyInfo
    HostKeyInfo (Map SshKeyType Desc
    new Map SshKeyType Desc -> Map SshKeyType Desc -> Map SshKeyType Desc
    forall k a. Ord k => Map k a -> Map k a -> Map k a
    `M.union` Map SshKeyType Desc
    old)

    instance Monoid HostKeyInfo where
    	mempty :: HostKeyInfo
    mempty = Map SshKeyType Desc -> HostKeyInfo
    HostKeyInfo Map SshKeyType Desc
    forall k a. Map k a
    M.empty
    	mappend :: HostKeyInfo -> HostKeyInfo -> HostKeyInfo
    mappend = HostKeyInfo -> HostKeyInfo -> HostKeyInfo
    forall a. Semigroup a => a -> a -> a
    (Sem.<>)

    userPubKeys :: User -> [(SshKeyType, PubKeyText)] -> Property (HasInfo + UnixLike)
    userPubKeys :: User -> [(SshKeyType, Desc)] -> Property (HasInfo + UnixLike)
    userPubKeys u :: User
    u@(User n :: Desc
    n) l :: [(SshKeyType, Desc)]
    l = Desc -> UserKeyInfo -> Property (HasInfo + UnixLike)
    forall v. IsInfo v => Desc -> v -> Property (HasInfo + UnixLike)
    pureInfoProperty ("ssh pubkey for " Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc
    n) (UserKeyInfo -> Property (HasInfo + UnixLike))
    -> UserKeyInfo -> Property (HasInfo + UnixLike)
    forall a b. (a -> b) -> a -> b
    $
    	Map User (Set (SshKeyType, Desc)) -> UserKeyInfo
    UserKeyInfo (User -> Set (SshKeyType, Desc) -> Map User (Set (SshKeyType, Desc))
    forall k a. k -> a -> Map k a
    M.singleton User
    u ([(SshKeyType, Desc)] -> Set (SshKeyType, Desc)
    forall a. Ord a => [a] -> Set a
    S.fromList [(SshKeyType, Desc)]
    l))

    getUserPubKeys :: User -> Propellor [(SshKeyType, PubKeyText)]
    getUserPubKeys :: User -> Propellor [(SshKeyType, Desc)]
    getUserPubKeys u :: User
    u = [(SshKeyType, Desc)]
    -> (Set (SshKeyType, Desc) -> [(SshKeyType, Desc)])
    -> Maybe (Set (SshKeyType, Desc))
    -> [(SshKeyType, Desc)]
    forall b a. b -> (a -> b) -> Maybe a -> b
    maybe [] Set (SshKeyType, Desc) -> [(SshKeyType, Desc)]
    forall a. Set a -> [a]
    S.toList (Maybe (Set (SshKeyType, Desc)) -> [(SshKeyType, Desc)])
    -> (UserKeyInfo -> Maybe (Set (SshKeyType, Desc)))
    -> UserKeyInfo
    -> [(SshKeyType, Desc)]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . User
    -> Map User (Set (SshKeyType, Desc))
    -> Maybe (Set (SshKeyType, Desc))
    forall k a. Ord k => k -> Map k a -> Maybe a
    M.lookup User
    u (Map User (Set (SshKeyType, Desc))
     -> Maybe (Set (SshKeyType, Desc)))
    -> (UserKeyInfo -> Map User (Set (SshKeyType, Desc)))
    -> UserKeyInfo
    -> Maybe (Set (SshKeyType, Desc))
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . UserKeyInfo -> Map User (Set (SshKeyType, Desc))
    fromUserKeyInfo (UserKeyInfo -> [(SshKeyType, Desc)])
    -> Propellor UserKeyInfo -> Propellor [(SshKeyType, Desc)]
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> Propellor UserKeyInfo
    forall v. IsInfo v => Propellor v
    askInfo

    newtype UserKeyInfo = UserKeyInfo
    	{ UserKeyInfo -> Map User (Set (SshKeyType, Desc))
    fromUserKeyInfo :: M.Map User (S.Set (SshKeyType, PubKeyText)) }
    	deriving (UserKeyInfo -> UserKeyInfo -> Bool
    (UserKeyInfo -> UserKeyInfo -> Bool)
    -> (UserKeyInfo -> UserKeyInfo -> Bool) -> Eq UserKeyInfo
    forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
    /= :: UserKeyInfo -> UserKeyInfo -> Bool
    $c/= :: UserKeyInfo -> UserKeyInfo -> Bool
    == :: UserKeyInfo -> UserKeyInfo -> Bool
    $c== :: UserKeyInfo -> UserKeyInfo -> Bool
    Eq, Eq UserKeyInfo
    Eq UserKeyInfo =>
    (UserKeyInfo -> UserKeyInfo -> Ordering)
    -> (UserKeyInfo -> UserKeyInfo -> Bool)
    -> (UserKeyInfo -> UserKeyInfo -> Bool)
    -> (UserKeyInfo -> UserKeyInfo -> Bool)
    -> (UserKeyInfo -> UserKeyInfo -> Bool)
    -> (UserKeyInfo -> UserKeyInfo -> UserKeyInfo)
    -> (UserKeyInfo -> UserKeyInfo -> UserKeyInfo)
    -> Ord UserKeyInfo
    UserKeyInfo -> UserKeyInfo -> Bool
    UserKeyInfo -> UserKeyInfo -> Ordering
    UserKeyInfo -> UserKeyInfo -> UserKeyInfo
    forall a.
    Eq a =>
    (a -> a -> Ordering)
    -> (a -> a -> Bool)
    -> (a -> a -> Bool)
    -> (a -> a -> Bool)
    -> (a -> a -> Bool)
    -> (a -> a -> a)
    -> (a -> a -> a)
    -> Ord a
    min :: UserKeyInfo -> UserKeyInfo -> UserKeyInfo
    $cmin :: UserKeyInfo -> UserKeyInfo -> UserKeyInfo
    max :: UserKeyInfo -> UserKeyInfo -> UserKeyInfo
    $cmax :: UserKeyInfo -> UserKeyInfo -> UserKeyInfo
    >= :: UserKeyInfo -> UserKeyInfo -> Bool
    $c>= :: UserKeyInfo -> UserKeyInfo -> Bool
    > :: UserKeyInfo -> UserKeyInfo -> Bool
    $c> :: UserKeyInfo -> UserKeyInfo -> Bool
    <= :: UserKeyInfo -> UserKeyInfo -> Bool
    $c<= :: UserKeyInfo -> UserKeyInfo -> Bool
    < :: UserKeyInfo -> UserKeyInfo -> Bool
    $c< :: UserKeyInfo -> UserKeyInfo -> Bool
    compare :: UserKeyInfo -> UserKeyInfo -> Ordering
    $ccompare :: UserKeyInfo -> UserKeyInfo -> Ordering
    $cp1Ord :: Eq UserKeyInfo
    Ord, Typeable, Int -> UserKeyInfo -> Desc -> Desc
    [UserKeyInfo] -> Desc -> Desc
    UserKeyInfo -> Desc
    (Int -> UserKeyInfo -> Desc -> Desc)
    -> (UserKeyInfo -> Desc)
    -> ([UserKeyInfo] -> Desc -> Desc)
    -> Show UserKeyInfo
    forall a.
    (Int -> a -> Desc -> Desc)
    -> (a -> Desc) -> ([a] -> Desc -> Desc) -> Show a
    showList :: [UserKeyInfo] -> Desc -> Desc
    $cshowList :: [UserKeyInfo] -> Desc -> Desc
    show :: UserKeyInfo -> Desc
    $cshow :: UserKeyInfo -> Desc
    showsPrec :: Int -> UserKeyInfo -> Desc -> Desc
    $cshowsPrec :: Int -> UserKeyInfo -> Desc -> Desc
    Show)

    instance IsInfo UserKeyInfo where
    	propagateInfo :: UserKeyInfo -> PropagateInfo
    propagateInfo _ = Bool -> PropagateInfo
    PropagateInfo Bool
    False

    instance Sem.Semigroup UserKeyInfo where
    	UserKeyInfo old :: Map User (Set (SshKeyType, Desc))
    old <> :: UserKeyInfo -> UserKeyInfo -> UserKeyInfo
    <> UserKeyInfo new :: Map User (Set (SshKeyType, Desc))
    new =
    		Map User (Set (SshKeyType, Desc)) -> UserKeyInfo
    UserKeyInfo ((Set (SshKeyType, Desc)
     -> Set (SshKeyType, Desc) -> Set (SshKeyType, Desc))
    -> Map User (Set (SshKeyType, Desc))
    -> Map User (Set (SshKeyType, Desc))
    -> Map User (Set (SshKeyType, Desc))
    forall k a. Ord k => (a -> a -> a) -> Map k a -> Map k a -> Map k a
    M.unionWith Set (SshKeyType, Desc)
    -> Set (SshKeyType, Desc) -> Set (SshKeyType, Desc)
    forall a. Ord a => Set a -> Set a -> Set a
    S.union Map User (Set (SshKeyType, Desc))
    old Map User (Set (SshKeyType, Desc))
    new)

    instance Monoid UserKeyInfo where
    	mempty :: UserKeyInfo
    mempty = Map User (Set (SshKeyType, Desc)) -> UserKeyInfo
    UserKeyInfo Map User (Set (SshKeyType, Desc))
    forall k a. Map k a
    M.empty
    	mappend :: UserKeyInfo -> UserKeyInfo -> UserKeyInfo
    mappend = UserKeyInfo -> UserKeyInfo -> UserKeyInfo
    forall a. Semigroup a => a -> a -> a
    (Sem.<>)

    -- | Sets up a user with the specified public keys, and the corresponding
    -- private keys from the privdata.
    --
    -- The public keys are added to the Info, so other properties like
    -- `authorizedKeysFrom` can use them.
    userKeys :: IsContext c => User -> c -> [(SshKeyType, PubKeyText)] -> Property (HasInfo + UnixLike)
    userKeys :: User -> c -> [(SshKeyType, Desc)] -> Property (HasInfo + UnixLike)
    userKeys user :: User
    user@(User name :: Desc
    name) context :: c
    context ks :: [(SshKeyType, Desc)]
    ks = Desc
    -> Props
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> Property
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    forall k (metatypes :: k).
    SingI metatypes =>
    Desc
    -> Props (MetaTypes metatypes) -> Property (MetaTypes metatypes)
    combineProperties Desc
    desc (Props
       (MetaTypes
          '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
             'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
     -> Property (HasInfo + UnixLike))
    -> Props
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> Property (HasInfo + UnixLike)
    forall a b. (a -> b) -> a -> b
    $ [Property
       (MetaTypes
          '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
             'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])]
    -> Props
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    forall k (metatypes :: k).
    [Property (MetaTypes metatypes)] -> Props (MetaTypes metatypes)
    toProps ([Property
        (MetaTypes
           '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
              'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])]
     -> Props
          (MetaTypes
             '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    -> [Property
          (MetaTypes
             '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])]
    -> Props
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    forall a b. (a -> b) -> a -> b
    $
    	User -> [(SshKeyType, Desc)] -> Property (HasInfo + UnixLike)
    userPubKeys User
    user [(SshKeyType, Desc)]
    ks Property
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
            'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> [Property
          (MetaTypes
             '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])]
    -> [Property
          (MetaTypes
             '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])]
    forall a. a -> [a] -> [a]
    : ((SshKeyType, Desc)
     -> Property
          (MetaTypes
             '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    -> [(SshKeyType, Desc)]
    -> [Property
          (MetaTypes
             '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])]
    forall a b. (a -> b) -> [a] -> [b]
    map (Maybe Desc
    -> User -> c -> (SshKeyType, Desc) -> Property (HasInfo + UnixLike)
    forall c.
    IsContext c =>
    Maybe Desc
    -> User -> c -> (SshKeyType, Desc) -> Property (HasInfo + UnixLike)
    userKeyAt Maybe Desc
    forall a. Maybe a
    Nothing User
    user c
    context) [(SshKeyType, Desc)]
    ks
      where
    	desc :: Desc
    desc = [Desc] -> Desc
    unwords
    		[ Desc
    name
    		, "has ssh key"
    		, "(" Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ [Desc] -> Desc
    unwords (((SshKeyType, Desc) -> Desc) -> [(SshKeyType, Desc)] -> [Desc]
    forall a b. (a -> b) -> [a] -> [b]
    map (SshKeyType -> Desc
    fromKeyType (SshKeyType -> Desc)
    -> ((SshKeyType, Desc) -> SshKeyType) -> (SshKeyType, Desc) -> Desc
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (SshKeyType, Desc) -> SshKeyType
    forall a b. (a, b) -> a
    fst) [(SshKeyType, Desc)]
    ks) Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ ")"
    		]

    -- | Sets up a user with the specified pubic key, and a private
    -- key from the privdata.
    --
    -- A FilePath can be specified to write the key to somewhere other than
    -- the default locations. Allows a user to have multiple keys for
    -- different roles.
    --
    -- When the FilePath is relative, is put inside the User's
    -- ~/.ssh/ directory.
    userKeyAt :: IsContext c => Maybe FilePath -> User -> c -> (SshKeyType, PubKeyText) -> Property (HasInfo + UnixLike)
    userKeyAt :: Maybe Desc
    -> User -> c -> (SshKeyType, Desc) -> Property (HasInfo + UnixLike)
    userKeyAt dest :: Maybe Desc
    dest user :: User
    user@(User u :: Desc
    u) context :: c
    context (keytype :: SshKeyType
    keytype, pubkeytext :: Desc
    pubkeytext) =
    	Desc
    -> Props
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> Property
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    forall k (metatypes :: k).
    SingI metatypes =>
    Desc
    -> Props (MetaTypes metatypes) -> Property (MetaTypes metatypes)
    combineProperties Desc
    desc (Props
       (MetaTypes
          '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
             'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
     -> Property (HasInfo + UnixLike))
    -> Props
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> Property (HasInfo + UnixLike)
    forall a b. (a -> b) -> a -> b
    $ Props UnixLike
    props
    		Props UnixLike
    -> Property UnixLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Property UnixLike
    pubkey
    		Props UnixLike
    -> Property
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Property
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
            'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    Property (HasInfo + UnixLike)
    privkey
      where
    	desc :: Desc
    desc = [Desc] -> Desc
    unwords ([Desc] -> Desc) -> [Desc] -> Desc
    forall a b. (a -> b) -> a -> b
    $ [Maybe Desc] -> [Desc]
    forall a. [Maybe a] -> [a]
    catMaybes
    		[ Desc -> Maybe Desc
    forall a. a -> Maybe a
    Just Desc
    u
    		, Desc -> Maybe Desc
    forall a. a -> Maybe a
    Just "has ssh key"
    		, Maybe Desc
    dest
    		, Desc -> Maybe Desc
    forall a. a -> Maybe a
    Just (Desc -> Maybe Desc) -> Desc -> Maybe Desc
    forall a b. (a -> b) -> a -> b
    $ "(" Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ SshKeyType -> Desc
    fromKeyType SshKeyType
    keytype Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ ")"
    		]
    	pubkey :: Property UnixLike
    	pubkey :: Property UnixLike
    pubkey = Desc
    -> (OuterMetaTypesWitness
          '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
             'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
        -> Propellor Result)
    -> Property UnixLike
    forall k (metatypes :: k).
    SingI metatypes =>
    Desc
    -> (OuterMetaTypesWitness metatypes -> Propellor Result)
    -> Property (MetaTypes metatypes)
    property' Desc
    desc ((OuterMetaTypesWitness
        '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
           'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
      -> Propellor Result)
     -> Property UnixLike)
    -> (OuterMetaTypesWitness
          '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
             'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
        -> Propellor Result)
    -> Property UnixLike
    forall a b. (a -> b) -> a -> b
    $ \w :: OuterMetaTypesWitness
      '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
         'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
    w ->
    		OuterMetaTypesWitness
      '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
         'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
    -> Property UnixLike -> Propellor Result
    forall (inner :: [MetaType]) (outer :: [MetaType]).
    EnsurePropertyAllowed inner outer =>
    OuterMetaTypesWitness outer
    -> Property (MetaTypes inner) -> Propellor Result
    ensureProperty OuterMetaTypesWitness
      '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
         'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
    w (Property UnixLike -> Propellor Result)
    -> Propellor (Property UnixLike) -> Propellor Result
    forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
    =<< (Desc -> [Desc] -> Property UnixLike)
    -> Desc -> [Desc] -> Propellor (Property UnixLike)
    installprop Desc -> [Desc] -> Property UnixLike
    File.hasContent ".pub" [Desc
    pubkeytext]
    	privkey :: Property (HasInfo + UnixLike)
    	privkey :: Property (HasInfo + UnixLike)
    privkey = PrivDataField
    -> c
    -> (((PrivData -> Propellor Result) -> Propellor Result)
        -> Property
             (MetaTypes
                '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                   'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    -> Property
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    forall c s metatypes.
    (IsContext c, IsPrivDataSource s,
     IncludesInfo metatypes ~ 'True) =>
    s
    -> c
    -> (((PrivData -> Propellor Result) -> Propellor Result)
        -> Property metatypes)
    -> Property metatypes
    withPrivData (SshKeyType -> Desc -> PrivDataField
    SshPrivKey SshKeyType
    keytype Desc
    u) c
    context ((PrivData -> Propellor Result) -> Propellor Result)
    -> Property
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    ((PrivData -> Propellor Result) -> Propellor Result)
    -> Property (HasInfo + UnixLike)
    privkey'
    	privkey' :: ((PrivData -> Propellor Result) -> Propellor Result) -> Property (HasInfo + UnixLike)
    	privkey' :: ((PrivData -> Propellor Result) -> Propellor Result)
    -> Property (HasInfo + UnixLike)
    privkey' getkey :: (PrivData -> Propellor Result) -> Propellor Result
    getkey = Desc
    -> (OuterMetaTypesWitness
          '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
             'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
        -> Propellor Result)
    -> Property
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    forall k (metatypes :: k).
    SingI metatypes =>
    Desc
    -> (OuterMetaTypesWitness metatypes -> Propellor Result)
    -> Property (MetaTypes metatypes)
    property' Desc
    desc ((OuterMetaTypesWitness
        '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
           'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
      -> Propellor Result)
     -> Property (HasInfo + UnixLike))
    -> (OuterMetaTypesWitness
          '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
             'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
        -> Propellor Result)
    -> Property (HasInfo + UnixLike)
    forall a b. (a -> b) -> a -> b
    $ \w :: OuterMetaTypesWitness
      '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
         'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
    w -> (PrivData -> Propellor Result) -> Propellor Result
    getkey ((PrivData -> Propellor Result) -> Propellor Result)
    -> (PrivData -> Propellor Result) -> Propellor Result
    forall a b. (a -> b) -> a -> b
    $ \k :: PrivData
    k ->
    		OuterMetaTypesWitness
      '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
         'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
    -> Property UnixLike -> Propellor Result
    forall (inner :: [MetaType]) (outer :: [MetaType]).
    EnsurePropertyAllowed inner outer =>
    OuterMetaTypesWitness outer
    -> Property (MetaTypes inner) -> Propellor Result
    ensureProperty OuterMetaTypesWitness
      '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
         'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
    w
    			(Property UnixLike -> Propellor Result)
    -> Propellor (Property UnixLike) -> Propellor Result
    forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
    =<< (Desc -> [Desc] -> Property UnixLike)
    -> Desc -> [Desc] -> Propellor (Property UnixLike)
    installprop Desc -> [Desc] -> Property UnixLike
    File.hasContentProtected "" (PrivData -> [Desc]
    privDataLines PrivData
    k)
    	installprop :: (Desc -> [Desc] -> Property UnixLike)
    -> Desc -> [Desc] -> Propellor (Property UnixLike)
    installprop writer :: Desc -> [Desc] -> Property UnixLike
    writer ext :: Desc
    ext key :: [Desc]
    key = do
    		Desc
    f <- IO Desc -> Propellor Desc
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO (IO Desc -> Propellor Desc) -> IO Desc -> Propellor Desc
    forall a b. (a -> b) -> a -> b
    $ Desc -> IO Desc
    keyfile Desc
    ext
    		Property UnixLike -> Propellor (Property UnixLike)
    forall (m :: * -> *) a. Monad m => a -> m a
    return (Property UnixLike -> Propellor (Property UnixLike))
    -> Property UnixLike -> Propellor (Property UnixLike)
    forall a b. (a -> b) -> a -> b
    $ Desc -> Props UnixLike -> Property UnixLike
    forall k (metatypes :: k).
    SingI metatypes =>
    Desc
    -> Props (MetaTypes metatypes) -> Property (MetaTypes metatypes)
    combineProperties Desc
    desc (Props UnixLike -> Property UnixLike)
    -> Props UnixLike -> Property UnixLike
    forall a b. (a -> b) -> a -> b
    $ Props UnixLike
    props
    			Props UnixLike
    -> Property UnixLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> Property UnixLike
    File.dirExists (Desc -> Desc
    takeDirectory Desc
    f)
    			Props UnixLike
    -> Property UnixLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> [Desc] -> Property UnixLike
    writer Desc
    f ([Desc] -> [Desc]
    keyFileContent [Desc]
    key)
    			Props UnixLike
    -> Property UnixLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> User -> Group -> Property UnixLike
    File.ownerGroup Desc
    f User
    user (User -> Group
    userGroup User
    user)
    			Props UnixLike
    -> Property UnixLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> User -> Group -> Property UnixLike
    File.ownerGroup (Desc -> Desc
    takeDirectory Desc
    f) User
    user (User -> Group
    userGroup User
    user)
    	keyfile :: Desc -> IO Desc
    keyfile ext :: Desc
    ext = case Maybe Desc
    dest of
    		Nothing -> Desc -> IO Desc
    relhomessh (Desc -> IO Desc) -> Desc -> IO Desc
    forall a b. (a -> b) -> a -> b
    $ "id_" Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ SshKeyType -> Desc
    fromKeyType SshKeyType
    keytype Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc
    ext
    		Just f :: Desc
    f
    			| Desc -> Bool
    isRelative Desc
    f -> Desc -> IO Desc
    relhomessh (Desc
    f Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc
    ext)
    			| Bool
    otherwise -> Desc -> IO Desc
    forall (m :: * -> *) a. Monad m => a -> m a
    return (Desc
    f Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc
    ext)
    	relhomessh :: Desc -> IO Desc
    relhomessh f :: Desc
    f = do
    		Desc
    home <- UserEntry -> Desc
    homeDirectory (UserEntry -> Desc) -> IO UserEntry -> IO Desc
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> Desc -> IO UserEntry
    getUserEntryForName Desc
    u
    		Desc -> IO Desc
    forall (m :: * -> *) a. Monad m => a -> m a
    return (Desc -> IO Desc) -> Desc -> IO Desc
    forall a b. (a -> b) -> a -> b
    $ Desc
    home Desc -> Desc -> Desc
    </> ".ssh" Desc -> Desc -> Desc
    </> Desc
    f

    fromKeyType :: SshKeyType -> String
    fromKeyType :: SshKeyType -> Desc
    fromKeyType SshRsa = "rsa"
    fromKeyType SshDsa = "dsa"
    fromKeyType SshEcdsa = "ecdsa"
    fromKeyType SshEd25519 = "ed25519"

    -- | Puts some host's ssh public key(s), as set using `hostPubKey`
    -- or `hostKey` into the known_hosts file for a user.
    knownHost :: [Host] -> HostName -> User -> Property UnixLike
    knownHost :: [Host] -> Desc -> User -> Property UnixLike
    knownHost hosts :: [Host]
    hosts hn :: Desc
    hn user :: User
    user@(User u :: Desc
    u) = Desc
    -> (OuterMetaTypesWitness
          '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
             'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
        -> Propellor Result)
    -> Property UnixLike
    forall k (metatypes :: k).
    SingI metatypes =>
    Desc
    -> (OuterMetaTypesWitness metatypes -> Propellor Result)
    -> Property (MetaTypes metatypes)
    property' Desc
    desc ((OuterMetaTypesWitness
        '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
           'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
      -> Propellor Result)
     -> Property UnixLike)
    -> (OuterMetaTypesWitness
          '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
             'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
        -> Propellor Result)
    -> Property UnixLike
    forall a b. (a -> b) -> a -> b
    $ \w :: OuterMetaTypesWitness
      '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
         'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
    w ->
    	OuterMetaTypesWitness
      '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
         'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
    -> [Desc] -> Propellor Result
    go OuterMetaTypesWitness
      '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
         'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
    w ([Desc] -> Propellor Result)
    -> Propellor [Desc] -> Propellor Result
    forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
    =<< [Host] -> Desc -> Propellor [Desc]
    knownHostLines [Host]
    hosts Desc
    hn
      where
    	desc :: Desc
    desc = Desc
    u Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ " knows ssh key for " Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc
    hn

    	go :: OuterMetaTypesWitness
      '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
         'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
    -> [Desc] -> Propellor Result
    go _ [] = do
    		Desc -> Propellor ()
    forall (m :: * -> *). MonadIO m => Desc -> m ()
    warningMessage (Desc -> Propellor ()) -> Desc -> Propellor ()
    forall a b. (a -> b) -> a -> b
    $ "no configured ssh host keys for " Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc
    hn
    		Result -> Propellor Result
    forall (m :: * -> *) a. Monad m => a -> m a
    return Result
    FailedChange
    	go w :: OuterMetaTypesWitness
      '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
         'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
    w ls :: [Desc]
    ls = do
    		Desc
    f <- IO Desc -> Propellor Desc
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO (IO Desc -> Propellor Desc) -> IO Desc -> Propellor Desc
    forall a b. (a -> b) -> a -> b
    $ Desc -> User -> IO Desc
    dotFile "known_hosts" User
    user
    		OuterMetaTypesWitness
      '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
         'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
    -> Property UnixLike -> Propellor Result
    forall (inner :: [MetaType]) (outer :: [MetaType]).
    EnsurePropertyAllowed inner outer =>
    OuterMetaTypesWitness outer
    -> Property (MetaTypes inner) -> Propellor Result
    ensureProperty OuterMetaTypesWitness
      '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
         'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
    w (Property UnixLike -> Propellor Result)
    -> Property UnixLike -> Propellor Result
    forall a b. (a -> b) -> a -> b
    $ User -> Desc -> Property UnixLike -> Property UnixLike
    modKnownHost User
    user Desc
    f (Property UnixLike -> Property UnixLike)
    -> Property UnixLike -> Property UnixLike
    forall a b. (a -> b) -> a -> b
    $
    			Desc
    f Desc -> [Desc] -> Property UnixLike
    `File.containsLines` [Desc]
    ls
    				Property UnixLike
    -> Property UnixLike
    -> CombinedType (Property UnixLike) (Property UnixLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `requires` Desc -> Property UnixLike
    File.dirExists (Desc -> Desc
    takeDirectory Desc
    f)

    -- | Reverts `knownHost`
    unknownHost :: [Host] -> HostName -> User -> Property UnixLike
    unknownHost :: [Host] -> Desc -> User -> Property UnixLike
    unknownHost hosts :: [Host]
    hosts hn :: Desc
    hn user :: User
    user@(User u :: Desc
    u) = Desc
    -> (OuterMetaTypesWitness
          '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
             'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
        -> Propellor Result)
    -> Property UnixLike
    forall k (metatypes :: k).
    SingI metatypes =>
    Desc
    -> (OuterMetaTypesWitness metatypes -> Propellor Result)
    -> Property (MetaTypes metatypes)
    property' Desc
    desc ((OuterMetaTypesWitness
        '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
           'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
      -> Propellor Result)
     -> Property UnixLike)
    -> (OuterMetaTypesWitness
          '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
             'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
        -> Propellor Result)
    -> Property UnixLike
    forall a b. (a -> b) -> a -> b
    $ \w :: OuterMetaTypesWitness
      '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
         'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
    w ->
    	OuterMetaTypesWitness
      '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
         'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
    -> [Desc] -> Propellor Result
    go OuterMetaTypesWitness
      '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
         'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
    w ([Desc] -> Propellor Result)
    -> Propellor [Desc] -> Propellor Result
    forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
    =<< [Host] -> Desc -> Propellor [Desc]
    knownHostLines [Host]
    hosts Desc
    hn
      where
    	desc :: Desc
    desc = Desc
    u Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ " does not know ssh key for " Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc
    hn

    	go :: OuterMetaTypesWitness
      '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
         'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
    -> [Desc] -> Propellor Result
    go _ [] = Result -> Propellor Result
    forall (m :: * -> *) a. Monad m => a -> m a
    return Result
    NoChange
    	go w :: OuterMetaTypesWitness
      '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
         'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
    w ls :: [Desc]
    ls = do
    		Desc
    f <- IO Desc -> Propellor Desc
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO (IO Desc -> Propellor Desc) -> IO Desc -> Propellor Desc
    forall a b. (a -> b) -> a -> b
    $ Desc -> User -> IO Desc
    dotFile "known_hosts" User
    user
    		Propellor Bool
    -> (Propellor Result, Propellor Result) -> Propellor Result
    forall (m :: * -> *) a. Monad m => m Bool -> (m a, m a) -> m a
    ifM (IO Bool -> Propellor Bool
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO (IO Bool -> Propellor Bool) -> IO Bool -> Propellor Bool
    forall a b. (a -> b) -> a -> b
    $ Desc -> IO Bool
    doesFileExist Desc
    f)
    			( OuterMetaTypesWitness
      '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
         'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
    -> Property UnixLike -> Propellor Result
    forall (inner :: [MetaType]) (outer :: [MetaType]).
    EnsurePropertyAllowed inner outer =>
    OuterMetaTypesWitness outer
    -> Property (MetaTypes inner) -> Propellor Result
    ensureProperty OuterMetaTypesWitness
      '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
         'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
    w (Property UnixLike -> Propellor Result)
    -> Property UnixLike -> Propellor Result
    forall a b. (a -> b) -> a -> b
    $ User -> Desc -> Property UnixLike -> Property UnixLike
    modKnownHost User
    user Desc
    f (Property UnixLike -> Property UnixLike)
    -> Property UnixLike -> Property UnixLike
    forall a b. (a -> b) -> a -> b
    $
    				Desc
    f Desc -> [Desc] -> Property UnixLike
    `File.lacksLines` [Desc]
    ls
    			, Result -> Propellor Result
    forall (m :: * -> *) a. Monad m => a -> m a
    return Result
    NoChange
    			)

    knownHostLines :: [Host] -> HostName -> Propellor [File.Line]
    knownHostLines :: [Host] -> Desc -> Propellor [Desc]
    knownHostLines hosts :: [Host]
    hosts hn :: Desc
    hn = Maybe (Map SshKeyType Desc) -> [Desc]
    keylines (Maybe (Map SshKeyType Desc) -> [Desc])
    -> Propellor (Maybe (Map SshKeyType Desc)) -> Propellor [Desc]
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> [Host]
    -> Desc
    -> Propellor (Map SshKeyType Desc)
    -> Propellor (Maybe (Map SshKeyType Desc))
    forall a. [Host] -> Desc -> Propellor a -> Propellor (Maybe a)
    fromHost [Host]
    hosts Desc
    hn Propellor (Map SshKeyType Desc)
    getHostPubKey
      where
    	keylines :: Maybe (Map SshKeyType Desc) -> [Desc]
    keylines (Just m :: Map SshKeyType Desc
    m) = (Desc -> Desc) -> [Desc] -> [Desc]
    forall a b. (a -> b) -> [a] -> [b]
    map (\k :: Desc
    k -> Desc
    hn Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ " " Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc
    k) (Map SshKeyType Desc -> [Desc]
    forall k a. Map k a -> [a]
    M.elems Map SshKeyType Desc
    m)
    	keylines Nothing = []

    modKnownHost :: User -> FilePath -> Property UnixLike -> Property UnixLike
    modKnownHost :: User -> Desc -> Property UnixLike -> Property UnixLike
    modKnownHost user :: User
    user f :: Desc
    f p :: Property UnixLike
    p = Property UnixLike
    p
    	Property UnixLike
    -> Property UnixLike
    -> CombinedType (Property UnixLike) (Property UnixLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `before` Desc -> User -> Group -> Property UnixLike
    File.ownerGroup Desc
    f User
    user (User -> Group
    userGroup User
    user)
    	Property UnixLike
    -> Property UnixLike
    -> CombinedType (Property UnixLike) (Property UnixLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `before` Desc -> User -> Group -> Property UnixLike
    File.ownerGroup (Desc -> Desc
    takeDirectory Desc
    f) User
    user (User -> Group
    userGroup User
    user)

    -- | Ensures that a local user's authorized_keys contains lines allowing
    -- logins from a remote user on the specified Host.
    --
    -- The ssh keys of the remote user can be set using `userKeys`
    --
    -- Any other lines in the authorized_keys file are preserved as-is.
    authorizedKeysFrom :: User -> (User, Host) -> Property UnixLike
    localuser :: User
    localuser@(User ln :: Desc
    ln) authorizedKeysFrom :: User -> (User, Host) -> Property UnixLike
    `authorizedKeysFrom` (remoteuser :: User
    remoteuser@(User rn :: Desc
    rn), remotehost :: Host
    remotehost) =
    	Desc
    -> (OuterMetaTypesWitness
          '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
             'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
        -> Propellor Result)
    -> Property UnixLike
    forall k (metatypes :: k).
    SingI metatypes =>
    Desc
    -> (OuterMetaTypesWitness metatypes -> Propellor Result)
    -> Property (MetaTypes metatypes)
    property' Desc
    desc (\w :: OuterMetaTypesWitness
      '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
         'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
    w -> OuterMetaTypesWitness
      '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
         'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
    -> [Desc] -> Propellor Result
    go OuterMetaTypesWitness
      '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
         'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
    w ([Desc] -> Propellor Result)
    -> Propellor [Desc] -> Propellor Result
    forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
    =<< User -> Host -> Propellor [Desc]
    authorizedKeyLines User
    remoteuser Host
    remotehost)
      where
    	remote :: Desc
    remote = Desc
    rn Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ "@" Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Host -> Desc
    hostName Host
    remotehost
    	desc :: Desc
    desc = Desc
    ln Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ " authorized_keys from " Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc
    remote

    	go :: OuterMetaTypesWitness
      '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
         'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
    -> [Desc] -> Propellor Result
    go _ [] = do
    		Desc -> Propellor ()
    forall (m :: * -> *). MonadIO m => Desc -> m ()
    warningMessage (Desc -> Propellor ()) -> Desc -> Propellor ()
    forall a b. (a -> b) -> a -> b
    $ "no configured ssh user keys for " Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc
    remote
    		Result -> Propellor Result
    forall (m :: * -> *) a. Monad m => a -> m a
    return Result
    FailedChange
    	go w :: OuterMetaTypesWitness
      '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
         'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
    w ls :: [Desc]
    ls = OuterMetaTypesWitness
      '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
         'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
    -> Property UnixLike -> Propellor Result
    forall (inner :: [MetaType]) (outer :: [MetaType]).
    EnsurePropertyAllowed inner outer =>
    OuterMetaTypesWitness outer
    -> Property (MetaTypes inner) -> Propellor Result
    ensureProperty OuterMetaTypesWitness
      '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
         'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
    w (Property UnixLike -> Propellor Result)
    -> Property UnixLike -> Propellor Result
    forall a b. (a -> b) -> a -> b
    $ Desc -> Props UnixLike -> Property UnixLike
    forall k (metatypes :: k).
    SingI metatypes =>
    Desc
    -> Props (MetaTypes metatypes) -> Property (MetaTypes metatypes)
    combineProperties Desc
    desc (Props UnixLike -> Property UnixLike)
    -> Props UnixLike -> Property UnixLike
    forall a b. (a -> b) -> a -> b
    $ [Property UnixLike] -> Props UnixLike
    forall k (metatypes :: k).
    [Property (MetaTypes metatypes)] -> Props (MetaTypes metatypes)
    toProps ([Property UnixLike] -> Props UnixLike)
    -> [Property UnixLike] -> Props UnixLike
    forall a b. (a -> b) -> a -> b
    $
    		(Desc -> Property UnixLike) -> [Desc] -> [Property UnixLike]
    forall a b. (a -> b) -> [a] -> [b]
    map (RevertableProperty UnixLike UnixLike -> Property UnixLike
    forall setupmetatypes undometatypes.
    RevertableProperty setupmetatypes undometatypes
    -> Property setupmetatypes
    setupRevertableProperty (RevertableProperty UnixLike UnixLike -> Property UnixLike)
    -> (Desc -> RevertableProperty UnixLike UnixLike)
    -> Desc
    -> Property UnixLike
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . User -> Desc -> RevertableProperty UnixLike UnixLike
    authorizedKey User
    localuser) [Desc]
    ls

    -- | Reverts `authorizedKeysFrom`
    unauthorizedKeysFrom :: User -> (User, Host) -> Property UnixLike
    localuser :: User
    localuser@(User ln :: Desc
    ln) unauthorizedKeysFrom :: User -> (User, Host) -> Property UnixLike
    `unauthorizedKeysFrom` (remoteuser :: User
    remoteuser@(User rn :: Desc
    rn), remotehost :: Host
    remotehost) =
    	Desc
    -> (OuterMetaTypesWitness
          '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
             'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
        -> Propellor Result)
    -> Property UnixLike
    forall k (metatypes :: k).
    SingI metatypes =>
    Desc
    -> (OuterMetaTypesWitness metatypes -> Propellor Result)
    -> Property (MetaTypes metatypes)
    property' Desc
    desc (\w :: OuterMetaTypesWitness
      '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
         'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
    w -> OuterMetaTypesWitness
      '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
         'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
    -> [Desc] -> Propellor Result
    go OuterMetaTypesWitness
      '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
         'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
    w ([Desc] -> Propellor Result)
    -> Propellor [Desc] -> Propellor Result
    forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
    =<< User -> Host -> Propellor [Desc]
    authorizedKeyLines User
    remoteuser Host
    remotehost)
      where
    	remote :: Desc
    remote = Desc
    rn Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ "@" Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Host -> Desc
    hostName Host
    remotehost
    	desc :: Desc
    desc = Desc
    ln Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ " unauthorized_keys from " Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc
    remote

    	go :: OuterMetaTypesWitness
      '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
         'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
    -> [Desc] -> Propellor Result
    go _ [] = Result -> Propellor Result
    forall (m :: * -> *) a. Monad m => a -> m a
    return Result
    NoChange
    	go w :: OuterMetaTypesWitness
      '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
         'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
    w ls :: [Desc]
    ls = OuterMetaTypesWitness
      '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
         'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
    -> Property UnixLike -> Propellor Result
    forall (inner :: [MetaType]) (outer :: [MetaType]).
    EnsurePropertyAllowed inner outer =>
    OuterMetaTypesWitness outer
    -> Property (MetaTypes inner) -> Propellor Result
    ensureProperty OuterMetaTypesWitness
      '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
         'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
    w (Property UnixLike -> Propellor Result)
    -> Property UnixLike -> Propellor Result
    forall a b. (a -> b) -> a -> b
    $ Desc -> Props UnixLike -> Property UnixLike
    forall k (metatypes :: k).
    SingI metatypes =>
    Desc
    -> Props (MetaTypes metatypes) -> Property (MetaTypes metatypes)
    combineProperties Desc
    desc (Props UnixLike -> Property UnixLike)
    -> Props UnixLike -> Property UnixLike
    forall a b. (a -> b) -> a -> b
    $ [Property UnixLike] -> Props UnixLike
    forall k (metatypes :: k).
    [Property (MetaTypes metatypes)] -> Props (MetaTypes metatypes)
    toProps ([Property UnixLike] -> Props UnixLike)
    -> [Property UnixLike] -> Props UnixLike
    forall a b. (a -> b) -> a -> b
    $
    		(Desc -> Property UnixLike) -> [Desc] -> [Property UnixLike]
    forall a b. (a -> b) -> [a] -> [b]
    map (RevertableProperty UnixLike UnixLike -> Property UnixLike
    forall setupmetatypes undometatypes.
    RevertableProperty setupmetatypes undometatypes
    -> Property undometatypes
    undoRevertableProperty (RevertableProperty UnixLike UnixLike -> Property UnixLike)
    -> (Desc -> RevertableProperty UnixLike UnixLike)
    -> Desc
    -> Property UnixLike
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . User -> Desc -> RevertableProperty UnixLike UnixLike
    authorizedKey User
    localuser) [Desc]
    ls

    authorizedKeyLines :: User -> Host -> Propellor [File.Line]
    authorizedKeyLines :: User -> Host -> Propellor [Desc]
    authorizedKeyLines remoteuser :: User
    remoteuser remotehost :: Host
    remotehost =
    	((SshKeyType, Desc) -> Desc) -> [(SshKeyType, Desc)] -> [Desc]
    forall a b. (a -> b) -> [a] -> [b]
    map (SshKeyType, Desc) -> Desc
    forall a b. (a, b) -> b
    snd ([(SshKeyType, Desc)] -> [Desc])
    -> Propellor [(SshKeyType, Desc)] -> Propellor [Desc]
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> Host
    -> Propellor [(SshKeyType, Desc)] -> Propellor [(SshKeyType, Desc)]
    forall a. Host -> Propellor a -> Propellor a
    fromHost' Host
    remotehost (User -> Propellor [(SshKeyType, Desc)]
    getUserPubKeys User
    remoteuser)

    -- | Makes a user have authorized_keys from the PrivData
    --
    -- This removes any other lines from the file.
    authorizedKeys :: IsContext c => User -> c -> Property (HasInfo + UnixLike)
    authorizedKeys :: User -> c -> Property (HasInfo + UnixLike)
    authorizedKeys user :: User
    user@(User u :: Desc
    u) context :: c
    context = PrivDataField
    -> c
    -> (((PrivData -> Propellor Result) -> Propellor Result)
        -> Property
             (MetaTypes
                '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                   'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    -> Property
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    forall c s metatypes.
    (IsContext c, IsPrivDataSource s,
     IncludesInfo metatypes ~ 'True) =>
    s
    -> c
    -> (((PrivData -> Propellor Result) -> Propellor Result)
        -> Property metatypes)
    -> Property metatypes
    withPrivData (Desc -> PrivDataField
    SshAuthorizedKeys Desc
    u) c
    context ((((PrivData -> Propellor Result) -> Propellor Result)
      -> Property
           (MetaTypes
              '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                 'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
     -> Property (HasInfo + UnixLike))
    -> (((PrivData -> Propellor Result) -> Propellor Result)
        -> Property
             (MetaTypes
                '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                   'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    -> Property (HasInfo + UnixLike)
    forall a b. (a -> b) -> a -> b
    $ \get :: (PrivData -> Propellor Result) -> Propellor Result
    get ->
    	Desc
    -> (OuterMetaTypesWitness
          '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
             'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
        -> Propellor Result)
    -> Property
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    forall k (metatypes :: k).
    SingI metatypes =>
    Desc
    -> (OuterMetaTypesWitness metatypes -> Propellor Result)
    -> Property (MetaTypes metatypes)
    property' Desc
    desc ((OuterMetaTypesWitness
        '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
           'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
      -> Propellor Result)
     -> Property
          (MetaTypes
             '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    -> (OuterMetaTypesWitness
          '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
             'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
        -> Propellor Result)
    -> Property
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    forall a b. (a -> b) -> a -> b
    $ \w :: OuterMetaTypesWitness
      '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
         'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
    w -> (PrivData -> Propellor Result) -> Propellor Result
    get ((PrivData -> Propellor Result) -> Propellor Result)
    -> (PrivData -> Propellor Result) -> Propellor Result
    forall a b. (a -> b) -> a -> b
    $ \v :: PrivData
    v -> do
    		Desc
    f <- IO Desc -> Propellor Desc
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO (IO Desc -> Propellor Desc) -> IO Desc -> Propellor Desc
    forall a b. (a -> b) -> a -> b
    $ Desc -> User -> IO Desc
    dotFile "authorized_keys" User
    user
    		OuterMetaTypesWitness
      '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
         'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
    -> Property UnixLike -> Propellor Result
    forall (inner :: [MetaType]) (outer :: [MetaType]).
    EnsurePropertyAllowed inner outer =>
    OuterMetaTypesWitness outer
    -> Property (MetaTypes inner) -> Propellor Result
    ensureProperty OuterMetaTypesWitness
      '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
         'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
    w (Property UnixLike -> Propellor Result)
    -> Property UnixLike -> Propellor Result
    forall a b. (a -> b) -> a -> b
    $ Desc -> Props UnixLike -> Property UnixLike
    forall k (metatypes :: k).
    SingI metatypes =>
    Desc
    -> Props (MetaTypes metatypes) -> Property (MetaTypes metatypes)
    combineProperties Desc
    desc (Props UnixLike -> Property UnixLike)
    -> Props UnixLike -> Property UnixLike
    forall a b. (a -> b) -> a -> b
    $ Props UnixLike
    props
    			Props UnixLike
    -> Property UnixLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> [Desc] -> Property UnixLike
    File.hasContentProtected Desc
    f ([Desc] -> [Desc]
    keyFileContent (PrivData -> [Desc]
    privDataLines PrivData
    v))
    			Props UnixLike
    -> Property UnixLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> User -> Group -> Property UnixLike
    File.ownerGroup Desc
    f User
    user (User -> Group
    userGroup User
    user)
    			Props UnixLike
    -> Property UnixLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> User -> Group -> Property UnixLike
    File.ownerGroup (Desc -> Desc
    takeDirectory Desc
    f) User
    user (User -> Group
    userGroup User
    user)
      where
    	desc :: Desc
    desc = Desc
    u Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ " has authorized_keys"

    -- | Ensures that a user's authorized_keys contains a line.
    -- Any other lines in the file are preserved as-is.
    authorizedKey :: User -> String -> RevertableProperty UnixLike UnixLike
    authorizedKey :: User -> Desc -> RevertableProperty UnixLike UnixLike
    authorizedKey user :: User
    user@(User u :: Desc
    u) l :: Desc
    l = Property UnixLike
    add Property UnixLike
    -> Property UnixLike -> RevertableProperty UnixLike UnixLike
    forall setupmetatypes undometatypes.
    Property setupmetatypes
    -> Property undometatypes
    -> RevertableProperty setupmetatypes undometatypes
    <!> Property UnixLike
    remove
      where
    	add :: Property UnixLike
    add = Desc
    -> (OuterMetaTypesWitness
          '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
             'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
        -> Propellor Result)
    -> Property UnixLike
    forall k (metatypes :: k).
    SingI metatypes =>
    Desc
    -> (OuterMetaTypesWitness metatypes -> Propellor Result)
    -> Property (MetaTypes metatypes)
    property' (Desc
    u Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ " has authorized_keys") ((OuterMetaTypesWitness
        '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
           'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
      -> Propellor Result)
     -> Property UnixLike)
    -> (OuterMetaTypesWitness
          '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
             'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
        -> Propellor Result)
    -> Property UnixLike
    forall a b. (a -> b) -> a -> b
    $ \w :: OuterMetaTypesWitness
      '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
         'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
    w -> do
    		Desc
    f <- IO Desc -> Propellor Desc
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO (IO Desc -> Propellor Desc) -> IO Desc -> Propellor Desc
    forall a b. (a -> b) -> a -> b
    $ Desc -> User -> IO Desc
    dotFile "authorized_keys" User
    user
    		OuterMetaTypesWitness
      '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
         'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
    -> Property UnixLike -> Propellor Result
    forall (inner :: [MetaType]) (outer :: [MetaType]).
    EnsurePropertyAllowed inner outer =>
    OuterMetaTypesWitness outer
    -> Property (MetaTypes inner) -> Propellor Result
    ensureProperty OuterMetaTypesWitness
      '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
         'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
    w (Property UnixLike -> Propellor Result)
    -> Property UnixLike -> Propellor Result
    forall a b. (a -> b) -> a -> b
    $ Desc -> User -> Property UnixLike -> Property UnixLike
    modAuthorizedKey Desc
    f User
    user (Property UnixLike -> Property UnixLike)
    -> Property UnixLike -> Property UnixLike
    forall a b. (a -> b) -> a -> b
    $
    			Desc
    f Desc -> Desc -> Property UnixLike
    `File.containsLine` Desc
    l
    				Property UnixLike
    -> Property UnixLike
    -> CombinedType (Property UnixLike) (Property UnixLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `requires` Desc -> Property UnixLike
    File.dirExists (Desc -> Desc
    takeDirectory Desc
    f)
    	remove :: Property UnixLike
    remove = Desc
    -> (OuterMetaTypesWitness
          '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
             'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
        -> Propellor Result)
    -> Property UnixLike
    forall k (metatypes :: k).
    SingI metatypes =>
    Desc
    -> (OuterMetaTypesWitness metatypes -> Propellor Result)
    -> Property (MetaTypes metatypes)
    property' (Desc
    u Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ " lacks authorized_keys") ((OuterMetaTypesWitness
        '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
           'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
      -> Propellor Result)
     -> Property UnixLike)
    -> (OuterMetaTypesWitness
          '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
             'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
        -> Propellor Result)
    -> Property UnixLike
    forall a b. (a -> b) -> a -> b
    $ \w :: OuterMetaTypesWitness
      '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
         'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
    w -> do
    		Desc
    f <- IO Desc -> Propellor Desc
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO (IO Desc -> Propellor Desc) -> IO Desc -> Propellor Desc
    forall a b. (a -> b) -> a -> b
    $ Desc -> User -> IO Desc
    dotFile "authorized_keys" User
    user
    		Propellor Bool
    -> (Propellor Result, Propellor Result) -> Propellor Result
    forall (m :: * -> *) a. Monad m => m Bool -> (m a, m a) -> m a
    ifM (IO Bool -> Propellor Bool
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO (IO Bool -> Propellor Bool) -> IO Bool -> Propellor Bool
    forall a b. (a -> b) -> a -> b
    $ Desc -> IO Bool
    doesFileExist Desc
    f)
    			( OuterMetaTypesWitness
      '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
         'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
    -> Property UnixLike -> Propellor Result
    forall (inner :: [MetaType]) (outer :: [MetaType]).
    EnsurePropertyAllowed inner outer =>
    OuterMetaTypesWitness outer
    -> Property (MetaTypes inner) -> Propellor Result
    ensureProperty OuterMetaTypesWitness
      '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
         'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
    w (Property UnixLike -> Propellor Result)
    -> Property UnixLike -> Propellor Result
    forall a b. (a -> b) -> a -> b
    $ Desc -> User -> Property UnixLike -> Property UnixLike
    modAuthorizedKey Desc
    f User
    user (Property UnixLike -> Property UnixLike)
    -> Property UnixLike -> Property UnixLike
    forall a b. (a -> b) -> a -> b
    $
    				Desc
    f Desc -> Desc -> Property UnixLike
    `File.lacksLine` Desc
    l
    			, Result -> Propellor Result
    forall (m :: * -> *) a. Monad m => a -> m a
    return Result
    NoChange
    			)

    modAuthorizedKey :: FilePath -> User -> Property UnixLike -> Property UnixLike
    modAuthorizedKey :: Desc -> User -> Property UnixLike -> Property UnixLike
    modAuthorizedKey f :: Desc
    f user :: User
    user p :: Property UnixLike
    p = Property UnixLike
    p
    	Property UnixLike
    -> Property UnixLike
    -> CombinedType (Property UnixLike) (Property UnixLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `before` Desc -> FileMode -> Property UnixLike
    File.mode Desc
    f ([FileMode] -> FileMode
    combineModes [FileMode
    ownerWriteMode, FileMode
    ownerReadMode])
    	Property UnixLike
    -> Property UnixLike
    -> CombinedType (Property UnixLike) (Property UnixLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `before` Desc -> User -> Group -> Property UnixLike
    File.ownerGroup Desc
    f User
    user (User -> Group
    userGroup User
    user)
    	Property UnixLike
    -> Property UnixLike
    -> CombinedType (Property UnixLike) (Property UnixLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `before` Desc -> User -> Group -> Property UnixLike
    File.ownerGroup (Desc -> Desc
    takeDirectory Desc
    f) User
    user (User -> Group
    userGroup User
    user)