# HG changeset patch # User Kim Alvefur # Date 1553976973 -3600 # Node ID 882180b459a023536a74c1663bf9e0c156b299f1 # Parent 42e9e3c5eb025796d9ea0ade05fdaf5fa1be5fd7 mod_pubsub_post: Restructure authentication and authorization (BC) This deprecates the default "superuser" actor model and makes the default equivalent to the previous "request.id". A single actor and secret per node is supported because HTTP and WebHooks don't normally include any authorization identity. Allowing authentication bypass when no secret is given should be relatively safe when the actor is unprivileged, as will be unless explicitly configured otherwise. diff -r 42e9e3c5eb02 -r 882180b459a0 mod_pubsub_post/README.markdown --- a/mod_pubsub_post/README.markdown Sat Mar 30 19:24:18 2019 +0100 +++ b/mod_pubsub_post/README.markdown Sat Mar 30 21:16:13 2019 +0100 @@ -17,56 +17,60 @@ # Configuration -## Authentication - -Authentication can be handled in two different ways. - -### None - -``` {.lua} -pubsub_post_actor = "superuser" -``` - -The module uses an internal actor that has all privileges and can always -do everything. It is strongly suggested that you do not expose this to -the Internet. *Maybe* it shouldn't be the default... +All settings are optional. -### IP - -``` {.lua} -pubsub_post_actor = "request.ip" -``` +## Actor identification -Uses the IP address from the HTTP request as actor, which means this -pseudo-JID must be given a 'publisher' affiliation. This should work -nicely with the `autocreate_on_publish` setting, where the first actor -to attempt to publish to a nonexistent node becomes owner of it, which -includes publishing rights. - -## WebSub +First we have to figure out who is making the request. +This is configured on a per-node basis like this: ``` {.lua} -- Per node secrets -pubsub_post_secrets = { - my_node = "shared secret" +pubsub_post_actors = { + princely_musings = "hamlet@denmark.lit" } - --- Same secret for all nodes -pubsub_post_secret = "shared secret" +pubsub_post_default_actor = "nobody@nowhere.invalid" ``` -This enables the +`pubsub_post_default_actor` is used when trying to publish to a node +that is not listed in `pubsub_post_actors`. Otherwise the IP address +of the connection is used. + +## Authentication + [WebSub](https://www.w3.org/TR/2018/REC-websub-20180123/) [Authenticated Content Distribution](https://www.w3.org/TR/2018/REC-websub-20180123/#authenticated-content-distribution) -authentication method, where payloads are signed using a shared secret. +authentication is used. + +``` {.lua} +pubsub_post_secrets = { + princely_musings = "shared secret" +} +pubsub_post_default_secret = "default secret" +``` -## Setting up affiliations +`pubsub_post_default_secret` is used when trying to publish to a node +that is not listed in `pubsub_post_secrets`. Otherwise the request +proceeds with the previously identified actor. + +::: {.alert .alert-danger} +If configured without a secret and a default actor that has permission +to create nodes the service becomes wide open. +::: + +## Authorization + +Authorization is handled via pubsub affiliations. Publishing requires an +affiliation with the _publish_ capability, usually `"publisher"`. + +### Setting up affiliations Prosodys PubSub module supports [setting affiliations via XMPP](https://xmpp.org/extensions/xep-0060.html#owner-affiliations), in trunk since [revision -384ef9732b81](https://hg.prosody.im/trunk/rev/384ef9732b81). +384ef9732b81](https://hg.prosody.im/trunk/rev/384ef9732b81), so +affiliations can be configured with a capable client. It can however be done from another plugin: diff -r 42e9e3c5eb02 -r 882180b459a0 mod_pubsub_post/mod_pubsub_post.lua --- a/mod_pubsub_post/mod_pubsub_post.lua Sat Mar 30 19:24:18 2019 +0100 +++ b/mod_pubsub_post/mod_pubsub_post.lua Sat Mar 30 21:16:13 2019 +0100 @@ -75,9 +75,14 @@ end end -local actor_source = module:get_option_string("pubsub_post_actor", "superuser"); -local actor_secret = module:get_option_string("pubsub_post_secret"); +local actor_source = module:get_option_string("pubsub_post_actor"); -- COMPAT +local default_secret = module:get_option_string("pubsub_post_default_secret"); local actor_secrets = module:get_option("pubsub_post_secrets"); +local actors = module:get_option("pubsub_post_actors"); +local default_actor = module:get_option_string("pubsub_post_default_actor"); +if not default_actor and actor_source == "superuser" then + default_actor = true; +end local function verify_signature(secret, body, signature) if not signature then return false; end @@ -93,22 +98,13 @@ module:log("debug", "Handling POST: \n%s\n", tostring(request.body)); local content_type = request.headers.content_type or "application/octet-stream"; - local actor; + local actor = actors and actors[path] or default_actor or request.ip; + local secret = actor_secrets and actor_secrets[path] or default_secret; - local secret = actor_secrets and actor_secrets[path] or actor_secret; if secret and not verify_signature(secret, request.body, request.headers.x_hub_signature) then return 401; end - if actor_source == "request.ip" then - actor = request.ip or request.conn:ip(); - elseif actor_source == "superuser" then - actor = true; - else - module:log("error", "pubsub_post_actor set to unsupported value %q", actor_source); - return 500; - end - if not actor then return 401; end