From 57d399ab91c160327aaab1ab318ce701e727807b Mon Sep 17 00:00:00 2001 From: =?utf8?q?Paul=20H=C3=A4nsch?= Date: Tue, 19 Oct 2021 17:11:10 +0200 Subject: [PATCH] Squashed 'cgilite/' changes from af27357..5a44f82 5a44f82 allow server site message page to confirm registration 24df501 perform _BASE striping outside of internal web server 343a22a cleaner display of activation link, include port number in activation link f6fa7fb strip _BASE path from PATH_INFO variable 6c1784b user invite function, handle invite/registration expire, always allow registration of first user git-subtree-dir: cgilite git-subtree-split: 5a44f82326f28410194e75a587c6a625842a94f7 --- cgilite.sh | 6 +- users.sh | 179 +++++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 144 insertions(+), 41 deletions(-) diff --git a/cgilite.sh b/cgilite.sh index a2aa329..310dd64 100755 --- a/cgilite.sh +++ b/cgilite.sh @@ -48,7 +48,7 @@ _EXEC="${_EXEC:-${0%/*}}" _DATA="${_DATA:-.}" _EXEC="${_EXEC%/}" _DATA="${_DATA%/}" _BASE="${_BASE%/}" -# Carriare Return and Line Break characters for convenience +# Carriage Return and Line Break characters for convenience CR=" " BR=' ' @@ -90,7 +90,7 @@ HEX_DECODE(){ *) out="${out}${in%"${in#?}"}"; in="${in#?}"; continue;; esac; - # Hex escaes for printf (e.g. \x41) are not portable + # Hex escapes for printf (e.g. \x41) are not portable # The portable way for Hex output is transforming Hex to Octal # (e.g. \x41 = \101) case $in in @@ -195,6 +195,8 @@ if [ "${REQUEST_METHOD}" = POST -a "${CONTENT_LENGTH:-0}" -gt 0 -a \ cgilite_post="$(head -c "$CONTENT_LENGTH")" fi +PATH_INFO="$(PATH "/${PATH_INFO#${_BASE}}")" + debug(){ [ $# -gt 0 ] && printf '%s\n' "$@" >&2 || tee -a /dev/stderr; } [ "${DEBUG+x}" ] && env >&2 diff --git a/users.sh b/users.sh index 4c730ee..7721def 100755 --- a/users.sh +++ b/users.sh @@ -13,24 +13,70 @@ HTTP_HOST="$(HEADER Host)" MAILFROM="${MAILDOMAIN:-noreply@${HTTP_HOST%:*}}" user_db="${_DATA}/users.db" -unset USER_ID USER_NAME USER_EMAIL +unset USER_ID USER_NAME USER_STATUS USER_EMAIL USER_PWSALT USER_PWHASH \ + USER_EXPIRE USER_DEVICES USER_FUTUREUSE # USER DB # UID UNAME STATUS (pending|active|deleted) EMAIL PWSALT PWHASH EXPIRE DEVICES FUTUREUSE -user_init(){ - local user_id="$(SESSION_VAR user_id)" - local UID UNAME STATUS EMAIL PWSALT PWHASH EXPIRE DEVICES FUTUREUSE - [ "$user_id" ] \ - && read -r UID UNAME STATUS EMAIL PWSALT PWHASH EXPIRE DEVICES FUTUREUSE <<-EOF +user_id="$(SESSION_VAR user_id)" +if [ "$user_id" -a -f "$user_db" -a -r "$user_db" ]; then + read -r USER_ID USER_NAME USER_STATUS USER_EMAIL USER_PWSALT USER_PWHASH \ + USER_EXPIRE USER_DEVICES USER_FUTUREUSE <<-EOF $(grep "^${user_id} " "$user_db") EOF - [ "$STATUS" -a "$EXPIRE" ] \ - && if [ "$STATUS" = active -a "$EXPIRE" -gt "$_DATE" ]; then - USER_ID="$UID" - USER_NAME="$(UNSTRING "$UNAME")" - USER_EMAIL="$(UNSTRING "$EMAIL")" - fi + if [ "$USER_ID" -a "$USER_STATUS" = active -a "$USER_EXPIRE" -gt "$_DATE" ]; then + USER_NAME="$(UNSTRING "$USER_NAME")" + USER_EMAIL="$(UNSTRING "$USER_EMAIL")" + USER_DEVICES="$(UNSTRING "$USER_DEVICES")" + unset USER_PWSALT USER_PWHASH + else + unset USER_ID USER_NAME USER_STATUS USER_EMAIL USER_PWSALT USER_PWHASH \ + USER_EXPIRE USER_DEVICES USER_FUTUREUSE + fi +fi +unset user_id + +user_update_user() { + # internal function for user update + local uid="$1" uname status email pwsalt pwhash expire devices futureuse + local UID UNAME STATUS EMAIL PWSALT PWHASH EXPIRE DEVICES FUTUREUSE + local arg + + for arg in "$@"; do case $arg in + uname=*) uname="${arg#*=}";; + status=*) status="${arg#*=}";; + email=*) email="${arg#*=}";; + password=*) pwsalt="$(randomid)"; pwhash="$(user_pwhash "$pwsalt" "${arg#*=}")";; + devices=*) devices="${arg#*=}";; + esac; done + + if LOCK "$user_db"; then + while read -r UID UNAME STATUS EMAIL PWSALT PWHASH EXPIRE DEVICES \ + FUTUREUSE; do + if [ "$UID" = "$uid" ]; then + printf '%s %s %s %s %s %s %s %s %s\n' \ + "$uid" "$(STRING "${uname-$(UNSTRING "$UNAME")}")" \ + "${status:-${status-${STATUS}}${status+\\}}" \ + "${email:-${email-${EMAIL}}${email+\\}}" \ + "${pwsalt:-${PWSALT}}" "${pwhash:-${PWHASH}}" \ + "$((_DATE + 86400 * 730))" \ + "$(STRING "${devices-$(UNSTRING "$DEVICES")}")" \ + "${FUTUREUSE:-\\}" + elif [ "$STATUS" = pending -a ! "$EXPIRE" -ge "$_DATE" ]; then + # omit expired invitations from output + : + else + printf '%s %s %s %s %s %s %s %s %s\n' \ + "$UID" "$UNAME" "$STATUS" "$EMAIL" "$PWSALT" "$PWHASH" \ + "$EXPIRE" "$DEVICES" "$FUTUREUSE" + fi + done <"$user_db" >"${user_db}.$$" + mv -- "${user_db}.$$" "$user_db" + RELEASE "$user_db" + else + return 1 + fi } user_checkname(){ @@ -89,7 +135,7 @@ user_register(){ local pwsalt="$(randomid)" local pw="$(POST pw |grep -m1 -xE '.{6,}' )" pwconfirm="$(POST pwconfirm)" - if [ "$USER_REGISTRATION" != true ]; then + if [ "$USER_REGISTRATION" != true -a -s "$user_db" ]; then REDIRECT "${_BASE}${PATH_INFO}#ERROR_REGISTRATION_DISABLED" fi @@ -103,6 +149,9 @@ user_register(){ "$uid" "$(STRING "$email")" "$(( $_DATE + 86400 ))" \ >>"$user_db" RELEASE "$user_db" + + debug "Sending Activation Link:" \ + "https://${HTTP_HOST}${_BASE}${PATH_INFO}?user_confirm=${uid}+$(session_mac "$uid")" sendmail -t -f "$MAILFROM" <<-EOF From: ${MAILFROM} To: ${email} @@ -112,7 +161,7 @@ user_register(){ You can activate your account using this link: - https://${HTTP_HOST%:*}/${_BASE}${PATH_INFO}?user_confirm=${uid}+$(session_mac "$uid") + https://${HTTP_HOST}${_BASE}${PATH_INFO}?user_confirm=${uid}+$(session_mac "$uid") This registration link will expire after 24 hours. @@ -156,6 +205,51 @@ user_register(){ fi } +user_invite(){ + local uid="$(timeid)" + local email="$(POST email |user_checkemail)" + local message="$(POST message)" + + if [ ! "email" ]; then + REDIRECT "${_BASE}${PATH_INFO}#ERROR_EMAIL_INVALID" + elif user_emailexist "$email"; then + REDIRECT "${_BASE}${PATH_INFO}#ERROR_EMAIL_EXISTS" + elif LOCK "$user_db"; then + printf '%s \\ pending %s \\ \\ %i \\ \\\n' \ + "$uid" "$(STRING "$email")" "$(( $_DATE + 86400 ))" \ + >>"$user_db" + RELEASE "$user_db" + debug "Sending Invitation Link:" \ + "https://${HTTP_HOST}${BASE}${PATH_INFO}?user_confirm=${uid}+$(session_mac "$uid")" + sendmail -t -f "$MAILFROM" <<-EOF + From: ${MAILFROM} + To: ${email} + Subject: You have been invited to ${HTTP_HOST%:*} + + ${USER_NAME:-Someone} has offered an invitation to this email address. + + ${message} + + You can create your account using this link: + + https://${HTTP_HOST}${_BASE}${PATH_INFO}?user_confirm=${uid}+$(session_mac "$uid") + + This registration link will expire after 24 hours. + + If you do not know what this is about, then someone else probably + entered your email address by accident. In this case you shoud + simply ignore this message and we will remove your email address from + our database within the next day. + + This is an automatic email. Any direct reply will not be received. + Your Account Registration Robot. + EOF + REDIRECT "${_BASE}${PATH_INFO}#USER_REGISTER_CONFIRM" + else + REDIRECT "${_BASE}${PATH_INFO}#ERROR_USER_NOLOCK" + fi +} + user_confirm(){ # enable account local UID UNAME STATUS EMAIL PWSALT PWHASH EXPIRE DEVICES FUTUREUSE @@ -165,6 +259,10 @@ user_confirm(){ local pwsalt="$(randomid)" local pw="$(POST pw |grep -m1 -xE '.{6,}' )" pwconfirm="$(POST pwconfirm)" + read -r UID UNAME STATUS EMAIL PWSALT PWHASH EXPIRE DEVICES FUTUREUSE <<-EOF + $(grep "^${uid:-invalid} " "$user_db") + EOF + if [ "$signature" != "$(session_mac "$uid")" ]; then REDIRECT "${_BASE}${PATH_INFO}?${QUERY_STRING}#ERROR_LINK_INVALID" elif [ ! "$uname" ]; then @@ -175,28 +273,12 @@ user_confirm(){ REDIRECT "${_BASE}${PATH_INFO}?${QUERY_STRING}#ERROR_PW_EMPTYTOOSHORT" elif [ "$pw" != "$pwconfirm" ]; then REDIRECT "${_BASE}${PATH_INFO}?${QUERY_STRING}#ERROR_PW_MISMATCH" - elif LOCK "$user_db"; then - read -r UID UNAME STATUS EMAIL PWSALT PWHASH EXPIRE DEVICES FUTUREUSE <<-EOF - $(grep "^${uid} " "$user_db") - EOF - - if [ "$STATUS" != pending -o "$EXPIRE" -le "$_DATE" ]; then - RELEASE "$user_db" - REDIRECT "${_BASE}${PATH_INFO}?${QUERY_STRING}#ERROR_LINK_INVALID" - else - printf '%s %s active %s %s %s %i %s %s\n' \ - "$UID" "$(STRING "$uname")" "$EMAIL" \ - "$pwsalt" "$(user_pwhash "$pwsalt" "$pw")" \ - "$(( $_DATE + 86400 * 730 ))" "$DEVICES" "$FUTUREUSE" \ - >"${user_db}.$$" - grep -v "^${uid} " "$user_db" >>"${user_db}.$$" - mv "${user_db}.$$" "${user_db}" - RELEASE "$user_db" - - SESSION_COOKIE new - SESSION_BIND user_id "$UID" - REDIRECT "${_BASE}${PATH_INFO}#USER_REGISTER_CONFIRM" - fi + elif [ "$STATUS" != pending -o \! "$EXPIRE" -gt "$_DATE" ]; then + REDIRECT "${_BASE}${PATH_INFO}?${QUERY_STRING}#ERROR_LINK_INVALID" + elif user_update_user "$UID" uname="$uname" status=active password="$pw"; then + SESSION_COOKIE new + SESSION_BIND user_id "$UID" + REDIRECT "${_BASE}${PATH_INFO}?user_register=confirm#USER_REGISTER_CONFIRM" else REDIRECT "${_BASE}${PATH_INFO}#ERROR_USER_NOLOCK" fi @@ -243,11 +325,10 @@ user_disable(){ : } -user_init - [ "$REQUEST_METHOD" = POST ] && case "$(POST action)" in user_register) user_register ;; user_confirm) user_confirm ;; + user_invite) user_invite ;; user_login) user_login ;; user_logout) user_logout ;; user_update) @@ -261,7 +342,7 @@ esac w_user_register(){ if [ "$(GET user_confirm)" ]; then w_user_confirm - elif [ "$USER_REGISTRATION" != true ]; then + elif [ "$USER_REGISTRATION" != true -a -s "$user_db" ]; then cat <<-EOF [div #user_register .disabled User Registration is disabled. @@ -326,6 +407,26 @@ w_user_confirm(){ fi } +w_user_invite(){ + if [ "$(GET user_confirm)" ]; then + w_user_confirm + elif [ "$USER_ID" ]; then + cat <<-EOF + [form #user_invite method=POST + [input placeholder="Email Recipient" name=email autocomplete=off] + [textarea name="message" placeholder="Message to recipient" . ] + [submit "action" "user_invite" Send Invitation] + ] + EOF + else + cat <<-EOF + [div #user_invite .notallowed + Only registered users may send an invitation to another user. + ] + EOF + fi +} + w_user_login(){ if [ ! "$USER_ID" ]; then cat <<-EOF -- 2.39.2