-- This file is part of Diohsc -- Copyright (C) 2020 Martin Bays -- -- This program is free software: you can redistribute it and/or modify -- it under the terms of version 3 of the GNU General Public License as -- published by the Free Software Foundation, or any later version. -- -- You should have received a copy of the GNU General Public License -- along with this program. If not, see http://www.gnu.org/licenses/. {-# LANGUAGE OverloadedStrings #-} module Identity where import Control.Monad (msum, when) import Control.Monad.IO.Class (liftIO) import Control.Monad.Trans (lift) import Control.Monad.Trans.Maybe import Data.Maybe (fromMaybe, mapMaybe) import Safe import System.Directory (listDirectory) import ANSIColour import ClientCert import MetaString import Mundanities import Prompt data Identity = Identity { identityName :: String, identityCert :: ClientCert } deriving (Eq,Show) isTemporary :: Identity -> Bool isTemporary = null . identityName normaliseIdName :: String -> Maybe String normaliseIdName n = headMay (words n) showIdentity :: MetaString a => Bool -> Identity -> a showIdentity ansi = showIdentityName ansi . fromString . identityName showIdentityName :: MetaString a => Bool -> String -> a showIdentityName ansi name = applyIf ansi (withColourStr Green) $ "[" <> fromString name <> "]" loadIdentity :: FilePath -> String -> IO (Maybe Identity) loadIdentity idsPath idName = (Identity idName <$>) <$> loadClientCert idsPath idName getIdentity :: Bool -> Bool -> FilePath -> KeyType -> String -> IO (Maybe Identity) getIdentity _ _ _ tp "" = runMaybeT $ Identity "" <$> liftIO (generateSelfSigned tp "") getIdentity interactive ansi idsPath tp idName' = runMaybeT $ do idName <- MaybeT . return $ normaliseIdName idName' msum [ MaybeT $ loadIdentity idsPath idName , do when interactive . lift $ do let keyTypeName = case tp of KeyRSA -> "RSA" KeyEd25519 -> "Ed25519" putStrLn $ "Creating a new " ++ keyTypeName ++ " identity." putStrLn $ "We will refer to it as " <> showIdentityName ansi idName <> ", but you may also set a \"Common Name\";" putStrLn "this is recorded in the identity certificate, and may be interpreted by the server as a username." putStrLn "The common name may be left blank. Use ^C to cancel identity generation." clientCert <- liftIO . generateSelfSigned tp . fromMaybe "" =<< if not interactive then return Nothing else MaybeT (promptLine "Common Name: ") liftIO $ mkdirhier idsPath lift $ saveClientCert idsPath idName clientCert return $ Identity idName clientCert ] getIdentityRequesting :: Bool -> FilePath -> IO (Maybe Identity) getIdentityRequesting ansi idsPath = runMaybeT $ do liftIO . putStrLn $ "Enter the name of an existing identity to use (tab completes),\n\t" ++ "or a name for a new identity to create and use,\n\t" ++ "or nothing to create and use a temporary anonymous identity,\n\t" ++ "or use ^C to abort." let prompt = applyIf ansi (withColourStr Green) "Identity" <> ": " idName <- (fromMaybe "" <$>) . MaybeT $ promptLineWithCompletions prompt =<< listIdentities idsPath MaybeT $ getIdentity True ansi idsPath KeyRSA idName listIdentities :: FilePath -> IO [String] listIdentities path = mapMaybe stripCrtExt <$> ignoreIOErr (listDirectory path) where stripCrtExt s = case splitAt (length s - 4) s of (s', ".crt") -> Just s' _ -> Nothing