diff --git a/doc/conf.py b/doc/conf.py index c0b91277a..4eef9c4d4 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -6,6 +6,7 @@ ] autoclass_content = 'both' # Merge the __init__ docstring into the class docstring. +autodoc_member_order = 'bysource' # Order by source ordering templates_path = ['_templates'] diff --git a/doc/contrib/settings.rst b/doc/contrib/settings.rst index d78ab3122..5d36fb2ee 100644 --- a/doc/contrib/settings.rst +++ b/doc/contrib/settings.rst @@ -3,3 +3,4 @@ Settings .. automodule:: oic.utils.settings :members: + :show-inheritance: diff --git a/src/oic/extension/client.py b/src/oic/extension/client.py index 2127490a1..ebf637f4e 100644 --- a/src/oic/extension/client.py +++ b/src/oic/extension/client.py @@ -1,5 +1,6 @@ import hashlib import logging +import warnings from jwkest import b64e @@ -13,6 +14,7 @@ from oic.oauth2.message import ErrorResponse from oic.utils.http_util import SUCCESSFUL from oic.utils.sanitize import sanitize +from oic.utils.settings import OauthClientSettings logger = logging.getLogger(__name__) @@ -36,18 +38,26 @@ def __init__( client_id=None, client_authn_method=None, keyjar=None, - verify_ssl=True, + verify_ssl=None, config=None, message_factory=ExtensionMessageFactory, settings=None, ): + self.settings = settings or OauthClientSettings() + if verify_ssl is not None: + warnings.warn( + "`verify_ssl` is deprecated, please use `settings` instead if you need to set a non-default value.", + DeprecationWarning, + stacklevel=2, + ) + self.settings.verify_ssl = verify_ssl super().__init__( client_id=client_id, client_authn_method=client_authn_method, keyjar=keyjar, - verify_ssl=verify_ssl, config=config, message_factory=message_factory, + settings=self.settings, ) self.allow = {} self.request2endpoint.update( diff --git a/src/oic/oauth2/__init__.py b/src/oic/oauth2/__init__.py index bb2d745cd..b26dfccfd 100644 --- a/src/oic/oauth2/__init__.py +++ b/src/oic/oauth2/__init__.py @@ -179,7 +179,7 @@ def __init__( client_cert=None, timeout=None, message_factory: Type[MessageFactory] = OauthMessageFactory, - settings: OauthClientSettings = None, + settings: PyoidcSettings = None, ): """ Initialize the instance. diff --git a/src/oic/oauth2/consumer.py b/src/oic/oauth2/consumer.py index c61d867c3..5ab1b8aec 100644 --- a/src/oic/oauth2/consumer.py +++ b/src/oic/oauth2/consumer.py @@ -160,7 +160,7 @@ def __init__( ) self.settings.timeout = client_config.pop("timeout") - Client.__init__(self, **client_config, settings=self.settings) + Client.__init__(self, settings=self.settings, **client_config) self.authz_page = authz_page self.response_type = response_type diff --git a/src/oic/oauth2/provider.py b/src/oic/oauth2/provider.py index 3954922f1..8b04c1611 100644 --- a/src/oic/oauth2/provider.py +++ b/src/oic/oauth2/provider.py @@ -8,6 +8,7 @@ from functools import cmp_to_key from typing import Dict # noqa - This is used for MyPy from typing import List # noqa - This is used for MyPy +from typing import Optional # noqa - This is used for MyPy from typing import Union # noqa - This is used for MyPy from urllib.parse import parse_qs from urllib.parse import splitquery # type: ignore @@ -54,6 +55,7 @@ from oic.utils.http_util import SeeOther from oic.utils.http_util import Unauthorized from oic.utils.http_util import make_cookie +from oic.utils.keyio import KeyJar # noqa from oic.utils.sanitize import sanitize from oic.utils.sdb import AccessCodeUsed from oic.utils.session_backend import AuthnEvent @@ -268,7 +270,7 @@ def __init__( self.session_cookie_name = "pyoic_session" self.sso_cookie_name = "pyoidc_sso" self.baseurl = baseurl - self.keyjar = None # type: KeyJar + self.keyjar = None # type: Optional[KeyJar] self.trace = None self.events = None self.scopes = ["offline_access"] @@ -671,7 +673,7 @@ def auth_init(self, request): try: keyjar = self.keyjar except AttributeError: - keyjar = "" + keyjar = None try: # verify that the request message is correct diff --git a/src/oic/oic/__init__.py b/src/oic/oic/__init__.py index 33b972b55..447744746 100644 --- a/src/oic/oic/__init__.py +++ b/src/oic/oic/__init__.py @@ -69,6 +69,7 @@ from oic.utils.http_util import Response from oic.utils.keyio import KeyJar from oic.utils.sanitize import sanitize +from oic.utils.settings import OicClientSettings from oic.utils.settings import OicServerSettings from oic.utils.settings import PyoidcSettings from oic.utils.webfinger import OIC_ISSUER @@ -331,22 +332,47 @@ def __init__( client_prefs=None, client_authn_method=None, keyjar=None, - verify_ssl=True, + verify_ssl=None, config=None, client_cert=None, requests_dir="requests", message_factory: Type[MessageFactory] = OIDCMessageFactory, + settings: PyoidcSettings = None, ): - + """ + Initialize the instance. + + Keyword Args: + settings + Instance of :class:`OauthClientSettings` with configuration options. + Currently used settings are: + - verify_ssl + - client_cert + - timeout + """ + self.settings = settings or OicClientSettings() + if verify_ssl is not None: + warnings.warn( + "`verify_ssl` is deprecated, please use `settings` instead if you need to set a non-default value.", + DeprecationWarning, + stacklevel=2, + ) + self.settings.verify_ssl = verify_ssl + if client_cert is not None: + warnings.warn( + "`client_cert` is deprecated, please use `settings` instead if you need to set a non-default value.", + DeprecationWarning, + stacklevel=2, + ) + self.settings.client_cert = client_cert oauth2.Client.__init__( self, client_id, client_authn_method=client_authn_method, keyjar=keyjar, - verify_ssl=verify_ssl, config=config, - client_cert=client_cert, message_factory=message_factory, + settings=self.settings, ) self.file_store = "./file/" diff --git a/src/oic/oic/claims_provider.py b/src/oic/oic/claims_provider.py index 560d4a276..5feac1f9b 100644 --- a/src/oic/oic/claims_provider.py +++ b/src/oic/oic/claims_provider.py @@ -1,4 +1,5 @@ import logging +import warnings from typing import Any # noqa from typing import Dict # noqa from typing import Optional # noqa @@ -19,6 +20,8 @@ from oic.utils.http_util import Response from oic.utils.keyio import KeyJar from oic.utils.sanitize import sanitize +from oic.utils.settings import OicClientSettings +from oic.utils.settings import OicProviderSettings __author__ = "rohe0002" @@ -67,8 +70,17 @@ def __init__( keyjar=None, hostname="", dist_claims_mode=None, - verify_ssl=True, + verify_ssl=None, + settings=None, ): + self.settings = settings or OicProviderSettings() + if verify_ssl is not None: + warnings.warn( + "`verify_ssl` is deprecated, please use `settings` instead if you need to set a non-default value.", + DeprecationWarning, + stacklevel=2, + ) + self.settings.verify_ssl = verify_ssl Provider.__init__( self, name, @@ -82,7 +94,7 @@ def __init__( urlmap, keyjar, hostname, - verify_ssl=verify_ssl, + settings=self.settings, ) if keyjar is None: @@ -176,9 +188,17 @@ def claims_info_endpoint(self, request, authn): class ClaimsClient(Client): - def __init__(self, client_id=None, verify_ssl=True): - - Client.__init__(self, client_id, verify_ssl=verify_ssl) + def __init__(self, client_id=None, verify_ssl=None, settings=None): + self.settings = settings or OicClientSettings() + if verify_ssl is not None: + warnings.warn( + "`verify_ssl` is deprecated, please use `settings` instead if you need to set a non-default value.", + DeprecationWarning, + stacklevel=2, + ) + self.settings.verify_ssl = verify_ssl + + Client.__init__(self, client_id, settings=self.settings) self.request2endpoint = REQUEST2ENDPOINT.copy() self.request2endpoint["UserClaimsRequest"] = "userclaims_endpoint" diff --git a/src/oic/utils/settings.py b/src/oic/utils/settings.py index 7432bf178..9f4617a12 100644 --- a/src/oic/utils/settings.py +++ b/src/oic/utils/settings.py @@ -27,7 +27,7 @@ class PyoidcSettings: Control TLS server certificate validation. If set to True the certificate is validated against the global settings, if set to False, no validation is performed. - If set to a filename his is used as a certificate bundle in openssl format. + If set to a filename this is used as a certificate bundle in openssl format. If set to a directory name this is used as a CA directory in the openssl format. client_cert Local cert to use as client side certificate. @@ -51,7 +51,7 @@ def __init__( def __setattr__(self, name, value): """This attempts to check if value matches the expected value.""" - annotation = typing.get_type_hints(self.__init__)[name] + annotation = typing.get_type_hints(self.__init__)[name] # type: ignore # Expand Union -> Since 3.8, this can be written as typing.get_origin if getattr(annotation, "__origin__", annotation) is Union: expanded = tuple(an for an in annotation.__args__) @@ -82,6 +82,10 @@ class OauthClientSettings(ClientSettings): """Specific settings for OAuth 2.0 consumer.""" +class OicClientSettings(OauthClientSettings): + """Settings for OpenID Connect Client.""" + + class OauthConsumerSettings(OauthClientSettings): """Settings for OAuth 2.0 client.""" diff --git a/tests/test_oauth2_consumer.py b/tests/test_oauth2_consumer.py index e609f9855..be8c41a4b 100644 --- a/tests/test_oauth2_consumer.py +++ b/tests/test_oauth2_consumer.py @@ -21,12 +21,13 @@ from oic.utils import time_util from oic.utils.http_util import make_cookie from oic.utils.session_backend import DictSessionBackend +from oic.utils.settings import OauthClientSettings __author__ = "rohe0002" +CLIENT_SETTINGS = OauthClientSettings(verify_ssl="/usr/local/etc/oic/ca_certs.txt") CLIENT_CONFIG = { "client_id": "number5", - "verify_ssl": "/usr/local/etc/oic/ca_certs.txt", } CONSUMER_CONFIG = { @@ -101,7 +102,11 @@ def test_stateID(): def test_factory(): sdb = DictSessionBackend() consumer = Consumer( - sdb, client_config=CLIENT_CONFIG, server_info=SERVER_INFO, **CONSUMER_CONFIG + sdb, + client_config=CLIENT_CONFIG, + server_info=SERVER_INFO, + settings=CLIENT_SETTINGS, + **CONSUMER_CONFIG, ) sid = stateID("https://example.org/", consumer.seed) _state = sid @@ -118,7 +123,7 @@ def test_factory(): CLIENT_CONFIG["client_id"], client_config=CLIENT_CONFIG, server_info=SERVER_INFO, - **CONSUMER_CONFIG + **CONSUMER_CONFIG, ) assert _oac.client_id == consumer.client_id @@ -132,7 +137,8 @@ def create_consumer(self): DictSessionBackend(), client_config=CLIENT_CONFIG, server_info=SERVER_INFO, - **CONSUMER_CONFIG + settings=CLIENT_SETTINGS, + **CONSUMER_CONFIG, ) def test_init(self): @@ -140,13 +146,17 @@ def test_init(self): DictSessionBackend(), client_config=CLIENT_CONFIG, server_info=SERVER_INFO, - **CONSUMER_CONFIG + settings=CLIENT_SETTINGS, + **CONSUMER_CONFIG, ) cons._backup("123456") assert "123456" in cons.sdb cons = Consumer( - DictSessionBackend(), client_config=CLIENT_CONFIG, **CONSUMER_CONFIG + DictSessionBackend(), + client_config=CLIENT_CONFIG, + settings=CLIENT_SETTINGS, + **CONSUMER_CONFIG, ) assert cons.authorization_endpoint is None diff --git a/tests/test_oic_provider_logout.py b/tests/test_oic_provider_logout.py index 9ebcd2693..ecd9b8a09 100644 --- a/tests/test_oic_provider_logout.py +++ b/tests/test_oic_provider_logout.py @@ -348,7 +348,7 @@ def test_let_user_verify_logout_with_id_token_hint(self): assert txt in res.message def test_end_session_endpoint_with_cookie(self): - self.provider.events = DummyEventStore() + self.provider.events = DummyEventStore() # type: ignore self._code_auth() cookie = self._create_cookie("username", "number5") @@ -818,7 +818,7 @@ def test_do_verified_logout_all(self): assert set(res.keys()) == {"iframe", "cookie"} def test_do_verified_logout_just_the_one(self): - self.provider.events = DummyEventStore() + self.provider.events = DummyEventStore() # type: ignore self._code_auth() self._code_auth2()