diff --git a/docs/source/conf.py b/docs/source/conf.py index dfe9393..372f86b 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -21,7 +21,14 @@ # -- General configuration --------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration -extensions = ['sphinx.ext.autodoc'] +extensions = [ + 'sphinx.ext.autodoc', + 'sphinx.ext.intersphinx', +] + +intersphinx_mapping = { + 'balderhub-auth': ('https://hub.balder.dev/projects/auth/en/latest/', None), +} templates_path = ['_templates'] exclude_patterns = [] diff --git a/docs/source/contrib.rst b/docs/source/contrib.rst new file mode 100644 index 0000000..51d9a86 --- /dev/null +++ b/docs/source/contrib.rst @@ -0,0 +1,35 @@ +Contribution To Other BalderHub +******************************* + +This package provides contribution code to some other BalderHub projects. Find more about it, within this module. + +If you want to install this package with support for all CONTRIB packages, you can install it like + +.. code-block:: none + + >>> pip install balderhub-html[all] + + +Contrib for ``balderhub-auth`` +============================== + +For activating this module, you need to install the package like shown below + +.. code-block:: none + + >>> pip install balderhub-webdriver[auth] + +Once installed you can use it. + +Permission Tests +---------------- + +For permission tests you can use the ready-to-use setup feature +:class:`balderhub.webdriver.contrib.auth.setup_features.client.OperationHandlingOverWebdriverFeature`. + +This feature provides the full implementation for opening :class:`balderhub.http.contrib.auth.utils.HttpOperation`. +You only need to add the webdriver setup feature to the same device and everything else is already implemented. + +.. note:: + You can find more information about the implementation of HTTP permission tests within the + `contrib section of the balderhub-http package `_. diff --git a/docs/source/index.rst b/docs/source/index.rst index 641a48f..7276fa4 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -28,6 +28,7 @@ provides are more detailed interface for all further guicontrol packages that su installation.rst topic_intro.rst + contrib.rst scenarios.rst features.rst examples.rst diff --git a/requirements.txt b/requirements.txt index 57bb0e6..4b533b3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,4 +5,6 @@ sphinx==6.1.3 setuptools~=78.1.1 pycryptodome>=3.17 balderhub-guicontrol -pylint==3.3.9 \ No newline at end of file +pylint==3.3.9 +balderhub-auth +balderhub-http[auth] \ No newline at end of file diff --git a/setup.cfg b/setup.cfg index 9c4503a..98982b8 100644 --- a/setup.cfg +++ b/setup.cfg @@ -32,6 +32,10 @@ project_urls = [options] packages = balderhub.webdriver + balderhub.webdriver.contrib + balderhub.webdriver.contrib.auth + balderhub.webdriver.contrib.auth.setup_features + balderhub.webdriver.contrib.auth.setup_features.client balderhub.webdriver.lib balderhub.webdriver.lib.scenario_features balderhub.webdriver.lib.utils @@ -49,3 +53,9 @@ setup_requires = setuptools setuptools-scm>=6.0 zip_safe = no +[options.extras_require] +auth = + balderhub-auth~=0.0.1b3 + balderhub-http[auth]~=0.0.1b0 +all = + balderhub-webdriver[auth] \ No newline at end of file diff --git a/src/balderhub/webdriver/contrib/__init__.py b/src/balderhub/webdriver/contrib/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/balderhub/webdriver/contrib/auth/__init__.py b/src/balderhub/webdriver/contrib/auth/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/balderhub/webdriver/contrib/auth/setup_features/__init__.py b/src/balderhub/webdriver/contrib/auth/setup_features/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/balderhub/webdriver/contrib/auth/setup_features/client/__init__.py b/src/balderhub/webdriver/contrib/auth/setup_features/client/__init__.py new file mode 100644 index 0000000..129bcf8 --- /dev/null +++ b/src/balderhub/webdriver/contrib/auth/setup_features/client/__init__.py @@ -0,0 +1,5 @@ +from .operation_handling_over_webdriver_feature import OperationHandlingOverWebdriverFeature + +__all__ = [ + 'OperationHandlingOverWebdriverFeature' +] diff --git a/src/balderhub/webdriver/contrib/auth/setup_features/client/operation_handling_over_webdriver_feature.py b/src/balderhub/webdriver/contrib/auth/setup_features/client/operation_handling_over_webdriver_feature.py new file mode 100644 index 0000000..b352441 --- /dev/null +++ b/src/balderhub/webdriver/contrib/auth/setup_features/client/operation_handling_over_webdriver_feature.py @@ -0,0 +1,67 @@ +import json +from typing import Any + +from balderhub.http.contrib.auth.utils.http_resource import HttpResource +from balderhub.http.contrib.auth.utils.actions import HttpAction +from balderhub.auth.lib.utils import Operation +import balderhub.webdriver.lib.scenario_features +from balderhub.auth.lib.scenario_features.client import OperationHandlingFeature + + +class OperationHandlingOverWebdriverFeature(OperationHandlingFeature): + """ + Handles operation execution over a WebDriver interface. + + This class enables executing HTTP operations over a WebDriver-based control + interface, leveraging browser automation to handle web requests. It provides + methods to manage entering and leaving operations, which may involve making + HTTP requests using JavaScript executed within the browser context. + """ + #: inner feature reference to the WebDriverControlFeature instance used to execute browser operations + webdriver = balderhub.webdriver.lib.scenario_features.WebdriverControlFeature() + + @property + def additional_headers(self) -> dict[str, Any]: + """ + Provides a property to provide additional headers, that should be appended to every request. + + This property retrieves a dictionary containing additional headers + that can be used in various contexts like HTTP requests. + """ + return {} + + def enter_operation(self, operation: Operation) -> bool: + resource: HttpResource = operation.resource + action: HttpAction = operation.action + + params = {"method": action.http_method} + if self.additional_headers: + params['headers'] = self.additional_headers + + response = self.webdriver.driver.execute_sync_script( + f'return fetch("{resource.url}", {json.dumps(params)})' + ) + + response_status = response['status'] + response_status_text = response['statusText'] + + if response_status == 401: + raise HttpResource.UnauthorizedError( + f'web request returned 401 (`{response_status_text}`): `{response}`') + if response_status == 403: + raise HttpResource.NoPermissionError( + f'web request returned 403 (`{response_status_text}`): `{response}`') + if response_status == 404: + raise HttpResource.DoesNotExistError( + f'web request returned 404 (`{response_status_text}`): `{response}`') + if response_status == 405: + raise HttpResource.NotAllowedMethodError( + f'web request returned 405 (`{response_status_text}`): `{response}`') + if response_status != 200: + raise HttpResource.ResourceEnterError( + f'web request returned unexpected status code {response["status"]} (`{response_status_text}`): ' + f'`{response}`') + return True + + def leave_operation(self, operation: Operation) -> bool: + return True