% A Tool to Manage a Set of YAML Objects Representing Account Information — pwutil A long time ago I wrote a bunch of scripts (first in bash, then zsh, and later perl) to manage a, sometimes encrypted, file containing account information I get asked to create and remember on a daily basis—accounts for shopping websites spring to mind. [pwutil](git://git.yggdrasil.li/pwutil) is the newest iteration in this line of bunches of scripts. ## Features * Support for embedding common operation in any kind of record keeping Thus support for almost any encryption known to man (with absolutely no online security), version control, and synchronisation * [Human readable](https://en.wikipedia.org/wiki/YAML) backstore * [Command Line Interface](https://en.wikipedia.org/wiki/Command-line_interface)-only * New accounts can be partially generated by user defined functions with out of the box support for [pwgen](http://sourceforge.net/projects/pwgen/) and SSH ## Usage ~~~ pwget [ …] Looks up and returns all accounts whose identifier contains any — case insensitive. pwadd [[--gen- [ …] …] --] [ …] Adds an account to the store — does not overwrite. ~~~ ## Documentation I shall document the project in a partial and file-wise fashion—amendments available on request. ### Structure ~~~ {#DirTree} pwutil ├── default.nix ├── PWAdd.hs ├── PWGet.hs ├── PWUtil │   ├── Encfs.hs │   ├── Extra │   │   ├── PWGen.hs │   │   └── SSHCmd.hs │   ├── Types.hs │   └── Util.hs ├── pwutil.hs ├── PWUtil.hs └── pwutil.nix ~~~ ### `pwutil.nix` is a [nix](https://nixos.org/nix) expression allowing easy installation using the nix package manager. A `~/.nixpkgs/config.nix` allowing one to do so might look thus: ~~~ {.numberLines} { packageOverrides = pkgs: { pwutil = pkgs.callPackage /path/to/pwutil.nix {}; }; } ~~~ ### `Types.hs` `PWLocation` describes the location of a file containing a YAML object mapping human readable identifiers to account information. `encryption`, which has essentially the same signature as [`withFile`](http://hackage.haskell.org/package/base-4.8.0.0/docs/System-IO.html#v:withFile) wraps all access to the file. This in an extremely powerful way to deal with any kind of encryption desired (currently I implemented automatic mounting of an [EncFs](http://en.wikipedia.org/wiki/EncFS) container and `plain`, which does nothing. `create` is used to try and create the location once should access fail due to a file [not existing](http://hackage.haskell.org/package/base-4.8.0.0/docs/System-IO-Error.html#v:doesNotExistErrorType). ~~~ {#Types.hs .haskell .numberLines} module PWUtil.Types ( PWLocation(..), Encryption(..), PW(..), PWConfig(..), Generator(..) ) where import System.IO (Handle(..), IOMode(..)) import Control.Monad.State import qualified Data.Map as M import Data.Yaml data PWLocation = PWLocation { path :: FilePath , encryption :: Encryption , create :: IO () } instance Show PWLocation where show loc = show $ path loc type Encryption = FilePath -> IOMode -> (Handle -> IO ()) -> IO () type PW = StateT PWConfig IO data PWConfig = PWConfig { location :: PWLocation , generators :: M.Map String Generator } type Generator = [String] -> IO Value ~~~ ### `pwutil.hs` is, in a [xmonad](http://xmonad.org) kind of way, the configuration file. ~~~ {#pwutil.hs .haskell .numberLines} import PWUtil import System.FilePath (()) import qualified Data.Map as M import System.Directory (getHomeDirectory) pWLocation :: IO PWLocation pWLocation = do h <- getHomeDirectory return PWLocation { path = h "accounts.yaml" , encryption = plain , create = createFile $ h "accounts.yaml" } myGenerators :: M.Map String Generator myGenerators = M.empty main :: IO () main = do myLocation <- pWLocation runPW (PWConfig { location = myLocation, generators = myGenerators }) pwutil ~~~ ### `Encfs.hs` shall serve as an example for a module providing an encryption wrapper. It exports ~~~{.haskell} encfs :: Encfs -> Encryption encfs (Encfs backStore mountpoint) = … ~~~ Upon execution it checks whether `mountpoint` is already mounted. If not it executes `encfs backStore mountpoint` interactively and calls `fusermount -u` after completion.