# Path: invenio_oauthclient/contrib/keycloak/helpers.py
# def _format_public_key(public_key):
#     """PEM-format the public key."""
#     public_key = public_key.strip()
# 
#     if not public_key.startswith("-----BEGIN PUBLIC KEY-----"):
#         public_key = "-----BEGIN PUBLIC KEY-----\n" + \
#                      public_key + \
#                      "\n-----END PUBLIC KEY-----"
# 
#     return public_key
# 
# def get_user_info(remote, resp_token,
#                   fallback_to_endpoint=True,
#                   options=dict()):
#     """Get the user information from Keycloak.
# 
#     :param remote: The OAuthClient remote app
#     :param resp: The response from the 'token' endpoint; expected to be a dict
#         and to contain a JWT 'id_token'
#     :param fallback_to_endpoint: Whether or not to fall back to the 'userinfo'
#         endpoint mechanism when verifying the 'id_token' fails
#     :param options: A dictionary with additional options for `jwt.decode`
#     """
#     config_prefix = _generate_config_prefix(remote)
#     try:
#         # try to parse the "id_token" part of Keycloak's (JWT) response
#         token = resp_token["id_token"]
#         realm_url = current_app.config[f"{config_prefix}_REALM_URL"]
#         pubkey = _format_public_key(get_public_key(remote, realm_url))
#         alg = jwt.get_unverified_header(token)["alg"]
# 
#         if not isinstance(options, dict):
#             options = {}
# 
#         # consult the config whether to check the target audience
#         should_verify_aud = current_app.config.get(
#             f"{config_prefix}_VERIFY_AUD",
#             False
#         )
#         expected_aud = current_app.config.get(
#             f"{config_prefix}_AUD",
#             None
#         )
# 
#         if should_verify_aud and (expected_aud is not None):
#             options.update({"verify_aud": True})
#         else:
#             options.update({"verify_aud": False})
# 
#         # consult the config whether to check signature expiration
#         should_verify_expiration = current_app.config.get(
#             f"{config_prefix}_VERIFY_EXP",
#             False
#         )
# 
#         if should_verify_expiration:
#             options.update({"verify_exp": True})
#         else:
#             options.update({"verify_exp": False})
# 
#         user_info = jwt.decode(token,
#                                key=pubkey,
#                                algorithms=[alg],
#                                audience=expected_aud,
#                                options=options)
# 
#     except Exception as e:
#         if not fallback_to_endpoint:
#             raise OAuthKeycloakUserInfoError(
#                 "Error while fetching user information: {}".format(e),
#                 remote,
#                 resp_token
#             )
# 
#         # as a fallback, we can still contact Keycloak's userinfo endpoint
#         # `remote.get(...)` automatically includes OAuth2 tokens in the header
#         # and the response's `data` field is a dict
#         url = current_app.config[f"{config_prefix}_USER_INFO_URL"]
#         user_info = remote.get(url).data
# 
#     return user_info
# 
# Path: invenio_oauthclient/errors.py
# class OAuthError(Exception):
#     """Base class for OAuth exceptions."""
# 
#     def __init__(self, message, remote):
#         """Initialize exception.
# 
#         :param message: Error message.
#         :param message: Remote application.
#         """
#         self.message = message
#         self.remote = remote


import httpretty
import jwt
import pytest
from flask import session, url_for
from flask_login import current_user, login_user
from flask_security.utils import hash_password
from helpers import get_state, mock_keycloak
from invenio_accounts.models import User
from invenio_db import db
from six.moves.urllib_parse import parse_qs, urlparse
from invenio_oauthclient.contrib.keycloak.helpers import _format_public_key, \
    get_user_info
from invenio_oauthclient.errors import OAuthError
from invenio_oauthclient.models import UserIdentity
        assert user_info is not None
        assert user_info == expected_result


@httpretty.activate
def test_get_userinfo_from_endpoint(app,
                                    example_keycloak_token,
                                    example_keycloak_userinfo,
                                    example_keycloak_realm_info):
    """Test the "/userinfo" mechanism when the "id_token" mechanism fails."""
    mock_keycloak(app.config,
                  example_keycloak_token,
                  example_keycloak_userinfo.data,
                  example_keycloak_realm_info)

    with app.test_client() as c:
        # ensure that remote apps have been loaded (before first request)
        c.get(url_for("invenio_oauthclient.login", remote_app="keycloak"))

        remote = app.extensions["oauthlib.client"].remote_apps["keycloak"]

        # the OAuthClient has to get its token from this call
        c.get(
            url_for(
                "invenio_oauthclient.authorized", remote_app="keycloak",
                code="test", state=get_state("keycloak")
            )
        )

        # force the endpoint mechanism by not providing a token

