From e1d1d84ee07c0cbac61ed28445af05eee884ab95 Mon Sep 17 00:00:00 2001 From: Conor Holden Date: Wed, 4 Dec 2024 11:06:14 +0100 Subject: [PATCH] :memo: add sphinx django model field directive --- docs/_ext/model_field/__init__.py | 16 ++++++++ docs/_ext/model_field/directives.py | 31 ++++++++++++++++ docs/_ext/model_field/roles.py | 22 +++++++++++ docs/_ext/model_field/utils.py | 20 ++++++++++ docs/conf.py | 4 ++ docs/setup_configuration.rst | 57 +++++++++++++---------------- 6 files changed, 118 insertions(+), 32 deletions(-) create mode 100644 docs/_ext/model_field/__init__.py create mode 100644 docs/_ext/model_field/directives.py create mode 100644 docs/_ext/model_field/roles.py create mode 100644 docs/_ext/model_field/utils.py diff --git a/docs/_ext/model_field/__init__.py b/docs/_ext/model_field/__init__.py new file mode 100644 index 0000000..ec46318 --- /dev/null +++ b/docs/_ext/model_field/__init__.py @@ -0,0 +1,16 @@ +from sphinx.application import Sphinx +from sphinx.util.typing import ExtensionMetadata + +from .directives import ModelFieldsDirective +from .roles import ModelFieldRole + + +def setup(app: Sphinx) -> ExtensionMetadata: + app.add_role("model_field", ModelFieldRole()) + app.add_directive("model_fields", ModelFieldsDirective) + + return { + "version": "0.1", + "parallel_read_safe": True, + "parallel_write_safe": True, + } diff --git a/docs/_ext/model_field/directives.py b/docs/_ext/model_field/directives.py new file mode 100644 index 0000000..a306596 --- /dev/null +++ b/docs/_ext/model_field/directives.py @@ -0,0 +1,31 @@ +from __future__ import annotations + +from django.utils.module_loading import import_string + +from docutils import nodes +from sphinx.util.docutils import SphinxDirective + +from .utils import get_field_representation + + +class ModelFieldsDirective(SphinxDirective): + """Displays a model's fields with their name, helptext and default in a list""" + + required_arguments = 1 + has_content = True + + def run(self) -> list[nodes.Node]: + + model_path = self.arguments[0] + model = import_string(model_path) + + field_list = [] + + for line in self.content: + field = model._meta.get_field(line) + node = nodes.paragraph("", "", *get_field_representation(field)) + field_list.append(nodes.list_item("", node)) + + bullet_list = nodes.bullet_list("", *field_list) + + return [bullet_list] diff --git a/docs/_ext/model_field/roles.py b/docs/_ext/model_field/roles.py new file mode 100644 index 0000000..bcf4d9b --- /dev/null +++ b/docs/_ext/model_field/roles.py @@ -0,0 +1,22 @@ +from django.utils.module_loading import import_string + +from docutils import nodes +from sphinx.util.docutils import SphinxRole + +from .utils import get_field_representation + + +class ModelFieldRole(SphinxRole): + """Displays a model field's name, helptext and default inline""" + + def run(self) -> tuple[list[nodes.Node], list[nodes.system_message]]: + + field_split = len(self.text) - self.text[::-1].index(".") - 1 + model_path = self.text[:field_split] + field_name = self.text[field_split + 1 :] + + model = import_string(model_path) + field = model._meta.get_field(field_name) + + field_nodes = get_field_representation(field) + return field_nodes, [] diff --git a/docs/_ext/model_field/utils.py b/docs/_ext/model_field/utils.py new file mode 100644 index 0000000..6941b5f --- /dev/null +++ b/docs/_ext/model_field/utils.py @@ -0,0 +1,20 @@ +from django.db.models.fields import Field + +from docutils import nodes + + +def get_field_representation(field: Field) -> list[nodes.literal | nodes.Text]: + + name = nodes.literal("", nodes.Text(field.name)) + description = nodes.Text(f": {field.help_text}.") + + field_nodes = [name, description] + + # Should it include blank that default to empty string? + if field.has_default(): + field_nodes.append(nodes.Text(" Defaults to ")) + field_nodes.append(nodes.literal("", nodes.Text(field.get_default()))) + else: + field_nodes.append(nodes.Text(" No default.")) + + return field_nodes diff --git a/docs/conf.py b/docs/conf.py index 2b5c33d..ba3ddfc 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -37,9 +37,13 @@ # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named "sphinx.ext.*") or your custom # ones. + +sys.path.append(os.path.abspath("./_ext")) + extensions = [ "sphinx.ext.autodoc", "sphinx.ext.todo", + "model_field", ] # Add any paths that contain templates here, relative to this directory. diff --git a/docs/setup_configuration.rst b/docs/setup_configuration.rst index be63a14..ef491ec 100644 --- a/docs/setup_configuration.rst +++ b/docs/setup_configuration.rst @@ -65,18 +65,17 @@ Required Fields: """""""""""""""" -* ``oidc_rp_client_id``: OpenID Connect client ID from the OIDC Provider. -* ``oidc_rp_client_secret``: OpenID Connect secret from the OIDC Provider. +* :model_field:`mozilla_django_oidc_db.models.OpenIDConnectConfig.oidc_rp_client_id` +* :model_field:`mozilla_django_oidc_db.models.OpenIDConnectConfig.oidc_rp_client_secret` * ``endpoint_config``: Dictionary containing endpoint information - * ``oidc_op_discovery_endpoint``: URL of your OpenID Connect provider discovery endpoint ending with a slash (`.well-known/...` will be added automatically). + * :model_field:`mozilla_django_oidc_db.models.OpenIDConnectConfig.oidc_op_discovery_endpoint` **OR** - * ``oidc_op_authorization_endpoint``: URL of your OpenID Connect provider authorization endpoint - * ``oidc_op_token_endpoint``: URL of your OpenID Connect provider token endpoint - * ``oidc_op_user_endpoint``: URL of your OpenID Connect provider userinfo endpoint - + * :model_field:`mozilla_django_oidc_db.models.OpenIDConnectConfig.oidc_op_authorization_endpoint` + * :model_field:`mozilla_django_oidc_db.models.OpenIDConnectConfig.oidc_op_token_endpoint` + * :model_field:`mozilla_django_oidc_db.models.OpenIDConnectConfig.oidc_op_user_endpoint` The endpoints must be provided in the ``endpoint_config`` dictionary. You can add the discovery endpoint to automatically fetch the other endpoints. @@ -85,7 +84,6 @@ Providing both will cause the validation to fail. Optional Fields: """""""""""""""" - .. warning:: Values that are not provided will use the default or empty value and will overwrite any setting changed in the admin. @@ -93,27 +91,22 @@ Optional Fields: All the following keys are placed in the ``oidc_db_config_admin_auth`` dictionary. -* ``enabled``: whether OIDC is enabled for admin login. Defaults to ``True``. -* ``oidc_op_jwks_endpoint``: URL of your OpenID Connect provider JSON Web Key Set endpoint. - Required if ``RS256`` is used as signing algorithm. No default value. -* ``claim_mapping``: Mapping from user-model fields to OIDC claims. - Defaults to ``{"email": ["email"], "first_name": ["given_name"], "last_name": ["family_name"]}`` -* ``username_claim``: The name of the OIDC claim that is used as the username. Defaults to ``["sub"]`` -* ``groups_claim``: The name of the OIDC claim that holds the values to map to local user groups. Defaults to ``["roles"]`` -* ``default_groups``: The default groups to which every user logging in with OIDC will be assigned. No default values. -* ``superuser_group_names``: If any of these group names are present in the claims upon login, the user will be marked as a superuser. - If none of these groups are present the user will lose superuser permissions. Defaults to empty list. -* ``make_users_staff``: Users will be flagged as being a staff user automatically. - This allows users to login to the admin interface. Defaults to ``False``. -* ``oidc_use_nonce``: Controls whether the OpenID Connect client uses nonce verification. Defaults to ``True``. -* ``oidc_nonce_size``: Sets the length of the random string used for OpenID Connect nonce verification. Defaults to ``32``. -* ``oidc_state_size``: Sets the length of the random string used for OpenID Connect state verification. Defaults to ``32``. -* ``oidc_rp_idp_sign_key``: Key the Identity Provider uses to sign ID tokens in the case of an RSA sign algorithm. - Should be the signing key in PEM or DER format. No default. -* ``oidc_rp_scopes_list``: OpenID Connect scopes that are requested during login. Defaults to ``["openid", "email", "profile"]``. -* ``oidc_rp_sign_algo``: Algorithm the Identity Provider uses to sign ID tokens. Defaults to ``"HS256"``. -* ``sync_groups``: If checked, local user groups will be created for group names present in the groups claim, - if they do not exist yet locally. Defaults to ``True``. -* ``sync_groups_glob_pattern``: The glob pattern that groups must match to be synchronized to the local database. Defaults to ``"*"``. -* ``userinfo_claims_source``: Indicates the source from which the user information claims should be extracted - (``"userinfo_endpoint"`` or ``"id_token"``). Defaults to ``"userinfo_endpoint"``. +.. model_fields:: mozilla_django_oidc_db.models.OpenIDConnectConfig + + enabled + oidc_op_jwks_endpoint + claim_mapping + username_claim + groups_claim + default_groups + superuser_group_names + make_users_staff + oidc_use_nonce + oidc_nonce_size + oidc_state_size + oidc_rp_idp_sign_key + oidc_rp_scopes_list + oidc_rp_sign_algo + sync_groups + sync_groups_glob_pattern + userinfo_claims_source