summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVivien Kraus <vivien@planete-kraus.eu>2021-05-05 15:22:01 +0200
committerVivien Kraus <vivien@planete-kraus.eu>2021-06-05 16:59:44 +0200
commitd9df259c06e2845d7baefe559fd3d6e9c5b4a642 (patch)
treef513fed87517ff7eaefa8b70bc8ab61ebe12b408
parent68221cd57d155c33a207cc89ced90b48ef5b1699 (diff)
Add a server for an application
-rwxr-xr-xbootstrap2
-rw-r--r--doc/webid-oidc.texi44
-rw-r--r--guix/vkraus/packages/webid-oidc.scm2
-rw-r--r--guix/vkraus/services/webid-oidc.scm81
-rw-r--r--man/Makefile.am6
-rw-r--r--po/POTFILES.in1
-rw-r--r--po/fr.po254
-rw-r--r--po/webid-oidc.pot92
-rw-r--r--src/Makefile.am2
-rw-r--r--src/scm/webid-oidc/client.scm275
-rwxr-xr-xsrc/webid-oidc-client-service7
-rw-r--r--tests/Makefile.am3
-rw-r--r--tests/client-manifest-not-modified.scm28
13 files changed, 660 insertions, 137 deletions
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 0148dbb..f9e4c90 100644
--- a/guix/vkraus/packages/webid-oidc.scm
+++ b/guix/vkraus/packages/webid-oidc.scm
@@ -67,7 +67,7 @@
(format #f "~a/bin/webid-oidc-~a" out program)
`("GUILE_LOAD_PATH" ":" = ,mod-paths)
`("GUILE_LOAD_COMPILED_PATH" ":" = ,go-paths)))
- '(issuer reverse-proxy hello))))))))
+ '(issuer reverse-proxy hello client-service))))))))
(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>
+ 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>
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>
+ 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-service-configuration>
+ 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 1cb81ed..d623df1 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
@@ -485,3 +491,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: <http://www.w3.org/ns/solid/terms#> .
+
+<~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 3cd7fed..8173b9b 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))))))))