# Path: invenio_oauthclient/contrib/keycloak/helpers.py
# def _format_public_key(public_key):
#     """PEM-format the public key."""
#     public_key = public_key.strip()
# 
#     if not public_key.startswith("-----BEGIN PUBLIC KEY-----"):
#         public_key = "-----BEGIN PUBLIC KEY-----\n" + \
#                      public_key + \
#                      "\n-----END PUBLIC KEY-----"
# 
#     return public_key
# 
# def get_user_info(remote, resp_token,
#                   fallback_to_endpoint=True,
#                   options=dict()):
#     """Get the user information from Keycloak.
# 
#     :param remote: The OAuthClient remote app
#     :param resp: The response from the 'token' endpoint; expected to be a dict
#         and to contain a JWT 'id_token'
#     :param fallback_to_endpoint: Whether or not to fall back to the 'userinfo'
#         endpoint mechanism when verifying the 'id_token' fails
#     :param options: A dictionary with additional options for `jwt.decode`
#     """
#     config_prefix = _generate_config_prefix(remote)
#     try:
#         # try to parse the "id_token" part of Keycloak's (JWT) response
#         token = resp_token["id_token"]
#         realm_url = current_app.config[f"{config_prefix}_REALM_URL"]
#         pubkey = _format_public_key(get_public_key(remote, realm_url))
#         alg = jwt.get_unverified_header(token)["alg"]
# 
#         if not isinstance(options, dict):
#             options = {}
# 
#         # consult the config whether to check the target audience
#         should_verify_aud = current_app.config.get(
#             f"{config_prefix}_VERIFY_AUD",
#             False
#         )
#         expected_aud = current_app.config.get(
#             f"{config_prefix}_AUD",
#             None
#         )
# 
#         if should_verify_aud and (expected_aud is not None):
#             options.update({"verify_aud": True})
#         else:
#             options.update({"verify_aud": False})
# 
#         # consult the config whether to check signature expiration
#         should_verify_expiration = current_app.config.get(
#             f"{config_prefix}_VERIFY_EXP",
#             False
#         )
# 
#         if should_verify_expiration:
#             options.update({"verify_exp": True})
#         else:
#             options.update({"verify_exp": False})
# 
#         user_info = jwt.decode(token,
#                                key=pubkey,
#                                algorithms=[alg],
#                                audience=expected_aud,
#                                options=options)
# 
#     except Exception as e:
#         if not fallback_to_endpoint:
#             raise OAuthKeycloakUserInfoError(
#                 "Error while fetching user information: {}".format(e),
#                 remote,
#                 resp_token
#             )
# 
#         # as a fallback, we can still contact Keycloak's userinfo endpoint
#         # `remote.get(...)` automatically includes OAuth2 tokens in the header
#         # and the response's `data` field is a dict
#         url = current_app.config[f"{config_prefix}_USER_INFO_URL"]
#         user_info = remote.get(url).data
# 
#     return user_info
# 
# Path: invenio_oauthclient/errors.py
# class OAuthError(Exception):
#     """Base class for OAuth exceptions."""
# 
#     def __init__(self, message, remote):
#         """Initialize exception.
# 
#         :param message: Error message.
#         :param message: Remote application.
#         """
#         self.message = message
#         self.remote = remote


# Path: tests/test_contrib_keycloak.py
import httpretty
import jwt
import pytest
from flask import session, url_for
from flask_login import current_user, login_user
from flask_security.utils import hash_password
from helpers import get_state, mock_keycloak
from invenio_accounts.models import User
from invenio_db import db
from six.moves.urllib_parse import parse_qs, urlparse
from invenio_oauthclient.contrib.keycloak.helpers import _format_public_key, \
    get_user_info
from invenio_oauthclient.errors import OAuthError
from invenio_oauthclient.models import UserIdentity

        assert user_info is not None
        assert user_info == expected_result


@httpretty.activate
def test_get_userinfo_from_endpoint(app,
                                    example_keycloak_token,
                                    example_keycloak_userinfo,
                                    example_keycloak_realm_info):
    """Test the "/userinfo" mechanism when the "id_token" mechanism fails."""
    mock_keycloak(app.config,
                  example_keycloak_token,
                  example_keycloak_userinfo.data,
                  example_keycloak_realm_info)

    with app.test_client() as c:
        # ensure that remote apps have been loaded (before first request)
        c.get(url_for("invenio_oauthclient.login", remote_app="keycloak"))

        remote = app.extensions["oauthlib.client"].remote_apps["keycloak"]

        # the OAuthClient has to get its token from this call
        c.get(
            url_for(
                "invenio_oauthclient.authorized", remote_app="keycloak",
                code="test", state=get_state("keycloak")
            )
        )

        # force the endpoint mechanism by not providing a token

        user_info = get_user_info(remote, None)