From e738981089accbfb39bad1cb313281d966c07ed7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=B6rnquist?= Date: Tue, 11 Oct 2022 01:20:53 +0200 Subject: Rewrote filter system. --- files/filters/hugo-highlighting.sh | 138 -------------- files/filters/hugo-pre.sh | 26 --- manifests/filter.pp | 53 ++++++ manifests/filter_setup.pp | 11 ++ manifests/init.pp | 46 ++--- metadata.json | 4 + templates/cgitrc.epp | 64 ------- templates/hugo-authentication.lua.epp | 329 ---------------------------------- templates/lower.epp | 31 ++++ templates/upper.epp | 21 +++ 10 files changed, 139 insertions(+), 584 deletions(-) delete mode 100755 files/filters/hugo-highlighting.sh delete mode 100755 files/filters/hugo-pre.sh create mode 100644 manifests/filter.pp create mode 100644 manifests/filter_setup.pp delete mode 100644 templates/cgitrc.epp delete mode 100644 templates/hugo-authentication.lua.epp create mode 100644 templates/lower.epp create mode 100644 templates/upper.epp diff --git a/files/filters/hugo-highlighting.sh b/files/filters/hugo-highlighting.sh deleted file mode 100755 index 603b8ab..0000000 --- a/files/filters/hugo-highlighting.sh +++ /dev/null @@ -1,138 +0,0 @@ -#!/bin/bash -# This script can be used to implement syntax highlighting in the cgit -# tree-view by refering to this file with the source-filter or repo.source- -# filter options in cgitrc. -# -# This script requires a shell supporting the ${var##pattern} syntax. -# It is supported by at least dash and bash, however busybox environments -# might have to use an external call to sed instead. -# -# Note: the highlight command (http://www.andre-simon.de/) uses css for syntax -# highlighting, so you'll probably want something like the following included -# in your css file: -# -# Style definition file generated by highlight 2.4.8, http://www.andre-simon.de/ -# -# table.blob .num { color:#2928ff; } -# table.blob .esc { color:#ff00ff; } -# table.blob .str { color:#ff0000; } -# table.blob .dstr { color:#818100; } -# table.blob .slc { color:#838183; font-style:italic; } -# table.blob .com { color:#838183; font-style:italic; } -# table.blob .dir { color:#008200; } -# table.blob .sym { color:#000000; } -# table.blob .kwa { color:#000000; font-weight:bold; } -# table.blob .kwb { color:#830000; } -# table.blob .kwc { color:#000000; font-weight:bold; } -# table.blob .kwd { color:#010181; } -# -# -# Style definition file generated by highlight 2.6.14, http://www.andre-simon.de/ -# -# body.hl { background-color:#ffffff; } -# pre.hl { color:#000000; background-color:#ffffff; font-size:10pt; font-family:'Courier New';} -# .hl.num { color:#2928ff; } -# .hl.esc { color:#ff00ff; } -# .hl.str { color:#ff0000; } -# .hl.dstr { color:#818100; } -# .hl.slc { color:#838183; font-style:italic; } -# .hl.com { color:#838183; font-style:italic; } -# .hl.dir { color:#008200; } -# .hl.sym { color:#000000; } -# .hl.line { color:#555555; } -# .hl.mark { background-color:#ffffbb;} -# .hl.kwa { color:#000000; font-weight:bold; } -# .hl.kwb { color:#830000; } -# .hl.kwc { color:#000000; font-weight:bold; } -# .hl.kwd { color:#010181; } -# -# -# Style definition file generated by highlight 3.8, http://www.andre-simon.de/ -# -# body.hl { background-color:#e0eaee; } -# pre.hl { color:#000000; background-color:#e0eaee; font-size:10pt; font-family:'Courier New';} -# .hl.num { color:#b07e00; } -# .hl.esc { color:#ff00ff; } -# .hl.str { color:#bf0303; } -# .hl.pps { color:#818100; } -# .hl.slc { color:#838183; font-style:italic; } -# .hl.com { color:#838183; font-style:italic; } -# .hl.ppc { color:#008200; } -# .hl.opt { color:#000000; } -# .hl.lin { color:#555555; } -# .hl.kwa { color:#000000; font-weight:bold; } -# .hl.kwb { color:#0057ae; } -# .hl.kwc { color:#000000; font-weight:bold; } -# .hl.kwd { color:#010181; } -# -# -# Style definition file generated by highlight 3.13, http://www.andre-simon.de/ -# -# body.hl { background-color:#e0eaee; } -# pre.hl { color:#000000; background-color:#e0eaee; font-size:10pt; font-family:'Courier New',monospace;} -# .hl.num { color:#b07e00; } -# .hl.esc { color:#ff00ff; } -# .hl.str { color:#bf0303; } -# .hl.pps { color:#818100; } -# .hl.slc { color:#838183; font-style:italic; } -# .hl.com { color:#838183; font-style:italic; } -# .hl.ppc { color:#008200; } -# .hl.opt { color:#000000; } -# .hl.ipl { color:#0057ae; } -# .hl.lin { color:#555555; } -# .hl.kwa { color:#000000; font-weight:bold; } -# .hl.kwb { color:#0057ae; } -# .hl.kwc { color:#000000; font-weight:bold; } -# .hl.kwd { color:#010181; } -# -# -# The following environment variables can be used to retrieve the configuration -# of the repository for which this script is called: -# CGIT_REPO_URL ( = repo.url setting ) -# CGIT_REPO_NAME ( = repo.name setting ) -# CGIT_REPO_PATH ( = repo.path setting ) -# CGIT_REPO_OWNER ( = repo.owner setting ) -# CGIT_REPO_DEFBRANCH ( = repo.defbranch setting ) -# CGIT_REPO_SECTION ( = section setting ) -# CGIT_REPO_CLONE_URL ( = repo.clone-url setting ) -# - -# store filename and extension in local vars -BASENAME="$1" -EXTENSION="${BASENAME##*.}" - -[ "${BASENAME}" = "${EXTENSION}" ] && EXTENSION=txt -[ -z "${EXTENSION}" ] && EXTENSION=txt - -# map Makefile and Makefile.* to .mk -[ "${BASENAME%%.*}" = "Makefile" ] && EXTENSION=mk - -# highlight versions 2 and 3 have different commandline options. Specifically, -# the -X option that is used for version 2 is replaced by the -O xhtml option -# for version 3. -# -# Version 2 can be found (for example) on EPEL 5, while version 3 can be -# found (for example) on EPEL 6. -# -# This is for version 2 -#exec highlight --force -f -I -X -S "$EXTENSION" 2>/dev/null - -# env - -extension=${REQUEST_URI: -3} - -case $extension in - org) - temp=$(mktemp) - cat - > $temp.org - emacs $temp.org \ - --quick \ - --batch \ - --funcall org-html-export-to-html \ - --kill - tail -n+10 $temp.html - exit 0 - ;; -esac - -exec highlight --force -f -I -O xhtml -S "$EXTENSION" # 2>/dev/null diff --git a/files/filters/hugo-pre.sh b/files/filters/hugo-pre.sh deleted file mode 100755 index cd303ec..0000000 --- a/files/filters/hugo-pre.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/bash - -name=$1 - -extension=${name: -3} - -case $extension in - .md) pandoc -f gfm -t html ;; - *) - cat <<- EOF -
$(cat -)
- EOF - ;; -esac - -prel_head="$(grep -Eo 'h=([-A-Za-z0-9.~_+]|%[A-Fa-f0-9]{2})*' <<< "$REQUEST_URI")" -if [ $? -eq 0 ]; then - head=$(cut -c 3- <<< "$prel_head") -else - head=$CGIT_REPO_DEFBRANCH -fi - -echo "
" -echo "
"
-cloc --git "$head" --quiet --md | pandoc -f gfm -t html
-echo "
" diff --git a/manifests/filter.pp b/manifests/filter.pp new file mode 100644 index 0000000..2dda015 --- /dev/null +++ b/manifests/filter.pp @@ -0,0 +1,53 @@ +define cgit::filter ( + String $filtername = $name, + Optional[String] $source = undef, + Optional[String] $content = undef, + Hash $file_props = {}, + Enum['lua', 'exec'] $type = stdlib::extname($source) ? { + '.lua' => 'lua', + default => 'exec', + } +) { + + $valid_filters = [ + 'about', + 'auth', + 'commit', + 'email', + 'owner', + 'source', + ] + + if ! member($valid_filters, $filtername) { + crit("${filtername} not a valid cgit filter") + } + + $dest = "${cgit::filterpath}/${filtername}-filter" + + file { $dest: + ensure => file, + mode => type ? { + 'lua' => '0444', + 'exec' => '0555', + } + source => $source, + content => $content, + * => $file_props, + } + + concat::fragment { "cgit config ${}": + target => $::cgit::cgitrc, + content => "${filter_name}-filter=${type}:${dest}\n", + } + + [$about_filter, $source_filter].each |$f| { + file { "${filterpath}/${f}": + ensure => file, + source => "puppet:///modules/cgit/filters/${f}", + mode => stdlib::extname($f) ? { + '.lua' => '0444', + default => '0555', + }, + } + } +} diff --git a/manifests/filter_setup.pp b/manifests/filter_setup.pp new file mode 100644 index 0000000..8f42537 --- /dev/null +++ b/manifests/filter_setup.pp @@ -0,0 +1,11 @@ +class cgit::filter_setup ( + String $filterpath = $::cgit::filterpath, +) { + file { dirname($filterpath): + ensure => directory, + } + + file { $filterpath: + ensure => directory, + } +} diff --git a/manifests/init.pp b/manifests/init.pp index 62cb2a3..bbd8edb 100644 --- a/manifests/init.pp +++ b/manifests/init.pp @@ -1,6 +1,6 @@ class cgit ( String $root = '/var/www/cgit', - String $filterpath = '/usr/lib/cgit/extra-filters', + String $filterpath = '/usr/lib/cgit/puppet-controlled-filters', String $root_title, String $root_desc, String $root_readme_source = "puppet:///modules/${module_name}/root_readme", @@ -20,6 +20,8 @@ class cgit ( Optional[String] $server_name = undef, Optional[String] $certname = undef, String $htpasswd = '/var/lib/nginx/cgit-htpasswd', + String $cgitrc = '/etc/cgitrc', + Hash[String, Hash] $filters = {}, ) { # TODO figure out where CSS comes from @@ -29,11 +31,24 @@ class cgit ( 'cgit', ], { ensure => installed }) - file { '/etc/cgitrc': - ensure => file, - content => epp('cgit/cgitrc.epp'), + + concat { $cgitrc: + ensure => present, + } + concat::fragment { 'cgit config upper half': + order => 0, + content => epp('cgit/upper.epp'), + target => $::cgit::cgitrc, + } + + concat::fragment { 'cgit config lower half': + order => 99, + content => epp('cgit/lower.epp'), + target => $::cgit::cgitrc, } + create_resources(git::filter, $filters) + file { "${root}/logo": ensure => directory, } @@ -71,30 +86,7 @@ class cgit ( } } - file { dirname($filterpath): - ensure => directory, - } - file { $filterpath: - ensure => directory, - } - - [$about_filter, $source_filter].each |$f| { - file { "${filterpath}/${f}": - ensure => file, - source => "puppet:///modules/cgit/filters/${f}", - mode => stdlib::extname($f) ? { - '.lua' => '0444', - default => '0555', - }, - } - } - - file { "${filterpath}/${auth_filter}": - ensure => file, - content => epp("cgit/${auth_filter}.epp"), - mode => '0444', - } if $manage_server { if $server_name == undef { diff --git a/metadata.json b/metadata.json index 4c985d4..42d44ed 100644 --- a/metadata.json +++ b/metadata.json @@ -13,6 +13,10 @@ { "name": "puppetlabs/stdlib", "version_requirement": ">= 8.1.0 < 9.0.0" + }, + { + "name": "puppetlabs/concat", + "version_requirement": ">= 7.2.0 < 8.0.0" } ], "operatingsystem_support": [ diff --git a/templates/cgitrc.epp b/templates/cgitrc.epp deleted file mode 100644 index 0f4636d..0000000 --- a/templates/cgitrc.epp +++ /dev/null @@ -1,64 +0,0 @@ -# -# cgit config -# see cgitrc(5) for details -# -# FILE MANAGED BY PUPPET -# - -# css=/cgit.css -logo=/logo/logo.png -# favicon=/favicon.ico - -enable-index-owner=1 - -root-title=<%= $cgit::root_title %> -#root-desc=Tändes endast mot lådans plån -root-desc=<%= $cgit::root_desc %> -# Also causes the `about' page to exist -# /usr/lib/cgit/readme -root-readme=<%= $cgit::root %>/root_readme - -#source-filter=/srv/filters/dispatch.sh - -# about-filter=/usr/local/lib/cgit/filters/hugo-pre.sh -# auth-filter=lua:/usr/local/lib/cgit/filters/hugo-authentication.lua -# source-filter=/usr/local/lib/cgit/filters/hugo-highlighting.sh -about-filter=<%= $cgit::filterpath %>/<%= $cgit::about_filter %> -auth-filter=<% if stdlib::extname($cgit::auth_filter) == '.lua' { - -%>lua:<% - } -%><%= $cgit::filterpath %>/<%= $cgit::auth_filter %> -source-filter=<%= $cgit::filterpath %>/<%= $cgit::source_filter %> - -enable-follow-links=1 -enable-subject-links=1 # show commit summary for parrent - -#side-by-side-diffs=1 -enable-commit-graph=1 -enable-index-links=1 -enable-remote-branches=1 -local-time=1 - -case-sensative-sort=0 - -max-repo-count=100 - -enable-http-clone=<%= if $cgit::enable_http_clone { 1 } else { 0 } %> -clone-url=<%= $cgit::clone_url.join(' ') %> - -readme=:README -readme=:README.md -readme=:README.txt -readme=:readme -readme=:readme.md -readme=:readme.txt - -virtual-root= -remove-suffix=1 -section-from-path=1 -enable-git-config=1 - -# section=~/git -snapshots=tar.gz tar.xz -scan-path=<%= $cgit::scan_path %> - -#scan-path=/var/www/git/repositories/ diff --git a/templates/hugo-authentication.lua.epp b/templates/hugo-authentication.lua.epp deleted file mode 100644 index 7ccce05..0000000 --- a/templates/hugo-authentication.lua.epp +++ /dev/null @@ -1,329 +0,0 @@ --- This script may be used with the auth-filter. Be sure to configure it as you wish. --- --- Requirements: --- luaossl --- --- luaposix --- --- -local sysstat = require("posix.sys.stat") -local unistd = require("posix.unistd") -local rand = require("openssl.rand") -local hmac = require("openssl.hmac") - --- --- --- Configure these variables for your settings. --- --- - --- A list of password protected repositories along with the users who can access them. -local protected_repos = { - glouglou = { laurent = true, jason = true }, - qt = { jason = true, bob = true } -} - -local public_repos = {} -<%- $cgit::public_repos.each |$repo| { -%> -public_repos["<%= $repo %>"] = true -<%- } -%> - --- A list of users and hashes, generated with `mkpasswd -m sha-512 -R 300000`. -local users = { - <%- $cgit::users.each |$user| { -%> - <%= $user['name'] %> = "<%= $user['pass'] %>", - <%- } -%> -} - --- Set this to a path this script can write to for storing a persistent --- cookie secret, which should be guarded. -local secret_filename = "/var/cache/cgit/auth-secret" - --- --- --- Authentication functions follow below. Swap these out if you want different authentication semantics. --- --- - --- Sets HTTP cookie headers based on post and sets up redirection. -function authenticate_post() - local hash = users[post["username"]] - local redirect = validate_value("redirect", post["redirect"]) - - if redirect == nil then - not_found() - return 0 - end - - redirect_to(redirect) - - if hash == nil or hash ~= unistd.crypt(post["password"], hash) then - set_cookie("cgitauth", "") - else - -- One week expiration time - local username = secure_value("username", post["username"], os.time() + 604800) - set_cookie("cgitauth", username) - end - - html("\n") - return 0 -end - - --- Returns 1 if the cookie is valid and 0 if it is not. -function authenticate_cookie() - - -- Everyone has access to the index page. - -- printenv(os.getenv("CGIT_REPO_NAME")); - if cgit["repo"] == "" then - return 1 - end - - ispublic = public_repos[cgit["repo"]] - -- accepted_users = protected_repos[cgit["repo"]] - if ispublic == true then - -- We return as valid if the repo is public - return 1 - end - - local username = validate_value("username", get_cookie(http["cookie"], "cgitauth")) - if username == nil then - return 0 - else - return 1 - end -end - --- Prints the html for the login form. -function body() - html("

