challenger

OAuth 2.0-based authentication service that validates user can receive messages at a certain address
Log | Files | Refs | Submodules | README | LICENSE

commit 424489cfc265a452fe0105d31a5df556d445fd04
parent e8fbd0b18ac4d2cf7fd3211b22cbf24a74d4f005
Author: Christian Grothoff <christian@grothoff.org>
Date:   Sat,  6 May 2023 22:32:35 +0200

rudimentary test skeleton

Diffstat:
Msrc/challenger/Makefile.am | 18+++++++++++++++++-
Asrc/challenger/cat.sh | 3+++
Asrc/challenger/challenger-send-email.sh | 3+++
Asrc/challenger/challenger-send-post.sh | 185+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/challenger/challenger-send-sms.sh | 28++++++++++++++++++++++++++++
Msrc/challenger/challenger.conf | 10++++++++++
Asrc/challenger/test-challenger.conf | 11+++++++++++
Asrc/challenger/test-challenger.sh | 72++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/challengerdb/challenger-0001.sql | 6+++---
Msrc/util/challenger-config.in | 2+-
10 files changed, 333 insertions(+), 5 deletions(-)

diff --git a/src/challenger/Makefile.am b/src/challenger/Makefile.am @@ -15,6 +15,17 @@ bin_PROGRAMS = \ challenger-admin \ challenger-httpd +bin_SCRIPTS = \ + challenger-send-email.sh \ + challenger-send-post.sh \ + challenger-send-sms.sh + +check_SCRIPTS = \ + test-challenger.sh + +TESTS = \ + $(check_SCRIPTS) + challenger_admin_SOURCES = \ challenger-admin.c challenger_admin_LDADD = \ @@ -50,4 +61,9 @@ challenger_httpd_LDADD = \ $(XLIB) EXTRA_DIST = \ - $(pkgcfg_DATA) + cat.sh \ + challenger.conf \ + test-challenger.conf \ + $(pkgcfg_DATA) \ + $(bin_SCRIPTS) \ + $(check_SCRIPTS) diff --git a/src/challenger/cat.sh b/src/challenger/cat.sh @@ -0,0 +1,3 @@ +#!/bin/sh +# This file is in the public domain. +cat - > "$1" diff --git a/src/challenger/challenger-send-email.sh b/src/challenger/challenger-send-email.sh @@ -0,0 +1,3 @@ +#!/bin/sh +# This file is in the public domain. +exec mail -s "Anastasis" -r noreply "$1" diff --git a/src/challenger/challenger-send-post.sh b/src/challenger/challenger-send-post.sh @@ -0,0 +1,185 @@ +#!/bin/bash +# This file is in the public domain. +set -eu + +# Theses are Anastasis SARL specific, do not share! +# CLIENT_ID= +# CLIENT_SECRET= +# ORG_ID= +. pingen-secrets + +ENDPOINT="https://api.v2.pingen.com" +LOGS="$PWD/authorization-post.log" + +MESSAGE=`cat -` +DATE=`date +%F` +ADDR="$1" +NAME=`echo $ADDR | jq -r .full_name` +STREET=`echo $ADDR | jq -r .street` + +LNUMBER=`echo $STREET | awk '{print $NF}'` +FNUMBER=`echo $STREET | awk '{print $1}'` +case $LNUMBER in + ''|*[!0-9]*) + case $FNUMBER in + ''|*[!0-9]*) + NUMBER=0 + ;; + *) + NUMBER=$FNUMBER + ;; + esac + ;; + *) + NUMBER=$LNUMBER + ;; +esac + + +CITY=`echo $ADDR | jq -r .city` +POSTCODE=`echo $ADDR | jq -r .postcode` +COUNTRY=`echo $ADDR | jq -r .country` + +MYDIR=`mktemp -d /tmp/authorization-post-XXXXXX` +cd "$MYDIR" +cat - | sed -e "s/%NAME%/$NAME/g" \ + -e "s/%STREET%/$STREET/g" \ + -e "s/%POSTCODE%/$POSTCODE/g" \ + -e "s/%CITY%/$CITY/g" \ + -e "s/%COUNTRY%/$COUNTRY/g" \ + -e "s/%MESSAGE%/$MESSAGE/g" > input.tex <<EOF +\NeedsTeXFormat{LaTeX2e} +\documentclass[fontsize=11pt,a4paper]{scrlttr2} +\makeatletter +\KOMAoptions{foldmarks=off} +%\@setplength{toaddrvpos}{30mm} +%\@setplength{toaddrhpos}{130mm} +%\@setplength{sigbeforevskip}{10mm} +\makeatother +\setkomavar{subject}{Anastasis Recovery} +%\setkomavar{fromname}{Anastasis SARL} +\setkomavar{signature}{Anastasis SARL} +\date{\today} +%\address{Anastasis SARL \\\\ 7 rue de Mondorf \\\\ 5431 Erpeldange} +%\signature{Anastasis SARL} +\begin{document} +\begin{letter}{\ \ %NAME% \\\\ \ \ %STREET% \\\\ \ \ %POSTCODE% %CITY% \\\\ \ \ %COUNTRY% } +\opening{To whom it may concern,} +%MESSAGE% +\closing{Best regards} +\end{letter} +\end{document} +EOF +pdflatex input.tex > /dev/null 2> /dev/null + +REPLY=`curl -s -X POST -H "Content-Type: application/x-www-form-urlencoded" \ + --data-urlencode "grant_type=client_credentials" \ + --data-urlencode "client_id=$CLIENT_ID" \ + --data-urlencode "client_secret=$CLIENT_SECRET" \ + --data-urlencode "scope=letter" \ + https://identity.pingen.com/auth/access-tokens` + +ACCESS_TOKEN=`echo $REPLY | jq -r .access_token` + +REPLY=`curl -s \ + -X GET "$ENDPOINT/file-upload" \ + -H "Authorization: Bearer $ACCESS_TOKEN"` +ATTRS=`echo $REPLY | jq .data.attributes` +UPLOAD_URL=`echo $ATTRS | jq -r .url` +URL_SIG=`echo $ATTRS | jq -r .url_signature` + +curl -s -X PUT -T input.pdf $UPLOAD_URL + + +RECIPIENT="$(jq -n ' + { + name: $NAME, + street: $STREET, + number: $NUMBER, + city: $CITY, + zip: $POSTCODE, + country: $COUNTRY, + }' \ + --arg NAME "$NAME" \ + --arg STREET "$STREET" \ + --arg NUMBER "$NUMBER" \ + --arg CITY "$CITY" \ + --arg POSTCODE "$POSTCODE" \ + --arg COUNTRY "$COUNTRY" \ + )" + +SENDER="$(jq -n ' + { + name: "Anastasis SARL", + street: "Rue de Mondorf", + number: "7", + zip: "5421", + city: "Erpeldange", + country: "LU" + }' + )" + +REQUEST="$(jq -n ' + { data: { + type: "letters", + attributes: { + file_original_name: "input.pdf", + file_url: $UPLOAD_URL, + file_url_signature: $URL_SIG, + address_position: "left", + delivery_product: "cheap", + print_mode: "duplex", + auto_send: true, + print_spectrum: "grayscale" + } } + }' \ + --argjson RECIPIENT "$RECIPIENT" \ + --argjson SENDER "$SENDER" \ + --arg UPLOAD_URL "$UPLOAD_URL" \ + --arg URL_SIG "$URL_SIG" \ + )" + +STATUS=$(curl -s --request POST \ + --url $ENDPOINT/organisations/${ORG_ID}/letters \ + --header 'Content-Type: application/vnd.api+json' \ + --header "Authorization: Bearer $ACCESS_TOKEN" \ + -d "$REQUEST" \ + -o $MYDIR/final-reply.txt \ + -w "%{http_code}" -s) +cat $MYDIR/final-reply.txt >> $LOGS +case $STATUS in + 201) + ;; + *) + echo "Failed to add letter: $STATUS" >> $LOGS + echo $REPLY + exit 1; + ;; +esac +LETTER_ID=`cat $MYDIR/final-reply.txt | jq -r .data.id` +REPLY=$MYDIR/delete-reply.txt +STATUS=409 +sleep 1; +while test $STATUS = 409; +do + STATUS=$(curl -s --request DELETE \ + --url $ENDPOINT/organisations/$ORG_ID/letters/$LETTER_ID \ + --header "Authorization: Bearer $ACCESS_TOKEN" \ + -o $REPLY \ + -w "%{http_code}" -s) + case $STATUS in + 204) + cat $REPLY >> $LOGS + ;; + 409) + # Happens, likely still in processing... + ;; + *) + echo "Failed to delete letter: $STATUS" >> $LOGS + ;; + esac +done + +rm -r $MYDIR + +exit 0 diff --git a/src/challenger/challenger-send-sms.sh b/src/challenger/challenger-send-sms.sh @@ -0,0 +1,28 @@ +#!/bin/sh +# This file is in the public domain. +set -eu +. telesign-secrets +# Set AUTH_TOKEN=... + +MESSAGE=`cat -` +TMPFILE=`mktemp /tmp/sms-loggingXXXXXX` +STATUS=$(curl --request POST \ + --url https://rest-api.telesign.com/v1/messaging \ + --header 'authorization: Basic $AUTH_TOKEN' \ + --header 'content-type: application/x-www-form-urlencoded' \ + --data account_livecycle_event=transact \ + --data "message=$MESSAGE" \ + --data message_type=OTP \ + --data "phone_number=$1" \ + -w "%{http_code}" -s -o $TMPFILE) +echo `cat $TMPFILE` >> /var/log/sms.log +rm -f $TMPFILE +case $STATUS in + 200|203|250|290|291|295) + exit 0; + ;; + *) + exit 1; + ;; +esac +exit 1 diff --git a/src/challenger/challenger.conf b/src/challenger/challenger.conf @@ -27,3 +27,13 @@ DB = postgres # How long is an individual validation request valid? VALIDATION_DURATION = 1d +# How long is an validation valid? +VALIDATION_EXPIRATION = 365d + +# Which external command should be used to transmit challenges? +# Example commands are challenger-send-{sms,email,post}.sh +# AUTH_COMMAND = + + +# What address type are we validating? (SMS, e-mail, etc.) +# ADDRESS_TYPE = diff --git a/src/challenger/test-challenger.conf b/src/challenger/test-challenger.conf @@ -0,0 +1,11 @@ +[challenger] + +# Which external command should be used to transmit challenges? +AUTH_COMMAND = cat.sh + +# What address type are we validating? (SMS, e-mail, etc.) +ADDRESS_TYPE = file-access + +[challengerdb-postgres] +#The connection string the plugin has to use for connecting to the database +CONFIG = postgres:///talercheck diff --git a/src/challenger/test-challenger.sh b/src/challenger/test-challenger.sh @@ -0,0 +1,72 @@ +#!/bin/bash +# This file is in the public domain. + +set -eu + +# Exit, with status code "skip" (no 'real' failure) +function exit_skip() { + echo " SKIP: $1" + exit 77 +} + +# Exit, with error message (hard failure) +function exit_fail() { + echo " FAIL: $1" + exit 1 +} + +# Cleanup to run whenever we exit +function cleanup() +{ + for n in `jobs -p` + do + kill $n 2> /dev/null || true + done + wait +} + +# Install cleanup handler (except for kill -9) +trap cleanup EXIT + +echo -n "Testing for jq" +jq -h > /dev/null || exit_skip "jq required" +echo " FOUND" +echo -n "Testing for curl" +curl -h > /dev/null || exit_skip "curl required" +echo " FOUND" +echo -n "Testing for wget" +wget -h > /dev/null || exit_skip "wget required" +echo " FOUND" +echo -n "Testing for challenger-httpd ..." +challenger-httpd -h > /dev/null || exit_skip "challenger-httpd required" +echo " FOUND" + +CONF="test-challenger.conf" +BURL="http://localhost:9967" + +echo -n "Initialize challenger database ..." +challenger-dbinit -r -c "${CONF}" &> dbinit.log +echo " OK" + +echo -n "Start challenger-httpd ..." +challenger-httpd -c "${CONF}" &> httpd.log & + +# Wait for challenger to be available +for n in `seq 1 50` +do + echo -n "." + sleep 0.2 + OK=0 + # bank + wget --tries=1 --timeout=1 "${BURL}/config" -o /dev/null -O /dev/null >/dev/null || continue + OK=1 + break +done +if [ 1 != $OK ] +then + exit_skip "Failed to launch challenger service" +fi + + + +exit 0 diff --git a/src/challengerdb/challenger-0001.sql b/src/challengerdb/challenger-0001.sql @@ -27,7 +27,7 @@ SET search_path TO challenger; CREATE TABLE IF NOT EXISTS clients - (client_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY + (client_serial_id BIGINT UNIQUE GENERATED BY DEFAULT AS IDENTITY ,url VARCHAR NOT NULL ,validation_counter INT8 NOT NULL DEFAULT(0) ,client_secret VARCHAR NOT NULL @@ -46,7 +46,7 @@ CREATE INDEX IF NOT EXISTS clients_serial ON clients (client_serial_id); CREATE TABLE IF NOT EXISTS validations - (validation_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY + (validation_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY UNIQUE ,client_serial_id INT8 NOT NULL REFERENCES clients (client_serial_id) ,nonce BYTEA CHECK (length(nonce)=32) UNIQUE ,expiration_time INT8 NOT NULL @@ -71,7 +71,7 @@ COMMENT ON COLUMN validations.client_scope IS 'Client-specific scope value identifying the requested scope'; COMMENT ON COLUMN validations.client_state IS 'Client-specific state value identifying the purpose of the validation'; -COMMENT ON COLUMN validations.client_redirect_uri +COMMENT ON COLUMN validations.client_redirect_url IS 'Client-specific URI where to redirect the user-agent back once access is granted (or denied)'; COMMENT ON COLUMN validations.address IS 'Address we are validating; provided by the user-agent; usually a phone number or e-mail address (depends on the client_scope)'; diff --git a/src/util/challenger-config.in b/src/util/challenger-config.in @@ -8,6 +8,6 @@ if ! type gnunet-config >/dev/null; then fi GC=`which gnunet-config` -SO=`ls %libdir%/libsyncutil.so.* | sort -n | tail -n1` +SO=`ls %libdir%/libchallengerutil.so.* | sort -n | tail -n1` export LD_PRELOAD=${LD_PRELOAD:-}:${SO} exec gnunet-config "$@"