packages feed

passman-cli-0.2.0.0: src/Passman/CLI/MasterPassword.hs

-- Copyright (C) 2017  Matthew Harm Bekkema
--
-- This file is part of passman-cli.
--
-- passman-cli is free software: you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation, either version 3 of the License, or
-- (at your option) any later version.
--
-- passman-cli is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-- GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License
-- along with this program.  If not, see <https://www.gnu.org/licenses/>.

module Passman.CLI.MasterPassword
    ( askNewMasterPassword
    , askMasterPassword
    ) where

import           Data.Function (fix)
import qualified Data.Text     as T
import           Data.Text     (Text)

import           Passman.Core.Hash (MasterPassword, masterPassword,
                                    fromMasterPassword, checkMasterPassword,
                                    hashMasterPassword)

import           Passman.CLI.Interaction


askNewMasterPassword :: Maybe (Bool, Text) -> IO Text
askNewMasterPassword Nothing = fix $ \loop ->
    askPassword "master password: " `withValidationE`
    validateMasterPassword >>=
    withConfirmationOr loop
askNewMasterPassword (Just (True, mp')) = fix $ \loop ->
    askPassword "master password (or blank for old): " `withValidationE`
    orDefault validateMasterPassword >>=
    maybe (pure mp') (withConfirmationOr loop)
askNewMasterPassword (Just (False, mp)) = pure mp

withConfirmationOr :: IO Text -> MasterPassword -> IO Text
withConfirmationOr loop mp = do
    vMp <- T.pack <$> askPassword "confirm master password: "
    if fromMasterPassword mp == vMp
        then hashMasterPassword mp
        else putStrLn "passwords do not match" >> loop

askMasterPassword :: Text -> IO MasterPassword
askMasterPassword hash = askPassword "master password: "
    `withValidationE` validateMasterPassword
    `withValidationE` validateCorrectMasterPassword hash

validateCorrectMasterPassword :: Text
                              -> MasterPassword
                              -> Either String MasterPassword
validateCorrectMasterPassword hash p = case checkMasterPassword hash p of
    False -> Left  "Incorrect master password."
    True  -> Right p

validateMasterPassword :: String -> Either String MasterPassword
validateMasterPassword = maybe (Left "Invalid master password.") Right .
    masterPassword . T.pack