==================
Optional: RabbitMQ
==================

Ploneintranet can connect to a RabbitMQ server using WebSockets.

Preparations
------------

RabbitMQ must be installed, e.g. if you are using a Debian based distribution::

    apt install rabbitmq-server


Setting up RabbitMQ
-------------------

RabbitMQ uses plugins to enable some features.
You will want to enable at least a couple of them::

    root@alita:/etc/rabbitmq# cat enabled_plugins
    [rabbitmq_management,rabbitmq_web_stomp].

The ``rabbitmq_management`` is handy but not required, it provides an easier way to manage the server.
See more on https://www.rabbitmq.com/management.html.

The ``rabbitmq_web_stomp`` plugin is the one required to connect to RabbitMQ using a WebSocket.
See more on https://www.rabbitmq.com/web-stomp.html.


Configuring Ploneintranet to use RabbitMQ
-----------------------------------------

To use RabbitMQ you need to instruct Ploneintranet about the connection parameters, see:

https://github.com/quaive/ploneintranet/blob/master/src/ploneintranet/notifications/tests/profiles/testing/registry.xml

For a development installation those parameters should be enough to have a quick start.

Production environments
-----------------------

Proxying websockets
^^^^^^^^^^^^^^^^^^^

On production environments you most probably want to proxy the WebSocket over https.

Example configuration for nginx::

    location /ws {
        proxy_set_header Upgrade websocket;
        proxy_buffering off;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Connection "upgrade";
        proxy_http_version 1.1;
        proxy_intercept_errors on;
        proxy_redirect off;
        proxy_cache_bypass $http_upgrade;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-NginX-Proxy true;
        proxy_ssl_session_reuse off;
        proxy_pass http://127.0.0.1:15674;
    }

Example configuration for the connection parameter in the Plone registry::

  <record name="ploneintranet.layout.push_server_reader_url">
    <value>wss://example.com/ws</value>
  </record>


Setting up users
^^^^^^^^^^^^^^^^

The ``guest`` user is by default only allowed to connect to the system with a loopback connection.
For this reason you need to set up at least a reader user account::

    rabbitmqctl add_user reader reader_password
    rabbitmqctl set_permissions reader -p / '^stomp-subscription-.*' '^stomp-subscription-.*' '.*'

You might want also to set up another admin account,
not limited to loopback connections.
This is necessary if RabbitMQ lives on another server.
For example you can run::

    rabbitmqctl add_user admin admin_password
    rabbitmqctl set_permissions -p / admin ".*" ".*" ".*"
    rabbitmqctl set_user_tags admin administrator

Editing the registry records for the connection should be straightforward
based on the example in
https://github.com/quaive/ploneintranet/blob/master/src/ploneintranet/notifications/tests/profiles/testing/registry.xml.

To inspect your users::

    root@alita:/etc/rabbitmq# rabbitmqctl list_users
    Listing users ...
    user    tags
    admin   [administrator]
    guest   [administrator]
    reader  []

Be sure that the ``reader`` account or whatever you specify
in the ``ploneintranet.layout.push_server_reader_login``
registry will not have the ``[administrator]`` tag.

Setting up the exchanges
^^^^^^^^^^^^^^^^^^^^^^^^

The example in https://github.com/quaive/ploneintranet/blob/master/src/ploneintranet/notifications/tests/profiles/testing/registry.xml,
uses the default exchange available on RabbitMQ (``amq.topic``) in both the registry records:

- ``ploneintranet.layout.notification_exchange``
- ``ploneintranet.layout.push_exchange``

You can add custom ones, e.g.::

    rabbitmqadmin declare exchange name=notification_exchange type=topic durable=true
    rabbitmqadmin declare exchange name=push_exchange type=topic durable=true


And check them::

    root@alita:/etc/rabbitmq# rabbitmqctl list_exchanges name,type,durable
    Listing exchanges for vhost / ...
    name    type    durable
    ...
    notification_exchange     topic   true
    push_exchange             topic   true
    ...


Using Plone as a backend for the authentication
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

RabbitMQ offers the possibility to delegate the authentication
to an external service over HTTP.

See https://github.com/rabbitmq/rabbitmq-auth-backend-http.

You need to have a configuration like this::

    root@alita:/etc/rabbitmq# cat enabled_plugins
    [rabbitmq_auth_backend_http,rabbitmq_management,rabbitmq_web_stomp].
    root@alita:/etc/rabbitmq# cat advanced.config
    [
    {rabbit, [{auth_backends, [rabbit_auth_backend_internal, rabbit_auth_backend_http]}]},
    {rabbitmq_auth_backend_http,
    [{http_method,   post},
        {user_path,     "http://127.0.0.1:9300/quaive/@@rabbit-auth/user"},
        {vhost_path,    "http://127.0.0.1:9300/quaive/@@rabbit-auth/vhost"},
        {resource_path, "http://127.0.0.1:9300/quaive/@@rabbit-auth/resource"},
        {topic_path,    "http://127.0.0.1:9300/quaive/@@rabbit-auth/topic"}]}
    ].


The important bits are:

- the ``rabbitmq_auth_backend_http`` plugin is enabled
- it is listed in the ``auth_backends``
- it is configured to point to the Quaive website

To take advantage of this, you need to edit the ``ploneintranet.layout.push_token``.
If it is set:

- instead of using the value stored in the registry record ``ploneintranet.layout.push_server_reader_login``,
  the username used to connect to RabbitMQ will be the authenticated user username
- instead of using the value stored in the registry record ``ploneintranet.layout.push_server_reader_password``,
  the password used to connect to RabbitMQ will be generated based on the token and the username
