-- Copyright (C) 2017 Matthew Harm Bekkema -- -- This file is part of passman-core -- -- passman-core 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-core 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 . ----------------------------------------------------------------------------- -- | -- Module : Passman.Core.Config -- Copyright : Matthew Harm Bekkema 2017 -- License : GPL-3 -- Maintainer : mbekkema97@gmail.com ----------------------------------------------------------------------------- {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TemplateHaskell #-} module Passman.Core.Config ( -- * Data Structures Config(..) -- * File IO , loadConfig , saveConfig ) where import Control.Exception (throwIO) import Data.Text (Text) import Data.Yaml (encodeFile, decodeFileEither) import Data.Aeson.TH (deriveJSON, defaultOptions, omitNothingFields) import System.FilePath (()) import System.Directory (XdgDirectory(XdgConfig), getXdgDirectory, createDirectoryIfMissing) import System.PosixCompat.Files (fileMode, getFileStatus, setFileMode, intersectFileModes, ownerModes) -- | The persistent configuration of the password manager. -- -- Intended to be saved to file in YAML format. This example config file: -- -- >masterPasswordHash: "$2y$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy" -- -- Would be parsed as: -- -- >Config { masterPasswordHash = "$2y$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy" -- > , passlistPath = Nothing -- > } -- -- Optionally, the `passlistPath` field can be present as well. This example -- config file: -- -- >masterPasswordHash: "$2y$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy" -- >passlistPath: "/path/to/passlist.txt" -- -- Would be parsed as: -- -- >Config { masterPasswordHash = "$2y$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy" -- > , passlistPath = Just "/path/to/passlist.txt" -- > } data Config = Config { -- | Hash of the master password generated by -- `Passman.Core.Hash.hashMasterPassword` masterPasswordHash :: Text -- | The path to the last used passlist file , passlistPath :: Maybe FilePath } deriving (Show, Eq) $(deriveJSON defaultOptions {omitNothingFields = True} ''Config) -- | Load a `Config` from file. loadConfig :: IO Config loadConfig = configFile >>= decodeFileEither >>= either throwIO pure -- | Save the specified `Config` to file, overwriting an existing config file. saveConfig :: Config -> IO () saveConfig config = configFile >>= flip encodeFile config -- | Create the path leading up to the config file with the correct permissions -- and return the config filepath. configFile :: IO FilePath configFile = do fp <- getXdgDirectory XdgConfig "passman" createDirectoryIfMissing False fp correctPermissions fp pure $ fp "config.yaml" -- | Remove all but the owner's permissions on the specified path. correctPermissions :: FilePath -> IO () correctPermissions fp = do currentMode <- fileMode <$> getFileStatus fp let newMode = intersectFileModes currentMode ownerModes setFileMode fp newMode