From f9cfcde441afed9d6db96e201935b6208903046d Mon Sep 17 00:00:00 2001 From: =?utf8?q?Paul=20H=C3=A4nsch?= Date: Tue, 10 May 2022 19:22:18 +0200 Subject: [PATCH] basic ACLs --- acl.sh | 114 ++++++++++++++++++++++++++++++++++++++ index.cgi | 60 ++++++++------------ pages/[wiki]/403/#page.md | 4 ++ themes/default.sh | 48 +++++++++++++--- 4 files changed, 181 insertions(+), 45 deletions(-) create mode 100755 acl.sh create mode 100644 pages/[wiki]/403/#page.md diff --git a/acl.sh b/acl.sh new file mode 100755 index 0000000..9ec2597 --- /dev/null +++ b/acl.sh @@ -0,0 +1,114 @@ +#!/bin/sh + +# ACL_OVERRIDE="${ACL_OVERRIDE:-Admin:read,write}" +ACL_DEFAULT="${ACL_DEFAULT:-All:read${BR}Known:read,write}" + +acl_cachepath='' +acl_collection='' + +acl_collect(){ + local path="${1:-${PATH_INFO}}" + # Get directory part of PATH_INFO + local path="${path%/*}/./" + local pagefile head acl + + if [ "$acl_cachepath" = "$path" ]; then + printf '%s\n' "$ACL_OVERRIDE" "$acl_collection" "$ACL_DEFAULT" + return 0 + else + acl_cachepath="$path" + acl_collection='' + fi + + printf '%s\n' "$ACL_OVERRIDE" + + while :; do + [ "$path" = / ] && break + path="${path%/*/}/" + + if [ -f "$_DATA/pages/$path/#page.md" ]; then + pagefile="$_DATA/pages/$path/#page.md" + elif [ -f "$_EXEC/pages/$path/#page.md" ]; then + pagefile="$_EXEC/pages/$path/#page.md" + else + continue + fi + + n=20; while read -r head acl; do + if [ "$head" = "%acl" ]; then + acl_collection="${acl%${CR}}${BR}" + printf "%s\n" "${acl%${CR}}" + n=$((n+1)) + fi + + n="$((n - 1))" + [ "$n" -eq 0 ] && break + done <"$pagefile" + done + + printf '%s\n' "$ACL_DEFAULT" +} + +acl_read(){ + local page="${1:-${PATH_INFO}}" + local acl + + while read -r acl; do + case ${acl##*:} in + read|*,read,*|read,*|*,read) + acl="${acl%%:*}:read";; + *) acl="${acl%%:*}:";; + esac + [ "$USER_NAME" ] && case $acl in + "Known:read") return 0;; + "Known:") return 1;; + "+Known:read") return 0;; + "-Known:read") return 1;; + "${USER_NAME}:read") return 0;; + "${USER_NAME}:") return 1;; + "+{$USER_NAME}:read") return 0;; + "-{$USER_NAME}:read") return 1;; + esac + case $acl in + "All:read") return 0;; + "All:") return 1;; + "+All:read") return 0;; + "-All:read") return 1;; + esac + done <<-EOF + $(acl_collect "$page") + EOF + return 1 +} + +acl_write(){ + local page="${1:-${PATH_INFO}}" + local acl + + while read -r acl; do + case ${acl##*:} in + write|*,write,*|write,*|*,write) + acl="${acl%%:*}:write";; + *) acl="${acl%%:*}:";; + esac + [ "$USER_NAME" ] && case ${acl} in + "Known:write") return 0;; + "Known:") return 1;; + "+Known:write") return 0;; + "-Known:write") return 1;; + "${USER_NAME}:write") return 0;; + "${USER_NAME}:") return 1;; + "+{$USER_NAME}:write") return 0;; + "-{$USER_NAME}:write") return 1;; + esac + case $acl in + "All:write") return 0;; + "All:") return 1;; + "+All:write") return 0;; + "-All:write") return 1;; + esac + done <<-EOF + $(acl_collect "$page") + EOF + return 1 +} diff --git a/index.cgi b/index.cgi index 9b38362..b1b2c2d 100755 --- a/index.cgi +++ b/index.cgi @@ -4,6 +4,7 @@ . "${_EXEC}/cgilite/session.sh" . "${_EXEC}/cgilite/file.sh" . "${_EXEC}/cgilite/users.sh" +. "${_EXEC}/acl.sh" . "${_EXEC}/themes/default.sh" @@ -19,35 +20,39 @@ else md() { cat; } fi -wiki_text() { - # Print source text of a wiki page - # Get page from data or underlay dir +mdfile(){ local page="$(PATH "$1")" - if [ -f "$_DATA/pages/$page/#page.md" ]; then - cat -- "$_DATA/pages/$page/#page.md" + if [ -f "$_DATA/pages/$page/#page.md" ]; then + printf %s\\n "$_DATA/pages/$page/#page.md" elif [ -f "$_EXEC/pages/$page/#page.md" ]; then - cat -- "$_EXEC/pages/$page/#page.md" + printf %s\\n "$_EXEC/pages/$page/#page.md" else return 1 fi } +wiki_text() { + # Print source text of a wiki page + # Get page from data or underlay dir + local page="$(PATH "$1")" mdfile + + mdfile="$(mdfile "$page")" || return 4 + acl_read "$page" || return 3 + cat -- "$mdfile" +} + wiki() { # Print content of a wiki page # Get page from data or underlay dir, handle caching - local page="$(PATH "$1")" md cache cachetime + local page="$(PATH "$1")" mdfile cache cachetime - cache="$_DATA/pages/$page/#page.cache" - if [ -f "$_DATA/pages/$page/#page.md" ]; then - md="$_DATA/pages/$page/#page.md" - elif [ -f "$_EXEC/pages/$page/#page.md" ]; then - md="$_EXEC/pages/$page/#page.md" - else - return 1 - fi + cache="$_DATA/pages/$page/#page.${USER_ID}.cache" - cachetime="$(stat -c %Y -- "$md" "$cache" 2>/dev/null)" + mdfile="$(mdfile "$page")" || return 4 + acl_read "$page" || return 3 + + cachetime="$(stat -c %Y -- "$mdfile" "$cache" 2>/dev/null)" if [ "${cachetime#*${BR}}" -gt "${cachetime%${BR}*}" \ -a "${cachetime#*${BR}}" -gt "$((_DATE - CACHE_AGE))" ]; then @@ -56,37 +61,18 @@ wiki() { mkdir -p -- "$_DATA/pages/$page/" # Macros expect to find page directory as working dir ( cd -- "$_DATA/pages/$page/"; - md <"$md" |tee -- "${cache}.$$" + md <"$mdfile" |tee -- "${cache}.$$" ) mv -- "${cache}.$$" "${cache}" fi } -attachment() { - local file="$(PATH "$1")" - - # TODO: deliver downscaled images, etc. - if [ -f "$_DATA/pages/${file%/*}/#attachments/${file#*/}" ]; then - FILE "$_DATA/pages/${file%/*}/#attachments/${file#*/}" - elif [ -f "$_EXEC/pages/${file%/*}/#attachments/${file#*/}" ]; then - FILE "$_EXEC/pages/${file%/*}/#attachments/${file#*/}" - elif [ -d "$_DATA/pages/${file}/" -o -d "$_EXEC/pages/${file}" ]; then - # path looks like a rogue page name (without trailing slash), so redirect - REDIRECT "$_BASE/${file}/" - else - return 1 - fi -} - case "${PATH_INFO}" in /"[.]"/*) FILE "${_EXEC}/${PATH_INFO#/\[.\]}" ;; */) - if [ -f "$_DATA/pages/$PATH_INFO/#page.md" \ - -o -f "$_EXEC/pages/$PATH_INFO/#page.md" ]; then - theme_page "${PATH_INFO}" - fi + theme_page "${PATH_INFO}" ;; */"[login]") [ "$USER_NAME" ] \ diff --git a/pages/[wiki]/403/#page.md b/pages/[wiki]/403/#page.md new file mode 100644 index 0000000..66ea5e2 --- /dev/null +++ b/pages/[wiki]/403/#page.md @@ -0,0 +1,4 @@ +403 +=== + +**Forbidden** diff --git a/themes/default.sh b/themes/default.sh index 1830e26..022a29a 100755 --- a/themes/default.sh +++ b/themes/default.sh @@ -22,6 +22,14 @@ theme_page(){ local page="$1" title title="${page%/}"; title="${title##*/}" + if [ ! "$(mdfile "$page")" ]; then + theme_404 + return 0 + elif ! acl_read "$page"; then + theme_403 + return 0 + fi + # Important! Web Server response including newline printf "%s\r\n" "Content-Type: text/html; charset=utf-8" "" @@ -33,12 +41,14 @@ theme_page(){ $(theme_header)
- + $(acl_write "$page" && printf %s \ + '' + )
- $(wiki "$page" || printf 'Page not found') + $(wiki "$page" || printf 'Error while loading page
function "wiki" of index.sh returned with an error.')
$(theme_footer) @@ -50,6 +60,14 @@ theme_editor(){ local page="$1" title title="${page%/}"; title="${title##*/}" + if [ ! "$(mdfile "$page")" ]; then + theme_404 + return 0 + elif ! acl_write "$page"; then + theme_403 + return 0 + fi + # Important! Web Server response including newline printf "%s\r\n" "Content-Type: text/html; charset=utf-8" "" @@ -110,15 +128,29 @@ theme_attachments(){ } theme_login(){ - theme_page '[wiki]/login/' + theme_page '/[wiki]/login/' } theme_register(){ - theme_page '[wiki]/register/' + theme_page '/[wiki]/register/' +} + +theme_403(){ + printf "%s\r\n" "Status: 403 Forbidden" + + if [ "$(mdfile '/[wiki]/403/')" ]; then + theme_page '/[wiki]/403/' + else + printf "Content-Length: 0\r\n\r\n" + fi } theme_404(){ printf "%s\r\n" "Status: 404 Not Found" - theme_page '[wiki]/404/' + if [ "$(mdfile '/[wiki]/404/')" ]; then + theme_page '/[wiki]/404/' + else + printf "Content-Length: 0\r\n\r\n" + fi } -- 2.39.2