smith-cli-0.0.1: src/Smith/Cli/Command/Issue.hs
-- |
-- Command for requesting a certificate.
--
{-# LANGUAGE OverloadedStrings #-}
module Smith.Cli.Command.Issue (
-- * Entry point
issue
-- * Errors
, IssueError (..)
, renderIssueError
) where
import Control.Monad.IO.Class (MonadIO (..))
import Control.Monad.Trans.Bifunctor (BifunctorTrans (..))
import Control.Monad.Trans.Except (ExceptT (..))
import Data.Foldable (for_)
import Data.Text (Text)
import qualified Data.Text as Text
import qualified Smith.Cli.Agent as Agent
import Smith.Cli.Data.Program (Program (..))
import Smith.Cli.KeyPair (Comment (..), EncodedRSAKeyPair (..))
import qualified Smith.Cli.KeyPair as KeyPair
import Smith.Client (Smith (..))
import qualified Smith.Client as Smith
import Smith.Client.Error (SmithError (..), ErrorCode (..))
import Smith.Client.Data.CertificateRequest (Principal (..), PublicKey (..), CertificateRequest (..))
import Smith.Client.Data.Environment (Environment (..))
import qualified System.Posix.Process as Posix
-- |
-- Requests a certificate, registering it with ssh-agent if possible.
--
-- If an ssh-agent is not currently running, it is started with
-- output to 'System.IO.stdout' suitable for use with eval.
--
-- If a program is provided, the program is executed with an
-- ssh-agent configured.
--
issue :: Smith -> Environment -> [Principal] -> Maybe Program -> ExceptT IssueError IO ()
issue smith environment principals program = do
agent <- liftIO Agent.connect >>= maybe (left AgentNotAvailableError) pure
keys <- liftIO KeyPair.newRSAKeyPair
encoded <- maybe (left KeyGenerationError) pure $
KeyPair.encodeRSAKeyPair (Comment . mconcat $ ["smith-", getEnvironment environment]) keys
certificate <- firstT SmithIssueError .
Smith.runRequestT smith . Smith.issue $
CertificateRequest
(PublicKey $ sshRSAPublicKey encoded)
principals
environment
Nothing
firstT IssueAddKeyError $
Agent.addKey agent certificate keys
liftIO $ for_ program exec
exec :: Program -> IO a
exec (Program command arguments) =
Posix.executeFile
(Text.unpack command)
True
(Text.unpack <$> arguments)
Nothing
data IssueError =
SmithIssueError SmithError
| AgentNotAvailableError
| KeyGenerationError
| IssueAddKeyError Agent.AddKeyError
deriving (Eq, Show)
renderIssueError :: IssueError -> Text
renderIssueError e =
case e of
SmithIssueError err ->
renderSmithError err
AgentNotAvailableError ->
"ssh-agent was not available, ensure it is running and SSH_AUTH_SOCK is set."
KeyGenerationError ->
"We couldn't generate a key-pair using OpenSSL for this access request, this is very unusual, try again and please raise a support issue if this persists."
IssueAddKeyError Agent.IncompleteKeyPair ->
"An invalid key-pair has been generated (via OpenSSL), this very unusual, try again and please raise a support issue if this persists."
IssueAddKeyError Agent.InvalidCertificate ->
"Smith has generated an invalid certificate, please ensure you are running a compatible client version. If running the correct client version please raise a support issue."
IssueAddKeyError Agent.CouldNotAddKey ->
"Smith has generated a key but could not add it to your ssh-agent. Please raise a support issue, and include your ssh-agent implementation and version."
IssueAddKeyError Agent.CouldNotAddCertificate ->
"Smith has generated a certificate but could not add it to your ssh-agent. Please raise a support issue, and include your ssh-agent implementation and version. Note that if you are using the gnome ssh-agent instead of openssh, it does not support certificates."
IssueAddKeyError (Agent.AddKeyProtocolError _) ->
"Smith had a problem talking to your ssh-agent and could not add key or certificate. Please raise a support issue, and include your ssh-agent implementation and version."
renderSmithError :: SmithError -> Text
renderSmithError e =
case e of
-- FUTURE: debug mode that prints message.
-- FUTURE: Handle specific error codes for better error messages.
SmithApplicationError code _message ->
mconcat ["There was an error performing your request [", getErrorCode code, "]."]
-- FUTURE: debug mode that prints message.
SmithAuthorizationError code _message ->
mconcat ["You are not authorized to perform this request [", getErrorCode code, "]."]
SmithAuthenticationError _err ->
mconcat ["Smith could not authenticate you, please check your credentials and connectivity to Smith."]
-- FUTURE: debug mode that prints body + message
SmithResponseParseError code _body _message ->
mconcat ["Smith response parse error [", Text.pack . show $ code, "]. Please check connectivity to Smith and retry request."]
-- FUTURE: debug mode that prints body.
SmithStatusCodeError code _body ->
mconcat ["Smith status code error [", Text.pack . show $ code, "]. Please check connectivity to Smith and retry request."]
SmithUrlParseError message ->
mconcat ["Smith client url-parse error [", message, "]. Check you are running the latest client version, and raise a supportissue if this issue persists ."]
left :: Applicative m => x -> ExceptT x m a
left =
ExceptT . pure . Left