bin/pw.nix (view raw)
1{ pkgs, ... }:
2
3let
4 name = "pw";
5 gpg = "${pkgs.gnupg}/bin/gpg";
6 pwgen = "${pkgs.pwgen}/bin/pwgen";
7 git = "${pkgs.git}/bin/git";
8 copy = "${pkgs.wl-clipboard}/bin/wl-copy";
9in
10pkgs.writeShellScriptBin name
11 ''
12 # pw - a mnml password manager
13 [[ -z "$PW_DIR" ]] && PW_DIR="$HOME/.pw"
14 init() {
15 if [[ ! -e "$PW_DIR" ]]; then
16 mkdir -p "$PW_DIR"
17 printf "pw: password directory initialized at %s\n" "$PW_DIR"
18 else
19 printf "PW_DIR is %s\n" "$PW_DIR"
20 die "$PW_DIR exists"
21 fi
22 }
23 add() {
24 # $1: path to file
25 # $2 [optional]: password text
26 [[ -z "$PW_KEY" ]] && die "\$PW_KEY not set"
27 if [[ "$#" -eq 2 ]]; then
28 pass="$2"
29 else
30 # uses default length of 25 chars, unless PW_LEN is set
31 pass="$(${pwgen} "''${PW_LEN:-25}" 1 -s)"
32 printf "pw: generated password for %s\n" "$1"
33 fi
34 if [[ ! -f "$PW_DIR/$1.gpg" ]]; then
35 printf "%s" "$pass" | ${gpg} -aer "$PW_KEY" -o "$PW_DIR/$1.gpg"
36 printf "pw: %s/%s.gpg created\n" "$PW_DIR" "$1"
37 else
38 die "the file $PW_DIR/$1.gpg exists"
39 fi
40 (
41 cd $PW_DIR
42 ${git} add .
43 ${git} commit -m "$(date)"
44 remote="$(${git} remote show)"
45 branch="$(${git} branch --show-current)"
46 ${git} pull -r "$remote" "$branch"
47 ${git} push "$remote" "$branch"
48 )
49 }
50 list() {
51 (cd "$PW_DIR"; find *.gpg | awk -F '.gpg' '{ print $1 }' )
52 }
53 del() {
54 checkf "$PW_DIR/$1.gpg"
55 read -rn 1 -p "pw: are you sure you want to delete $1? [y/n]: "
56 printf "\n"
57 [[ "$REPLY" == [yY] ]] && {
58 rm -f "$PW_DIR/$1.gpg"
59 printf "pw: deleted %s" "$1"
60 }
61 }
62 show() {
63 checkf "$PW_DIR/$1.gpg"
64 ${gpg} --decrypt --quiet --use-agent "$PW_DIR/$1.gpg"
65 }
66 # TODO: rework having to checkf twice
67 copy() {
68 checkf "$PW_DIR/$1.gpg"
69 if [[ "$OSTYPE" =~ darwin* ]]; then
70 show "$1" | head -1 | pbcopy | tr -d '\n'
71 else
72 show "$1" | head -1 | ${copy} -n
73 fi
74 printf "pw: copied %s to clipboard\n" "$1"
75 }
76 usage() {
77 usage="
78 pw - mnml password manager
79 usage: pw [options] [NAME]
80 All options except -i and -h require a NAME argument.
81 options:
82 -i Initializes password directory at \$HOME/.pw or at \$PW_DIR, if it exists.
83 -a Add a password.
84 -g Generate a password.
85 -s Print password to STDOUT.
86 -l List out all passwords.
87 -c Copy existing password to clipboard.
88 -d Delete password.
89 -h Display this help message and exit.
90 Requires PW_KEY to be set. Optionally, set PW_DIR for custom directory location.
91 Set PW_LEN to an integer of your choice, to override the default password length of 25.
92 "
93 printf "%s" "$usage"
94 exit 1
95 }
96 checkf() {
97 [[ ! -f "$1" ]] &&
98 die "$1 does not exist"
99 }
100 die() {
101 printf "error: %s\n" "$1" >&2
102 exit 1
103 }
104 main() {
105 [[ -z "$1" ]] && {
106 usage
107 }
108 while getopts "ila:g:s:c:d:h" options
109 do
110 # shellcheck disable=SC2221,SC2222
111 case "$options" in
112 i) init ;;
113 l) list ;;
114 g) add "$OPTARG" ;;
115 a)
116 read -rsp "enter password: " pass
117 printf "\n"
118 add "$OPTARG" "$pass"
119 ;;
120 s) show "$OPTARG" ;;
121 c) copy "$OPTARG" ;;
122 d) del "$OPTARG" ;;
123 *|h) usage ;;
124 esac
125 done
126 shift $(( OPTIND -1 ))
127 }
128 main "$@"
129 ''