This is a bunch of shell functions to organize my immensly growing amount of user accounts.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

558 lines
10 KiB

#!/bin/sh
AMNGRDB="${AMNGRDB:-${HOME}/.account.db}"
AMNGRID="${AMNGRID:-${USER}@${HOSTNAME}}"
AMNGRPWLEN="${AMNGRPWLEN:-10}"
alias random="cat /dev/urandom"
function rand_printable() {
local OPT OPTARG OPTIND
local USAGE="$(printf "Usage: %s [-h] [len]" "$0")"
while getopts h OPT
do
case $OPT in
h)
echo "${USAGE}"
return 0;;
?)
echo "${USAGE}"
return 1;;
esac
done
shift $(($OPTIND-1))
if ! [[ "${1}" =~ ^[0-9]*$ ]]
then
echo "${USAGE}"
return 1
fi
echo -n "$(random | tr -dc ' !#-&(-~' | head -c${1:-512})"
}
function amngrdbinit() {
local OPT OPTARG OPTIND DB
local USAGE="$(printf "Usage: %s [-h] [dbfile]" "$0")"
while getopts h OPT
do
case $OPT in
h)
echo "${USAGE}"
return 0;;
?)
echo "${USAGE}"
return 1;;
esac
done
shift $(($OPTIND-1))
DB="${1:-${AMNGRDB}}"
if [ ! \( -d "$(dirname "${DB}")" \) ]
then
echo "${USAGE}"
return 1
fi
sqlite3 "${DB}" <<-EOD
PRAGMA foreign_keys = ON;
CREATE TABLE IF NOT EXISTS account (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name VARCHAR(128) UNIQUE NOT NULL,
desc TEXT DEFAULT NULL);
CREATE TABLE IF NOT EXISTS cred (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user VARCHAR(128) NOT NULL,
pass TEXT NOT NULL);
CREATE TABLE IF NOT EXISTS account_cred (
account_id INTEGER,
cred_id INTEGER,
state SMALLINT(1) NOT NULL,
FOREIGN KEY(account_id) REFERENCES account(id)
ON UPDATE CASCADE ON DELETE CASCADE,
FOREIGN KEY(cred_id) REFERENCES cred(id)
ON UPDATE CASCADE ON DELETE CASCADE);
EOD
}
function amngrdbdestroy() {
local OPT OPTARG OPTIND DB CHECK
local USAGE="$(printf "Usage: %s [-h] [dbfile]" "$0")"
while getopts h OPT
do
case $OPT in
h)
echo "${USAGE}"
return 0;;
?)
echo "${USAGE}"
return 1;;
esac
done
shift $(($OPTIND-1))
DB="${1:-${AMNGRDB}}"
cat <<-EOT
WARNING: You are about to remove your account data. There is no way to
recover from this. Are you really sure you want to do this?
EOT
printf "Really remove %s (Yes|[No]): " "${DB}" && read CHECK
CHECK="${CHECK:-No}"
test "${CHECK}" == "Yes" && rm -f "${DB}"
}
function amngrid() {
local OPT OPTARG OPTIND ACCOUNT_NAME DB
local USAGE="$(printf "Usage: %s [-h] [-D dbfile] account" "$0")"
while getopts hD: OPT
do
case $OPT in
D)
DB="${OPTARG}";;
h)
echo "${USAGE}"
return 0;;
?)
echo "${USAGE}"
return 1;;
esac
done
shift $(($OPTIND-1))
ACCOUNT_NAME="${1}"
DB="${DB:-${AMNGRDB}}"
if [ -z "${ACCOUNT_NAME}" ]
then
echo "${USAGE}"
return 1
fi
echo "$(sqlite3 "${DB}" <<-EOD
SELECT id FROM account WHERE name='${ACCOUNT_NAME}';
EOD
)"
}
function amngrcrypt() {
local OPT OPTARG OPTIND PLAIN RECIPIENT
local USAGE="$(printf "Usage: %s [-h] [-r recipient] password" "$0")"
while getopts hr: OPT
do
case $OPT in
r)
RECIPIENT="${OPTARG}";;
h)
echo "${USAGE}"
return 0;;
?)
echo "${USAGE}"
return 1;;
esac
done
shift $(($OPTIND-1))
RECIPIENT="${RECIPIENT:-${AMNGRID}}"
PLAIN="${1}"
if [ -z "${PLAIN}" ]
then
echo "${USAGE}"
return 1
fi
echo -n "${PLAIN}" | gpg -aeqr "${RECIPIENT}"
}
function amngrgen() {
local OPT OPTARG OPTIND RECIPIENT
local USAGE="$(printf "Usage: %s [-h] [-r recipient] [len]" "$0")"
while getopts hr: OPT
do
case $OPT in
r)
RECIPIENT="${OPTARG}";;
h)
echo "${USAGE}"
return 0;;
?)
echo "${USAGE}"
return 1;;
esac
done
shift $(($OPTIND-1))
local LEN="${1:-${AMNGRPWLEN}}"
if [ "${RECIPIENT}" ]
then
amngrcrypt -r "${RECIPIENT}" "$(rand_printable "${LEN}")"
else
amngrcrypt "$(rand_printable "${LEN}")"
fi
}
function amngradd() {
local OPT OPTARG OPTIND DESCRIPTION RECIPIENT DB
local USAGE="$(cat <<-EOT
Usage: $0 [-h] [-d description] [-D dbfile] [-r recipient]
account [username [password]]
EOT
)"
while getopts d:D:hr: OPT
do
case $OPT in
d)
DESCRIPTION="${OPTARG}";;
D)
DB="${OPTARG}";;
r)
RECIPIENT="${OPTARG}";;
h)
echo "${USAGE}"
return 0;;
?)
echo "${USAGE}"
return 1;;
esac
done
shift $(($OPTIND-1))
RECIPIENT="${RECIPIENT:-${AMNGRID}}"
DB="${DB:-${AMNGRDB}}"
local ACCOUNT_NAME="${1}"
DESCRIPTION="${DESCRIPTION:-NO DESCRIPTION}"
if [ -z "${ACCOUNT_NAME}" ]
then
echo "No account name given."
echo "${USAGE}"
return 1
fi
if [ "${2}" ]
then
USER="${2}"
else
USER="$(amngrgetuser -D "${DB}" -c "${ACCOUNT_NAME}")"
fi
if [ -z "${USER}" ]
then
printf "Can't find current user for account %s\n" "${ACCOUNT_NAME}"
echo "${USAGE}"
return 2
fi
if [ "${3}" ]
then
PASSWORD="$(amngrcrypt -r "${RECIPIENT}" "${3}")"
else
PASSWORD="$(amngrgen -r "${RECIPIENT}")"
echo -n "${PASSWORD}" | gpg -dq | xclip -i
fi
if [ -z "${PASSWORD}" ]
then
printf "Failed to create encrypted password for account %s\n" \
"${ACCOUNT_NAME}"
echo "${USAGE}"
return 3
fi
local ACCOUNT_ID="$(amngrid -D "${DB}" "${ACCOUNT_NAME}")"
local QUERY="$(cat <<-EOD
PRAGMA foreign_keys = ON;
BEGIN TRANSACTION;
INSERT INTO cred (user, pass)
VALUES ('${USER}', '${PASSWORD}');
EOD
)"
if [ -z "${ACCOUNT_ID}" ]
then
QUERY="$(cat <<-EOD
${QUERY}
INSERT INTO account (name, desc)
VALUES ('${ACCOUNT_NAME}', '${DESCRIPTION}');
EOD
)"
else
QUERY="$(cat <<-EOD
${QUERY}
UPDATE account_cred SET state=0
WHERE account_id=(SELECT id FROM account
WHERE name='${ACCOUNT_NAME}' AND state=2);
UPDATE account_cred SET state=2
WHERE account_id=(SELECT id FROM account
WHERE name='${ACCOUNT_NAME}' AND state=1);
EOD
)"
fi
QUERY="$(cat <<-EOD
${QUERY}
INSERT INTO account_cred (account_id, cred_id, state)
VALUES ((SELECT id FROM account WHERE name='${ACCOUNT_NAME}'),
(SELECT id FROM cred WHERE user='${USER}' AND pass='${PASSWORD}'),
1);
COMMIT TRANSACTION;
EOD
)"
sqlite3 "${DB}" "${QUERY}"
}
function amngrgetuser() {
local OPT OPTARG OPTIND DB
local STDOUT=0
local USAGE="$(cat <<-EOT
Usage: $0 [-h] [-c] [-D dbfile] account [state]
EOT
)"
while getopts hcD: OPT
do
case $OPT in
c)
STDOUT=1;;
D)
DB="${OPTARG}";;
h)
echo "${USAGE}"
return 0;;
?)
echo "${USAGE}"
return 1;;
esac
done
shift $(($OPTIND-1))
local ACCOUNT_NAME="${1}"
local STATE="${2:-"1"}"
DB="${DB:-${AMNGRDB}}"
if [ -z "${ACCOUNT_NAME}" ]
then
echo "${USAGE}"
return 1
fi
test "${STATE}" != "1" -a "${STATE}" != "2" && STATE="1"
if [ $STDOUT -eq 0 ]
then
sqlite3 "${DB}" <<-EOD |\
awk 'NR>1{print p}{p=$0}END{ORS="";print}' | xclip -i
SELECT user FROM account
JOIN account_cred ON account.id=account_cred.account_id
JOIN cred ON cred.id=account_cred.cred_id
WHERE name='${ACCOUNT_NAME}' AND state=${STATE};
EOD
else
sqlite3 "${DB}" <<-EOD |\
awk 'NR>1{print p}{p=$0}END{ORS="";print}'
SELECT user FROM account
JOIN account_cred ON account.id=account_cred.account_id
JOIN cred ON cred.id=account_cred.cred_id
WHERE name='${ACCOUNT_NAME}' AND state=${STATE};
EOD
fi
}
function amngrgetolduser() {
amngrgetuser "$@" "2"
}
function amngrgetpass() {
local OPT OPTARG OPTIND DB
local USAGE="$(printf "Usage: %s [-h] [-D dbfile] account [state]" "$0")"
while getopts hD: OPT
do
case $OPT in
D)
DB="${OPTARG}";;
h)
echo "${USAGE}"
return 0;;
?)
echo "${USAGE}"
return 1;;
esac
done
shift $(($OPTIND-1))
local ACCOUNT_NAME="${1}"
local STATE="${2:-1}"
DB="${DB:-${AMNGRDB}}"
if [ -z "${ACCOUNT_NAME}" ]
then
echo "${USAGE}"
return 1
fi
test "${STATE}" != "1" -a "${STATE}" != "2" && STATE="1"
sqlite3 "${DB}" <<-EOD |\
awk 'NR>1{print p}{p=$0}END{ORS="";print}' | gpg -dq | xclip -i
SELECT pass FROM account
JOIN account_cred ON account.id=account_cred.account_id
JOIN cred ON cred.id=account_cred.cred_id
WHERE name='${ACCOUNT_NAME}' AND state=${STATE};
EOD
}
function amngrgetoldpass() {
amngrgetpass "$@" "2"
}
function amngrrename() {
local OPT OPTARG OPTIND DB
local USAGE="$(cat <<-EOT
Usage: $0 [-h] [-D dbfile] old_account new_account
EOT
)"
while getopts hD: OPT
do
case $OPT in
D)
DB="${OPTARG}";;
h)
echo "${USAGE}"
return 0;;
?)
echo "${USAGE}"
return 1;;
esac
done
shift $(($OPTIND-1))
local OLD_NAME="${1}"
local NEW_NAME="${2}"
DB="${DB:-${AMNGRDB}}"
if [ -z "${OLD_NAME}" -o -z "${NEW_NAME}" ]
then
echo "${USAGE}"
return 1
fi
sqlite3 "${DB}" <<-EOD
UPDATE account SET name='${NEW_NAME}'
WHERE name='${OLD_NAME}';
EOD
}
function amngrdelete() {
local OPT OPTARG OPTIND DB
local USAGE="$(printf "Usage: %s [-h] [-D dbfile] account" "$0")"
while getopts hD: OPT
do
case $OPT in
D)
DB="${OPTARG}";;
h)
echo "${USAGE}"
return 0;;
?)
echo "${USAGE}"
return 1;;
esac
done
shift $(($OPTIND-1))
local ACCOUNT_NAME="${1}"
DB="${DB:-${AMNGRDB}}"
if [ -z "${ACCOUNT_NAME}" ]
then
echo "${USAGE}"
return 1
fi
sqlite3 "${DB}" <<-EOD
PRAGMA foreign_keys = ON;
BEGIN TRANSACTION;
DELETE FROM cred WHERE id IN (
SELECT cred_id FROM account
JOIN account_cred ON account.id=account_cred.account_id
WHERE name='${ACCOUNT_NAME}');
DELETE FROM account WHERE name='${ACCOUNT_NAME}';
COMMIT TRANSACTION;
EOD
}
function amngrsearch() {
local OPT OPTARG OPTIND SEPARATOR PATTERN
local USAGE="$(cat <<-EOT
Usage: $0 [-h] [-D dbfile] [-s separator] [pattern]
EOT
)"
while getopts hD:s: OPT
do
case $OPT in
D)
DB="${OPTARG}";;
s)
SEPARATOR="${OPTARG}";;
h)
echo "${USAGE}"
return 0;;
?)
echo "${USAGE}"
return 1;;
esac
done
shift $(($OPTIND-1))
PATTERN="${1}"
SEPARATOR="${SEPARATOR:-" => "}"
DB="${DB:-${AMNGRDB}}"
sqlite3 -separator "${SEPARATOR}" "${DB}" <<-EOD
SELECT name, user, desc FROM account
JOIN account_cred ON account.id=account_cred.account_id
JOIN cred ON cred.id=account_cred.cred_id
WHERE state=1 AND (
name LIKE '%${PATTERN}%' OR user LIKE '%${PATTERN}%' OR
desc LIKE '%${PATTERN}%');
EOD
}
case "$(basename -- "$0")" in
random) random;;
rand_printable) rand_printable "$@";;
amngrdbinit) amngrdbinit "$@";;
amngrdbdestroy) amngrdbdestroy "$@";;
amngradd) amngradd "$@";;
amngrcreate) amngrcreate "$@";;
amngrcrypt) amngrcrypt "$@";;
amngrdelete) amngrdelete "$@";;
amngrgen) amngrgen "$@";;
amngrgetoldpass) amngrgetoldpass "$@";;
amngrgetolduser) amngrgetolduser "$@";;
amngrgetpass) amngrgetpass "$@";;
amngrgetuser) amngrgetuser "$@";;
amngrid) amngrid "$@";;
amngrlist) amngrlist "$@";;
amngrrename) amngrrename "$@";;
amngrsearch) amngrsearch "$@";;
*) ;;
esac
# vim: set ft=sh ts=4 sw=4: