diff --git a/README.md b/README.md index 344696e..379cba8 100644 --- a/README.md +++ b/README.md @@ -168,6 +168,30 @@ Inside the `.env` file, two _environment variables_ can be accessed: - `AUTOENV_CUR_FILE` - The file being sourced - `AUTOENV_CUR_DIR` - Equivalent to `dirname "$AUTOENV_CUR_FILE"` +#### Programmatic Authorization + +You can programmatically authorize, deny, or remove authorization for enter and leave scripts using the following functions: + +- `autoenv_authorize [directory] [enter|leave|both]` - Authorize `.env` and/or `.env.leave` files in a directory to be automatically sourced. The directory defaults to the current directory, and the second argument defaults to `both`. +- `autoenv_unauthorize [directory] [enter|leave|both]` - Deny (mark as not authorized) `.env` and/or `.env.leave` files in a directory. They will not be sourced and you won't be prompted for authorization. +- `autoenv_deauthorize [directory] [enter|leave|both]` - Remove authorization for `.env` and/or `.env.leave` files in a directory. You will be prompted for authorization again when entering the directory. + +Example usage: + +```bash +# Authorize both .env and .env.leave in the current directory. +autoenv_authorize + +# Authorize only the .env file in a specific directory. +autoenv_authorize /path/to/project enter + +# Deny the .env.leave file in a specific directory. +autoenv_unauthorize /path/to/project leave + +# Remove authorization for both files in the current directory. +autoenv_deauthorize +``` + ## Shells autoenv is tested on: diff --git a/activate.sh b/activate.sh index 327de27..afb1fbd 100755 --- a/activate.sh +++ b/activate.sh @@ -84,6 +84,18 @@ _autoenv_draw_line() { fi } +# @description Convert a directory path to absolute path +# @args $1: directory path +# @internal +_autoenv_get_abs_dir() { + local _dir="${1}" + if \command -v chdir >/dev/null 2>&1; then + ( \chdir "${_dir}" && \pwd -P ) + else + ( \builtin cd "${_dir}" && \pwd -P ) + fi +} + # @description Main initialization function # @internal autoenv_init() { @@ -241,6 +253,132 @@ autoenv_authorize_env() { autoenv_hashline "${_envfile}" >> "${AUTOENV_AUTH_FILE}" } +# @description Authorize enter and/or leave scripts in a directory +# @usage autoenv_authorize [directory] [enter|leave|both] +# @args $1: directory path (defaults to current directory) +# @args $2: which files to authorize - "enter", "leave", or "both" (defaults to "both") +# @exitcode 0 on success +# @public +autoenv_authorize() { + local _dir="${1:-.}" _which="${2:-both}" _abs_dir + + if [ ! -d "${_dir}" ]; then + \printf '%s\n' "autoenv: error: '${_dir}' is not a directory" >&2 + \return 1 + fi + + case "${_which}" in + enter|leave|both) + ;; + *) + \printf '%s\n' "autoenv: error: invalid argument '${_which}' (expected 'enter', 'leave', or 'both')" >&2 + \return 1 + ;; + esac + + _abs_dir=$(_autoenv_get_abs_dir "${_dir}") + + if [ "${_which}" = "enter" ] || [ "${_which}" = "both" ]; then + if [ -f "${_abs_dir}/${AUTOENV_ENV_FILENAME}" ]; then + autoenv_authorize_env "${_abs_dir}/${AUTOENV_ENV_FILENAME}" + \printf '%s\n' "Authorized: ${_abs_dir}/${AUTOENV_ENV_FILENAME}" + fi + fi + + if [ "${_which}" = "leave" ] || [ "${_which}" = "both" ]; then + if [ -f "${_abs_dir}/${AUTOENV_ENV_LEAVE_FILENAME}" ]; then + autoenv_authorize_env "${_abs_dir}/${AUTOENV_ENV_LEAVE_FILENAME}" + \printf '%s\n' "Authorized: ${_abs_dir}/${AUTOENV_ENV_LEAVE_FILENAME}" + fi + fi + + \return 0 +} + +# @description Deny (unauthorize) enter and/or leave scripts in a directory +# @usage autoenv_unauthorize [directory] [enter|leave|both] +# @args $1: directory path (defaults to current directory) +# @args $2: which files to deny - "enter", "leave", or "both" (defaults to "both") +# @exitcode 0 on success +# @public +autoenv_unauthorize() { + local _dir="${1:-.}" _which="${2:-both}" _abs_dir + + if [ ! -d "${_dir}" ]; then + \printf '%s\n' "autoenv: error: '${_dir}' is not a directory" >&2 + \return 1 + fi + + case "${_which}" in + enter|leave|both) + ;; + *) + \printf '%s\n' "autoenv: error: invalid argument '${_which}' (expected 'enter', 'leave', or 'both')" >&2 + \return 1 + ;; + esac + + _abs_dir=$(_autoenv_get_abs_dir "${_dir}") + + if [ "${_which}" = "enter" ] || [ "${_which}" = "both" ]; then + if [ -f "${_abs_dir}/${AUTOENV_ENV_FILENAME}" ]; then + autoenv_unauthorize_env "${_abs_dir}/${AUTOENV_ENV_FILENAME}" + \printf '%s\n' "Denied: ${_abs_dir}/${AUTOENV_ENV_FILENAME}" + fi + fi + + if [ "${_which}" = "leave" ] || [ "${_which}" = "both" ]; then + if [ -f "${_abs_dir}/${AUTOENV_ENV_LEAVE_FILENAME}" ]; then + autoenv_unauthorize_env "${_abs_dir}/${AUTOENV_ENV_LEAVE_FILENAME}" + \printf '%s\n' "Denied: ${_abs_dir}/${AUTOENV_ENV_LEAVE_FILENAME}" + fi + fi + + \return 0 +} + +# @description Remove authorization for enter and/or leave scripts in a directory +# @usage autoenv_deauthorize [directory] [enter|leave|both] +# @args $1: directory path (defaults to current directory) +# @args $2: which files to deauthorize - "enter", "leave", or "both" (defaults to "both") +# @exitcode 0 on success +# @public +autoenv_deauthorize() { + local _dir="${1:-.}" _which="${2:-both}" _abs_dir + + if [ ! -d "${_dir}" ]; then + \printf '%s\n' "autoenv: error: '${_dir}' is not a directory" >&2 + \return 1 + fi + + case "${_which}" in + enter|leave|both) + ;; + *) + \printf '%s\n' "autoenv: error: invalid argument '${_which}' (expected 'enter', 'leave', or 'both')" >&2 + \return 1 + ;; + esac + + _abs_dir=$(_autoenv_get_abs_dir "${_dir}") + + if [ "${_which}" = "enter" ] || [ "${_which}" = "both" ]; then + if [ -f "${_abs_dir}/${AUTOENV_ENV_FILENAME}" ]; then + autoenv_deauthorize_env "${_abs_dir}/${AUTOENV_ENV_FILENAME}" + \printf '%s\n' "Deauthorized: ${_abs_dir}/${AUTOENV_ENV_FILENAME}" + fi + fi + + if [ "${_which}" = "leave" ] || [ "${_which}" = "both" ]; then + if [ -f "${_abs_dir}/${AUTOENV_ENV_LEAVE_FILENAME}" ]; then + autoenv_deauthorize_env "${_abs_dir}/${AUTOENV_ENV_LEAVE_FILENAME}" + \printf '%s\n' "Deauthorized: ${_abs_dir}/${AUTOENV_ENV_LEAVE_FILENAME}" + fi + fi + + \return 0 +} + # @description Actually source a file # @internal autoenv_source() { diff --git a/tests/test_authorize_api.sh b/tests/test_authorize_api.sh new file mode 100644 index 0000000..3c2dd32 --- /dev/null +++ b/tests/test_authorize_api.sh @@ -0,0 +1,138 @@ +# shellcheck shell=sh + +. "${FUNCTIONS}" +. "${ACTIVATE_SH}" + +# Prepare test directories and files. +mkdir -pv 'dir1' 'dir2' 'dir3' +echo 'echo enter1' > "dir1/.env" +echo 'echo leave1' > "dir1/.env.leave" +echo 'echo enter2' > "dir2/.env" +echo 'echo leave2' > "dir2/.env.leave" +echo 'echo enter3' > "dir3/.env" + +abs_dir1=$( + if \command -v chdir >/dev/null 2>&1; then + ( \chdir dir1 && \pwd -P ) + else + ( \builtin cd dir1 && \pwd -P ) + fi +) +abs_dir2=$( + if \command -v chdir >/dev/null 2>&1; then + ( \chdir dir2 && \pwd -P ) + else + ( \builtin cd dir2 && \pwd -P ) + fi +) +abs_dir3=$( + if \command -v chdir >/dev/null 2>&1; then + ( \chdir dir3 && \pwd -P ) + else + ( \builtin cd dir3 && \pwd -P ) + fi +) + +# Test autoenv_authorize with both enter and leave files. +out=$(autoenv_authorize dir1) +ret="$?" +if [ ${ret} -ne 0 ]; then + echo "autoenv_authorize failed with exit code ${ret}." + exit 1 +fi +if ! printf '%s\n' "${out}" | grep -q "Authorized: ${abs_dir1}/.env" ; then + echo "autoenv_authorize did not authorize .env file." + exit 1 +fi +if ! printf '%s\n' "${out}" | grep -q "Authorized: ${abs_dir1}/.env.leave" ; then + echo "autoenv_authorize did not authorize .env.leave file." + exit 1 +fi + +# Verify the files are authorized by checking if they're in the auth file. +# Use absolute paths for hash calculation. +hash1=$(autoenv_hashline "${abs_dir1}/.env") +hash2=$(autoenv_hashline "${abs_dir1}/.env.leave") +if ! grep -q "${hash1}" "${AUTOENV_AUTH_FILE}"; then + echo ".env file was not added to auth file." + exit 1 +fi +if ! grep -q "${hash2}" "${AUTOENV_AUTH_FILE}"; then + echo ".env.leave file was not added to auth file." + exit 1 +fi + +# Test autoenv_authorize with only "enter" option. +out=$(autoenv_authorize dir2 enter) +if ! printf '%s\n' "${out}" | grep -q "Authorized: ${abs_dir2}/.env" ; then + echo "autoenv_authorize with 'enter' did not authorize .env file." + exit 1 +fi +if printf '%s\n' "${out}" | grep -q "Authorized: ${abs_dir2}/.env.leave" ; then + echo "autoenv_authorize with 'enter' incorrectly authorized .env.leave file." + exit 1 +fi + +# Test autoenv_authorize with only "leave" option. +out=$(autoenv_authorize dir2 leave) +if ! printf '%s\n' "${out}" | grep -q "Authorized: ${abs_dir2}/.env.leave" ; then + echo "autoenv_authorize with 'leave' did not authorize .env.leave file." + exit 1 +fi + +# Test autoenv_deauthorize. +out=$(autoenv_deauthorize dir1) +if ! printf '%s\n' "${out}" | grep -q "Deauthorized: ${abs_dir1}/.env" ; then + echo "autoenv_deauthorize did not deauthorize .env file." + exit 1 +fi +if ! printf '%s\n' "${out}" | grep -q "Deauthorized: ${abs_dir1}/.env.leave" ; then + echo "autoenv_deauthorize did not deauthorize .env.leave file." + exit 1 +fi + +# Verify the files are deauthorized by checking they're not in the auth file. +if grep -q "${hash1}" "${AUTOENV_AUTH_FILE}"; then + echo ".env file was not removed from auth file." + exit 1 +fi +if grep -q "${hash2}" "${AUTOENV_AUTH_FILE}"; then + echo ".env.leave file was not removed from auth file." + exit 1 +fi + +# Test autoenv_unauthorize (deny). +out=$(autoenv_unauthorize dir3) +if ! printf '%s\n' "${out}" | grep -q "Denied: ${abs_dir3}/.env" ; then + echo "autoenv_unauthorize did not deny .env file." + exit 1 +fi +hash3=$(autoenv_hashline "${abs_dir3}/.env") +if ! grep -q "${hash3}" "${AUTOENV_NOTAUTH_FILE}"; then + echo ".env file was not added to not-authorized file." + exit 1 +fi + +# Test error handling with non-existent directory. +out=$(autoenv_authorize nonexistent 2>&1) +ret="$?" +if [ ${ret} -eq 0 ]; then + echo "autoenv_authorize should have failed with non-existent directory." + exit 1 +fi +if ! printf '%s\n' "${out}" | grep -q "is not a directory" ; then + echo "autoenv_authorize did not report proper error for non-existent directory." + exit 1 +fi + +# Test error handling with invalid option. +out=$(autoenv_authorize dir1 invalid 2>&1) +ret="$?" +if [ ${ret} -eq 0 ]; then + echo "autoenv_authorize should have failed with invalid option." + exit 1 +fi +if ! printf '%s\n' "${out}" | grep -q "invalid argument" ; then + echo "autoenv_authorize did not report proper error for invalid option." + exit 1 +fi