From 7b97db1634394f90e653d0cd25bc45ce770b10f3 Mon Sep 17 00:00:00 2001 From: Vivien Kraus Date: Wed, 5 May 2021 15:22:01 +0200 Subject: Add a server for an application --- bootstrap | 2 +- doc/webid-oidc.texi | 44 ++++++ guix/vkraus/packages/webid-oidc.scm | 2 +- guix/vkraus/services/webid-oidc.scm | 81 +++++++++- man/Makefile.am | 6 +- po/POTFILES.in | 1 + po/fr.po | 254 +++++++++++++++--------------- po/webid-oidc.pot | 92 ++++++++++- src/Makefile.am | 2 +- src/scm/webid-oidc/client.scm | 275 ++++++++++++++++++++++++++++++++- src/webid-oidc-client-service | 7 + tests/Makefile.am | 3 +- tests/client-manifest-not-modified.scm | 28 ++++ 13 files changed, 660 insertions(+), 137 deletions(-) create mode 100755 src/webid-oidc-client-service create mode 100644 tests/client-manifest-not-modified.scm diff --git a/bootstrap b/bootstrap index ea8a192..9bb00e8 100755 --- a/bootstrap +++ b/bootstrap @@ -4,7 +4,7 @@ autoreconf -vif || exit 1 sed -i 's|SHELL = /bin/sh|SHELL = @SHELL@|g' po/Makefile.in.in || exit 1 ## Prepare the man pages -SCRIPTS_THAT_GET_EXECUTED="../src/webid-oidc-issuer ../src/webid-oidc-reverse-proxy" +SCRIPTS_THAT_GET_EXECUTED="../src/webid-oidc-issuer ../src/webid-oidc-reverse-proxy ../src/webid-oidc-client-service" mkdir -p .native || exit 1 cd .native || exit 1 diff --git a/doc/webid-oidc.texi b/doc/webid-oidc.texi index 55a92d9..b9c9f65 100644 --- a/doc/webid-oidc.texi +++ b/doc/webid-oidc.texi @@ -614,6 +614,50 @@ requests. used to issue DPoP proofs. @end deffn +The identity provider needs to call the application on the web. So, +your client should have a public endpoint on the web. + +@deffn function serve-application @var{id} @var{redirect-uri} @var{[#client-name]} @var{[#client-uri]} +Return a handler for web requests to serve the application manifest +and the redirection to transmit the authorization code. You should set +the @var{client-name} to your application name and @var{client-uri} to +point to where to a presentation of your application. +@end deffn + +The @code{webid-oidc-client-service} program can run a server to serve +these resources. It is invoked with the following options: + +@table @asis +@item @code{-h}, or @code{--help} +prints a summary of the options and exit. +@item @code{-v}, or @code{--version} +prints the version of the program and exits. +@item @code{-i @var{URI}}, or @code{--client-id=@var{URI}} +sets the global identitifier of the application, which is dereferenced +to a semantic resource. +@item @code{-r @var{URI}}, or @code{--redirect-uri=@var{URI}} +sets the redirection URI. +@item @code{-n @var{NAME}}, or @code{--client-name=@var{NAME}} +sets the name of your application, so that it is shown when the user +gets an authorization. The webid-oidc issuer program that comes with +this package does not display it, because it could be dishonest, but +other implementations might. +@item @code{-u @var{URI}}, or @code{--client-uri=@var{URI}} +sets an URI for the identity provider to learn more about your app. +@item @code{-p @var{PORT}}, or @code{--port=@var{PORT}} +change the port number used by the server. By default, it is set to +8080. +@item @code{-l @var{FILE.log}}, or @code{--log-file=@var{FILE.log}} +let the server dump all its output to @var{FILE.log}. +@item @code{-e @var{FILE.err}}, or @code{--error-file=@var{FILE.err}} +let the server dump all its errors to @var{FILE.err}. +@end table + +The program is sensitive to the environment variable @emph{LANG}, +which influences how the program is internationalized to the server +administrator. This changes the long form of the options, and the +language in the log files. + @node Exceptional conditions @chapter Exceptional conditions diff --git a/guix/vkraus/packages/webid-oidc.scm b/guix/vkraus/packages/webid-oidc.scm index f5ef57f..8d80cb5 100644 --- a/guix/vkraus/packages/webid-oidc.scm +++ b/guix/vkraus/packages/webid-oidc.scm @@ -79,7 +79,7 @@ (format #f "~a/bin/webid-oidc-~a" out program) `("GUILE_LOAD_PATH" ":" = ,mod-paths) `("GUILE_LOAD_COMPILED_PATH" ":" = ,go-paths))) - '(hello issuer reverse-proxy)))))))) + '(client-service hello issuer reverse-proxy)))))))) (native-inputs `(("pkg-config" ,pkg-config) ("guile" ,guile-3.0) diff --git a/guix/vkraus/services/webid-oidc.scm b/guix/vkraus/services/webid-oidc.scm index 33b4fae..e510ba9 100644 --- a/guix/vkraus/services/webid-oidc.scm +++ b/guix/vkraus/services/webid-oidc.scm @@ -57,6 +57,21 @@ webid-oidc-hello-configuration-extra-options (default '()))) +(define-record-type* + webid-oidc-client-service-configuration + make-webid-oidc-client-service-configuration + webid-oidc-client-service-configuration? + (webid-oidc webid-oidc-client-service-configuration-webid-oidc + (default webid-oidc)) + (client-id webid-oidc-client-service-configuration-client-id) + (redirect-uri webid-oidc-client-service-configuration-redirect-uri) + (client-name webid-oidc-client-service-configuration-client-name (default "Example Solid App")) + (client-uri webid-oidc-client-service-configuration-client-uri (default "https://webid-oidc.planete-kraus.eu/Running-a-client.html#Running-a-client")) + (port webid-oidc-client-service-configuration-port (default 8088)) + (extra-options + webid-oidc-client-service-configuration-extra-options + (default '()))) + (export webid-oidc-issuer-configuration make-webid-oidc-issuer-configuration @@ -87,7 +102,18 @@ webid-oidc-hello-configuration? webid-oidc-hello-configuration-webid-oidc webid-oidc-hello-configuration-port - webid-oidc-hello-configuration-extra-options) + webid-oidc-hello-configuration-extra-options + + webid-oidc-client-service-configuration + make-webid-oidc-client-service-configuration + webid-oidc-client-service-configuration? + webid-oidc-client-service-configuration-webid-oidc + webid-oidc-client-service-configuration-client-id + webid-oidc-client-service-configuration-redirect-uri + webid-oidc-client-service-configuration-client-name + webid-oidc-client-service-configuration-client-uri + webid-oidc-client-service-configuration-port + webid-oidc-client-service-configuration-extra-options) (define webid-oidc-issuer-shepherd-service (match-lambda @@ -223,6 +249,48 @@ "LANG=C")))) (stop #~(make-kill-destructor)))))))) +(define webid-oidc-client-service-shepherd-service + (match-lambda + (($ + webid-oidc client-id redirect-uri client-name client-uri port + extra-options) + (with-imported-modules + (source-module-closure + '((gnu build shepherd) + (gnu system file-systems))) + (list (shepherd-service + (provision '(webid-oidc-client-service)) + (documentation "Run a server for a Solid application.") + (requirement '(user-processes)) + (modules '((gnu build shepherd) + (gnu system file-systems))) + (start + #~(begin + (let* ((user (getpwnam "webid-oidc")) + (prepare-directory + (lambda (dir) + (mkdir-p dir) + (chown dir (passwd:uid user) (passwd:gid user)) + (chmod dir #o700)))) + (prepare-directory "/var/log/webid-oidc")) + (make-forkexec-constructor + (list + (string-append #$webid-oidc "/bin/webid-oidc-client-service") + "--client-id" #$client-id + "--redirect-uri" #$redirect-uri + "--client-name" #$client-name + "--client-uri" #$client-uri + "--port" (with-output-to-string (lambda () (display #$port))) + "--log-file" "client-service.log" + "--error-file" "client-service.err" + #$@extra-options) + #:user "webid-oidc" + #:group "webid-oidc" + #:directory "/var/log/webid-oidc" + #:environment-variables + `("LANG=C")))) + (stop #~(make-kill-destructor)))))))) + (define %webid-oidc-accounts (list (user-group (name "webid-oidc") (system? #t)) @@ -266,3 +334,14 @@ (service-extension shepherd-root-service-type webid-oidc-hello-shepherd-service))))) + +(define-public webid-oidc-client-service-service-type + (service-type + (name 'webid-oidc-client-service) + (extensions + (list + (service-extension account-service-type + (const %webid-oidc-accounts)) + (service-extension + shepherd-root-service-type + webid-oidc-client-service-shepherd-service))))) diff --git a/man/Makefile.am b/man/Makefile.am index 100d314..5a80f50 100644 --- a/man/Makefile.am +++ b/man/Makefile.am @@ -1,4 +1,4 @@ -dist_man8_MANS = webid-oidc-issuer.man webid-oidc-reverse-proxy.man +dist_man8_MANS = webid-oidc-issuer.man webid-oidc-reverse-proxy.man webid-oidc-client-service.man EXTRA_DIST = ./reset-env @@ -9,3 +9,7 @@ webid-oidc-issuer.man: ../src/scm/webid-oidc/identity-provider.scm ../configure. webid-oidc-reverse-proxy.man: ../src/scm/webid-oidc/reverse-proxy.scm ../configure.ac $(AM_V_GEN) ../pre-inst-env ./reset-env $(HELP2MAN) $(srcdir)/../src/webid-oidc-reverse-proxy > $@-t mv $@-t $(srcdir)/$@ + +webid-oidc-client-service.man: ../src/scm/webid-oidc/client.scm ../configure.ac + $(AM_V_GEN) ../pre-inst-env ./reset-env $(HELP2MAN) $(srcdir)/../src/webid-oidc-client-service > $@-t + mv $@-t $(srcdir)/$@ diff --git a/po/POTFILES.in b/po/POTFILES.in index 0115b93..dffc889 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -12,3 +12,4 @@ src/scm/webid-oidc/authorization-page-unsafe.scm src/scm/webid-oidc/resource-server.scm src/scm/webid-oidc/reverse-proxy.scm src/scm/webid-oidc/hello-world.scm +src/scm/webid-oidc/client.scm diff --git a/po/fr.po b/po/fr.po index d2ee64b..a3cd5cc 100644 --- a/po/fr.po +++ b/po/fr.po @@ -742,12 +742,12 @@ msgstr "Attention : génération d'une nouvelle paire de clé." #: src/scm/webid-oidc/identity-provider.scm:148 #: src/scm/webid-oidc/reverse-proxy.scm:124 -#: src/scm/webid-oidc/hello-world.scm:31 +#: src/scm/webid-oidc/hello-world.scm:31 src/scm/webid-oidc/client.scm:618 msgid "command-line|version" msgstr "version" #: src/scm/webid-oidc/identity-provider.scm:150 -#: src/scm/webid-oidc/hello-world.scm:33 +#: src/scm/webid-oidc/hello-world.scm:33 src/scm/webid-oidc/client.scm:620 msgid "comand-line|help" msgstr "aide" @@ -780,17 +780,17 @@ msgid "comand-line|token-endpoint-uri" msgstr "uri-terminal-jeton" #: src/scm/webid-oidc/identity-provider.scm:166 -#: src/scm/webid-oidc/hello-world.scm:35 +#: src/scm/webid-oidc/hello-world.scm:35 src/scm/webid-oidc/client.scm:630 msgid "comand-line|port" msgstr "port" #: src/scm/webid-oidc/identity-provider.scm:168 -#: src/scm/webid-oidc/reverse-proxy.scm:136 +#: src/scm/webid-oidc/reverse-proxy.scm:136 src/scm/webid-oidc/client.scm:632 msgid "comand-line|log-file" msgstr "fichier-journal" #: src/scm/webid-oidc/identity-provider.scm:170 -#: src/scm/webid-oidc/reverse-proxy.scm:138 +#: src/scm/webid-oidc/reverse-proxy.scm:138 src/scm/webid-oidc/client.scm:634 msgid "comand-line|error-file" msgstr "fichier-erreur" @@ -942,7 +942,7 @@ msgstr "" #: src/scm/webid-oidc/identity-provider.scm:267 #: src/scm/webid-oidc/reverse-proxy.scm:210 -#: src/scm/webid-oidc/hello-world.scm:61 +#: src/scm/webid-oidc/hello-world.scm:61 src/scm/webid-oidc/client.scm:699 #, scheme-format msgid "~a version ~a\n" msgstr "~a version ~a\n" @@ -977,11 +977,12 @@ msgstr "Vous devez définir l'URI du terminal de jeton.\n" #: src/scm/webid-oidc/identity-provider.scm:327 #: src/scm/webid-oidc/reverse-proxy.scm:238 -#: src/scm/webid-oidc/hello-world.scm:72 +#: src/scm/webid-oidc/hello-world.scm:72 src/scm/webid-oidc/client.scm:737 msgid "The port should be a number between 0 and 65535.\n" msgstr "Le port doit être un nombre entre 0 et 65535.\n" #: src/scm/webid-oidc/identity-provider.scm:346 +#: src/scm/webid-oidc/client.scm:748 #, scheme-format msgid "~a: Internal server error: ~a\n" msgstr "~a : Erreur interne du serveur : ~a\n" @@ -1307,6 +1308,129 @@ msgstr "" " -p PORT, --~a=8080 :\n" " définit le port à lier.\n" +#: src/scm/webid-oidc/client.scm:622 +msgid "comand-line|client-id" +msgstr "id-client" + +#: src/scm/webid-oidc/client.scm:624 +msgid "comand-line|redirect-uri" +msgstr "uri-redirection" + +#: src/scm/webid-oidc/client.scm:626 +msgid "comand-line|client-name" +msgstr "nom-client" + +#: src/scm/webid-oidc/client.scm:628 +msgid "comand-line|client-uri" +msgstr "uri-client" + +#: src/scm/webid-oidc/client.scm:649 +#, scheme-format +msgid "" +"Usage: ~a [OPTIONS]...\n" +"\n" +"Serve public pages for an application.\n" +"\n" +"Options:\n" +" -h, --~a:\n" +" display this help message and exit.\n" +" -v, --~a:\n" +" display the version information (~a) and exit.\n" +" -i URI, --~a=URI:\n" +" set the webid of the client.\n" +" -r FILE, --~a=URI:\n" +" set the redirection URI where to get the authorization code.\n" +" -n NAME, --~a=NAME:\n" +" set the name of the application.\n" +" -u URI, --~a=URI:\n" +" set the address of the application (informative).\n" +" -p PORT, --~a=PORT:\n" +" set the port to bind (instead of 8080).\n" +" -l FILE.log, --~a=FILE.log:\n" +" dump the standard output to that file.\n" +" -e FILE.err, --~a=FILE.err:\n" +" dump the standard error to that file.\n" +"\n" +"Environment variables:\n" +"\n" +" LANG: set the locale of the sysadmin-facing interface, for log files\n" +"and command-line interface. It is currently ~a.\n" +"\n" +"Example used in webid-oidc-demo.planete-kraus.eu (except it’s managed\n" +"by shepherd in reality):\n" +"\n" +" export LANG=C\n" +" webid-oidc-client-service \\\n" +" --client-id 'https://webid-oidc-demo.planete-kraus.eu/example-" +"application#id' \\\n" +" --redirect-uri 'https://webid-oidc-demo.planete-kraus.eu/authorized' " +"\\\n" +" --client-name 'Example Solid Application' \\\n" +" --client-uri 'https://webid-oidc.planete-kraus.eu/Running-a-client." +"html#Running-a-client' \\\n" +" --port $PORT\n" +"\n" +"If you find a bug, send a report to ~a.\n" +msgstr "" +"Utilisation : ~a [OPTIONS]...\n" +"\n" +"Sert les pages publiques d’une application.\n" +"\n" +"Options :\n" +" -h, --~a :\n" +" affiche ce message d’aide et quitte.\n" +" -v, --~a :\n" +" affiche le numéro de version (~a) et quitte.\n" +" -i URI, --~a=URI :\n" +" définit le webid du client.\n" +" -r URI, --~a=URI :\n" +" définit l’URI de redirection où obtenir le code d’autorisation.\n" +" -n NOM, --~a=NOM :\n" +" définit le nom de l’application.\n" +" -u URI, --~a=URI :\n" +" définit l’adresse de l’application (à titre informatif).\n" +" -p PORT, --~a=PORT :\n" +" définit le port à lier (au lieu de 8080).\n" +" -l FICHIER.log, --~a=FICHIER.log :\n" +" déverser la sortie standard vers ce fichier.\n" +" -e FICHIER.err, --~a=FICHIER.err :\n" +" déverser la sortie d’erreur vers ce fichier.\n" +"\n" +"Variables d’environnement :\n" +"\n" +" LANG : définit la locale de l’interface de l’administrateur système,\n" +"pour les fichiers de journaux et l’interface en ligne de\n" +"commande. Elle vaut actuellement ~a.\n" +"\n" +"Exemple utilisé pour webid-oidc-demo.planete-kraus.eu (sauf que le\n" +"service est géré par shepherd en réalité, et les URI sont en\n" +"anglais) :\n" +"\n" +" export LANG=fr_FR.UTF-8\n" +" webid-oidc-client-service \\\n" +" --id-client 'https://webid-oidc-demo.planete-kraus.eu/application-" +"exemple#id' \\\n" +" --uri-redirection 'https://webid-oidc-demo.planete-kraus.eu/autorisé' " +"\\\n" +" --nom-client 'Exemple d’application Solid' \\\n" +" --uri-client 'https://webid-oidc.planete-kraus.eu/Running-a-client." +"html#Running-a-client' \\\n" +" --port $PORT\n" +"\n" +"Si vous trouvez une erreur dans le programme, envoyez-en un rapport à ~a.\n" + +#: src/scm/webid-oidc/client.scm:722 +msgid "You need to set the client ID as an URI.\n" +msgstr "Vous devez définir l’identifiant du client comme URI.\n" + +#: src/scm/webid-oidc/client.scm:726 +msgid "You need to set the redirect URI.\n" +msgstr "Vous devez définir l'URI de redirection.\n" + +#: src/scm/webid-oidc/client.scm:730 +msgid "The client URI should be an URI.\n" +msgstr "L’URI du client doit être un URI.\n" + #, scheme-format #~ msgid "the resource ~s could not be found (because ~a)" #~ msgstr "la ressource ~s n’a pas été trouvée (parce que ~a)" @@ -1361,122 +1485,6 @@ msgstr "" #~ msgid "there is an external error" #~ msgstr "il y a une erreur externe" -#~ msgid "comand-line|client-id" -#~ msgstr "id-client" - -#~ msgid "comand-line|redirect-uri" -#~ msgstr "uri-redirection" - -#~ msgid "comand-line|client-name" -#~ msgstr "nom-client" - -#~ msgid "comand-line|client-uri" -#~ msgstr "uri-client" - -#, scheme-format -#~ msgid "" -#~ "Usage: ~a [OPTIONS]...\n" -#~ "\n" -#~ "Serve public pages for an application.\n" -#~ "\n" -#~ "Options:\n" -#~ " -h, --~a:\n" -#~ " display this help message and exit.\n" -#~ " -v, --~a:\n" -#~ " display the version information (~a) and exit.\n" -#~ " -i URI, --~a=URI:\n" -#~ " set the webid of the client.\n" -#~ " -r FILE, --~a=URI:\n" -#~ " set the redirection URI where to get the authorization code.\n" -#~ " -n NAME, --~a=NAME:\n" -#~ " set the name of the application.\n" -#~ " -u URI, --~a=URI:\n" -#~ " set the address of the application (informative).\n" -#~ " -p PORT, --~a=PORT:\n" -#~ " set the port to bind (instead of 8080).\n" -#~ " -l FILE.log, --~a=FILE.log:\n" -#~ " dump the standard output to that file.\n" -#~ " -e FILE.err, --~a=FILE.err:\n" -#~ " dump the standard error to that file.\n" -#~ "\n" -#~ "Environment variables:\n" -#~ "\n" -#~ " LANG: set the locale of the sysadmin-facing interface, for log files\n" -#~ "and command-line interface. It is currently ~a.\n" -#~ "\n" -#~ "Example used in webid-oidc-demo.planete-kraus.eu (except it’s managed\n" -#~ "by shepherd in reality):\n" -#~ "\n" -#~ " export LANG=C\n" -#~ " webid-oidc-client-service \\\n" -#~ " --client-id 'https://webid-oidc-demo.planete-kraus.eu/example-" -#~ "application#id' \\\n" -#~ " --redirect-uri 'https://webid-oidc-demo.planete-kraus.eu/" -#~ "authorized' \\\n" -#~ " --client-name 'Example Solid Application' \\\n" -#~ " --client-uri 'https://webid-oidc.planete-kraus.eu/Running-a-client." -#~ "html#Running-a-client' \\\n" -#~ " --port $PORT\n" -#~ "\n" -#~ "If you find a bug, send a report to ~a.\n" -#~ msgstr "" -#~ "Utilisation : ~a [OPTIONS]...\n" -#~ "\n" -#~ "Sert les pages publiques d’une application.\n" -#~ "\n" -#~ "Options :\n" -#~ " -h, --~a :\n" -#~ " affiche ce message d’aide et quitte.\n" -#~ " -v, --~a :\n" -#~ " affiche le numéro de version (~a) et quitte.\n" -#~ " -i URI, --~a=URI :\n" -#~ " définit le webid du client.\n" -#~ " -r URI, --~a=URI :\n" -#~ " définit l’URI de redirection où obtenir le code d’autorisation.\n" -#~ " -n NOM, --~a=NOM :\n" -#~ " définit le nom de l’application.\n" -#~ " -u URI, --~a=URI :\n" -#~ " définit l’adresse de l’application (à titre informatif).\n" -#~ " -p PORT, --~a=PORT :\n" -#~ " définit le port à lier (au lieu de 8080).\n" -#~ " -l FICHIER.log, --~a=FICHIER.log :\n" -#~ " déverser la sortie standard vers ce fichier.\n" -#~ " -e FICHIER.err, --~a=FICHIER.err :\n" -#~ " déverser la sortie d’erreur vers ce fichier.\n" -#~ "\n" -#~ "Variables d’environnement :\n" -#~ "\n" -#~ " LANG : définit la locale de l’interface de l’administrateur système,\n" -#~ "pour les fichiers de journaux et l’interface en ligne de\n" -#~ "commande. Elle vaut actuellement ~a.\n" -#~ "\n" -#~ "Exemple utilisé pour webid-oidc-demo.planete-kraus.eu (sauf que le\n" -#~ "service est géré par shepherd en réalité, et les URI sont en\n" -#~ "anglais) :\n" -#~ "\n" -#~ " export LANG=fr_FR.UTF-8\n" -#~ " webid-oidc-client-service \\\n" -#~ " --id-client 'https://webid-oidc-demo.planete-kraus.eu/application-" -#~ "exemple#id' \\\n" -#~ " --uri-redirection 'https://webid-oidc-demo.planete-kraus.eu/" -#~ "autorisé' \\\n" -#~ " --nom-client 'Exemple d’application Solid' \\\n" -#~ " --uri-client 'https://webid-oidc.planete-kraus.eu/Running-a-client." -#~ "html#Running-a-client' \\\n" -#~ " --port $PORT\n" -#~ "\n" -#~ "Si vous trouvez une erreur dans le programme, envoyez-en un rapport à " -#~ "~a.\n" - -#~ msgid "You need to set the client ID as an URI.\n" -#~ msgstr "Vous devez définir l’identifiant du client comme URI.\n" - -#~ msgid "You need to set the redirect URI.\n" -#~ msgstr "Vous devez définir l'URI de redirection.\n" - -#~ msgid "The client URI should be an URI.\n" -#~ msgstr "L’URI du client doit être un URI.\n" - #, scheme-format #~ msgid "~a.\t~a, certified by ~a;\n" #~ msgstr "~a.\t~a, certifié par ~a ;\n" diff --git a/po/webid-oidc.pot b/po/webid-oidc.pot index 2f7d082..306f186 100644 --- a/po/webid-oidc.pot +++ b/po/webid-oidc.pot @@ -721,12 +721,12 @@ msgstr "" #: src/scm/webid-oidc/identity-provider.scm:148 #: src/scm/webid-oidc/reverse-proxy.scm:124 -#: src/scm/webid-oidc/hello-world.scm:31 +#: src/scm/webid-oidc/hello-world.scm:31 src/scm/webid-oidc/client.scm:618 msgid "command-line|version" msgstr "" #: src/scm/webid-oidc/identity-provider.scm:150 -#: src/scm/webid-oidc/hello-world.scm:33 +#: src/scm/webid-oidc/hello-world.scm:33 src/scm/webid-oidc/client.scm:620 msgid "comand-line|help" msgstr "" @@ -759,17 +759,17 @@ msgid "comand-line|token-endpoint-uri" msgstr "" #: src/scm/webid-oidc/identity-provider.scm:166 -#: src/scm/webid-oidc/hello-world.scm:35 +#: src/scm/webid-oidc/hello-world.scm:35 src/scm/webid-oidc/client.scm:630 msgid "comand-line|port" msgstr "" #: src/scm/webid-oidc/identity-provider.scm:168 -#: src/scm/webid-oidc/reverse-proxy.scm:136 +#: src/scm/webid-oidc/reverse-proxy.scm:136 src/scm/webid-oidc/client.scm:632 msgid "comand-line|log-file" msgstr "" #: src/scm/webid-oidc/identity-provider.scm:170 -#: src/scm/webid-oidc/reverse-proxy.scm:138 +#: src/scm/webid-oidc/reverse-proxy.scm:138 src/scm/webid-oidc/client.scm:634 msgid "comand-line|error-file" msgstr "" @@ -848,7 +848,7 @@ msgstr "" #: src/scm/webid-oidc/identity-provider.scm:267 #: src/scm/webid-oidc/reverse-proxy.scm:210 -#: src/scm/webid-oidc/hello-world.scm:61 +#: src/scm/webid-oidc/hello-world.scm:61 src/scm/webid-oidc/client.scm:699 #, scheme-format msgid "~a version ~a\n" msgstr "" @@ -883,11 +883,12 @@ msgstr "" #: src/scm/webid-oidc/identity-provider.scm:327 #: src/scm/webid-oidc/reverse-proxy.scm:238 -#: src/scm/webid-oidc/hello-world.scm:72 +#: src/scm/webid-oidc/hello-world.scm:72 src/scm/webid-oidc/client.scm:737 msgid "The port should be a number between 0 and 65535.\n" msgstr "" #: src/scm/webid-oidc/identity-provider.scm:346 +#: src/scm/webid-oidc/client.scm:748 #, scheme-format msgid "~a: Internal server error: ~a\n" msgstr "" @@ -1137,3 +1138,80 @@ msgid "" " -p PORT, --port=~a:\n" " set the port to bind.\n" msgstr "" + +#: src/scm/webid-oidc/client.scm:622 +msgid "comand-line|client-id" +msgstr "" + +#: src/scm/webid-oidc/client.scm:624 +msgid "comand-line|redirect-uri" +msgstr "" + +#: src/scm/webid-oidc/client.scm:626 +msgid "comand-line|client-name" +msgstr "" + +#: src/scm/webid-oidc/client.scm:628 +msgid "comand-line|client-uri" +msgstr "" + +#: src/scm/webid-oidc/client.scm:649 +#, scheme-format +msgid "" +"Usage: ~a [OPTIONS]...\n" +"\n" +"Serve public pages for an application.\n" +"\n" +"Options:\n" +" -h, --~a:\n" +" display this help message and exit.\n" +" -v, --~a:\n" +" display the version information (~a) and exit.\n" +" -i URI, --~a=URI:\n" +" set the webid of the client.\n" +" -r FILE, --~a=URI:\n" +" set the redirection URI where to get the authorization code.\n" +" -n NAME, --~a=NAME:\n" +" set the name of the application.\n" +" -u URI, --~a=URI:\n" +" set the address of the application (informative).\n" +" -p PORT, --~a=PORT:\n" +" set the port to bind (instead of 8080).\n" +" -l FILE.log, --~a=FILE.log:\n" +" dump the standard output to that file.\n" +" -e FILE.err, --~a=FILE.err:\n" +" dump the standard error to that file.\n" +"\n" +"Environment variables:\n" +"\n" +" LANG: set the locale of the sysadmin-facing interface, for log files\n" +"and command-line interface. It is currently ~a.\n" +"\n" +"Example used in webid-oidc-demo.planete-kraus.eu (except it’s managed\n" +"by shepherd in reality):\n" +"\n" +" export LANG=C\n" +" webid-oidc-client-service \\\n" +" --client-id 'https://webid-oidc-demo.planete-kraus.eu/example-" +"application#id' \\\n" +" --redirect-uri 'https://webid-oidc-demo.planete-kraus.eu/authorized' " +"\\\n" +" --client-name 'Example Solid Application' \\\n" +" --client-uri 'https://webid-oidc.planete-kraus.eu/Running-a-client." +"html#Running-a-client' \\\n" +" --port $PORT\n" +"\n" +"If you find a bug, send a report to ~a.\n" +msgstr "" + +#: src/scm/webid-oidc/client.scm:722 +msgid "You need to set the client ID as an URI.\n" +msgstr "" + +#: src/scm/webid-oidc/client.scm:726 +msgid "You need to set the redirect URI.\n" +msgstr "" + +#: src/scm/webid-oidc/client.scm:730 +msgid "The client URI should be an URI.\n" +msgstr "" diff --git a/src/Makefile.am b/src/Makefile.am index d990641..527f201 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,6 +1,6 @@ lib_LTLIBRARIES += %reldir%/libwebidoidc.la -dist_bin_SCRIPTS += %reldir%/webid-oidc-issuer %reldir%/webid-oidc-reverse-proxy %reldir%/webid-oidc-hello +dist_bin_SCRIPTS += %reldir%/webid-oidc-issuer %reldir%/webid-oidc-reverse-proxy %reldir%/webid-oidc-hello %reldir%/webid-oidc-client-service AM_CPPFLAGS += -I %reldir% -I $(srcdir)/%reldir% diff --git a/src/scm/webid-oidc/client.scm b/src/scm/webid-oidc/client.scm index ef0d116..6fe9cc2 100644 --- a/src/scm/webid-oidc/client.scm +++ b/src/scm/webid-oidc/client.scm @@ -7,15 +7,21 @@ #:use-module (webid-oidc jwk) #:use-module ((webid-oidc stubs) #:prefix stubs:) #:use-module ((webid-oidc refresh-token) #:prefix refresh:) + #:use-module ((webid-oidc config) #:prefix cfg:) #:use-module (web uri) #:use-module (web client) + #:use-module (web request) #:use-module (web response) #:use-module (web server) #:use-module (web http) #:use-module (ice-9 optargs) #:use-module (ice-9 receive) #:use-module (srfi srfi-19) - #:use-module (rnrs bytevectors)) + #:use-module (rnrs bytevectors) + #:use-module (ice-9 i18n) + #:use-module (ice-9 getopt-long) + #:use-module (ice-9 suspendable-ports) + #:use-module (sxml simple)) (define*-public (authorize host-or-webid #:key @@ -486,3 +492,270 @@ (parse-args uri 'GET '() '() args)) (lambda (uri . args) (parse-http-request-args uri args))) + +(define*-public (serve-application id redirect-uri + #:key + (client-name "Example application") + (client-uri "https://webid-oidc-demo.planete-kraus.eu")) + (when (string? id) + (set! id (string->uri id))) + (when (string? redirect-uri) + (set! redirect-uri (string->uri redirect-uri))) + (when (string? client-uri) + (set! client-uri (string->uri client-uri))) + (let* ((manifest + (format #f + "@prefix solid: . + +<~a> solid:oidcRegistration \"\"\"{ + \"client_id\" : \"~a\", + \"redirect_uris\" : [\"~a\"], + \"client_name\" : \"~a\", + \"client_uri\" : \"~a\", + \"grant_types\" : [\"refresh_token\", \"authorization_code\"], + \"response_types\" : [\"code\"] +}\"\"\" . +" + (uri->string id) + (uri->string id) + (uri->string redirect-uri) + client-name + (uri->string id))) + (manifest-etag (stubs:hash 'SHA-256 manifest))) + (lambda (request request-body) + (let ((uri (request-uri request))) + (cond + ((equal? (uri-path uri) (uri-path id)) + (let ((if-none-match (request-if-none-match request))) + (if (and (list? if-none-match) + (member manifest-etag + (map car (request-if-none-match request)))) + (values + (build-response + #:code 304 + #:reason-phrase "Not Modified" + #:headers `((content-type text/turtle) + (etag . (,manifest-etag . #t)))) + #f) + (values + (build-response + #:headers `((content-type text/turtle) + (etag . (,manifest-etag . #t)) + (cache-control public must-revalidate))) + manifest)))) + ((equal? (uri-path uri) (uri-path redirect-uri)) + (let ((query-args + (map + (lambda (key=value) + (let ((splits + (map uri-decode (string-split key=value #\=)))) + (if (or (null? splits) (null? (cdr splits))) + splits + (cons (string->symbol (car splits)) (cdr splits))))) + (string-split (uri-query uri) #\&)))) + (let ((code (assq-ref query-args 'code))) + (if code + (values + (build-response + #:headers `((content-type application/xhtml+xml))) + (with-output-to-string + (lambda () + (sxml->xml + `(*TOP* + (*PI* xml "version=\"1.0\" encoding=\"utf-8\"") + (html (@ (xmlns "http://www.w3.org/1999/xhtml") + (xml:lang "en")) + (head + (title "Authorization")) + (body + (p "You have been authorized. Please paste the following code in the application:") + (p (strong ,code))))))))) + (values + (build-response + #:code 400 + #:reason-phrase "Invalid Request" + #:headers `((content-type application/xhtml+xml))) + (with-output-to-string + (lambda () + (sxml->xml + `(*TOP* + (*PI* xml "version=\"1.0\" encoding=\"utf-8\"") + (html (@ (xmlns "http://www.w3.org/1999/xhtml") + (xml:lang "en")) + (head + (title "Error")) + (body + (p "Your identity provider did not authorize you. :(")))))))))))) + (else + (values + (build-response + #:code 404 + #:reason-phrase "Not Found" + #:headers `((content-type application/xhtml+xml))) + (with-output-to-string + (lambda () + (sxml->xml + `(*TOP* + (*PI* xml "version=\"1.0\" encoding=\"utf-8\"") + (html (@ (xmlns "http://www.w3.org/1999/xhtml") + (xml:lang "en")) + (head + (title "Not Found")) + (body + (p "This page does not exist on the server.")))))))))))))) + +(define (G_ text) + (let ((out (gettext text))) + (if (string=? out text) + ;; No translation, disambiguate + (car (reverse (string-split text #\|))) + out))) + +(define-public (main-server) + (setlocale LC_ALL "") + (bindtextdomain cfg:package cfg:localedir) + (textdomain cfg:package) + (let ((version-sym + (string->symbol (G_ "command-line|version"))) + (help-sym + (string->symbol (G_ "comand-line|help"))) + (client-id-sym + (string->symbol (G_ "comand-line|client-id"))) + (redirect-uri-sym + (string->symbol (G_ "comand-line|redirect-uri"))) + (client-name-sym + (string->symbol (G_ "comand-line|client-name"))) + (client-uri-sym + (string->symbol (G_ "comand-line|client-uri"))) + (port-sym + (string->symbol (G_ "comand-line|port"))) + (log-file-sym + (string->symbol (G_ "comand-line|log-file"))) + (error-file-sym + (string->symbol (G_ "comand-line|error-file")))) + (let ((options + (let ((option-spec + `((,version-sym (single-char #\v) (value #f)) + (,help-sym (single-char #\h) (value #f)) + (,client-id-sym (single-char #\i) (value #t)) + (,redirect-uri-sym (single-char #\r) (value #t)) + (,client-name-sym (single-char #\n) (value #t)) + (,client-uri-sym (single-char #\u) (value #t)) + (,port-sym (single-char #\p) (value #t)) + (,log-file-sym (single-char #\l) (value #t)) + (,error-file-sym (single-char #\e) (value #t))))) + (getopt-long (command-line) option-spec)))) + (cond + ((option-ref options help-sym #f) + (format #t (G_ "Usage: ~a [OPTIONS]... + +Serve public pages for an application. + +Options: + -h, --~a: + display this help message and exit. + -v, --~a: + display the version information (~a) and exit. + -i URI, --~a=URI: + set the webid of the client. + -r FILE, --~a=URI: + set the redirection URI where to get the authorization code. + -n NAME, --~a=NAME: + set the name of the application. + -u URI, --~a=URI: + set the address of the application (informative). + -p PORT, --~a=PORT: + set the port to bind (instead of 8080). + -l FILE.log, --~a=FILE.log: + dump the standard output to that file. + -e FILE.err, --~a=FILE.err: + dump the standard error to that file. + +Environment variables: + + LANG: set the locale of the sysadmin-facing interface, for log files +and command-line interface. It is currently ~a. + +Example used in webid-oidc-demo.planete-kraus.eu (except it’s managed +by shepherd in reality): + + export LANG=C + webid-oidc-client-service \\ + --client-id 'https://webid-oidc-demo.planete-kraus.eu/example-application#id' \\ + --redirect-uri 'https://webid-oidc-demo.planete-kraus.eu/authorized' \\ + --client-name 'Example Solid Application' \\ + --client-uri 'https://webid-oidc.planete-kraus.eu/Running-a-client.html#Running-a-client' \\ + --port $PORT + +If you find a bug, send a report to ~a. +") + (car (command-line)) + help-sym version-sym + cfg:version + client-id-sym redirect-uri-sym client-name-sym client-uri-sym port-sym + log-file-sym error-file-sym + (or (getenv "LANG") "") + cfg:package-bugreport)) + ((option-ref options version-sym #f) + (format #t (G_ "~a version ~a\n") + cfg:package cfg:version)) + (else + (let ((client-id (option-ref options client-id-sym #f)) + (redirect-uri (option-ref options redirect-uri-sym #f)) + (client-name (option-ref options client-name-sym "Example Solid App")) + (client-uri + (option-ref options client-uri-sym + "https://webid-oidc.planete-kraus.eu/Running-a-client.html#Running-a-client")) + (port-string + (option-ref options port-sym "8080")) + (log-file-string + (option-ref options log-file-sym #f)) + (error-file-string + (option-ref options error-file-sym #f))) + (when log-file-string + (set-current-output-port (stubs:open-output-file* log-file-string)) + (setvbuf (current-output-port) 'none)) + (when error-file-string + (set-current-error-port (stubs:open-output-file* error-file-string)) + (setvbuf (current-error-port) 'none)) + (unless (and client-id (string->uri client-id)) + (format (current-error-port) + (G_ "You need to set the client ID as an URI.\n")) + (exit 1)) + (unless (and redirect-uri (string->uri redirect-uri)) + (format (current-error-port) + (G_ "You need to set the redirect URI.\n")) + (exit 2)) + (unless (string->uri client-uri) + (format (current-error-port) + (G_ "The client URI should be an URI.\n")) + (exit 3)) + (unless (and (string->number port-string) + (integer? (string->number port-string)) + (>= (string->number port-string) 0) + (<= (string->number port-string) 65535)) + (format (current-error-port) + (G_ "The port should be a number between 0 and 65535.\n")) + (exit 1)) + (let ((handler + (serve-application client-id redirect-uri + #:client-name client-name + #:client-uri client-uri))) + (let ((handler-with-log + (lambda (request request-body) + (with-exception-handler + (lambda (error) + (format (current-error-port) + (G_ "~a: Internal server error: ~a\n") + (date->string (time-utc->date (current-time))) + (error->str error)) + (values + (build-response #:code 500 + #:reason-phrase "Internal Server Error") + "Sorry, there was an error.")) + (lambda () + (handler request request-body)) + #:unwind? #t)))) + (install-suspendable-ports!) + (run-server handler 'http + (list #:port (string->number port-string))))))))))) diff --git a/src/webid-oidc-client-service b/src/webid-oidc-client-service new file mode 100755 index 0000000..1139abd --- /dev/null +++ b/src/webid-oidc-client-service @@ -0,0 +1,7 @@ +#!/usr/local/bin/guile \ +--no-auto-compile -s +!# + +(use-modules (webid-oidc client)) + +(main-server) diff --git a/tests/Makefile.am b/tests/Makefile.am index 52a0083..5ec6e49 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -39,7 +39,8 @@ TESTS = %reldir%/load-library.scm \ %reldir%/provider-confirmation.scm \ %reldir%/resource-server.scm \ %reldir%/client-authorization.scm \ - %reldir%/client-token.scm + %reldir%/client-token.scm \ + %reldir%/client-manifest-not-modified.scm EXTRA_DIST += $(TESTS) %reldir%/ChangeLog diff --git a/tests/client-manifest-not-modified.scm b/tests/client-manifest-not-modified.scm new file mode 100644 index 0000000..440c9b8 --- /dev/null +++ b/tests/client-manifest-not-modified.scm @@ -0,0 +1,28 @@ +(use-modules (webid-oidc client) + (webid-oidc testing) + (webid-oidc errors) + (web uri) + (srfi srfi-19) + (web request) + (web response) + (ice-9 optargs) + (ice-9 receive)) + +(with-test-environment + "client-manifest-not-modified" + (lambda () + (let ((handler (serve-application + (string->uri "https://example.com/manifest") + (string->uri "https://example.com/authorized")))) + (receive (response response-body) + (handler (build-request (string->uri "https://example.com/manifest")) + "") + (let ((etag (response-etag response))) + (unless etag + (exit 1)) + (receive (second-response second-response-body) + (handler (build-request (string->uri "https://example.com/manifest") + #:headers `((if-none-match . (,etag)))) + "") + (unless (eqv? (response-code second-response) 304) + (exit 2)))))))) -- cgit v1.2.3