Authentication Required

") - -- html("HTML = ") - -- html(tostring(cgit["repo"] == "")) - html("
") - html("") - html("") - html("") - html("") - html("") - html("
") - - return 0 -end - - - --- --- --- Wrapper around filter API, exposing the http table, the cgit table, and the post table to the above functions. --- --- - -local actions = {} -actions["authenticate-post"] = authenticate_post -actions["authenticate-cookie"] = authenticate_cookie -actions["body"] = body - -function filter_open(...) - action = actions[select(1, ...)] - - http = {} - http["cookie"] = select(2, ...) - http["method"] = select(3, ...) - http["query"] = select(4, ...) - http["referer"] = select(5, ...) - http["path"] = select(6, ...) - http["host"] = select(7, ...) - http["https"] = select(8, ...) - - cgit = {} - cgit["repo"] = select(9, ...) - cgit["page"] = select(10, ...) - cgit["url"] = select(11, ...) - cgit["login"] = select(12, ...) - -end - -function filter_close() - return action() -end - -function filter_write(str) - post = parse_qs(str) -end - - --- --- --- Utility functions based on keplerproject/wsapi. --- --- - -function url_decode(str) - if not str then - return "" - end - str = string.gsub(str, "+", " ") - str = string.gsub(str, "%%(%x%x)", function(h) return string.char(tonumber(h, 16)) end) - str = string.gsub(str, "\r\n", "\n") - return str -end - -function url_encode(str) - if not str then - return "" - end - str = string.gsub(str, "\n", "\r\n") - str = string.gsub(str, "([^%w ])", function(c) return string.format("%%%02X", string.byte(c)) end) - str = string.gsub(str, " ", "+") - return str -end - -function parse_qs(qs) - local tab = {} - for key, val in string.gmatch(qs, "([^&=]+)=([^&=]*)&?") do - tab[url_decode(key)] = url_decode(val) - end - return tab -end - -function get_cookie(cookies, name) - cookies = string.gsub(";" .. cookies .. ";", "%s*;%s*", ";") - return url_decode(string.match(cookies, ";" .. name .. "=(.-);")) -end - -function tohex(b) - local x = "" - for i = 1, #b do - x = x .. string.format("%.2x", string.byte(b, i)) - end - return x -end - --- --- --- Cookie construction and validation helpers. --- --- - -local secret = nil - --- Loads a secret from a file, creates a secret, or returns one from memory. -function get_secret() - if secret ~= nil then - return secret - end - local secret_file = io.open(secret_filename, "r") - if secret_file == nil then - local old_umask = sysstat.umask(63) - local temporary_filename = secret_filename .. ".tmp." .. tohex(rand.bytes(16)) - local temporary_file = io.open(temporary_filename, "w") - if temporary_file == nil then - os.exit(177) - end - temporary_file:write(tohex(rand.bytes(32))) - temporary_file:close() - unistd.link(temporary_filename, secret_filename) -- Intentionally fails in the case that another process is doing the same. - unistd.unlink(temporary_filename) - sysstat.umask(old_umask) - secret_file = io.open(secret_filename, "r") - end - if secret_file == nil then - os.exit(177) - end - secret = secret_file:read() - secret_file:close() - if secret:len() ~= 64 then - os.exit(177) - end - return secret -end - --- Returns value of cookie if cookie is valid. Otherwise returns nil. -function validate_value(expected_field, cookie) - local i = 0 - local value = "" - local field = "" - local expiration = 0 - local salt = "" - local chmac = "" - - if cookie == nil or cookie:len() < 3 or cookie:sub(1, 1) == "|" then - return nil - end - - for component in string.gmatch(cookie, "[^|]+") do - if i == 0 then - field = component - elseif i == 1 then - value = component - elseif i == 2 then - expiration = tonumber(component) - if expiration == nil then - expiration = -1 - end - elseif i == 3 then - salt = component - elseif i == 4 then - chmac = component - else - break - end - i = i + 1 - end - - if chmac == nil or chmac:len() == 0 then - return nil - end - - -- Lua hashes strings, so these comparisons are time invariant. - if chmac ~= tohex(hmac.new(get_secret(), "sha256"):final(field .. "|" .. value .. "|" .. tostring(expiration) .. "|" .. salt)) then - return nil - end - - if expiration == -1 or (expiration ~= 0 and expiration <= os.time()) then - return nil - end - - if url_decode(field) ~= expected_field then - return nil - end - - return url_decode(value) -end - -function secure_value(field, value, expiration) - if value == nil or value:len() <= 0 then - return "" - end - - local authstr = "" - local salt = tohex(rand.bytes(16)) - value = url_encode(value) - field = url_encode(field) - authstr = field .. "|" .. value .. "|" .. tostring(expiration) .. "|" .. salt - authstr = authstr .. "|" .. tohex(hmac.new(get_secret(), "sha256"):final(authstr)) - return authstr -end - -function set_cookie(cookie, value) - html("Set-Cookie: " .. cookie .. "=" .. value .. "; HttpOnly") - if http["https"] == "yes" or http["https"] == "on" or http["https"] == "1" then - html("; secure") - end - html("\n") -end - -function redirect_to(url) - html("Status: 302 Redirect\n") - html("Cache-Control: no-cache, no-store\n") - html("Location: " .. url .. "\n") -end - -function not_found() - html("Status: 404 Not Found\n") - html("Cache-Control: no-cache, no-store\n\n") -end diff --git a/templates/lower.epp b/templates/lower.epp new file mode 100644 index 0000000..f77c8a1 --- /dev/null +++ b/templates/lower.epp @@ -0,0 +1,31 @@ + +enable-follow-links=1 +enable-subject-links=1 # show commit summary for parrent + +#side-by-side-diffs=1 +enable-commit-graph=1 +enable-index-links=1 +enable-remote-branches=1 +local-time=1 + +case-sensative-sort=0 + +max-repo-count=100 + +enable-http-clone=<%= if $cgit::enable_http_clone { 1 } else { 0 } %> +clone-url=<%= $cgit::clone_url.join(' ') %> + +readme=:README +readme=:README.md +readme=:README.txt +readme=:readme +readme=:readme.md +readme=:readme.txt + +virtual-root= +remove-suffix=1 +section-from-path=1 +enable-git-config=1 + +snapshots=tar.gz tar.xz +scan-path=<%= $cgit::scan_path %> diff --git a/templates/upper.epp b/templates/upper.epp new file mode 100644 index 0000000..de93118 --- /dev/null +++ b/templates/upper.epp @@ -0,0 +1,21 @@ +# +# cgit config +# see cgitrc(5) for details +# +# FILE MANAGED BY PUPPET +# + +# css=/cgit.css +logo=/logo/logo.png +# favicon=/favicon.ico + +enable-index-owner=1 + +root-title=<%= $cgit::root_title %> +#root-desc=Tändes endast mot lådans plån +root-desc=<%= $cgit::root_desc %> +# Also causes the `about' page to exist +# /usr/lib/cgit/readme +root-readme=<%= $cgit::root %>/root_readme + + -- cgit v1.2.3