Usage
=====

This document describes the steps for creating and configuring a SAML 2.0
plugin. For this example the external website URL is https://www.example.com,
which is mapped to a folder ``/mysite`` in Zope. The Zope instance is installed
on the server filesystem at ``/opt/zope``

You should read the :doc:`configuration` document first to familiarize yourself
with some of the settings this document refers to.

.. contents:: Document contents
    :local:


Set the configuration file location
-----------------------------------

Locate the ``zope.conf`` configuration file for your Zope instance and add the
following at the end before restarting :term:`Zope`:

.. code::

    <product-config saml2plugins>
      configuration_folder /opt/zope/etc
    </product-config>

If you use the :term:`zc.buildout` recipe `plone.recipe.zope2instance
<https://pypi.org/project/plone.recipe.zope2instance/>`_ to create your Zope
instance, add the following to the buildout section for the Zope instance,
re-run the buildout and restart Zope:

.. code::

    zope-conf-additional =
        <product-config saml2plugins>
          configuration_folder /opt/zope/etc
        </product-config>


Create the plugin
-----------------

In the :term:`Zope` :term:`ZMI`, navigate to ``/mysite/manage_main`` and, if
you haven't done so yet, create a :term:`PluggableAuthService` instance. Then,
click on the :term:`PluggableAuthService` ``acl_users``. From the dropdown
named `Select type to add`, select `SAML 2.0 Plugin`:

.. image:: _static/zmi_add_1.png

On the initial form, set an ID and optional title:

.. image:: _static/zmi_add_2.png

Click on the SAML 2.0 plugin you just created and visit the `Configuration`
tab. It will tell you that the plugin cannot find the :term:`pysaml2`
configuration file that is required:

.. image:: _static/zmi_configuration_empty.png

Copy the filesystem path that it is looking for, you will need it when creating
the :term:`pysaml2` configuration file below.


Create a key and certificate pair
---------------------------------

A key and certificate are needed to cryptographically sign SAML 2.0 request
data or the SAML metadata generated by the plugin. On the filesystem, change to
the folder ``/opt/zope/etc`` and create them:

.. code:: console

    openssl req -nodes -new -x509 -keyout saml.key -out saml.crt

Get XML metadata for your identity provider
-------------------------------------------

XML metadata describes how service provider nd identity provider interact. When
you choose an identity provider they will usually provide a ready-made XML file
for this purpose. Create or download this metadata XML file and store it inside
``/opt/zope/etc``. This example assumes the filename ``idp.xml``.


Create a pysaml2 configuration file
-----------------------------------

Create a file at the filesystem path from the previous step. Here's the start
configuration for this test scenario:

.. literalinclude:: _static/saml2_cfg_saml_example.py

You can either restart the Zope instance or use the button `Reload
configuration` on the plugin's `Configuration` tab in the :term:`ZMI` to load
the configuration. The configuration values will now show on the page.

.. image:: _static/zmi_configuration.png


Check the SAML 2.0 plugin properties
------------------------------------

In the :term:`Zope` :term:`ZMI`, click on the plugin's `Properties` tab. Check
the property values and adjust where needed before clicking `Save`. Check the
:doc:`configuration` document for details about each setting.

.. image:: _static/zmi_properties.png


Activate the plugin
-------------------

In the :term:`Zope` :term:`ZMI`, click on the plugin's `Activate` tab. For this
example we assume the plugin is the only one that is allowed to present a login
form, so check all checkboxes and click `Update`.

.. image:: _static/zmi_activate.png

Now go back to the user folder at ``/mysite/acl_users/manage_main`` to check
these activation settings on the ``plugins`` registry. Click on ``plugins``,
then select the :term:`ZMI` tag `Active`. The new plugin should be the only one
listed in the `Challenge Plugins` line. If not, click on the
`Challenge Plugins` link and remove all other plugins from the `Active Plugins`
box.

.. image:: _static/zmi_plugins_active.png


Configure the identity provider
-------------------------------

The identity provider you choose for handling logins needs to be configured to
know and trust your website as service provider. How to do so differs between
identity providers. Usually you're asked to create a login at the identity
provider and then provide information where your service provider is and how it
signs its messages. Many identity providers let you upload all this data as XML
file, which the plugin generates for you. In the :term:`Zope` :term:`ZMI`,
click on the plugin's `Metadata` tab. The generated metadata is shown on the
page, and clicking the link `Download the metadata file` will download it to
your computer for upload to the identity provider.


Testing it all
--------------

Go back to the root folder of your site at ``/mysite/manage_main``. Create a
`Page Template` ``restricted_html`` and go to its :term:`ZMI` `Security` tab
at ``/mysite/restricted_html/manage_access``. Scroll down the list of
permissions until you get to `View`. Uncheck the checkbox in the `Acquire?`
column and check the checkbox in the `Authenticated` column. Then click `Save
Changes` at the bottom. With this change, the page at
https://www.example.com/restricted_html is only visible for authenticated
users. When you open a new browser with which you're not logged into your site
at https://www.example.com and visit the page
https://www.example.com/restricted_html you should be redirected to the
identity provider for logging in. If the login at the identity provider has
been successful you should be redirected to
https://www.example.com/restricted_html and should be able to see the page.


Presenting a login link
-----------------------

The plugin has a publicly available method ``login`` that can be used to
manually trigger the login flow to the identity provider and back to your Zope
instance. Simply create a link or a button that links to the method. In the
example case used in this document the link would point to
``https://www.example.com/acl_users/saml/login``. Using rewrite rules in your
front-end web server you can shorten that to e.g.
``https://www.example.com/login`` and proxy to the internal path in
:term:`Zope`.

You can add additional information to that link with a query string:

- the query string variable ``idp`` can be used to select a specific identity
  provider if you have more than one. Take the value you see in the select list
  `Default entity provider` on the plugin's `Properties` :term:`ZMI` tab and
  quote it to an URL-safe format by applying Python's ``urllib.parse.quote``.
  An example would be ``idp=https%3A//idp.ssocircle.com``.
- to redirect the user back to a specific page on your site after successful
  login at the identity provider you can pass the query string variable
  ``came_from``, for example ``came_from=/logged_in.html``.
