Skip to content

Anatomy of a response micro service

Ivan Kanakarakis edited this page Mar 4, 2020 · 11 revisions

SATOSA micro-services

Anatomy of a response micro_service

A micro-service has configuration and code. The configuration is given to the micro-service on initialization. Any validation on the configuration should be done at this point. The micro-service is loaded and awaits execution.

When the right time comes, the micro-service is invoked through the process() function. The process() function receives two parameters: the context of the current request, and data which reflects the internal data representation of the protocol information, as transformed by the corresponding frontend or backend.

The micro-service (most of the time) will add/remove/change data.attributes and in the end it will return the result for the next micro-service to work upon.

Configuration example

module: example.satosa.micro-services.NameOfMicroservice
name: An example micro-service skeleton
config:
  option: "abcd"

Code example

"""A micro-service example."""

import logging

import satosa
import satosa.logging_util as lu
from satosa.micro_services.base import ResponseMicroService


logger = logging.getLogger(__name__)
component = __name__.split(".")[-1].upper()


class NameOfMicroservice(ResponseMicroService):
    """
    A micro-service to show an example skeleton.

    Example configuration:

    ```yaml
    module: python.path.to.the.class.NameOfMicroservice
    name: An example micro-service skeleton
    config:
        option: value
    ```
    """

    def __init__(self, config, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.option = self.config.get("option")

    def process(self, context: satosa.context.Context, data: satosa.internal.InternalData):
        # do things with data.attributes
        # ...

        # log a message
        message = "hello from a micro-service"
        logger.info(
            lu.LOG_FMT.format(
                id=lu.get_session_id(context.state),
                message="[{component}] {message}".format(
                    component=component, message=message
                ),
            )
        )

        # return context and data
        return super().process(context, data)

Example content of data param

data = {
    # type: satosa.internal.AuthenticationInformation
    # This part holds the authentication information
    # namely, the authentication context classes (ie, the LoA reference)
    # and the issuer of the given identity (ie, the IdP entity-id)
    "auth_info": {
        "auth_class_ref": "urn:oasis:names:tc:SAML:2.0:ac:classes:Password",
        "timestamp": "2020-02-22T19:30:04Z",
        "issuer": "https://www.rediris.es/sir/umaidp",
    },

    # the requester of the authentication; this is typically the SP (or RP) entity-id
    "requester": "https://example.org/saml-sp/metadata.xml",
    "requester_name": [{"text": None, "lang": "en"}],

    # the subject identifier as expressed in different protocols, along with its type or format.
    # this will match `NameID` for SAML2 and `sub` for OIDC.
    "subject_id": "69e83a116ed953279999d4463541c2799795c816",
    "subject_type": "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent",

    # the attributes of the subject as expressed in different protocols.
    # this will be a representation of claims for OIDC,
    # or attribute statements of an assertion for SAML2.
    "attributes": {
        "edupersontargetedid": ["04e8192d48a040f3ef495999958908f8aa10b4a5"],
        "displayname": ["Somename Somesurname"],
        "givenname": ["Somename"],
        "mail": ["someone@uma.es"],
        "name": ["Somename"],
        "surname": ["Somesurname"],
        "epsa": ["alum@uma.es", "staff@uma.es"],
        "eppn": ["06100004X@uma.es"],
        "spuc": [
            "urn:schac:personalUniqueCode:es:rediris:sir:mbid:{sha1}0c938d124632017100980299997b1ab174789657",
            "urn:schac:personalUniqueCode:es:uma:CAU:id:822",
            "urn:schac:personalUniqueCode:es:uma:ESC:code:a98b1a8c-9215-11e9-8545-000077349997",
            "urn:schac:personalUniqueCode:es:uma:codUni:06100004X",
        ],
    },
}

Example content of context param

context = {
    "cookie": 'SATOSA_PROXY_STATE="..."',
    "internal_data": {
        "metadata_store": <saml2.mdstore.MetadataStore object at 0x7fc60e32cb70>
    },
    "request": None,
    "request_authorization": "",
    "state": {
        "ROUTER": "Saml2IDP",
        "SATOSA_BASE": {"requester": "..."},
        "Saml2IDP": {
            "relay_state": "https://example.org/authenticate?as=some-sp",
            "resp_args": {
                "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST",
                "destination": "https://example.org/saml-sp/saml2-acs",
                "in_response_to": "_e923792fde97aa0b6bc82999cba5274a61eae4b96c",
                "name_id_policy": """<ns0:NameIDPolicy xmlns:ns0="urn:oasis:names:tc:SAML:2.0:protocol" AllowCreate="true" Format="urn:oasis:names:tc:SAML:2.0:nameid-format:transient"/>""",
                "sp_entity_id": "...",
            },
        },
        "memorized_idp": "...",
    },
    "target_backend": "saml2sp",
    "target_frontend": None,
    "target_micro_service": None,
}