From 01ae32cb5ce4fd8a61c9e7aee4c566a18d7bd39e Mon Sep 17 00:00:00 2001 From: Milov Dmitriy Date: Tue, 18 Feb 2025 14:46:15 +0300 Subject: [PATCH 01/11] add: ruff config --- pyproject.toml | 200 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 133 insertions(+), 67 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index ee692e65..9a036a42 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -61,24 +61,6 @@ watchdog = "4.0.2" requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" -[tool.isort] -known_first_party = [ - "ldap_protocol", - "client", - "config", - "models", - "api", - "security", - "tests", - "web_app", - "ioc", - "schedule", - "extra", -] -include_trailing_comma = true -line_length = 79 -multi_line_output = 3 - [tool.mypy] plugins = ["sqlalchemy.ext.mypy.plugin", "pydantic.mypy"] ignore_missing_imports = true @@ -105,65 +87,149 @@ show_missing = true [tool.coverage.run] concurrency = ["thread", "gevent"] -[tool.ruff] -# Exclude a variety of commonly ignored directories. -exclude = [ - ".bzr", - ".direnv", - ".eggs", - ".git", - ".git-rewrite", - ".hg", - ".ipynb_checkpoints", - ".mypy_cache", - ".nox", - ".pants.d", - ".pyenv", - ".pytest_cache", - ".pytype", - ".ruff_cache", - ".svn", - ".tox", - ".venv", - ".vscode", - "__pypackages__", - "_build", - "buck-out", - "build", - "dist", - "node_modules", - "site-packages", - "venv", -] -line-length = 79 -indent-width = 4 +# RUFF +# Ruff is a linter, not a type checker. +# +# commands: +# ruff check . --preview +# ruff check . --fix --unsafe-fixes +# ruff format . +[tool.ruff] target-version = "py312" +line-length = 79 +output-format = "grouped" +unsafe-fixes = true + +[tool.ruff.format] +docstring-code-format = true +docstring-code-line-length = 79 +line-ending = "lf" +skip-magic-trailing-comma = false # default: false [tool.ruff.lint] -# Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default. -# Unlike Flake8, Ruff doesn't enable pycodestyle warnings (`W`) or -# McCabe complexity (`C901`) by default. -select = ["E4", "E7", "E9", "F"] -ignore = [] +# Flake8 S101, I900, G004, IF100, S311, D301, E231 +# Для alembic предлагаю оставить включенным, только некоторые правила можно вырубить. +# S101 - done. Использование assert в коде. Вырубил только в тестах +# I900 - done. Это аналог RUF100. Проверка использования комментариев # noqa +# G004 - done. Аналог S102. Предлагаю врубить. Проверка использования небезопасных методов и функций (например, eval, exec) +# IF100 - done. Это аналог SIM101. Предлагаю врубить. Проверка использования if условий, которые могут быть упрощены +# S311 - done. Это аналог S311. Вырубил. +# D301 - done. Вырублены все D-rules. Мб включим когда-нибудь, а пока добавлю и закомментирую. +# E231 - done. Это не надо вырубать. + +# Ruff contains flake8-awesome. +# flake8-annotations -> ANN (Ruff) +# flake8-bandit -> S (Ruff) +# flake8-bugbear -> B (Ruff) +# flake8-commas -> COM (Ruff) +# flake8-docstrings -> D (Ruff) +# flake8-isort -> I (Ruff) +# flake8-pytest -> PT (Ruff) +# flake8-quotes -> Q (Ruff) +# flake8-simplify -> SIM (Ruff) +# flake8-type-checking -> TC (Ruff) +select = [ + "F", # Pyflakes. Must have + "E", # pycodestyle (Error), check tool.ruff.lint.pycodestyle. Must have + "W", # pycodestyle (Warnings), check tool.ruff.lint.pycodestyle + "C90", # mccabe (max_complexity), check tool.ruff.lint.mccabe + "I", # isort, check tool.ruff.lint.isort. Must have + "N", # pep8-naming + "A", # flake8 builtin-attribute-shadowing + "D", # pydocstyle, check tool.ruff.lint.pydocstyle + "UP", # pyupgrade, check tool.ruff.lint.pyupgrade. Must have + "ANN", # flake8-annotations, check tool.ruff.lint.flake8-annotations + # "ASYNC", # flake8-async TODO uncomment, ruff fix and fix error + "S", # flake8-bandit + "B", # flake8-bugbear. Must have + "COM", # flake8-commas + # "CPY", # flake8-copyright TODO uncomment, ruff fix and fix error + # "PIE", # flake8-pie TODO uncomment, ruff fix and fix error + # "PYI", # flake8-pyi TODO uncomment, ruff fix and fix error + "PT", # flake8-pytest + "Q", # flake8-quotes + # "RET", # flake8-return TODO uncomment, ruff fix and fix error + # "SLF", # flake8-self TODO uncomment, ruff fix and fix error + "SIM", # flake8-simplify. Must have + "TC", # flake8-type-checking, check flake8-type-checking + # "ARG", # flake8-unused-arguments TODO uncomment, ruff fix and fix error + # "TD", # flake8-todos TODO uncomment, ruff fix and fix error + # "ERA", # eradicate TODO uncomment, ruff fix and fix error + # "PGH", # pygrep-hooks TODO does we need it? uncomment, ruff fix and fix error + # "PL", # Pylint TODO uncomment, ruff fix and fix error + # "DOC", # pydoclint TODO uncomment, ruff fix and fix error + # "RUF", # Ruff-specific rules TODO uncomment, ruff fix and fix error + "RUF100", # Ruff100-specific rule TODO delete that and uncomment "RUF"-rule in line up. +] + +# Gradually remove all values marked 'TODO' and fix errors. +ignore = [ + "D101", # TODO delete that and fix all errors + "D102", # TODO delete that and fix all errors + "D103", # TODO delete that and fix all errors + "D104", # TODO delete that and fix all errors + "D105", # TODO delete that and fix all errors + "D106", # TODO delete that and fix all errors + "D107", # TODO delete that and fix all errors + "D203", # this is necessary. Conflict with `D211` + "D205", # TODO delete that and fix all errors + "D213", # this is necessary. Conflict with `D212` + "UP007", # TODO delete that and fix all errors + "UP015", # TODO delete that and fix all errors + "UP017", # TODO delete that and fix all errors + "UP032", # TODO delete that and fix all errors + "UP034", # TODO delete that and fix all errors + "UP035", # this is necessary. We allowed deprecated import + "UP037", # TODO delete that and fix all errors + "UP040", # TODO delete that and fix all errors + "ANN001", # TODO delete that and fix all errors + "ANN002", # this is necessary. + "ANN003", # this is necessary. + "ANN401", # TODO delete that and fix all errors + "S311", # this is necessary. + "B904", # this is necessary. + "COM812", # this is necessary. Cause conflicts when used with the formatter + "TC001", # this is necessary. + "TC002", # this is necessary. + "TC003", # this is necessary. + "SIM101", # analogue simplify-boolean-expressions IF100 +] + +extend-select = [] -# Allow fix for all enabled rules (when `--fix`) is provided. fixable = ["ALL"] unfixable = [] -# Allow unused variables when underscore-prefixed. -dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" +[tool.ruff.lint.per-file-ignores] +"tests/*.py" = ["S101"] # Ignore `Flake8-bandit S101` rule for the `tests/` directory. +# "alembic/*.py" = ["I001"] # Ignore `Flake8-isort IO01` rule for the `alembic/` directory. It works incorrect in CI ruff test. -[tool.ruff.format] -# Like Black, use double quotes for strings. -quote-style = "double" +[tool.ruff.lint.mccabe] +# 30 Complexity level is too high, need to reduce this level or ignore it `# noqa: C901`. +max-complexity = 30 -# Like Black, indent with spaces, rather than tabs. -indent-style = "space" +[tool.ruff.lint.isort] +known-first-party = [ + "ldap_protocol", + "client", + "config", + "models", + "api", + "security", + "tests", + "web_app", + "ioc", + "schedule", + "extra", +] +split-on-trailing-comma = false +combine-as-imports = true -# Like Black, respect magic trailing commas. -skip-magic-trailing-comma = false +[tool.ruff.lint.flake8-annotations] +suppress-dummy-args = true +suppress-none-returning = true -# Like Black, automatically detect the appropriate line ending. -line-ending = "auto" +[tool.ruff.lint.flake8-type-checking] +quote-annotations = true From 60f60d2e54c1586f57b41d0fd0e49017399cd505 Mon Sep 17 00:00:00 2001 From: Milov Dmitriy Date: Tue, 18 Feb 2025 14:46:34 +0300 Subject: [PATCH 02/11] add: ruff check and fix --- .kerberos/config_server.py | 4 +- .../versions/6f8fe2548893_fix_read_only.py | 26 +- .../8c2bd40dd809_add_protocols_attr.py | 10 +- .../bf435bbd95ff_add_rdn_attr_name.py | 20 +- .../bv546ccd35fa_fix_krbadmin_attrs.py | 14 +- .../dafg3a4b22ab_add_preauth_princ.py | 14 +- .../versions/f68a134a3685_add_bypass.py | 20 +- app/alembic/versions/fafc3d0b11ec_.py | 14 +- app/api/auth/oauth2.py | 2 +- app/api/auth/router.py | 8 +- app/api/auth/router_mfa.py | 13 +- app/api/auth/schema.py | 16 +- app/api/auth/utils.py | 2 +- app/api/main/ap_router.py | 4 +- app/api/main/dns_router.py | 16 +- app/api/main/krb5_router.py | 18 +- app/api/main/schema.py | 6 +- app/api/network/router.py | 12 +- app/api/network/schema.py | 8 +- app/api/shadow/router.py | 4 +- app/config.py | 12 +- app/extra/dev_data.py | 4 +- app/extra/scripts/krb_pass_sync.py | 2 +- app/extra/scripts/uac_sync.py | 2 +- app/extra/setup_dev.py | 4 +- app/ldap_protocol/asn1parser.py | 44 ++- app/ldap_protocol/dialogue.py | 2 +- app/ldap_protocol/dns.py | 44 ++- app/ldap_protocol/filter_interpreter.py | 8 +- app/ldap_protocol/kerberos.py | 51 ++- app/ldap_protocol/ldap_requests/add.py | 4 +- app/ldap_protocol/ldap_requests/base.py | 4 +- app/ldap_protocol/ldap_requests/bind.py | 15 +- .../ldap_requests/bind_methods/base.py | 4 +- .../ldap_requests/bind_methods/sasl_gssapi.py | 2 +- app/ldap_protocol/ldap_requests/delete.py | 2 +- app/ldap_protocol/ldap_requests/extended.py | 2 +- app/ldap_protocol/ldap_requests/modify.py | 10 +- app/ldap_protocol/ldap_requests/modify_dn.py | 2 +- app/ldap_protocol/ldap_requests/search.py | 39 ++- app/ldap_protocol/ldap_responses.py | 14 +- app/ldap_protocol/multifactor.py | 5 +- app/ldap_protocol/policies/access_policy.py | 4 +- app/ldap_protocol/policies/network_policy.py | 10 +- app/ldap_protocol/policies/password_policy.py | 8 +- app/ldap_protocol/server.py | 35 +- app/ldap_protocol/session_storage.py | 9 +- app/ldap_protocol/user_account_control.py | 5 +- app/ldap_protocol/utils/cte.py | 17 +- app/ldap_protocol/utils/queries.py | 2 +- app/models.py | 22 +- app/multidirectory.py | 8 +- interface | 2 +- tests/conftest.py | 18 +- tests/test_api/test_auth/test_pwd_policy.py | 6 +- tests/test_api/test_auth/test_router_mfa.py | 26 +- tests/test_api/test_main/conftest.py | 4 +- tests/test_api/test_main/test_dns.py | 28 +- tests/test_api/test_main/test_kadmin.py | 166 ++++----- tests/test_api/test_main/test_multifactor.py | 2 +- .../test_main/test_router/test_add.py | 110 +++--- .../test_main/test_router/test_delete.py | 24 +- .../test_main/test_router/test_login.py | 20 +- .../test_main/test_router/test_modify.py | 138 ++++---- .../test_main/test_router/test_modify_dn.py | 104 +++--- .../test_main/test_router/test_search.py | 92 ++--- tests/test_api/test_network/test_router.py | 314 +++++++++--------- tests/test_api/test_shadow/conftest.py | 9 +- tests/test_api/test_shadow/test_router.py | 16 +- tests/test_ldap/test_bind.py | 2 +- tests/test_ldap/test_ldap3_lib.py | 16 +- tests/test_ldap/test_ldap3_whoami.py | 6 +- tests/test_ldap/test_passwd_change.py | 12 +- tests/test_ldap/test_pool_client_handler.py | 18 +- tests/test_ldap/test_util/test_add.py | 76 ++--- tests/test_ldap/test_util/test_delete.py | 52 +-- tests/test_ldap/test_util/test_modify.py | 138 ++++---- tests/test_ldap/test_util/test_search.py | 150 ++++----- tests/test_ldap/test_util/test_whoami.py | 14 +- 79 files changed, 1084 insertions(+), 1106 deletions(-) diff --git a/.kerberos/config_server.py b/.kerberos/config_server.py index 6cae9461..ee6e9bd9 100644 --- a/.kerberos/config_server.py +++ b/.kerberos/config_server.py @@ -688,9 +688,7 @@ def get_status(request: Request) -> bool: """ kadmind = getattr(request.app.state, "kadmind", None) - if kadmind is not None: - return True - return False + return kadmind is not None def create_app() -> FastAPI: diff --git a/app/alembic/versions/6f8fe2548893_fix_read_only.py b/app/alembic/versions/6f8fe2548893_fix_read_only.py index df785926..e94d9ac1 100644 --- a/app/alembic/versions/6f8fe2548893_fix_read_only.py +++ b/app/alembic/versions/6f8fe2548893_fix_read_only.py @@ -13,8 +13,8 @@ from models import Attribute, Directory # revision identifiers, used by Alembic. -revision = '6f8fe2548893' -down_revision = 'fafc3d0b11ec' +revision = "6f8fe2548893" +down_revision = "fafc3d0b11ec" branch_labels = None depends_on = None @@ -25,34 +25,34 @@ def upgrade() -> None: session = Session(bind=bind) ro_dir = session.scalar(select(Directory).where( - Directory.name == 'readonly domain controllers')) + Directory.name == "readonly domain controllers")) if not ro_dir: return session.execute(delete(Attribute).where( - Attribute.name == 'objectSid', Attribute.directory == ro_dir)) + Attribute.name == "objectSid", Attribute.directory == ro_dir)) session.execute( update(Attribute) .where( - Attribute.name == 'sAMAccountName', + Attribute.name == "sAMAccountName", Attribute.directory == ro_dir, - Attribute.value == 'domain users', + Attribute.value == "domain users", ) - .values({'value': ro_dir.name}), + .values({"value": ro_dir.name}), ) attr_object_class = session.scalar( select(Attribute) .where( - Attribute.name == 'objectClass', + Attribute.name == "objectClass", Attribute.directory == ro_dir, - Attribute.value == 'group', + Attribute.value == "group", ), ) if not attr_object_class: session.add(Attribute( - name='objectClass', value='group', directory=ro_dir)) + name="objectClass", value="group", directory=ro_dir)) session.add(Attribute( name=ro_dir.rdname, value=ro_dir.name, @@ -60,14 +60,14 @@ def upgrade() -> None: ), ) session.add(Attribute( - name='gidNumber', + name="gidNumber", value=str(create_integer_hash(ro_dir.name)), directory=ro_dir, ), ) - domain_sid = '-'.join(ro_dir.object_sid.split('-')[:-1]) - ro_dir.object_sid = domain_sid + '-521' + domain_sid = "-".join(ro_dir.object_sid.split("-")[:-1]) + ro_dir.object_sid = domain_sid + "-521" session.commit() diff --git a/app/alembic/versions/8c2bd40dd809_add_protocols_attr.py b/app/alembic/versions/8c2bd40dd809_add_protocols_attr.py index d291619c..6ebb0804 100644 --- a/app/alembic/versions/8c2bd40dd809_add_protocols_attr.py +++ b/app/alembic/versions/8c2bd40dd809_add_protocols_attr.py @@ -9,8 +9,8 @@ from alembic import op # revision identifiers, used by Alembic. -revision = '8c2bd40dd809' -down_revision = '6f8fe2548893' +revision = "8c2bd40dd809" +down_revision = "6f8fe2548893" branch_labels = None depends_on = None @@ -19,11 +19,11 @@ def upgrade() -> None: """Upgrade.""" for protocol_field in ("is_http", "is_ldap", "is_kerberos"): op.add_column( - 'Policies', + "Policies", sa.Column( protocol_field, sa.Boolean(), - server_default=sa.text('true'), + server_default=sa.text("true"), nullable=False, ), ) @@ -32,4 +32,4 @@ def upgrade() -> None: def downgrade() -> None: """Downgrade.""" for protocol_field in ("is_http", "is_ldap", "is_kerberos"): - op.drop_column('Policies', protocol_field) + op.drop_column("Policies", protocol_field) diff --git a/app/alembic/versions/bf435bbd95ff_add_rdn_attr_name.py b/app/alembic/versions/bf435bbd95ff_add_rdn_attr_name.py index cf1a9926..e39599a9 100644 --- a/app/alembic/versions/bf435bbd95ff_add_rdn_attr_name.py +++ b/app/alembic/versions/bf435bbd95ff_add_rdn_attr_name.py @@ -12,15 +12,15 @@ from models import Attribute, Directory # revision identifiers, used by Alembic. -revision = 'bf435bbd95ff' -down_revision = '196f0d327c6a' +revision = "bf435bbd95ff" +down_revision = "196f0d327c6a" branch_labels = None depends_on = None def upgrade() -> None: """Upgrade.""" - op.add_column('Directory', sa.Column('rdname', sa.String(length=64))) + op.add_column("Directory", sa.Column("rdname", sa.String(length=64))) bind = op.get_bind() session = Session(bind=bind) @@ -29,13 +29,13 @@ def upgrade() -> None: for directory in session.query(Directory): if directory.is_domain: - directory.rdname = '' + directory.rdname = "" continue - rdname = directory.path[-1].split('=')[0] + rdname = directory.path[-1].split("=")[0] directory.rdname = rdname - if rdname == 'krbprincipalname': + if rdname == "krbprincipalname": continue # already exists attrs.append(Attribute( @@ -47,7 +47,7 @@ def upgrade() -> None: session.add_all(attrs) session.commit() - op.alter_column('Directory', 'rdname', nullable=False) + op.alter_column("Directory", "rdname", nullable=False) def downgrade() -> None: @@ -57,16 +57,16 @@ def downgrade() -> None: for directory in session.query(Directory): if directory.is_domain: - directory.rdname = '' + directory.rdname = "" continue session.execute( sa.delete(Attribute) .where( Attribute.name == directory.rdname, - Attribute.name != 'krbprincipalname', + Attribute.name != "krbprincipalname", Attribute.directory_id == directory.id, ), ) - op.drop_column('Directory', 'rdname') + op.drop_column("Directory", "rdname") diff --git a/app/alembic/versions/bv546ccd35fa_fix_krbadmin_attrs.py b/app/alembic/versions/bv546ccd35fa_fix_krbadmin_attrs.py index bbf23d67..cfa08334 100644 --- a/app/alembic/versions/bv546ccd35fa_fix_krbadmin_attrs.py +++ b/app/alembic/versions/bv546ccd35fa_fix_krbadmin_attrs.py @@ -12,8 +12,8 @@ from models import Attribute, Directory # revision identifiers, used by Alembic. -revision = 'bv546ccd35fa' -down_revision = '8c2bd40dd809' +revision = "bv546ccd35fa" +down_revision = "8c2bd40dd809" branch_labels = None depends_on = None @@ -26,7 +26,7 @@ def upgrade() -> None: krb_admin_user = session.scalar( sa.select(Directory) .join(Directory.user) - .filter(Directory.name == 'krbadmin'), + .filter(Directory.name == "krbadmin"), ) if krb_admin_user: @@ -52,19 +52,19 @@ def upgrade() -> None: krb_admin_group = session.scalar( sa.select(Directory) .join(Directory.group) - .filter(Directory.name == 'krbadmin'), + .filter(Directory.name == "krbadmin"), ) session.execute( sa.delete(Attribute) .where( - Attribute.name == 'gidNumber', + Attribute.name == "gidNumber", Attribute.directory_id == krb_admin_group.id, ), ) session.add(Attribute( - name='gidNumber', - value='800', + name="gidNumber", + value="800", directory_id=krb_admin_group.id, )) diff --git a/app/alembic/versions/dafg3a4b22ab_add_preauth_princ.py b/app/alembic/versions/dafg3a4b22ab_add_preauth_princ.py index 599833bc..94c7b510 100644 --- a/app/alembic/versions/dafg3a4b22ab_add_preauth_princ.py +++ b/app/alembic/versions/dafg3a4b22ab_add_preauth_princ.py @@ -13,8 +13,8 @@ from models import Attribute, CatalogueSetting, User # revision identifiers, used by Alembic. -revision = 'dafg3a4b22ab' -down_revision = 'f68a134a3685' +revision = "dafg3a4b22ab" +down_revision = "f68a134a3685" branch_labels = None depends_on = None @@ -25,23 +25,23 @@ def upgrade() -> None: session = Session(bind=bind) for user in session.query(User): - if user.sam_accout_name == 'krbadmin': + if user.sam_accout_name == "krbadmin": continue - username, domain = user.user_principal_name.split('@') + username, domain = user.user_principal_name.split("@") principal = f"{username}@{domain.upper()}" attr_principal = session.scalar( sa.select(Attribute) .filter( - Attribute.name == 'krbprincipalname', + Attribute.name == "krbprincipalname", Attribute.value == principal, ), ) if attr_principal: session.add(Attribute( - name='krbticketflags', - value='128', + name="krbticketflags", + value="128", directory_id=attr_principal.directory_id, )) diff --git a/app/alembic/versions/f68a134a3685_add_bypass.py b/app/alembic/versions/f68a134a3685_add_bypass.py index a91a26bc..3bc532a6 100644 --- a/app/alembic/versions/f68a134a3685_add_bypass.py +++ b/app/alembic/versions/f68a134a3685_add_bypass.py @@ -9,8 +9,8 @@ from alembic import op # revision identifiers, used by Alembic. -revision = 'f68a134a3685' -down_revision = 'bv546ccd35fa' +revision = "f68a134a3685" +down_revision = "bv546ccd35fa" branch_labels = None depends_on = None @@ -18,20 +18,20 @@ def upgrade() -> None: """Upgrade.""" op.add_column( - 'Policies', + "Policies", sa.Column( - 'bypass_no_connection', + "bypass_no_connection", sa.Boolean(), - server_default=sa.text('false'), + server_default=sa.text("false"), nullable=False, ), ) op.add_column( - 'Policies', + "Policies", sa.Column( - 'bypass_service_failure', + "bypass_service_failure", sa.Boolean(), - server_default=sa.text('false'), + server_default=sa.text("false"), nullable=False, ), ) @@ -39,5 +39,5 @@ def upgrade() -> None: def downgrade() -> None: """Downgrade.""" - op.drop_column('Policies', 'bypass_service_failure') - op.drop_column('Policies', 'bypass_no_connection') + op.drop_column("Policies", "bypass_service_failure") + op.drop_column("Policies", "bypass_no_connection") diff --git a/app/alembic/versions/fafc3d0b11ec_.py b/app/alembic/versions/fafc3d0b11ec_.py index 3d9b167e..3da1d958 100644 --- a/app/alembic/versions/fafc3d0b11ec_.py +++ b/app/alembic/versions/fafc3d0b11ec_.py @@ -19,8 +19,8 @@ from models import AccessPolicy, Directory # revision identifiers, used by Alembic. -revision = 'fafc3d0b11ec' -down_revision = 'bf435bbd95ff' +revision = "fafc3d0b11ec" +down_revision = "bf435bbd95ff" branch_labels = None depends_on = None @@ -38,12 +38,12 @@ async def _create_readonly_grp_and_plcy(connection) -> None: group_dir = (await session.scalars( select( exists(Directory) - .where(Directory.name == 'readonly domain controllers')), + .where(Directory.name == "readonly domain controllers")), )).one() if not group_dir: dir_, _ = await create_group( - 'readonly domain controllers', 521, session) + "readonly domain controllers", 521, session) await session.flush() except (IntegrityError, DBAPIError): @@ -52,12 +52,12 @@ async def _create_readonly_grp_and_plcy(connection) -> None: has_ro_access_policy = (await session.scalars( select( exists(AccessPolicy) - .where(AccessPolicy.name == 'ReadOnly Access Policy')), + .where(AccessPolicy.name == "ReadOnly Access Policy")), )).one() if not has_ro_access_policy: await create_access_policy( - name='ReadOnly Access Policy', + name="ReadOnly Access Policy", can_add=False, can_modify=False, can_read=True, @@ -87,7 +87,7 @@ async def _delete_readonly_grp_and_plcy(connection) -> None: await session.execute( delete(AccessPolicy) - .where(AccessPolicy.name == 'ReadOnly Access Policy'), + .where(AccessPolicy.name == "ReadOnly Access Policy"), ) await session.execute( diff --git a/app/api/auth/oauth2.py b/app/api/auth/oauth2.py index e8baeadb..ec80e89e 100644 --- a/app/api/auth/oauth2.py +++ b/app/api/auth/oauth2.py @@ -52,7 +52,7 @@ async def authenticate_user( @inject -async def get_current_user( # noqa: D103 +async def get_current_user( settings: FromDishka[Settings], session: FromDishka[AsyncSession], session_storage: FromDishka[SessionStorage], diff --git a/app/api/auth/router.py b/app/api/auth/router.py index 14ba3e7d..c89637d6 100644 --- a/app/api/auth/router.py +++ b/app/api/auth/router.py @@ -82,7 +82,7 @@ async def login( :raises HTTPException: 403 if user not part of network policy :raises HTTPException: 426 if mfa required :return None: None - """ # noqa: D205, D301 + """ # noqa: D301 user = await authenticate_user(session, form.username, form.password) if not user: @@ -92,7 +92,7 @@ async def login( headers={"WWW-Authenticate": "Bearer"}, ) - query = ( # noqa: ECE001 + query = ( select(Group) .join(Group.users) .join(Group.directory) @@ -169,7 +169,7 @@ async def password_reset( session: FromDishka[AsyncSession], kadmin: FromDishka[AbstractKadmin], ) -> None: - """Reset user's (entry) password. + r"""Reset user's (entry) password. - **identity**: user identity, any `userPrincipalName`, `saMAccountName` or `DN` @@ -244,7 +244,7 @@ async def first_setup( if setup_already_performed: raise HTTPException(status.HTTP_423_LOCKED) - data = [ # noqa + data = [ { "name": "groups", "object_class": "container", diff --git a/app/api/auth/router_mfa.py b/app/api/auth/router_mfa.py index f32ca031..f8f1b611 100644 --- a/app/api/auth/router_mfa.py +++ b/app/api/auth/router_mfa.py @@ -35,8 +35,7 @@ ) from ldap_protocol.policies.network_policy import get_user_network_policy from ldap_protocol.session_storage import SessionStorage -from models import CatalogueSetting -from models import User as DBUser +from models import CatalogueSetting, User as DBUser from .oauth2 import ALGORITHM, authenticate_user from .schema import ( @@ -66,7 +65,7 @@ async def setup_mfa( \f :param MFACreateRequest mfa: MuliFactor credentials :param FromDishka[AsyncSession] session: db - :return bool: status + :return bool: status. """ # noqa: D301 async with session.begin_nested(): await session.execute( @@ -95,7 +94,7 @@ async def remove_mfa( scope: Literal["ldap", "http"], ) -> None: """Remove mfa credentials.""" - if scope == 'http': + if scope == "http": keys = ["mfa_key", "mfa_secret"] else: keys = ["mfa_key_ldap", "mfa_secret_ldap"] @@ -114,7 +113,7 @@ async def get_mfa( ) -> MFAGetResponse: """Get MFA creds. \f - :return MFAGetResponse: response + :return MFAGetResponse: response. """ # noqa: D301 if not mfa_creds: mfa_creds = MFA_HTTP_Creds(Creds(None, None)) @@ -191,7 +190,7 @@ async def two_factor_protocol( ip: Annotated[IPv4Address | IPv6Address, Depends(get_ip_from_request)], user_agent: Annotated[str, Depends(get_user_agent_from_request)], ) -> MFAChallengeResponse: - """Initiate two factor protocol with app. + r"""Initiate two factor protocol with app. \f :param Annotated[OAuth2Form, Depends form: password form :param Request request: FastAPI request @@ -206,7 +205,7 @@ async def two_factor_protocol( :raises HTTPException: network policy violation :raises HTTPException: Multifactor error :return MFAChallengeResponse: - {'status': 'pending', 'message': https://example.com} + {'status': 'pending', 'message': https://example.com}. """ if not api: raise HTTPException( diff --git a/app/api/auth/schema.py b/app/api/auth/schema.py index e40c8a8d..cb283b59 100644 --- a/app/api/auth/schema.py +++ b/app/api/auth/schema.py @@ -37,10 +37,10 @@ class Login(BaseModel): class OAuth2Form(OAuth2PasswordRequestForm): """OAuth2 custom form.""" - def __init__( # noqa: D107 + def __init__( self, - username: str = Form(), # noqa: B008 - password: str = Form(), # noqa: B008 + username: str = Form(), + password: str = Form(), ): self.username = username self.password = password @@ -51,7 +51,7 @@ class Token(BaseModel): access_token: str refresh_token: str - type: str # noqa: A003 + type: str class SetupRequest(BaseModel): @@ -80,7 +80,7 @@ class MFACreateRequest(BaseModel): @computed_field # type: ignore @property - def key_name(self) -> str: # noqa + def key_name(self) -> str: if self.is_ldap_scope: return "mfa_key_ldap" @@ -88,7 +88,7 @@ def key_name(self) -> str: # noqa @computed_field # type: ignore @property - def secret_name(self) -> str: # noqa + def secret_name(self) -> str: if self.is_ldap_scope: return "mfa_secret_ldap" @@ -114,9 +114,9 @@ class MFAChallengeResponse(BaseModel): class SessionContentSchema(BaseModel): """Session content schema.""" - model_config = ConfigDict(extra='allow') + model_config = ConfigDict(extra="allow") - id: int # noqa: A003 + id: int sign: str = Field("", description="Session signature") issued: datetime ip: IPv4Address | IPv6Address diff --git a/app/api/auth/utils.py b/app/api/auth/utils.py index 70ee1b08..dd78b640 100644 --- a/app/api/auth/utils.py +++ b/app/api/auth/utils.py @@ -39,7 +39,7 @@ def get_user_agent_from_request(request: Request) -> str: :return str: The user agent header. """ user_agent_header = request.headers.get("User-Agent") - return "" if not user_agent_header else user_agent_header + return user_agent_header if user_agent_header else "" async def create_and_set_session_key( diff --git a/app/api/main/ap_router.py b/app/api/main/ap_router.py index 1bca3555..4f9a8d14 100644 --- a/app/api/main/ap_router.py +++ b/app/api/main/ap_router.py @@ -23,10 +23,10 @@ async def get_access_policies( session: FromDishka[AsyncSession], ) -> list[MaterialAccessPolicySchema]: - """Get APs. + r"""Get APs. \f :param AccessPolicySchema policy: ap - :param FromDishka[AsyncSession] session: db + :param FromDishka[AsyncSession] session: db. """ return [ MaterialAccessPolicySchema( diff --git a/app/api/main/dns_router.py b/app/api/main/dns_router.py index 5827daf4..9ebfa64e 100644 --- a/app/api/main/dns_router.py +++ b/app/api/main/dns_router.py @@ -28,14 +28,14 @@ ) dns_router = APIRouter( - prefix='/dns', - tags=['DNS_SERVICE'], + prefix="/dns", + tags=["DNS_SERVICE"], dependencies=[Depends(get_current_user)], route_class=DishkaRoute, ) -@dns_router.post('/record') +@dns_router.post("/record") async def create_record( data: DNSServiceRecordCreateRequest, dns_manager: FromDishka[AbstractDNSManager], @@ -49,7 +49,7 @@ async def create_record( ) -@dns_router.delete('/record') +@dns_router.delete("/record") async def delete_single_record( data: DNSServiceRecordDeleteRequest, dns_manager: FromDishka[AbstractDNSManager], @@ -62,7 +62,7 @@ async def delete_single_record( ) -@dns_router.patch('/record') +@dns_router.patch("/record") async def update_record( data: DNSServiceRecordUpdateRequest, dns_manager: FromDishka[AbstractDNSManager], @@ -76,7 +76,7 @@ async def update_record( ) -@dns_router.get('/record') +@dns_router.get("/record") async def get_all_records( dns_manager: FromDishka[AbstractDNSManager], ) -> list[DNSRecords]: @@ -84,7 +84,7 @@ async def get_all_records( return await dns_manager.get_all_records() -@dns_router.get('/status') +@dns_router.get("/status") async def get_dns_status( session: FromDishka[AsyncSession], dns_settings: FromDishka[DNSManagerSettings], @@ -98,7 +98,7 @@ async def get_dns_status( } -@dns_router.post('/setup') +@dns_router.post("/setup") async def setup_dns( data: DNSServiceSetupRequest, dns_manager: FromDishka[AbstractDNSManager], diff --git a/app/api/main/krb5_router.py b/app/api/main/krb5_router.py index 79163228..e98b24c3 100644 --- a/app/api/main/krb5_router.py +++ b/app/api/main/krb5_router.py @@ -152,7 +152,7 @@ async def setup_kdc( settings: FromDishka[Settings], kadmin: FromDishka[AbstractKadmin], ) -> None: - """Set up KDC server. + r"""Set up KDC server. Create data structure in catalogue, generate config files, trigger commands @@ -291,11 +291,11 @@ async def add_principal( instance: Annotated[LIMITED_STR, Body()], kadmin: FromDishka[AbstractKadmin], ) -> None: - """Create principal in kerberos with given name. + r"""Create principal in kerberos with given name. \f :param Annotated[str, Body principal_name: upn :param Annotated[LDAPSession, Depends ldap_session: ldap - :raises HTTPException: on failed kamin request + :raises HTTPException: on failed kamin request. """ try: await kadmin.add_principal(f"{primary}/{instance}", None) @@ -310,12 +310,12 @@ async def rename_principal( principal_new_name: Annotated[LIMITED_STR, Body()], kadmin: FromDishka[AbstractKadmin], ) -> None: - """Rename principal in kerberos with given name. + r"""Rename principal in kerberos with given name. \f :param Annotated[str, Body principal_name: upn :param Annotated[LIMITED_STR, Body principal_new_name: _description_ :param Annotated[LDAPSession, Depends ldap_session: ldap - :raises HTTPException: on failed kamin request + :raises HTTPException: on failed kamin request. """ try: await kadmin.rename_princ(principal_name, principal_new_name) @@ -330,12 +330,12 @@ async def reset_principal_pw( new_password: Annotated[LIMITED_STR, Body()], kadmin: FromDishka[AbstractKadmin], ) -> None: - """Reset principal password in kerberos with given name. + r"""Reset principal password in kerberos with given name. \f :param Annotated[str, Body principal_name: upn :param Annotated[LIMITED_STR, Body new_password: _description_ :param Annotated[LDAPSession, Depends ldap_session: ldap - :raises HTTPException: on failed kamin request + :raises HTTPException: on failed kamin request. """ try: await kadmin.change_principal_password(principal_name, new_password) @@ -349,11 +349,11 @@ async def delete_principal( principal_name: Annotated[LIMITED_STR, Body(embed=True)], kadmin: FromDishka[AbstractKadmin], ) -> None: - """Delete principal in kerberos with given name. + r"""Delete principal in kerberos with given name. \f :param Annotated[str, Body principal_name: upn :param FromDishka[AbstractKadmin] kadmin: _description_ - :raises HTTPException: on failed kamin request + :raises HTTPException: on failed kamin request. """ try: await kadmin.del_principal(principal_name) diff --git a/app/api/main/schema.py b/app/api/main/schema.py index 7cb3ffbb..3df2741c 100644 --- a/app/api/main/schema.py +++ b/app/api/main/schema.py @@ -18,7 +18,7 @@ class SearchRequest(LDAPSearchRequest): """Search request for web api.""" - filter: str = Field( # noqa: A003 + filter: str = Field( ..., examples=["(objectClass=*)"]) # type: ignore def cast_filter(self) -> UnaryExpression | ColumnElement: @@ -35,7 +35,7 @@ async def handle_api( # type: ignore return await self._handle_api(container) # type: ignore -class SearchResponse(SearchResultDone): # noqa: D101 +class SearchResponse(SearchResultDone): search_result: list[SearchResultEntry] @@ -57,7 +57,7 @@ class _PolicyFields: class _MaterialFields: - id: int # noqa: A003 + id: int class AccessPolicySchema(_PolicyFields, BaseModel): diff --git a/app/api/network/router.py b/app/api/network/router.py index ed982df9..61ce3967 100644 --- a/app/api/network/router.py +++ b/app/api/network/router.py @@ -48,7 +48,7 @@ async def add_network_policy( :raises HTTPException: 422 invalid group DN :raises HTTPException: 422 Entry already exists :return PolicyResponse: Ready policy - """ # noqa: D205, D301 + """ # noqa: D301 new_policy = NetworkPolicy( name=policy.name, netmasks=policy.complete_netmasks, @@ -110,7 +110,7 @@ async def get_list_network_policies( \f :return list[PolicyResponse]: all policies - """ # noqa: D205, D301 + """ # noqa: D301 groups = selectinload(NetworkPolicy.groups).selectinload(Group.directory) mfa_groups = ( selectinload(NetworkPolicy.mfa_groups) @@ -162,7 +162,7 @@ async def delete_network_policy( :raises HTTPException: 422 On last active policy, at least 1 should be in database. :return bool: status of delete - """ # noqa: D205, D301 + """ # noqa: D301 policy = await session.get(NetworkPolicy, policy_id, with_for_update=True) if not policy: @@ -204,7 +204,7 @@ async def switch_network_policy( :raises HTTPException: 422 On last active policy, at least 1 should be active :return bool: status of update - """ # noqa: D205, D301 + """ # noqa: D301 policy = await session.get(NetworkPolicy, policy_id, with_for_update=True) if not policy: @@ -231,7 +231,7 @@ async def update_network_policy( :raises HTTPException: 422 Invalid group DN :raises HTTPException: 422 Entry already exists :return PolicyResponse: Policy from database - """ # noqa: D205, D301 + """ # noqa: D301 selected_policy = await session.get( NetworkPolicy, request.id, @@ -328,7 +328,7 @@ async def swap_network_policy( :param int second_policy_id: policy to swap :raises HTTPException: 404 :return SwapResponse: policy new priorities - """ # noqa: D205, D301 + """ # noqa: D301 policy1 = await session.get( NetworkPolicy, swap.first_policy_id, with_for_update=True, ) diff --git a/app/api/network/schema.py b/app/api/network/schema.py index d0358901..fa8df37b 100644 --- a/app/api/network/schema.py +++ b/app/api/network/schema.py @@ -53,7 +53,7 @@ def complete_netmasks(self) -> list[IPv4Address | IPv4Network]: @field_validator("groups") @classmethod - def validate_group(cls, groups: list[str]) -> list[str]: # noqa + def validate_group(cls, groups: list[str]) -> list[str]: if not groups: return groups if all(validate_entry(group) for group in groups): @@ -63,7 +63,7 @@ def validate_group(cls, groups: list[str]) -> list[str]: # noqa @field_validator("mfa_groups") @classmethod - def validate_mfa_group(cls, mfa_groups: list[str]) -> list[str]: # noqa + def validate_mfa_group(cls, mfa_groups: list[str]) -> list[str]: if not mfa_groups: return mfa_groups if all(validate_entry(group) for group in mfa_groups): @@ -115,7 +115,7 @@ class PolicyResponse(BaseModel): model_config = ConfigDict(from_attributes=True) - id: int # noqa + id: int name: str netmasks: list[IPv4Network] raw: list[str | dict] @@ -134,7 +134,7 @@ class PolicyResponse(BaseModel): class PolicyUpdate(BaseModel, NetmasksMixin): """Update request.""" - id: int # noqa + id: int name: str | None = None netmasks: IPv4IntefaceListType | None = None # type: ignore groups: list[str] | None = None diff --git a/app/api/shadow/router.py b/app/api/shadow/router.py index b270ce52..7f52741c 100644 --- a/app/api/shadow/router.py +++ b/app/api/shadow/router.py @@ -50,9 +50,7 @@ async def proxy_request( if not network_policy.is_kerberos: raise HTTPException(status.HTTP_403_FORBIDDEN) - if not mfa: # noqa: R505 - return - elif network_policy.mfa_status == MFAFlags.DISABLED: + if not mfa or network_policy.mfa_status == MFAFlags.DISABLED: return elif network_policy.mfa_status in (MFAFlags.ENABLED, MFAFlags.WHITELIST): if ( diff --git a/app/config.py b/app/config.py index 8de7cdb5..8381d921 100644 --- a/app/config.py +++ b/app/config.py @@ -101,15 +101,15 @@ def POSTGRES_URI(self) -> PostgresDsn: # noqa KRB5_LDAP_KEYTAB: str = "/LDAP_keytab/ldap.keytab" TEMPLATES: ClassVar[jinja2.Environment] = jinja2.Environment( - loader=jinja2.FileSystemLoader('extra/templates'), + loader=jinja2.FileSystemLoader("extra/templates"), enable_async=True, autoescape=True, ) - DNS_BIND_HOST: str = 'bind_dns' - DNS_TSIG_KEY: str = '/DNS_server_file/zone.key' - DNS_ZONE_FILE: str = '/DNS_server_file/db.zone' - DNS_SERVER_NAMED_CONF: str = '/DNS_server_configs/named.conf' - DNS_SERVER_NAMED_CONF_LOCAL: str = '/DNS_server_configs/named.conf.local' + DNS_BIND_HOST: str = "bind_dns" + DNS_TSIG_KEY: str = "/DNS_server_file/zone.key" + DNS_ZONE_FILE: str = "/DNS_server_file/db.zone" + DNS_SERVER_NAMED_CONF: str = "/DNS_server_configs/named.conf" + DNS_SERVER_NAMED_CONF_LOCAL: str = "/DNS_server_configs/named.conf.local" GSSAPI_MAX_OUTPUT_TOKEN_SIZE: int = 1024 diff --git a/app/extra/dev_data.py b/app/extra/dev_data.py index 755341a9..cccf4bcc 100644 --- a/app/extra/dev_data.py +++ b/app/extra/dev_data.py @@ -13,7 +13,7 @@ } -DATA = [ # noqa +DATA = [ { "name": "main", "object_class": "builtinDomain", @@ -200,7 +200,7 @@ }, ] -TEST_DATA = [ # noqa +TEST_DATA = [ { "name": "groups", "object_class": "container", diff --git a/app/extra/scripts/krb_pass_sync.py b/app/extra/scripts/krb_pass_sync.py index 93bc0f85..9b1edb6d 100644 --- a/app/extra/scripts/krb_pass_sync.py +++ b/app/extra/scripts/krb_pass_sync.py @@ -24,7 +24,7 @@ async def read_and_save_krb_pwds(session: AsyncSession) -> None: :param AsyncSession session: db """ - files = [ # noqa: ECE001 + files = [ fp for f in os.listdir(_PATH) if os.path.isfile(fp := os.path.join(_PATH, f)) and f != _LOCK_FILE diff --git a/app/extra/scripts/uac_sync.py b/app/extra/scripts/uac_sync.py index 57383e0b..85c43367 100644 --- a/app/extra/scripts/uac_sync.py +++ b/app/extra/scripts/uac_sync.py @@ -56,7 +56,7 @@ async def disable_accounts( Attribute.name == "userAccountControl", ] - ids = await session.scalars( # noqa: ECE001 + ids = await session.scalars( update(Attribute) .values(value=new_value) .where(*conditions) diff --git a/app/extra/setup_dev.py b/app/extra/setup_dev.py index e36abf2d..8999f92a 100644 --- a/app/extra/setup_dev.py +++ b/app/extra/setup_dev.py @@ -159,7 +159,7 @@ async def setup_enviroment( ) domain.path = [f"dc={path}" for path in reversed(dn.split("."))] domain.depth = len(domain.path) - domain.rdname = '' + domain.rdname = "" async with session.begin_nested(): session.add(domain) @@ -180,5 +180,5 @@ async def setup_enviroment( except Exception: import traceback - logger.error(traceback.format_exc()) # noqa + logger.error(traceback.format_exc()) raise diff --git a/app/ldap_protocol/asn1parser.py b/app/ldap_protocol/asn1parser.py index ee60cd18..166fdfd2 100644 --- a/app/ldap_protocol/asn1parser.py +++ b/app/ldap_protocol/asn1parser.py @@ -92,20 +92,20 @@ def _handle_extensible_match(self) -> str: oid = child_value elif tag_value == 2: attribute = ( - child_value.decode(errors='replace') + child_value.decode(errors="replace") if isinstance(child_value, bytes) else child_value ) elif tag_value == 3: value = ( - child_value.decode(errors='replace') + child_value.decode(errors="replace") if isinstance(child_value, bytes) else child_value ) elif tag_value == 4: dn_attributes = bool(child_value) - match = '' + match = "" if attribute: match += attribute if oid: @@ -122,7 +122,7 @@ def _handle_extensible_match(self) -> str: def _handle_substring(self) -> str: """Process and format substring operations for LDAP.""" value = ( - self.value.decode(errors='replace') + self.value.decode(errors="replace") if isinstance(self.value, bytes) else str(self.value) ) @@ -135,13 +135,12 @@ def _handle_substring(self) -> str: substring_tag = SubstringTag(self.tag_id) except ValueError: raise ValueError( - f'Invalid tag_id ({self.tag_id}) in substring') + f"Invalid tag_id ({self.tag_id}) in substring") return substring_tag_map[substring_tag] - def serialize(self, obj: 'ASN1Row' | T | None = None) -> str: - """ - Serialize an ASN.1 object or list into a string. + def serialize(self, obj: "ASN1Row" | T | None = None) -> str: + """Serialize an ASN.1 object or list into a string. Recursively processes ASN.1 structures to construct a valid LDAP filter string based on LDAP operations such as AND, OR, and @@ -150,7 +149,7 @@ def serialize(self, obj: 'ASN1Row' | T | None = None) -> str: if obj is None: obj = self - if isinstance(obj, ASN1Row): # noqa: R505 + if isinstance(obj, ASN1Row): value = obj.value operator = None @@ -162,7 +161,7 @@ def serialize(self, obj: 'ASN1Row' | T | None = None) -> str: TagNumbers.OR, TagNumbers.NOT, ): - subfilters = ''.join(self.serialize(v) for v in value) + subfilters = "".join(self.serialize(v) for v in value) if obj.tag_id == TagNumbers.AND: return f"(&{subfilters})" @@ -180,24 +179,24 @@ def serialize(self, obj: 'ASN1Row' | T | None = None) -> str: else: operator_map: dict[int, str] = { - TagNumbers.EQUALITY_MATCH: '=', - TagNumbers.SUBSTRING: '*=', - TagNumbers.GE: '>=', - TagNumbers.LE: '<=', - TagNumbers.APPROX_MATCH: '~=', + TagNumbers.EQUALITY_MATCH: "=", + TagNumbers.SUBSTRING: "*=", + TagNumbers.GE: ">=", + TagNumbers.LE: "<=", + TagNumbers.APPROX_MATCH: "~=", } operator = operator_map.get(obj.tag_id) if operator is None: raise ValueError( - f'Invalid tag_id ({obj.tag_id}) in context') + f"Invalid tag_id ({obj.tag_id}) in context") if isinstance(obj.value, list): if len(obj.value) == 2: attr = self.serialize(value[0]) val = value[1] - if operator == '*=': - operator = '=' + if operator == "*=": + operator = "=" substrings = val.value[0]._handle_substring() value_str = substrings else: @@ -205,15 +204,15 @@ def serialize(self, obj: 'ASN1Row' | T | None = None) -> str: return f"({attr}{operator}{value_str})" - return ''.join(self.serialize(v) for v in obj.value) + return "".join(self.serialize(v) for v in obj.value) return self.serialize(obj.value) elif isinstance(obj, list): - return ''.join(self.serialize(v) for v in obj) + return "".join(self.serialize(v) for v in obj) elif isinstance(obj, bytes): - return obj.decode(errors='replace') + return obj.decode(errors="replace") elif isinstance(obj, str): return obj @@ -225,8 +224,7 @@ def serialize(self, obj: 'ASN1Row' | T | None = None) -> str: raise TypeError def to_ldap_filter(self) -> str: - """ - Convert the ASN.1 object into an LDAP filter string. + """Convert the ASN.1 object into an LDAP filter string. The method recursively serializes ASN.1 rows into the LDAP filter format based on tag IDs and class IDs. diff --git a/app/ldap_protocol/dialogue.py b/app/ldap_protocol/dialogue.py index 9637410f..3aab232d 100644 --- a/app/ldap_protocol/dialogue.py +++ b/app/ldap_protocol/dialogue.py @@ -31,7 +31,7 @@ class UserSchema: """User model, alias for db user.""" - id: int # noqa: A003 + id: int session_id: str sam_accout_name: str user_principal_name: str diff --git a/app/ldap_protocol/dns.py b/app/ldap_protocol/dns.py index 0cb1afef..bccc2be9 100644 --- a/app/ldap_protocol/dns.py +++ b/app/ldap_protocol/dns.py @@ -11,11 +11,9 @@ from enum import Enum, StrEnum from typing import Any, Awaitable, Callable -from dns.asyncquery import inbound_xfr as make_inbound_xfr -from dns.asyncquery import tcp as asynctcp +from dns.asyncquery import inbound_xfr as make_inbound_xfr, tcp as asynctcp from dns.asyncresolver import Resolver as AsyncResolver -from dns.message import Message -from dns.message import make_query as make_dns_query +from dns.message import Message, make_query as make_dns_query from dns.name import from_text from dns.rdataclass import IN from dns.rdatatype import AXFR @@ -35,11 +33,11 @@ DNS_MANAGER_TSIG_KEY_NAME = "DNSManagerTSIGKey" -log = loguru_logger.bind(name='DNSManager') +log = loguru_logger.bind(name="DNSManager") log.add( "logs/dnsmanager_{time:DD-MM-YYYY}.log", - filter=lambda rec: rec["extra"].get("name") == 'dnsmanager', + filter=lambda rec: rec["extra"].get("name") == "dnsmanager", retention="10 days", rotation="1d", colorize=False) @@ -59,7 +57,7 @@ async def wrapped(*args: str, **kwargs: str) -> Any: try: result = await func(*args, **kwargs) except DNSConnectionError as err: - logger.error(f'{name} call raised: {err}') + logger.error(f"{name} call raised: {err}") raise else: @@ -131,9 +129,9 @@ class DNSRecords: class DNSManagerState(StrEnum): """DNSManager state enum.""" - NOT_CONFIGURED = '0' - SELFHOSTED = '1' - HOSTED = '2' + NOT_CONFIGURED = "0" + SELFHOSTED = "1" + HOSTED = "2" class AbstractDNSManager(ABC): @@ -163,7 +161,7 @@ async def setup( f.write(named_conf_local_part) with open(settings.DNS_SERVER_NAMED_CONF, "a") as f: - f.write("\ninclude \"/opt/zone.key\";") + f.write('\ninclude "/opt/zone.key";') with open(settings.DNS_TSIG_KEY, "r") as f: key_file_content = f.read() @@ -188,25 +186,25 @@ async def setup( for name, value in new_settings.items()]) @abstractmethod - async def create_record( # noqa + async def create_record( self, hostname: str, ip: str, record_type: str, ttl: int | None, ) -> None: ... @abstractmethod - async def update_record( # noqa + async def update_record( self, hostname: str, ip: str | None, record_type: str, ttl: int | None, ) -> None: ... @abstractmethod - async def delete_record( # noqa + async def delete_record( self, hostname: str, ip: str, record_type: str, ) -> None: ... @abstractmethod - async def get_all_records(self) -> list[DNSRecords]: ... # noqa + async def get_all_records(self) -> list[DNSRecords]: ... class DNSManager(AbstractDNSManager): @@ -306,19 +304,19 @@ class StubDNSManager(AbstractDNSManager): """Stub client.""" @logger_wraps(is_stub=True) - async def create_record( # noqa + async def create_record( self, hostname: str, ip: str, record_type: str, ttl: int | None, ) -> None: ... @logger_wraps(is_stub=True) - async def update_record( # noqa + async def update_record( self, hostname: str, ip: str, record_type: str, ttl: int, ) -> None: ... @logger_wraps(is_stub=True) - async def delete_record( # noqa + async def delete_record( self, hostname: str, ip: str, record_type: str, ) -> None: ... @@ -331,7 +329,7 @@ async def get_all_records(self) -> list[DNSRecords]: async def get_dns_state( session: AsyncSession, -) -> 'DNSManagerState': +) -> "DNSManagerState": """Get or create DNS manager state.""" state = await session.scalar( select(CatalogueSetting) @@ -376,7 +374,7 @@ async def resolve_dns_server_ip(host: str) -> str: async def get_dns_manager_settings( session: AsyncSession, resolve_coro: Awaitable[str], -) -> 'DNSManagerSettings': +) -> "DNSManagerSettings": """Get DNS manager's settings.""" settings_dict = {} for setting in await session.scalars( @@ -388,15 +386,15 @@ async def get_dns_manager_settings( ): settings_dict[setting.name] = setting.value - dns_server_ip = settings_dict.get(DNS_MANAGER_IP_ADDRESS_NAME, None) + dns_server_ip = settings_dict.get(DNS_MANAGER_IP_ADDRESS_NAME) if await get_dns_state(session) == DNSManagerState.SELFHOSTED: dns_server_ip = await resolve_coro return DNSManagerSettings( - zone_name=settings_dict.get(DNS_MANAGER_ZONE_NAME, None), + zone_name=settings_dict.get(DNS_MANAGER_ZONE_NAME), dns_server_ip=dns_server_ip, - tsig_key=settings_dict.get(DNS_MANAGER_TSIG_KEY_NAME, None), + tsig_key=settings_dict.get(DNS_MANAGER_TSIG_KEY_NAME), ) diff --git a/app/ldap_protocol/filter_interpreter.py b/app/ldap_protocol/filter_interpreter.py index 59192475..36077f05 100644 --- a/app/ldap_protocol/filter_interpreter.py +++ b/app/ldap_protocol/filter_interpreter.py @@ -113,7 +113,7 @@ def _get_filter_function(column: str) -> Callable[..., UnaryExpression]: else: ValueError("Incorrect attribute specified") - if attribute == "memberof": # noqa: R505 + if attribute == "memberof": if oid == LDAPMatchingRule.LDAP_MATCHING_RULE_TRANSITIVE_EVAL: return _recursive_filter_memberof return _filter_memberof @@ -153,7 +153,7 @@ def _cast_item(item: ASN1Row) -> UnaryExpression | ColumnElement: return Attribute.name.ilike(item.value.lower()) if len(item.value) == 3 and isinstance(item.value[1].value, bytes): - if item.value[1].value.decode('utf-8').lower() in MEMBERS_ATTRS: + if item.value[1].value.decode("utf-8").lower() in MEMBERS_ATTRS: return _ldap_filter_by_attribute(*item.value) # NOTE: oid left, right = item.value @@ -161,7 +161,7 @@ def _cast_item(item: ASN1Row) -> UnaryExpression | ColumnElement: is_substring = item.tag_id == TagNumbers.SUBSTRING - if attr in User.search_fields: # noqa: R505 + if attr in User.search_fields: return _from_filter(User, item, attr, right) elif attr in Directory.search_fields: return _from_filter(Directory, item, attr, right) @@ -225,7 +225,7 @@ def _cast_filt_item(item: Filter) -> UnaryExpression | ColumnElement: is_substring = item.val.startswith("*") or item.val.endswith("*") - if item.attr in User.search_fields: # noqa: R505 + if item.attr in User.search_fields: return _from_str_filter(User, is_substring, item) elif item.attr in Directory.search_fields: return _from_str_filter(Directory, is_substring, item) diff --git a/app/ldap_protocol/kerberos.py b/app/ldap_protocol/kerberos.py index 76e6fa9f..3569f39a 100644 --- a/app/ldap_protocol/kerberos.py +++ b/app/ldap_protocol/kerberos.py @@ -212,28 +212,28 @@ async def setup( ) @abstractmethod - async def add_principal( # noqa + async def add_principal( self, name: str, password: str | None, timeout: int | float = 1, ) -> None: ... @abstractmethod - async def get_principal(self, name: str) -> dict: ... # noqa + async def get_principal(self, name: str) -> dict: ... @abstractmethod - async def del_principal(self, name: str) -> None: ... # noqa + async def del_principal(self, name: str) -> None: ... @abstractmethod - async def change_principal_password( # noqa + async def change_principal_password( self, name: str, password: str, - ) -> None: ... # noqa + ) -> None: ... @abstractmethod - async def create_or_update_principal_pw( # noqa + async def create_or_update_principal_pw( self, name: str, password: str, - ) -> None: ... # noqa + ) -> None: ... @abstractmethod - async def rename_princ(self, name: str, new_name: str) -> None: ... # noqa + async def rename_princ(self, name: str, new_name: str) -> None: ... @backoff.on_exception( backoff.constant, @@ -260,10 +260,10 @@ async def get_status(self, wait_for_positive: bool = False) -> bool | None: return status @abstractmethod - async def ktadd(self, names: list[str]) -> httpx.Response: ... # noqa + async def ktadd(self, names: list[str]) -> httpx.Response: ... @abstractmethod - async def create_or_update_policy( # noqa + async def create_or_update_policy( self, minlife: int, maxlife: int, @@ -272,10 +272,10 @@ async def create_or_update_policy( # noqa ) -> None: ... @abstractmethod - async def lock_principal(self, name: str) -> None: ... # noqa + async def lock_principal(self, name: str) -> None: ... @abstractmethod - async def force_princ_pw_change(self, name: str) -> None: ... # noqa + async def force_princ_pw_change(self, name: str) -> None: ... async def ldap_principal_setup(self, name: str, path: str) -> None: """LDAP principal setup. @@ -457,7 +457,7 @@ async def setup(self, *args, **kwargs) -> None: # type: ignore await super().setup(*args, **kwargs) @logger_wraps(is_stub=True) - async def add_principal( # noqa D102 + async def add_principal( self, name: str, password: str | None, @@ -465,37 +465,37 @@ async def add_principal( # noqa D102 ) -> None: ... @logger_wraps(is_stub=True) - async def get_principal(self, name: str) -> None: # noqa D102 + async def get_principal(self, name: str) -> None: ... @logger_wraps(is_stub=True) - async def del_principal(self, name: str) -> None: # noqa D102 + async def del_principal(self, name: str) -> None: ... @logger_wraps(is_stub=True) - async def change_principal_password( # noqa D102 + async def change_principal_password( self, name: str, password: str, - ) -> None: # noqa + ) -> None: ... @logger_wraps(is_stub=True) - async def create_or_update_principal_pw( # noqa D102 + async def create_or_update_principal_pw( self, name: str, password: str, - ) -> None: # noqa + ) -> None: ... @logger_wraps(is_stub=True) - async def rename_princ(self, name: str, new_name: str) -> None: # noqa D102 + async def rename_princ(self, name: str, new_name: str) -> None: ... @logger_wraps(is_stub=True) - async def ktadd(self, names: list[str]) -> NoReturn: # noqa + async def ktadd(self, names: list[str]) -> NoReturn: raise KRBAPIError @logger_wraps(is_stub=True) - async def create_or_update_policy( # noqa + async def create_or_update_policy( self, minlife: int, maxlife: int, @@ -504,11 +504,11 @@ async def create_or_update_policy( # noqa ) -> None: ... @logger_wraps(is_stub=True) - async def lock_principal(self, name: str) -> None: # noqa + async def lock_principal(self, name: str) -> None: ... @logger_wraps(is_stub=True) - async def force_princ_pw_change(self, name: str) -> None: # noqa + async def force_princ_pw_change(self, name: str) -> None: ... @@ -526,8 +526,7 @@ async def get_krb_server_state(session: AsyncSession) -> "KerberosState": async def set_state(session: AsyncSession, state: "KerberosState") -> None: - """ - Set the server state in the database. + """Set the server state in the database. This function updates the server state in the database by either adding a new entry, updating an existing entry, or deleting and re-adding the diff --git a/app/ldap_protocol/ldap_requests/add.py b/app/ldap_protocol/ldap_requests/add.py index b80ece2c..146d7405 100644 --- a/app/ldap_protocol/ldap_requests/add.py +++ b/app/ldap_protocol/ldap_requests/add.py @@ -70,7 +70,7 @@ class AddRequest(BaseRequest): password: SecretStr | None = Field(None, examples=["password"]) @property - def attr_names(self) -> dict[str, list[str | bytes]]: # noqa: D102 + def attr_names(self) -> dict[str, list[str | bytes]]: return {attr.type.lower(): attr.vals for attr in self.attributes} @classmethod @@ -313,7 +313,7 @@ async def handle( ), ) - if (is_user or is_group) and 'gidnumber' not in self.attr_names: + if (is_user or is_group) and "gidnumber" not in self.attr_names: reverse_d_name = new_dir.name[::-1] value = ( "513" diff --git a/app/ldap_protocol/ldap_requests/base.py b/app/ldap_protocol/ldap_requests/base.py index eb885fca..ddbedf4d 100644 --- a/app/ldap_protocol/ldap_requests/base.py +++ b/app/ldap_protocol/ldap_requests/base.py @@ -35,7 +35,7 @@ ) handler: TypeAlias = Callable[..., AsyncGenerator[BaseResponse, None]] -serializer: TypeAlias = Callable[..., 'BaseRequest'] +serializer: TypeAlias = Callable[..., "BaseRequest"] if TYPE_CHECKING: @@ -49,7 +49,7 @@ async def _handle_api( ) -> list[BaseResponse] | BaseResponse: ... else: - class _APIProtocol: ... # noqa + class _APIProtocol: ... class BaseRequest(ABC, _APIProtocol, BaseModel): diff --git a/app/ldap_protocol/ldap_requests/bind.py b/app/ldap_protocol/ldap_requests/bind.py index bebe460c..7848df2e 100644 --- a/app/ldap_protocol/ldap_requests/bind.py +++ b/app/ldap_protocol/ldap_requests/bind.py @@ -4,6 +4,7 @@ License: https://github.com/MultiDirectoryLab/MultiDirectory/blob/main/LICENSE """ +import contextlib from typing import AsyncGenerator, ClassVar import httpx @@ -77,7 +78,7 @@ def from_data(cls, data: list[ASN1Row]) -> "BindRequest": password=password, otpassword=otpassword, ) - elif auth == SaslAuthentication.METHOD_ID: # noqa: R506 + elif auth == SaslAuthentication.METHOD_ID: sasl_method = data[2].value[0].value auth_choice = sasl_mechanism_map[sasl_method].from_data( data[2].value, @@ -118,15 +119,11 @@ async def check_mfa( try: return await api.ldap_validate_mfa(identity, otp) except MultifactorAPI.MFAConnectError: - if policy.bypass_no_connection: - return True - return False + return bool(policy.bypass_no_connection) except MultifactorAPI.MFAMissconfiguredError: return True except MultifactorAPI.MultifactorError: - if policy.bypass_service_failure: - return True - return False + return bool(policy.bypass_service_failure) async def handle( self, @@ -205,14 +202,12 @@ async def handle( yield get_bad_response(LDAPBindErrors.LOGON_FAILURE) return - try: + with contextlib.suppress(KRBAPIError, httpx.TimeoutException): await kadmin.add_principal( user.get_upn_prefix(), self.authentication_choice.password.get_secret_value(), 0.1, ) - except (KRBAPIError, httpx.TimeoutException): - pass await ldap_session.set_user(user) await set_last_logon_user(user, session, settings.TIMEZONE) diff --git a/app/ldap_protocol/ldap_requests/bind_methods/base.py b/app/ldap_protocol/ldap_requests/bind_methods/base.py index 2ee09b9d..f8fab6bd 100644 --- a/app/ldap_protocol/ldap_requests/bind_methods/base.py +++ b/app/ldap_protocol/ldap_requests/bind_methods/base.py @@ -47,7 +47,7 @@ class LDAPBindErrors(StrEnum): PASSWORD_MUST_CHANGE = "773" # noqa ACCOUNT_LOCKED_OUT = "775" - def __str__(self) -> str: # noqa + def __str__(self) -> str: return ( "80090308: LdapErr: DSID-0C09030B, " "comment: AcceptSecurityContext error, " @@ -79,7 +79,7 @@ class AbstractLDAPAuth(ABC, BaseModel): @property @abstractmethod - def METHOD_ID(self) -> int: # noqa: N802, D102 + def METHOD_ID(self) -> int: # noqa: N802 """Abstract method id.""" @abstractmethod diff --git a/app/ldap_protocol/ldap_requests/bind_methods/sasl_gssapi.py b/app/ldap_protocol/ldap_requests/bind_methods/sasl_gssapi.py index ebf6266f..464175e5 100644 --- a/app/ldap_protocol/ldap_requests/bind_methods/sasl_gssapi.py +++ b/app/ldap_protocol/ldap_requests/bind_methods/sasl_gssapi.py @@ -276,5 +276,5 @@ async def get_user( # type: ignore if not ctx: return None - username = str(ctx.initiator_name).split('@')[0] + username = str(ctx.initiator_name).split("@")[0] return await get_user(session, username) diff --git a/app/ldap_protocol/ldap_requests/delete.py b/app/ldap_protocol/ldap_requests/delete.py index b5710cb3..23d6c6fd 100644 --- a/app/ldap_protocol/ldap_requests/delete.py +++ b/app/ldap_protocol/ldap_requests/delete.py @@ -42,7 +42,7 @@ class DeleteRequest(BaseRequest): entry: str @classmethod - def from_data(cls, data: ASN1Row) -> "DeleteRequest": # noqa: D102 + def from_data(cls, data: ASN1Row) -> "DeleteRequest": return cls(entry=data) async def handle( diff --git a/app/ldap_protocol/ldap_requests/extended.py b/app/ldap_protocol/ldap_requests/extended.py index 0524efd4..c254ed7b 100644 --- a/app/ldap_protocol/ldap_requests/extended.py +++ b/app/ldap_protocol/ldap_requests/extended.py @@ -292,7 +292,7 @@ async def handle( ldap_session, session, kadmin, settings, ) except PermissionError as err: - logger.critical(err) # noqa + logger.critical(err) yield ExtendedResponse( result_code=LDAPCodes.OPERATIONS_ERROR, response_name=self.request_name, diff --git a/app/ldap_protocol/ldap_requests/modify.py b/app/ldap_protocol/ldap_requests/modify.py index 6084c428..6b9c313e 100644 --- a/app/ldap_protocol/ldap_requests/modify.py +++ b/app/ldap_protocol/ldap_requests/modify.py @@ -90,11 +90,11 @@ class ModifyRequest(BaseRequest): PROTOCOL_OP: ClassVar[int] = 6 - object: str # noqa: A003 + object: str changes: list[Changes] @classmethod - def from_data(cls, data: list[ASN1Row]) -> "ModifyRequest": # noqa: D102 + def from_data(cls, data: list[ASN1Row]) -> "ModifyRequest": entry, proto_changes = data changes = [] @@ -131,7 +131,7 @@ async def handle( yield ModifyResponse(result_code=LDAPCodes.INVALID_DN_SYNTAX) return - query = ( # noqa: ECE001 + query = ( select(Directory) .join(Directory.attributes) .options( @@ -356,7 +356,7 @@ async def _add( if name == "useraccountcontrol": uac_val = int(value) - if not UserAccountControlFlag.is_value_valid(uac_val): # noqa + if not UserAccountControlFlag.is_value_valid(uac_val): continue elif ( @@ -468,7 +468,7 @@ async def _add( raise PermissionError("TLS required") if isinstance(value, bytes): - raise ValueError('password is bytes') + raise ValueError("password is bytes") try: value = value.replace("\\x00", "\x00") diff --git a/app/ldap_protocol/ldap_requests/modify_dn.py b/app/ldap_protocol/ldap_requests/modify_dn.py index 01889406..13772e16 100644 --- a/app/ldap_protocol/ldap_requests/modify_dn.py +++ b/app/ldap_protocol/ldap_requests/modify_dn.py @@ -200,7 +200,7 @@ async def handle( await session.flush() if self.deleteoldrdn: - old_attr_name = directory.path[-1].split('=')[0] + old_attr_name = directory.path[-1].split("=")[0] await session.execute( update(Attribute) .where( diff --git a/app/ldap_protocol/ldap_requests/search.py b/app/ldap_protocol/ldap_requests/search.py index 4679ce1a..fcefc0ee 100644 --- a/app/ldap_protocol/ldap_requests/search.py +++ b/app/ldap_protocol/ldap_requests/search.py @@ -87,7 +87,7 @@ class SearchRequest(BaseRequest): size_limit: int = Field(ge=0, le=sys.maxsize, examples=[1000]) time_limit: int = Field(ge=0, le=sys.maxsize, examples=[1000]) types_only: bool - filter: ASN1Row = Field(...) # noqa: A003 + filter: ASN1Row = Field(...) attributes: list[str] page_number: int | None = Field(None, ge=1, examples=[1]) # only json API @@ -105,7 +105,7 @@ def serialize_filter( return val.to_ldap_filter() if isinstance(val, ASN1Row) else None @classmethod - def from_data( # noqa: D102 + def from_data( cls, data: dict[str, list[ASN1Row]], ) -> "SearchRequest": ( @@ -131,7 +131,7 @@ def from_data( # noqa: D102 ) @cached_property - def requested_attrs(self) -> list[str]: # noqa + def requested_attrs(self) -> list[str]: return [attr.lower() for attr in self.attributes] def _get_subschema(self) -> SearchResultEntry: @@ -290,19 +290,19 @@ async def get_result( ) @cached_property - def member_of(self) -> bool: # noqa + def member_of(self) -> bool: return "memberof" in self.requested_attrs or self.all_attrs @cached_property - def member(self) -> bool: # noqa + def member(self) -> bool: return "member" in self.requested_attrs or self.all_attrs @cached_property - def token_groups(self) -> bool: # noqa + def token_groups(self) -> bool: return "tokengroups" in self.requested_attrs @cached_property - def all_attrs(self) -> bool: # noqa + def all_attrs(self) -> bool: return "*" in self.requested_attrs or not self.requested_attrs def build_query( @@ -311,7 +311,7 @@ def build_query( user: UserSchema, ) -> Select: """Build tree query.""" - query = ( # noqa: ECE001 + query = ( select(Directory) .join(User, isouter=True) .join(Directory.attributes, isouter=True) @@ -372,7 +372,7 @@ def build_query( query = query.options( defaultload(Directory.groups).joinedload(Group.directory)) - return query # noqa + return query async def paginate_query( self, @@ -445,19 +445,18 @@ async def tree_view( for group in directory.groups: attrs["memberOf"].append(group.directory.path_dn) - if self.token_groups: - if "user" in obj_classes: - attrs["tokenGroups"].append( - str(string_to_sid(directory.object_sid))) + if self.token_groups and "user" in obj_classes: + attrs["tokenGroups"].append( + str(string_to_sid(directory.object_sid))) - group_directories = await get_all_parent_group_directories( - directory.groups, session, - ) + group_directories = await get_all_parent_group_directories( + directory.groups, session, + ) - if group_directories is not None: - async for directory_ in group_directories: - attrs["tokenGroups"].append( - str(string_to_sid(directory_.object_sid))) + if group_directories is not None: + async for directory_ in group_directories: + attrs["tokenGroups"].append( + str(string_to_sid(directory_.object_sid))) if self.member: if "group" in obj_classes and directory.group: diff --git a/app/ldap_protocol/ldap_responses.py b/app/ldap_protocol/ldap_responses.py index 49cbbc69..c4f6bd94 100644 --- a/app/ldap_protocol/ldap_responses.py +++ b/app/ldap_protocol/ldap_responses.py @@ -33,7 +33,7 @@ class LDAPResult(BaseModel): matched_dn: str = Field("", alias="matchedDN") error_message: str = Field("", alias="errorMessage") - class Config: # noqa + class Config: populate_by_name = True arbitrary_types_allowed = True json_encoders = { @@ -44,7 +44,7 @@ class Config: # noqa class BaseEncoder(BaseModel): """Class with encoder methods.""" - def _get_asn1_fields(self) -> dict: # noqa + def _get_asn1_fields(self) -> dict: fields = self.model_dump() fields.pop("PROTOCOL_OP", None) return fields @@ -60,7 +60,7 @@ class BaseResponse(ABC, BaseEncoder): @property @abstractmethod - def PROTOCOL_OP(self) -> int: # noqa: N802, D102 + def PROTOCOL_OP(self) -> int: # noqa: N802 """Protocol OP response code.""" @@ -92,17 +92,17 @@ def to_asn1(self, enc: Encoder) -> None: class PartialAttribute(BaseModel): """Partial attribite structure. Description in rfc2251 4.1.6.""" - type: Annotated[str, annotated_types.Len(max_length=8100)] # noqa: A003 + type: Annotated[str, annotated_types.Len(max_length=8100)] vals: list[Annotated[str | bytes, annotated_types.Len(max_length=100000)]] @field_validator("type", mode="before") @classmethod - def validate_type(cls, v: str | bytes | int) -> str: # noqa + def validate_type(cls, v: str | bytes | int) -> str: return str(v) @field_validator("vals", mode="before") @classmethod - def validate_vals(cls, vals: list[str | int | bytes]) -> list[str | bytes]: # noqa + def validate_vals(cls, vals: list[str | int | bytes]) -> list[str | bytes]: return [v if isinstance(v, bytes) else str(v) for v in vals] class Config: @@ -161,7 +161,7 @@ class SearchResultDone(LDAPResult, BaseResponse): total_pages: int = 0 total_objects: int = 0 - def _get_asn1_fields(self) -> dict: # noqa + def _get_asn1_fields(self) -> dict: fields = super()._get_asn1_fields() fields.pop("total_pages") fields.pop("total_objects") diff --git a/app/ldap_protocol/multifactor.py b/app/ldap_protocol/multifactor.py index 37e34ecf..1b1643d0 100644 --- a/app/ldap_protocol/multifactor.py +++ b/app/ldap_protocol/multifactor.py @@ -98,6 +98,7 @@ class MultifactorAPI: - `REFRESH_URL`: Endpoint URL for token refresh. - `client`: Asynchronous HTTP client for making requests. - `settings`: Configuration settings for the MFA service. + """ MultifactorError = _MultifactorError @@ -197,9 +198,7 @@ async def ldap_validate_mfa( }, ) - if data.get("model", {}).get("status") != "Granted": - return False - return True + return data.get("model", {}).get("status") == "Granted" @log_mfa.catch(reraise=True) async def get_create_mfa( diff --git a/app/ldap_protocol/policies/access_policy.py b/app/ldap_protocol/policies/access_policy.py index 4b36eb87..08a49303 100644 --- a/app/ldap_protocol/policies/access_policy.py +++ b/app/ldap_protocol/policies/access_policy.py @@ -90,8 +90,8 @@ def mutate_ap( if action == "read": user_path = get_search_path(user.dn) - get_upper_tree_elem = text( # noqa: F811 - "(:path)[1:\"Directory\".\"depth\"]", + get_upper_tree_elem = text( + '(:path)[1:"Directory"."depth"]', ).bindparams(bindparam("path", value=user_path, type_=ARRAY(String))) ap_filter = or_( diff --git a/app/ldap_protocol/policies/network_policy.py b/app/ldap_protocol/policies/network_policy.py index 735a0ed1..b402212b 100644 --- a/app/ldap_protocol/policies/network_policy.py +++ b/app/ldap_protocol/policies/network_policy.py @@ -19,8 +19,7 @@ def build_policy_query( protocol_field_name: Literal["is_http", "is_ldap", "is_kerberos"], user_group_ids: list[int] | None = None, ) -> Select: - """ - Build a base query for network policies with optional group filtering. + """Build a base query for network policies with optional group filtering. :param IPv4Address ip: IP address to filter :param Literal["is_http", "is_ldap", "is_kerberos"] protocol_field_name @@ -29,7 +28,7 @@ def build_policy_query( :return: Select query """ protocol_field = getattr(NetworkPolicy, protocol_field_name) - query = ( # noqa + query = ( select(NetworkPolicy) .filter_by(enabled=True) .options( @@ -82,8 +81,7 @@ async def get_user_network_policy( user: User, session: AsyncSession, ) -> NetworkPolicy | None: - """ - Get the highest priority network policy for user, ip and protocol. + """Get the highest priority network policy for user, ip and protocol. :param User user: user object :param AsyncSession session: db session @@ -114,7 +112,7 @@ async def is_user_group_valid( if not policy.groups: return True - query = ( # noqa: ECE001 + query = ( select(Group) .join(Group.users) .join(Group.policies, isouter=True) diff --git a/app/ldap_protocol/policies/password_policy.py b/app/ldap_protocol/policies/password_policy.py index 14af18a7..0e174fcb 100644 --- a/app/ldap_protocol/policies/password_policy.py +++ b/app/ldap_protocol/policies/password_policy.py @@ -201,9 +201,7 @@ def validate_min_age(self, last_pwd_set: Attribute) -> bool: password_exists = self._count_password_exists_days(last_pwd_set) - if password_exists < self.minimum_password_age_days: - return True - return False + return password_exists < self.minimum_password_age_days def validate_max_age(self, last_pwd_set: Attribute) -> bool: """Validate max password change age. @@ -220,9 +218,7 @@ def validate_max_age(self, last_pwd_set: Attribute) -> bool: password_exists = self._count_password_exists_days(last_pwd_set) - if password_exists > self.maximum_password_age_days: - return True - return False + return password_exists > self.maximum_password_age_days async def validate_password_with_policy( self, diff --git a/app/ldap_protocol/server.py b/app/ldap_protocol/server.py index 7aece110..13ed974b 100644 --- a/app/ldap_protocol/server.py +++ b/app/ldap_protocol/server.py @@ -358,25 +358,24 @@ async def _wrap_response( ldap_session.gssapi_authenticated and protocol_op != 1 and ldap_session.gssapi_security_context + ) and ldap_session.gssapi_security_layer in ( + GSSAPISL.INTEGRITY_PROTECTION, + GSSAPISL.CONFIDENTIALITY, ): - if ldap_session.gssapi_security_layer in ( - GSSAPISL.INTEGRITY_PROTECTION, - GSSAPISL.CONFIDENTIALITY, - ): - encrypt = ( - ldap_session.gssapi_security_layer == ( - GSSAPISL.CONFIDENTIALITY - ) + encrypt = ( + ldap_session.gssapi_security_layer == ( + GSSAPISL.CONFIDENTIALITY ) - wrap_data = ( - ldap_session.gssapi_security_context.wrap( - data, - encrypt=encrypt, - ) + ) + wrap_data = ( + ldap_session.gssapi_security_context.wrap( + data, + encrypt=encrypt, ) - sasl_buffer_length = len(wrap_data.message).to_bytes(4, "big") # noqa + ) + sasl_buffer_length = len(wrap_data.message).to_bytes(4, "big") - return sasl_buffer_length + wrap_data.message + return sasl_buffer_length + wrap_data.message return data @@ -415,7 +414,7 @@ async def _run_server(server: asyncio.base_events.Server) -> None: await server.serve_forever() @staticmethod - def log_addrs(server: asyncio.base_events.Server) -> None: # noqa + def log_addrs(server: asyncio.base_events.Server) -> None: addrs = ", ".join(str(sock.getsockname()) for sock in server.sockets) log.info(f"Server on {addrs}") @@ -423,8 +422,8 @@ async def start(self) -> None: """Run and log tcp server.""" server = await self._get_server() log.info( - f'started {'DEBUG' if self.settings.DEBUG else 'PROD'} ' - f'{'LDAPS' if self.settings.USE_CORE_TLS else 'LDAP'} server', + f"started {'DEBUG' if self.settings.DEBUG else 'PROD'} " + f"{'LDAPS' if self.settings.USE_CORE_TLS else 'LDAP'} server", ) try: diff --git a/app/ldap_protocol/session_storage.py b/app/ldap_protocol/session_storage.py index 07f1914d..38c4f6bc 100644 --- a/app/ldap_protocol/session_storage.py +++ b/app/ldap_protocol/session_storage.py @@ -2,6 +2,7 @@ from __future__ import annotations +import contextlib import hashlib import hmac import json @@ -263,7 +264,7 @@ async def get_user_sessions(self, uid: int) -> dict: retval = {} - for k, v in zip(keys, data): + for k, v in zip(keys, data, strict=False): if v is not None: tmp = json.loads(v) if k.startswith("ldap"): @@ -485,10 +486,8 @@ async def delete_user_session(self, session_id: str) -> None: uid = int(tmp) keys = await self._get_user_keys(uid) - try: + with contextlib.suppress(KeyError): keys.remove(session_id) - except KeyError: - pass self._session_batch[self._get_id_hash(uid)] = list(keys) await self.delete([session_id]) @@ -568,4 +567,4 @@ async def rekey_session(self, session_id: str, settings: Settings) -> str: key = await self.create_session(uid, settings, extra_data=extra_data) await self.delete_user_session(session_id) - return key # noqa: R504 + return key diff --git a/app/ldap_protocol/user_account_control.py b/app/ldap_protocol/user_account_control.py index 75597ce2..c7cef5b3 100644 --- a/app/ldap_protocol/user_account_control.py +++ b/app/ldap_protocol/user_account_control.py @@ -70,8 +70,7 @@ class UserAccountControlFlag(IntFlag): @classmethod def is_value_valid(cls, uac_value: str | int) -> bool: - """ - Check all flags set in the userAccountControl value. + """Check all flags set in the userAccountControl value. :param int uac_value: userAccountControl attribute value :return: True if the value is valid (only known flags), False otherwise @@ -86,7 +85,7 @@ def is_value_valid(cls, uac_value: str | int) -> bool: if uac_value == 0: return False - return False if uac_value & ~sum(flag.value for flag in cls) else True + return not uac_value & ~sum(flag.value for flag in cls) async def get_check_uac( diff --git a/app/ldap_protocol/utils/cte.py b/app/ldap_protocol/utils/cte.py index 8bbbd023..d6a745c7 100644 --- a/app/ldap_protocol/utils/cte.py +++ b/app/ldap_protocol/utils/cte.py @@ -68,21 +68,22 @@ def find_members_recursive_cte(dn: str) -> CTE: SELECT * FROM anon_1; Example: - -------- + ------- Group1 includes user1, user2, and group2. Group2 includes users user3 and group3. Group3 includes user4. In the case of a recursive search through the specified group1, the search result will be as follows: user1, user2, group2, user3, group3, user4. + """ - directory_hierarchy = ( # noqa: ECE001 + directory_hierarchy = ( select(Directory.id.label("directory_id"), Group.id.label("group_id")) .join(Directory.group) .select_from(Directory) .where(get_filter_from_path(dn)) ).cte(recursive=True) - recursive_part = ( # noqa: ECE001 + recursive_part = ( select( DirectoryMembership.directory_id.label("directory_id"), Group.id.label("group_id"), @@ -125,7 +126,7 @@ def find_root_group_recursive_cte(dn_list: list) -> CTE: SELECT * FROM anon_1; Example: - -------- + ------- Group1 includes user1, user2, and group2. Group2 includes users user3 and group3. Group3 includes user4. @@ -133,8 +134,9 @@ def find_root_group_recursive_cte(dn_list: list) -> CTE: In the case of a recursive search through the specified user4, the search result will be as follows: group1, group2, group3, user4. + """ - directory_hierarchy = ( # noqa: ECE001 + directory_hierarchy = ( select( Directory.id.label("directory_id"), Group.id.label("group_id"), ) @@ -142,7 +144,7 @@ def find_root_group_recursive_cte(dn_list: list) -> CTE: .join(Directory.group, isouter=True) .where(or_(*[get_filter_from_path(dn) for dn in dn_list])) ).cte(recursive=True) - recursive_part = ( # noqa: ECE001 + recursive_part = ( select( Group.directory_id.label("directory_id"), Group.id.label("group_id"), @@ -164,7 +166,7 @@ async def get_members_root_group( """Get all members root group by dn. Example: - -------- + ------- Group1 includes user1, user2, and group2. Group2 includes users user3 and group3. Group3 includes user4. @@ -172,6 +174,7 @@ async def get_members_root_group( In the case of a recursive search through the specified user4, the search result will be as follows: group1, user1, user2, group2, user3, group3, user4. + """ cte = find_root_group_recursive_cte([dn]) result = await session.scalars(select(cte.c.directory_id)) diff --git a/app/ldap_protocol/utils/queries.py b/app/ldap_protocol/utils/queries.py index 4f7398d8..1368c1f1 100644 --- a/app/ldap_protocol/utils/queries.py +++ b/app/ldap_protocol/utils/queries.py @@ -146,7 +146,7 @@ async def check_kerberos_group( if user is None: return False - query = ( # noqa: ECE001 + query = ( select(Group) .join(Group.users) .join(Group.directory) diff --git a/app/models.py b/app/models.py index c752edb5..d1abdc89 100644 --- a/app/models.py +++ b/app/models.py @@ -73,7 +73,7 @@ class CatalogueSetting(Base): __tablename__ = "Settings" - id: Mapped[int] = mapped_column(primary_key=True) # noqa + id: Mapped[int] = mapped_column(primary_key=True) name: Mapped[str] = mapped_column(nullable=False, index=True) value: Mapped[str] = mapped_column(nullable=False) @@ -134,7 +134,7 @@ class Directory(Base): __tablename__ = "Directory" - id: Mapped[int] = mapped_column(primary_key=True) # noqa: A003 + id: Mapped[int] = mapped_column(primary_key=True) parent_id: Mapped[int] = mapped_column( "parentId", @@ -300,7 +300,7 @@ class User(Base): __tablename__ = "Users" - id: Mapped[int] = mapped_column(primary_key=True) # noqa: A003 + id: Mapped[int] = mapped_column(primary_key=True) directory_id: Mapped[int] = mapped_column( "directoryId", @@ -353,7 +353,7 @@ class User(Base): "homedirectory": "homeDirectory", } - password_history: Mapped[list[str]] = mapped_column( # noqa TAE002 + password_history: Mapped[list[str]] = mapped_column( MutableList.as_mutable(postgresql.ARRAY(String)), server_default="{}", nullable=False, @@ -365,7 +365,7 @@ class User(Base): primaryjoin="User.directory_id == DirectoryMembership.directory_id", secondaryjoin="DirectoryMembership.group_id == Group.id", back_populates="users", - lazy='selectin', + lazy="selectin", cascade="all", passive_deletes=True, overlaps="group,groups,directory", @@ -391,7 +391,7 @@ def is_expired(self) -> bool: now = datetime.now(tz=timezone.utc) user_account_exp = self.account_exp.astimezone(timezone.utc) - return True if now > user_account_exp else False + return now > user_account_exp class Group(Base): @@ -399,7 +399,7 @@ class Group(Base): __tablename__ = "Groups" - id: Mapped[int] = mapped_column(primary_key=True) # noqa: A003 + id: Mapped[int] = mapped_column(primary_key=True) directory_id: Mapped[int] = mapped_column( "directoryId", @@ -490,7 +490,7 @@ class Attribute(Base): ), ) - id: Mapped[int] = mapped_column(primary_key=True) # noqa: A003 + id: Mapped[int] = mapped_column(primary_key=True) directory_id: Mapped[int] = mapped_column( "directoryId", @@ -520,7 +520,7 @@ class NetworkPolicy(Base): __tablename__ = "Policies" - id: Mapped[int] = mapped_column(primary_key=True) # noqa: A003 + id: Mapped[int] = mapped_column(primary_key=True) name: Mapped[str] = mapped_column(nullable=False, unique=True) raw: Mapped[dict | list] = mapped_column(postgresql.JSON, nullable=False) @@ -566,7 +566,7 @@ class PasswordPolicy(Base): __tablename__ = "PasswordPolicies" - id: Mapped[int] = mapped_column(primary_key=True) # noqa: A003 + id: Mapped[int] = mapped_column(primary_key=True) name: Mapped[str] = mapped_column( String(255), nullable=False, @@ -593,7 +593,7 @@ class AccessPolicy(Base): __tablename__ = "AccessPolicies" - id: Mapped[int] = mapped_column(primary_key=True) # noqa: A003 + id: Mapped[int] = mapped_column(primary_key=True) name: Mapped[str] = mapped_column(String(255), nullable=False, unique=True) can_read: Mapped[nbool] diff --git a/app/multidirectory.py b/app/multidirectory.py index 33b55b16..377f958e 100644 --- a/app/multidirectory.py +++ b/app/multidirectory.py @@ -180,10 +180,10 @@ def _run() -> None: parser = argparse.ArgumentParser(description="Run ldap or http") group = parser.add_mutually_exclusive_group(required=True) - group.add_argument('--ldap', action='store_true', help="Run ldap") - group.add_argument('--http', action='store_true', help="Run http") - group.add_argument('--shadow', action='store_true', help="Run http") - group.add_argument('--scheduler', action='store_true', help="Run tasks") + group.add_argument("--ldap", action="store_true", help="Run ldap") + group.add_argument("--http", action="store_true", help="Run http") + group.add_argument("--shadow", action="store_true", help="Run http") + group.add_argument("--scheduler", action="store_true", help="Run tasks") args = parser.parse_args() diff --git a/interface b/interface index 4693222c..6804fdcb 160000 --- a/interface +++ b/interface @@ -1 +1 @@ -Subproject commit 4693222c44d9f434d367964e5154bc35d33715c7 +Subproject commit 6804fdcb3eb5ffbc325e318b27a77e4445cb35d9 diff --git a/tests/conftest.py b/tests/conftest.py index 5613116b..e73381be 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -78,7 +78,7 @@ async def get_kadmin(self) -> AsyncIterator[AsyncMock]: ok_response = Mock() ok_response.status_code = 200 - ok_response.aiter_bytes.return_value = map(bytes, zip(b'test_string')) + ok_response.aiter_bytes.return_value = map(bytes, zip(b"test_string", strict=False)) kadmin.setup = AsyncMock() kadmin.ktadd = AsyncMock(return_value=ok_response) @@ -131,10 +131,10 @@ async def get_dns_mngr(self) -> AsyncIterator[AsyncMock]: @provide(scope=Scope.REQUEST, provides=DNSManagerSettings, cache=False) async def get_dns_mngr_settings( self, session: AsyncSession, - ) -> AsyncIterator['DNSManagerSettings']: + ) -> AsyncIterator["DNSManagerSettings"]: """Get DNS manager's settings.""" async def resolve() -> str: - return '127.0.0.1' + return "127.0.0.1" resolver = resolve() yield await get_dns_manager_settings(session, resolver) @@ -327,7 +327,7 @@ async def setup_session(session: AsyncSession) -> None: domain = domain_ex.one() await create_access_policy( - name='Root Access Policy', + name="Root Access Policy", can_add=True, can_modify=True, can_read=True, @@ -400,7 +400,7 @@ async def app( """App creator fixture.""" async with container(scope=Scope.APP) as container: app = _create_basic_app(settings) - app.include_router(shadow_router, prefix='/shadow') + app.include_router(shadow_router, prefix="/shadow") setup_dishka(container, app) yield app @@ -414,7 +414,7 @@ async def unbound_http_client( :yield Iterator[AsyncIterator[httpx.AsyncClient]]: yield client """ async with httpx.AsyncClient( - transport=httpx.ASGITransport(app=app, root_path='/api'), + transport=httpx.ASGITransport(app=app, root_path="/api"), timeout=3, base_url="http://test") as client: yield client @@ -437,7 +437,7 @@ async def http_client( "username": creds.un, "password": creds.pw}) assert response.status_code == 200 - assert unbound_http_client.cookies.get('id') + assert unbound_http_client.cookies.get("id") return unbound_http_client @@ -445,13 +445,13 @@ async def http_client( @pytest.fixture def creds(user: dict) -> TestCreds: """Get creds from test data.""" - return TestCreds(user['sam_accout_name'], user['password']) + return TestCreds(user["sam_accout_name"], user["password"]) @pytest.fixture def user() -> dict: """Get user data.""" - return TEST_DATA[1]['children'][0]['organizationalPerson'] # type: ignore + return TEST_DATA[1]["children"][0]["organizationalPerson"] # type: ignore @pytest.fixture diff --git a/tests/test_api/test_auth/test_pwd_policy.py b/tests/test_api/test_auth/test_pwd_policy.py index e02b776b..f7f73f6a 100644 --- a/tests/test_api/test_auth/test_pwd_policy.py +++ b/tests/test_api/test_auth/test_pwd_policy.py @@ -12,7 +12,7 @@ @pytest.mark.asyncio -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("session") async def test_policy_password(http_client: AsyncClient) -> None: """Test create policy.""" policy_data = { @@ -34,8 +34,8 @@ async def test_policy_password(http_client: AsyncClient) -> None: assert response.json() == policy_data changed_data = copy(policy_data) - changed_data['maximum_password_age_days'] = 80 - changed_data['minimum_password_age_days'] = 30 + changed_data["maximum_password_age_days"] = 80 + changed_data["minimum_password_age_days"] = 30 response = await http_client.put( "/password-policy", diff --git a/tests/test_api/test_auth/test_router_mfa.py b/tests/test_api/test_auth/test_router_mfa.py index f0cdb393..c4226a63 100644 --- a/tests/test_api/test_auth/test_router_mfa.py +++ b/tests/test_api/test_auth/test_router_mfa.py @@ -25,9 +25,9 @@ async def test_set_and_remove_mfa( response = await http_client.post( "/multifactor/setup", json={ - 'mfa_key': "123", - 'mfa_secret': "123", - 'is_ldap_scope': False, + "mfa_key": "123", + "mfa_secret": "123", + "is_ldap_scope": False, }, ) @@ -50,7 +50,7 @@ async def test_set_and_remove_mfa( @pytest.mark.asyncio -@pytest.mark.usefixtures('setup_session') +@pytest.mark.usefixtures("setup_session") async def test_connect_mfa( http_client: httpx.AsyncClient, session: AsyncSession, @@ -58,18 +58,18 @@ async def test_connect_mfa( ) -> None: """Test websocket mfa.""" session.add( - CatalogueSetting(name='mfa_secret', value='123'), + CatalogueSetting(name="mfa_secret", value="123"), ) - session.add(CatalogueSetting(name='mfa_key', value='123')) + session.add(CatalogueSetting(name="mfa_key", value="123")) await session.commit() redirect_url = "example.com" response = await http_client.post( - '/multifactor/connect', - data={'username': creds.un, 'password': creds.pw}) + "/multifactor/connect", + data={"username": creds.un, "password": creds.pw}) - assert response.json() == {'status': 'pending', 'message': redirect_url} + assert response.json() == {"status": "pending", "message": redirect_url} user = await authenticate_user(session, creds.un, creds.pw) @@ -77,11 +77,11 @@ async def test_connect_mfa( exp = datetime.now() + timedelta(minutes=5) - token = jwt.encode({'aud': '123', "uid": user.id, "exp": exp}, '123') + token = jwt.encode({"aud": "123", "uid": user.id, "exp": exp}, "123") response = await http_client.post( - '/multifactor/create', - data={'accessToken': token}, follow_redirects=False) + "/multifactor/create", + data={"accessToken": token}, follow_redirects=False) assert response.status_code == 302 - assert response.cookies.get('id') + assert response.cookies.get("id") diff --git a/tests/test_api/test_main/conftest.py b/tests/test_api/test_main/conftest.py index fe6a852f..a7a33538 100644 --- a/tests/test_api/test_main/conftest.py +++ b/tests/test_api/test_main/conftest.py @@ -118,7 +118,7 @@ async def adding_test_user( assert data["resultCode"] == LDAPCodes.SUCCESS async with AsyncClient( - transport=ASGITransport(app=app, root_path='/api'), + transport=ASGITransport(app=app, root_path="/api"), timeout=3, base_url="http://test") as client: @@ -133,7 +133,7 @@ async def adding_test_user( assert auth.cookies.get("id") -@pytest_asyncio.fixture(scope='function') +@pytest_asyncio.fixture(scope="function") async def add_dns_settings( session: AsyncSession, ) -> None: diff --git a/tests/test_api/test_main/test_dns.py b/tests/test_api/test_main/test_dns.py index fe37574d..d71c5512 100644 --- a/tests/test_api/test_main/test_dns.py +++ b/tests/test_api/test_main/test_dns.py @@ -7,7 +7,7 @@ @pytest.mark.asyncio -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("session") async def test_dns_create_record( http_client: AsyncClient, dns_manager: AbstractDNSManager, @@ -38,7 +38,7 @@ async def test_dns_create_record( @pytest.mark.asyncio -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("session") async def test_dns_delete_record( http_client: AsyncClient, dns_manager: AbstractDNSManager, @@ -48,8 +48,8 @@ async def test_dns_delete_record( ip = "127.0.0.1" record_type = "A" response = await http_client.request( - 'DELETE', - '/dns/record', + "DELETE", + "/dns/record", json={ "record_name": hostname, "record_value": ip, @@ -66,7 +66,7 @@ async def test_dns_delete_record( @pytest.mark.asyncio -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("session") async def test_dns_update_record( http_client: AsyncClient, dns_manager: AbstractDNSManager, @@ -77,8 +77,8 @@ async def test_dns_update_record( record_type = "A" ttl = 3600 response = await http_client.request( - 'PATCH', - '/dns/record', + "PATCH", + "/dns/record", json={ "record_name": hostname, "record_value": ip, @@ -96,10 +96,10 @@ async def test_dns_update_record( @pytest.mark.asyncio -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("session") async def test_dns_get_all_records(http_client: AsyncClient) -> None: """DNS Manager get all records test.""" - response = await http_client.get('/dns/record') + response = await http_client.get("/dns/record") assert response.status_code == status.HTTP_200_OK @@ -115,7 +115,7 @@ async def test_dns_get_all_records(http_client: AsyncClient) -> None: @pytest.mark.asyncio -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("session") async def test_dns_setup_selfhosted( http_client: AsyncClient, dns_manager: AbstractDNSManager, @@ -126,7 +126,7 @@ async def test_dns_setup_selfhosted( tsig_key = None dns_ip_address = None response = await http_client.post( - '/dns/setup', + "/dns/setup", json={ "dns_status": dns_status, "domain": domain, @@ -141,11 +141,11 @@ async def test_dns_setup_selfhosted( @pytest.mark.asyncio -@pytest.mark.usefixtures('add_dns_settings') -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("add_dns_settings") +@pytest.mark.usefixtures("session") async def test_dns_get_status(http_client: AsyncClient) -> None: """DNS Manager get status test.""" - response = await http_client.get('/dns/status') + response = await http_client.get("/dns/status") assert response.status_code == status.HTTP_200_OK assert response.json() == { diff --git a/tests/test_api/test_main/test_kadmin.py b/tests/test_api/test_main/test_kadmin.py index 4d524934..28a2a6e6 100644 --- a/tests/test_api/test_main/test_kadmin.py +++ b/tests/test_api/test_main/test_kadmin.py @@ -25,7 +25,7 @@ def _create_test_user_data( "entry": "cn=ktest,dc=md,dc=test", "password": pw, "attributes": [ - {"type": "mail", "vals": ['123@mil.com']}, + {"type": "mail", "vals": ["123@mil.com"]}, {"type": "objectClass", "vals": [ "user", "top", "person", "organizationalPerson", @@ -40,7 +40,7 @@ def _create_test_user_data( {"type": "uid", "vals": ["ktest"]}, {"type": "homeDirectory", "vals": ["/home/ktest"]}, {"type": "sAMAccountName", "vals": [name]}, - {"type": "userPrincipalName", "vals": ['ktest']}, + {"type": "userPrincipalName", "vals": ["ktest"]}, {"type": "displayName", "vals": ["Kerberos Administrator"]}, {"type": "userAccountControl", "vals": ["512"]}, ]} @@ -55,9 +55,9 @@ async def test_tree_creation( settings: Settings, ) -> None: """Test tree creation.""" - krbadmin_pw = 'Password123' - response = await http_client.post('/kerberos/setup/tree', json={ - "mail": '777@example.com', + krbadmin_pw = "Password123" + response = await http_client.post("/kerberos/setup/tree", json={ + "mail": "777@example.com", "krbadmin_password": krbadmin_pw, }) @@ -78,11 +78,11 @@ async def test_tree_creation( }, ) assert response.json()[ - 'search_result'][0]['object_name'] == "ou=services,dc=md,dc=test" + "search_result"][0]["object_name"] == "ou=services,dc=md,dc=test" bind = MutePolicyBindRequest( version=0, - name='cn=krbadmin,ou=users,dc=md,dc=test', + name="cn=krbadmin,ou=users,dc=md,dc=test", AuthenticationChoice=SimpleAuthentication(password=krbadmin_pw), ) @@ -92,26 +92,26 @@ async def test_tree_creation( @pytest.mark.asyncio -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("session") async def test_tree_collision(http_client: AsyncClient) -> None: """Test tree collision double creation.""" - response = await http_client.post('/kerberos/setup/tree', json={ - "mail": '777@example.com', - "krbadmin_password": 'Password123', + response = await http_client.post("/kerberos/setup/tree", json={ + "mail": "777@example.com", + "krbadmin_password": "Password123", }) assert response.status_code == status.HTTP_200_OK - response = await http_client.post('/kerberos/setup/tree', json={ - "mail": '777@example.com', - "krbadmin_password": 'Password123', + response = await http_client.post("/kerberos/setup/tree", json={ + "mail": "777@example.com", + "krbadmin_password": "Password123", }) assert response.status_code == status.HTTP_409_CONFLICT @pytest.mark.asyncio -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("session") async def test_setup_call( http_client: AsyncClient, kadmin: Mock, @@ -122,37 +122,37 @@ async def test_setup_call( :param AsyncClient http_client: http cl :param LDAPSession ldap_session: ldap """ - response = await http_client.post('/kerberos/setup', json={ - "krbadmin_password": 'Password123', + response = await http_client.post("/kerberos/setup", json={ + "krbadmin_password": "Password123", "admin_password": creds.pw, - "stash_password": 'Password123', + "stash_password": "Password123", }) assert response.status_code == status.HTTP_200_OK kadmin.setup.assert_called() - krb_doc = kadmin.setup.call_args.kwargs.pop('krb5_config').encode() - kdc_doc = kadmin.setup.call_args.kwargs.pop('kdc_config').encode() + krb_doc = kadmin.setup.call_args.kwargs.pop("krb5_config").encode() + kdc_doc = kadmin.setup.call_args.kwargs.pop("kdc_config").encode() # NOTE: Asserting documents integrity, tests template rendering - assert blake2b(krb_doc, digest_size=8).hexdigest() == '84d7b6b78bca55fb' - assert blake2b(kdc_doc, digest_size=8).hexdigest() == '79e43649d34fe577' + assert blake2b(krb_doc, digest_size=8).hexdigest() == "84d7b6b78bca55fb" + assert blake2b(kdc_doc, digest_size=8).hexdigest() == "79e43649d34fe577" assert kadmin.setup.call_args.kwargs == { - 'domain': 'md.test', - 'admin_dn': 'cn=user0,ou=users,dc=md,dc=test', - 'services_dn': 'ou=services,dc=md,dc=test', - 'krbadmin_dn': 'cn=krbadmin,ou=users,dc=md,dc=test', - 'krbadmin_password': 'Password123', - 'ldap_keytab_path': '/LDAP_keytab/ldap.keytab', - 'admin_password': creds.pw, - 'stash_password': 'Password123', + "domain": "md.test", + "admin_dn": "cn=user0,ou=users,dc=md,dc=test", + "services_dn": "ou=services,dc=md,dc=test", + "krbadmin_dn": "cn=krbadmin,ou=users,dc=md,dc=test", + "krbadmin_password": "Password123", + "ldap_keytab_path": "/LDAP_keytab/ldap.keytab", + "admin_password": creds.pw, + "stash_password": "Password123", } @pytest.mark.asyncio -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("session") async def test_status_change( http_client: AsyncClient, creds: TestCreds, @@ -163,23 +163,23 @@ async def test_status_change( :param LDAPSession ldap_session: ldap """ response = await http_client.get( - '/kerberos/status') + "/kerberos/status") assert response.status_code == status.HTTP_200_OK assert response.json() == KerberosState.NOT_CONFIGURED - await http_client.post('/kerberos/setup', json={ - "krbadmin_password": 'Password123', + await http_client.post("/kerberos/setup", json={ + "krbadmin_password": "Password123", "admin_password": creds.pw, - "stash_password": 'Password123', + "stash_password": "Password123", }) response = await http_client.get( - '/kerberos/status') + "/kerberos/status") assert response.json() == KerberosState.WAITING_FOR_RELOAD @pytest.mark.asyncio -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("session") async def test_ktadd( http_client: AsyncClient, kadmin: AbstractKadmin, @@ -189,21 +189,21 @@ async def test_ktadd( :param AsyncClient http_client: http cl :param LDAPSession ldap_session: ldap """ - names = ['test1', 'test2'] - response = await http_client.post('/kerberos/ktadd', json=names) + names = ["test1", "test2"] + response = await http_client.post("/kerberos/ktadd", json=names) kadmin.ktadd.assert_called() # type: ignore assert kadmin.ktadd.call_args.args[0] == names # type: ignore assert response.status_code == status.HTTP_200_OK - assert response.content == b'test_string' + assert response.content == b"test_string" assert response.headers[ - 'Content-Disposition'] == 'attachment; filename="krb5.keytab"' - assert response.headers['content-type'] == 'application/txt' + "Content-Disposition"] == 'attachment; filename="krb5.keytab"' + assert response.headers["content-type"] == "application/txt" @pytest.mark.asyncio -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("session") async def test_ktadd_404( http_client: AsyncClient, kadmin: AbstractKadmin, @@ -215,14 +215,14 @@ async def test_ktadd_404( """ kadmin.ktadd.side_effect = KRBAPIError() # type: ignore - names = ['test1', 'test2'] - response = await http_client.post('/kerberos/ktadd', json=names) + names = ["test1", "test2"] + response = await http_client.post("/kerberos/ktadd", json=names) assert response.status_code == status.HTTP_404_NOT_FOUND @pytest.mark.asyncio -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("session") async def test_ldap_add( http_client: AsyncClient, kadmin: AbstractKadmin, @@ -232,8 +232,8 @@ async def test_ldap_add( :param AsyncClient http_client: http :param TestKadminClient kadmin: kadmin """ - san = 'ktest' - pw = 'Password123' + san = "ktest" + pw = "Password123" response = await http_client.post( "/entry/add", @@ -244,7 +244,7 @@ async def test_ldap_add( @pytest.mark.asyncio -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("session") async def test_ldap_kadmin_delete_user( http_client: AsyncClient, kadmin: AbstractKadmin, @@ -252,7 +252,7 @@ async def test_ldap_kadmin_delete_user( """Test API for delete object.""" await http_client.post( "/entry/add", - json=_create_test_user_data('ktest', 'Password123')) + json=_create_test_user_data("ktest", "Password123")) response = await http_client.request( "delete", "/entry/delete", @@ -261,13 +261,13 @@ async def test_ldap_kadmin_delete_user( data = response.json() - assert data.get('resultCode') == LDAPCodes.SUCCESS + assert data.get("resultCode") == LDAPCodes.SUCCESS assert kadmin.del_principal.call_args.args[0] == "ktest" # type: ignore @pytest.mark.asyncio -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("session") async def test_ldap_kadmin_delete_computer( http_client: AsyncClient, kadmin: AbstractKadmin, @@ -289,9 +289,9 @@ async def test_ldap_kadmin_delete_computer( data = response.json() - assert data.get('resultCode') == LDAPCodes.SUCCESS + assert data.get("resultCode") == LDAPCodes.SUCCESS principal = kadmin.del_principal.call_args.args[0] # type: ignore - assert principal == 'host/ktest.md.test' + assert principal == "host/ktest.md.test" @pytest.mark.asyncio @@ -301,16 +301,16 @@ async def test_bind_create_user( settings: Settings, ) -> None: """Test bind create user.""" - san = 'ktest' - pw = 'Password123' + san = "ktest" + pw = "Password123" await http_client.post("/entry/add", json=_create_test_user_data(san, pw)) proc = await asyncio.create_subprocess_exec( - 'ldapwhoami', '-x', - '-H', f'ldap://{settings.HOST}:{settings.PORT}', - '-D', san, - '-w', pw, + "ldapwhoami", "-x", + "-H", f"ldap://{settings.HOST}:{settings.PORT}", + "-D", san, + "-w", pw, ) assert await proc.wait() == 0 @@ -319,9 +319,9 @@ async def test_bind_create_user( @pytest.mark.asyncio -@pytest.mark.usefixtures('setup_session') -@pytest.mark.usefixtures('session') -@pytest.mark.usefixtures('_force_override_tls') +@pytest.mark.usefixtures("setup_session") +@pytest.mark.usefixtures("session") +@pytest.mark.usefixtures("_force_override_tls") async def test_extended_pw_change_call( event_loop: asyncio.BaseEventLoop, ldap_client: Connection, @@ -338,7 +338,7 @@ async def test_extended_pw_change_call( result = await event_loop.run_in_executor( None, - partial( # noqa: S106 + partial( ldap_client.extend.standard.modify_password, old_password=password, new_password=new_test_password, @@ -347,11 +347,11 @@ async def test_extended_pw_change_call( assert result kadmin_args = ( kadmin.create_or_update_principal_pw.call_args.args) # type: ignore - assert kadmin_args == ('user0', new_test_password) + assert kadmin_args == ("user0", new_test_password) @pytest.mark.asyncio -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("session") async def test_add_princ( http_client: AsyncClient, kadmin: AbstractKadmin, @@ -362,7 +362,7 @@ async def test_add_princ( :param LDAPSession ldap_session: ldap """ response = await http_client.post( - '/kerberos/principal/add', + "/kerberos/principal/add", json={ "primary": "host", "instance": "12345", @@ -374,7 +374,7 @@ async def test_add_princ( @pytest.mark.asyncio -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("session") async def test_rename_princ( http_client: AsyncClient, kadmin: AbstractKadmin, @@ -385,7 +385,7 @@ async def test_rename_princ( :param LDAPSession ldap_session: ldap """ response = await http_client.patch( - '/kerberos/principal/rename', + "/kerberos/principal/rename", json={ "principal_name": "name", "principal_new_name": "nname", @@ -397,7 +397,7 @@ async def test_rename_princ( @pytest.mark.asyncio -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("session") async def test_change_princ( http_client: AsyncClient, kadmin: AbstractKadmin, @@ -408,7 +408,7 @@ async def test_change_princ( :param LDAPSession ldap_session: ldap """ response = await http_client.patch( - '/kerberos/principal/reset', + "/kerberos/principal/reset", json={ "principal_name": "name", "new_password": "pw123", @@ -421,7 +421,7 @@ async def test_change_princ( @pytest.mark.asyncio -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("session") async def test_delete_princ( http_client: AsyncClient, kadmin: AbstractKadmin, @@ -433,7 +433,7 @@ async def test_delete_princ( """ response = await http_client.request( "delete", - '/kerberos/principal/delete', + "/kerberos/principal/delete", json={"principal_name": "name"}, ) assert response.status_code == status.HTTP_200_OK @@ -441,29 +441,29 @@ async def test_delete_princ( @pytest.mark.asyncio -@pytest.mark.usefixtures('session') -@pytest.mark.usefixtures('setup_session') +@pytest.mark.usefixtures("session") +@pytest.mark.usefixtures("setup_session") async def test_admin_incorrect_pw_setup(http_client: AsyncClient) -> None: """Test setup args. :param AsyncClient http_client: http cl :param LDAPSession ldap_session: ldap """ - response = await http_client.get('/kerberos/status') + response = await http_client.get("/kerberos/status") assert response.status_code == status.HTTP_200_OK assert response.json() == KerberosState.NOT_CONFIGURED - response = await http_client.post('/kerberos/setup', json={ - "krbadmin_password": 'Password123', - "admin_password": '----', - "stash_password": 'Password123', + response = await http_client.post("/kerberos/setup", json={ + "krbadmin_password": "Password123", + "admin_password": "----", + "stash_password": "Password123", }) data = response.json() - assert data['detail'] == 'Incorrect password' + assert data["detail"] == "Incorrect password" @pytest.mark.asyncio -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("session") async def test_api_update_password( http_client: AsyncClient, kadmin: AbstractKadmin, @@ -479,7 +479,7 @@ async def test_api_update_password( @pytest.mark.asyncio -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("session") async def test_update_password( http_client: AsyncClient, kadmin: AbstractKadmin, diff --git a/tests/test_api/test_main/test_multifactor.py b/tests/test_api/test_main/test_multifactor.py index aca4b399..f677620c 100644 --- a/tests/test_api/test_main/test_multifactor.py +++ b/tests/test_api/test_main/test_multifactor.py @@ -10,7 +10,7 @@ @pytest.mark.asyncio -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("session") @pytest.mark.parametrize( ( "mock_post_side_effect", diff --git a/tests/test_api/test_main/test_router/test_add.py b/tests/test_api/test_main/test_router/test_add.py index 1b5a438f..d647c35b 100644 --- a/tests/test_api/test_main/test_router/test_add.py +++ b/tests/test_api/test_main/test_router/test_add.py @@ -12,7 +12,7 @@ @pytest.mark.asyncio -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("session") async def test_api_correct_add(http_client: AsyncClient) -> None: """Test api correct add.""" response = await http_client.post( @@ -47,12 +47,12 @@ async def test_api_correct_add(http_client: AsyncClient) -> None: assert isinstance(data, dict) assert response.status_code == status.HTTP_200_OK - assert data.get('resultCode') == LDAPCodes.SUCCESS - assert data.get('errorMessage') == '' + assert data.get("resultCode") == LDAPCodes.SUCCESS + assert data.get("errorMessage") == "" @pytest.mark.asyncio -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("session") async def test_api_add_computer(http_client: AsyncClient) -> None: """Test api correct add computer.""" new_entry = "cn=PC,dc=md,dc=test" @@ -96,19 +96,19 @@ async def test_api_add_computer(http_client: AsyncClient) -> None: ) data = response.json() - assert data['search_result'][0]['object_name'] == new_entry + assert data["search_result"][0]["object_name"] == new_entry - for attr in data['search_result'][0]['partial_attributes']: - if attr['type'] == 'userAccountControl': - assert int(attr['vals'][0]) &\ + for attr in data["search_result"][0]["partial_attributes"]: + if attr["type"] == "userAccountControl": + assert int(attr["vals"][0]) &\ UserAccountControlFlag.WORKSTATION_TRUST_ACCOUNT break else: - raise Exception('Computer without userAccountControl') + raise Exception("Computer without userAccountControl") @pytest.mark.asyncio -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("session") async def test_api_correct_add_double_member_of( http_client: AsyncClient) -> None: """Test api correct add a group with a register. @@ -136,11 +136,11 @@ async def test_api_correct_add_double_member_of( }, { "type": "groupType", - "vals": ['-2147483646'], + "vals": ["-2147483646"], }, { "type": "instanceType", - "vals": ['4'], + "vals": ["4"], }, ], }, @@ -148,7 +148,7 @@ async def test_api_correct_add_double_member_of( data = response.json() assert response.status_code == status.HTTP_200_OK - assert data.get('resultCode') == LDAPCodes.SUCCESS + assert data.get("resultCode") == LDAPCodes.SUCCESS response = await http_client.post( "entry/search", @@ -166,10 +166,10 @@ async def test_api_correct_add_double_member_of( ) data = response.json() - assert data['search_result'][0]['object_name'] == new_group + assert data["search_result"][0]["object_name"] == new_group - for attr in data['search_result'][0]['partial_attributes']: - assert attr['type'] != 'memberOf' + for attr in data["search_result"][0]["partial_attributes"]: + assert attr["type"] != "memberOf" response = await http_client.post( "/entry/add", @@ -219,7 +219,7 @@ async def test_api_correct_add_double_member_of( data = response.json() assert response.status_code == status.HTTP_200_OK - assert data.get('resultCode') == LDAPCodes.SUCCESS + assert data.get("resultCode") == LDAPCodes.SUCCESS response = await http_client.post( "entry/search", @@ -238,28 +238,28 @@ async def test_api_correct_add_double_member_of( data = response.json() assert response.status_code == status.HTTP_200_OK - assert data.get('resultCode') == LDAPCodes.SUCCESS - assert data['search_result'][0]['object_name'] == user + assert data.get("resultCode") == LDAPCodes.SUCCESS + assert data["search_result"][0]["object_name"] == user created_groups = groups + ["cn=domain users,cn=groups,dc=md,dc=test"] - for attr in data['search_result'][0]['partial_attributes']: - if attr['type'] == 'memberOf': - assert all(group in created_groups for group in attr['vals']) + for attr in data["search_result"][0]["partial_attributes"]: + if attr["type"] == "memberOf": + assert all(group in created_groups for group in attr["vals"]) break else: - raise Exception('memberOf not found') + raise Exception("memberOf not found") - for attr in data['search_result'][0]['partial_attributes']: - if attr['type'] == 'userAccountControl': - assert attr['vals'][0] == "514" + for attr in data["search_result"][0]["partial_attributes"]: + if attr["type"] == "userAccountControl": + assert attr["vals"][0] == "514" break else: - raise Exception('userAccountControl not found') + raise Exception("userAccountControl not found") @pytest.mark.asyncio -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("session") async def test_api_add_user_inccorect_uac( http_client: AsyncClient) -> None: """Test api add.""" @@ -310,7 +310,7 @@ async def test_api_add_user_inccorect_uac( data = response.json() assert response.status_code == status.HTTP_200_OK - assert data.get('resultCode') == LDAPCodes.SUCCESS + assert data.get("resultCode") == LDAPCodes.SUCCESS response = await http_client.post( "entry/search", @@ -329,24 +329,24 @@ async def test_api_add_user_inccorect_uac( data = response.json() assert response.status_code == status.HTTP_200_OK - assert data.get('resultCode') == LDAPCodes.SUCCESS - assert data['search_result'][0]['object_name'] == user + assert data.get("resultCode") == LDAPCodes.SUCCESS + assert data["search_result"][0]["object_name"] == user - for attr in data['search_result'][0]['partial_attributes']: - if attr['type'] == 'userAccountControl': - assert attr['vals'][0] == "512" + for attr in data["search_result"][0]["partial_attributes"]: + if attr["type"] == "userAccountControl": + assert attr["vals"][0] == "512" break else: - raise Exception('userAccountControl not found') + raise Exception("userAccountControl not found") @pytest.mark.asyncio -@pytest.mark.usefixtures('setup_session') -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("setup_session") +@pytest.mark.usefixtures("session") async def test_api_add_non_auth_user(unbound_http_client: AsyncClient) -> None: """Test API add for unauthorized user.""" unbound_http_client.cookies.set( - 'id', "09e67421-2f92-8ddc-494108a6e04f") + "id", "09e67421-2f92-8ddc-494108a6e04f") response = await unbound_http_client.post( "/entry/add", json={ @@ -359,11 +359,11 @@ async def test_api_add_non_auth_user(unbound_http_client: AsyncClient) -> None: data = response.json() assert response.status_code == 401 - assert data.get('detail') == 'Could not validate credentials' + assert data.get("detail") == "Could not validate credentials" @pytest.mark.asyncio -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("session") async def test_api_add_with_incorrect_dn(http_client: AsyncClient) -> None: """Test API add a user with incorrect DN.""" response = await http_client.post( @@ -377,11 +377,11 @@ async def test_api_add_with_incorrect_dn(http_client: AsyncClient) -> None: data = response.json() assert isinstance(data, dict) - assert data.get('resultCode') == LDAPCodes.INVALID_DN_SYNTAX + assert data.get("resultCode") == LDAPCodes.INVALID_DN_SYNTAX @pytest.mark.asyncio -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("session") async def test_api_add_with_incorrect_name(http_client: AsyncClient) -> None: """Test API add a user with incorrect name.""" response = await http_client.post( @@ -394,11 +394,11 @@ async def test_api_add_with_incorrect_name(http_client: AsyncClient) -> None: ) data = response.json() - assert data.get('resultCode') == LDAPCodes.INVALID_DN_SYNTAX + assert data.get("resultCode") == LDAPCodes.INVALID_DN_SYNTAX @pytest.mark.asyncio -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("session") async def test_api_add_with_space_end_name(http_client: AsyncClient) -> None: """Test API add a user with incorrect name.""" entry = "cn=test test ,dc=md,dc=test" @@ -417,7 +417,7 @@ async def test_api_add_with_space_end_name(http_client: AsyncClient) -> None: ) data = response.json() - assert data.get('resultCode') == LDAPCodes.SUCCESS + assert data.get("resultCode") == LDAPCodes.SUCCESS response = await http_client.post( "entry/search", @@ -435,11 +435,11 @@ async def test_api_add_with_space_end_name(http_client: AsyncClient) -> None: ) data = response.json() - assert data['search_result'][0]['object_name'] == entry + assert data["search_result"][0]["object_name"] == entry @pytest.mark.asyncio -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("session") async def test_api_add_with_non_exist_parent(http_client: AsyncClient) -> None: """Test API add a user with non-existen parent.""" response = await http_client.post( @@ -454,13 +454,13 @@ async def test_api_add_with_non_exist_parent(http_client: AsyncClient) -> None: data = response.json() assert isinstance(data, dict) - assert data.get('resultCode') == LDAPCodes.NO_SUCH_OBJECT + assert data.get("resultCode") == LDAPCodes.NO_SUCH_OBJECT @pytest.mark.asyncio -@pytest.mark.usefixtures('adding_test_user') -@pytest.mark.usefixtures('setup_session') -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("adding_test_user") +@pytest.mark.usefixtures("setup_session") +@pytest.mark.usefixtures("session") async def test_api_double_add(http_client: AsyncClient) -> None: """Test API for adding a user who already exists.""" response = await http_client.post( @@ -494,11 +494,11 @@ async def test_api_double_add(http_client: AsyncClient) -> None: data = response.json() assert isinstance(data, dict) - assert data.get('resultCode') == LDAPCodes.ENTRY_ALREADY_EXISTS + assert data.get("resultCode") == LDAPCodes.ENTRY_ALREADY_EXISTS @pytest.mark.asyncio -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("session") async def test_api_add_double_case_insensetive( http_client: AsyncClient) -> None: """Test api double add.""" @@ -529,7 +529,7 @@ async def test_api_add_double_case_insensetive( }, ) - assert response.json().get('resultCode') == LDAPCodes.SUCCESS + assert response.json().get("resultCode") == LDAPCodes.SUCCESS response = await http_client.post( "/entry/add", @@ -558,4 +558,4 @@ async def test_api_add_double_case_insensetive( }, ) - assert response.json().get('resultCode') == LDAPCodes.ENTRY_ALREADY_EXISTS + assert response.json().get("resultCode") == LDAPCodes.ENTRY_ALREADY_EXISTS diff --git a/tests/test_api/test_main/test_router/test_delete.py b/tests/test_api/test_main/test_router/test_delete.py index 54499bc4..cde77fb5 100644 --- a/tests/test_api/test_main/test_router/test_delete.py +++ b/tests/test_api/test_main/test_router/test_delete.py @@ -10,9 +10,9 @@ @pytest.mark.asyncio -@pytest.mark.usefixtures('adding_test_user') -@pytest.mark.usefixtures('setup_session') -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("adding_test_user") +@pytest.mark.usefixtures("setup_session") +@pytest.mark.usefixtures("session") async def test_api_correct_delete(http_client: AsyncClient) -> None: """Test API for delete object.""" response = await http_client.request( @@ -23,13 +23,13 @@ async def test_api_correct_delete(http_client: AsyncClient) -> None: data = response.json() assert isinstance(data, dict) - assert data.get('resultCode') == LDAPCodes.SUCCESS + assert data.get("resultCode") == LDAPCodes.SUCCESS @pytest.mark.asyncio -@pytest.mark.usefixtures('adding_test_user') -@pytest.mark.usefixtures('setup_session') -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("adding_test_user") +@pytest.mark.usefixtures("setup_session") +@pytest.mark.usefixtures("session") async def test_api_delete_with_incorrect_dn(http_client: AsyncClient) -> None: """Test API for delete object with incorrect DN.""" response = await http_client.request( @@ -43,13 +43,13 @@ async def test_api_delete_with_incorrect_dn(http_client: AsyncClient) -> None: data = response.json() assert isinstance(data, dict) - assert data.get('resultCode') == LDAPCodes.INVALID_DN_SYNTAX + assert data.get("resultCode") == LDAPCodes.INVALID_DN_SYNTAX @pytest.mark.asyncio -@pytest.mark.usefixtures('adding_test_user') -@pytest.mark.usefixtures('setup_session') -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("adding_test_user") +@pytest.mark.usefixtures("setup_session") +@pytest.mark.usefixtures("session") async def test_api_delete_non_exist_object( http_client: AsyncClient) -> None: """Test API for delete non-existen object.""" @@ -64,4 +64,4 @@ async def test_api_delete_non_exist_object( data = response.json() assert isinstance(data, dict) - assert data.get('resultCode') == LDAPCodes.NO_SUCH_OBJECT + assert data.get("resultCode") == LDAPCodes.NO_SUCH_OBJECT diff --git a/tests/test_api/test_main/test_router/test_login.py b/tests/test_api/test_main/test_router/test_login.py index e86fef2b..0e77522a 100644 --- a/tests/test_api/test_main/test_router/test_login.py +++ b/tests/test_api/test_main/test_router/test_login.py @@ -12,7 +12,7 @@ @pytest.mark.asyncio -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("session") async def test_api_before_setup( unbound_http_client: httpx.AsyncClient) -> None: """Test api before setup.""" @@ -22,8 +22,8 @@ async def test_api_before_setup( @pytest.mark.asyncio -@pytest.mark.usefixtures('adding_test_user') -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("adding_test_user") +@pytest.mark.usefixtures("session") async def test_api_auth_after_change_account_exp( http_client: httpx.AsyncClient) -> None: """Test api auth.""" @@ -45,8 +45,8 @@ async def test_api_auth_after_change_account_exp( auth = await http_client.post( "auth/", data={ - "username": 'new_user@md.test', - "password": 'P@ssw0rd', + "username": "new_user@md.test", + "password": "P@ssw0rd", }) assert auth.status_code == status.HTTP_403_FORBIDDEN @@ -69,14 +69,14 @@ async def test_api_auth_after_change_account_exp( auth = await http_client.post( "auth/", data={ - "username": 'new_user@md.test', - "password": 'P@ssw0rd', + "username": "new_user@md.test", + "password": "P@ssw0rd", }) - assert auth.cookies.get('id') + assert auth.cookies.get("id") -@pytest.mark.usefixtures('setup_session') +@pytest.mark.usefixtures("setup_session") async def test_refresh_and_logout_flow( unbound_http_client: httpx.AsyncClient, creds: TestCreds) -> None: @@ -85,7 +85,7 @@ async def test_refresh_and_logout_flow( "auth/", data={"username": creds.un, "password": creds.pw}) - old_token = unbound_http_client.cookies.get('id') + old_token = unbound_http_client.cookies.get("id") assert old_token diff --git a/tests/test_api/test_main/test_router/test_modify.py b/tests/test_api/test_main/test_router/test_modify.py index e649d225..6c1f8978 100644 --- a/tests/test_api/test_main/test_router/test_modify.py +++ b/tests/test_api/test_main/test_router/test_modify.py @@ -11,12 +11,12 @@ @pytest.mark.asyncio -@pytest.mark.usefixtures('adding_test_user') -@pytest.mark.usefixtures('setup_session') -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("adding_test_user") +@pytest.mark.usefixtures("setup_session") +@pytest.mark.usefixtures("session") async def test_api_correct_modify(http_client: AsyncClient) -> None: """Test API for modify object attribute.""" - entry_dn = 'cn=test,dc=md,dc=test' + entry_dn = "cn=test,dc=md,dc=test" new_value = "133632677730000000" response = await http_client.patch( "/entry/update", @@ -37,7 +37,7 @@ async def test_api_correct_modify(http_client: AsyncClient) -> None: data = response.json() assert isinstance(data, dict) - assert data.get('resultCode') == LDAPCodes.SUCCESS + assert data.get("resultCode") == LDAPCodes.SUCCESS response = await http_client.post( "entry/search", @@ -56,21 +56,21 @@ async def test_api_correct_modify(http_client: AsyncClient) -> None: data = response.json() - assert data['resultCode'] == LDAPCodes.SUCCESS - assert data['search_result'][0]['object_name'] == entry_dn + assert data["resultCode"] == LDAPCodes.SUCCESS + assert data["search_result"][0]["object_name"] == entry_dn - for attr in data['search_result'][0]['partial_attributes']: - if attr['type'] == 'accountExpires': - assert attr['vals'][0] == new_value + for attr in data["search_result"][0]["partial_attributes"]: + if attr["type"] == "accountExpires": + assert attr["vals"][0] == new_value @pytest.mark.asyncio -@pytest.mark.usefixtures('adding_test_user') -@pytest.mark.usefixtures('setup_session') -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("adding_test_user") +@pytest.mark.usefixtures("setup_session") +@pytest.mark.usefixtures("session") async def test_api_modify_many(http_client: AsyncClient) -> None: """Test API for modify object attribute.""" - entry_dn = 'cn=test,dc=md,dc=test' + entry_dn = "cn=test,dc=md,dc=test" new_value = "133632677730000000" response = await http_client.patch( "/entry/update_many", @@ -106,7 +106,7 @@ async def test_api_modify_many(http_client: AsyncClient) -> None: assert isinstance(data, list) for result in data: - assert result.get('resultCode') == LDAPCodes.SUCCESS + assert result.get("resultCode") == LDAPCodes.SUCCESS response = await http_client.post( "entry/search", @@ -125,20 +125,20 @@ async def test_api_modify_many(http_client: AsyncClient) -> None: data = response.json() - assert data['resultCode'] == LDAPCodes.SUCCESS - assert data['search_result'][0]['object_name'] == entry_dn + assert data["resultCode"] == LDAPCodes.SUCCESS + assert data["search_result"][0]["object_name"] == entry_dn - for attr in data['search_result'][0]['partial_attributes']: - if attr['type'] == 'accountExpires': - assert attr['vals'][0] == new_value - if attr['type'] == 'testing_attr': - assert attr['vals'][0] == 'test1' + for attr in data["search_result"][0]["partial_attributes"]: + if attr["type"] == "accountExpires": + assert attr["vals"][0] == new_value + if attr["type"] == "testing_attr": + assert attr["vals"][0] == "test1" @pytest.mark.asyncio -@pytest.mark.usefixtures('adding_test_user') -@pytest.mark.usefixtures('setup_session') -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("adding_test_user") +@pytest.mark.usefixtures("setup_session") +@pytest.mark.usefixtures("session") async def test_api_modify_with_incorrect_dn(http_client: AsyncClient) -> None: """Test API for modify object attribute with incorrect DN.""" response = await http_client.patch( @@ -160,11 +160,11 @@ async def test_api_modify_with_incorrect_dn(http_client: AsyncClient) -> None: data = response.json() assert isinstance(data, dict) - assert data.get('resultCode') == LDAPCodes.INVALID_DN_SYNTAX + assert data.get("resultCode") == LDAPCodes.INVALID_DN_SYNTAX @pytest.mark.asyncio -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("session") async def test_api_modify_non_exist_object(http_client: AsyncClient) -> None: """Test API for modify object attribute with non-existen attribute.""" response = await http_client.patch( @@ -186,18 +186,18 @@ async def test_api_modify_non_exist_object(http_client: AsyncClient) -> None: data = response.json() assert isinstance(data, dict) - assert data.get('resultCode') == LDAPCodes.NO_SUCH_OBJECT + assert data.get("resultCode") == LDAPCodes.NO_SUCH_OBJECT @pytest.mark.asyncio -@pytest.mark.usefixtures('adding_test_user') -@pytest.mark.usefixtures('setup_session') -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("adding_test_user") +@pytest.mark.usefixtures("setup_session") +@pytest.mark.usefixtures("session") async def test_api_correct_modify_replace_memberof( http_client: AsyncClient) -> None: """Test API for modify object attribute.""" - user = 'cn=user1,ou=moscow,ou=russia,ou=users,dc=md,dc=test' - new_group = 'cn=domain admins,cn=groups,dc=md,dc=test' + user = "cn=user1,ou=moscow,ou=russia,ou=users,dc=md,dc=test" + new_group = "cn=domain admins,cn=groups,dc=md,dc=test" response = await http_client.patch( "/entry/update", json={ @@ -215,7 +215,7 @@ async def test_api_correct_modify_replace_memberof( ) data = response.json() - assert data['resultCode'] == LDAPCodes.SUCCESS + assert data["resultCode"] == LDAPCodes.SUCCESS response = await http_client.post( "entry/search", @@ -234,33 +234,33 @@ async def test_api_correct_modify_replace_memberof( data = response.json() - assert user == data['search_result'][0]['object_name'] + assert user == data["search_result"][0]["object_name"] - for attr in data['search_result'][0]['partial_attributes']: - if attr['type'] == 'memberOf': - assert attr['vals'] == [new_group] + for attr in data["search_result"][0]["partial_attributes"]: + if attr["type"] == "memberOf": + assert attr["vals"] == [new_group] break else: - raise Exception('No groups') + raise Exception("No groups") @pytest.mark.asyncio -@pytest.mark.usefixtures('adding_test_user') -@pytest.mark.usefixtures('setup_session') -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("adding_test_user") +@pytest.mark.usefixtures("setup_session") +@pytest.mark.usefixtures("session") async def test_api_modify_add_loop_detect_member( http_client: AsyncClient) -> None: """Test API for modify object attribute.""" response = await http_client.patch( "/entry/update", json={ - "object": 'cn=developers,cn=groups,dc=md,dc=test', + "object": "cn=developers,cn=groups,dc=md,dc=test", "changes": [ { "operation": Operation.ADD, "modification": { "type": "member", - "vals": ['cn=user0,ou=users,dc=md,dc=test'], + "vals": ["cn=user0,ou=users,dc=md,dc=test"], }, }, ], @@ -268,26 +268,26 @@ async def test_api_modify_add_loop_detect_member( ) data = response.json() - assert data['resultCode'] == LDAPCodes.LOOP_DETECT + assert data["resultCode"] == LDAPCodes.LOOP_DETECT @pytest.mark.asyncio -@pytest.mark.usefixtures('adding_test_user') -@pytest.mark.usefixtures('setup_session') -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("adding_test_user") +@pytest.mark.usefixtures("setup_session") +@pytest.mark.usefixtures("session") async def test_api_modify_add_loop_detect_memberof( http_client: AsyncClient) -> None: """Test API for modify object attribute.""" response = await http_client.patch( "/entry/update", json={ - "object": 'cn=user0,ou=users,dc=md,dc=test', + "object": "cn=user0,ou=users,dc=md,dc=test", "changes": [ { "operation": Operation.ADD, "modification": { "type": "memberOf", - "vals": ['cn=developers,cn=groups,dc=md,dc=test'], + "vals": ["cn=developers,cn=groups,dc=md,dc=test"], }, }, ], @@ -295,27 +295,27 @@ async def test_api_modify_add_loop_detect_memberof( ) data = response.json() - assert data['resultCode'] == LDAPCodes.LOOP_DETECT + assert data["resultCode"] == LDAPCodes.LOOP_DETECT @pytest.mark.asyncio -@pytest.mark.usefixtures('adding_test_user') -@pytest.mark.usefixtures('setup_session') -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("adding_test_user") +@pytest.mark.usefixtures("setup_session") +@pytest.mark.usefixtures("session") async def test_api_modify_replace_loop_detect_member( http_client: AsyncClient) -> None: """Test API for modify object attribute.""" response = await http_client.patch( "/entry/update", json={ - "object": 'cn=developers,cn=groups,dc=md,dc=test', + "object": "cn=developers,cn=groups,dc=md,dc=test", "changes": [ { "operation": Operation.REPLACE, "modification": { "type": "member", "vals": [ - 'cn=user0,ou=users,dc=md,dc=test', + "cn=user0,ou=users,dc=md,dc=test", 'cn=user1,ou=moscow,ou=russia,ou=users,dc=md,dc=test', # noqa ], }, @@ -325,28 +325,28 @@ async def test_api_modify_replace_loop_detect_member( ) data = response.json() - assert data['resultCode'] == LDAPCodes.LOOP_DETECT + assert data["resultCode"] == LDAPCodes.LOOP_DETECT @pytest.mark.asyncio -@pytest.mark.usefixtures('adding_test_user') -@pytest.mark.usefixtures('setup_session') -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("adding_test_user") +@pytest.mark.usefixtures("setup_session") +@pytest.mark.usefixtures("session") async def test_api_modify_replace_loop_detect_memberof( http_client: AsyncClient) -> None: """Test API for modify object attribute.""" response = await http_client.patch( "/entry/update", json={ - "object": 'cn=user0,ou=users,dc=md,dc=test', + "object": "cn=user0,ou=users,dc=md,dc=test", "changes": [ { "operation": Operation.REPLACE, "modification": { "type": "memberOf", "vals": [ - 'cn=developers,cn=groups,dc=md,dc=test', - 'cn=domain admins,cn=groups,dc=md,dc=test', + "cn=developers,cn=groups,dc=md,dc=test", + "cn=domain admins,cn=groups,dc=md,dc=test", ], }, }, @@ -355,24 +355,24 @@ async def test_api_modify_replace_loop_detect_memberof( ) data = response.json() - assert data['resultCode'] == LDAPCodes.LOOP_DETECT + assert data["resultCode"] == LDAPCodes.LOOP_DETECT @pytest.mark.asyncio -@pytest.mark.usefixtures('adding_test_user') -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("adding_test_user") +@pytest.mark.usefixtures("session") async def test_api_modify_incorrect_uac(http_client: AsyncClient) -> None: """Test API for modify object attribute.""" response = await http_client.patch( "/entry/update", json={ - "object": 'cn=user0,ou=users,dc=md,dc=test', + "object": "cn=user0,ou=users,dc=md,dc=test", "changes": [ { "operation": Operation.REPLACE, "modification": { "type": "userAccountControl", - "vals": ['string'], + "vals": ["string"], }, }, ], @@ -380,4 +380,4 @@ async def test_api_modify_incorrect_uac(http_client: AsyncClient) -> None: ) data = response.json() - assert data['resultCode'] == LDAPCodes.UNDEFINED_ATTRIBUTE_TYPE + assert data["resultCode"] == LDAPCodes.UNDEFINED_ATTRIBUTE_TYPE diff --git a/tests/test_api/test_main/test_router/test_modify_dn.py b/tests/test_api/test_main/test_router/test_modify_dn.py index aa6ea23e..77d90b16 100644 --- a/tests/test_api/test_main/test_router/test_modify_dn.py +++ b/tests/test_api/test_main/test_router/test_modify_dn.py @@ -10,20 +10,20 @@ @pytest.mark.asyncio -@pytest.mark.usefixtures('adding_test_user') -@pytest.mark.usefixtures('setup_session') -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("adding_test_user") +@pytest.mark.usefixtures("setup_session") +@pytest.mark.usefixtures("session") async def test_api_correct_update_dn(http_client: AsyncClient) -> None: """Test API for update DN.""" root_dn = "ou=moscow,ou=russia,ou=users,dc=md,dc=test" old_user_dn = "cn=user1," + root_dn newrdn_user = "cn=new_test2" - new_user_dn = ','.join((newrdn_user, root_dn)) + new_user_dn = ",".join((newrdn_user, root_dn)) old_group_dn = "cn=developers,cn=groups,dc=md,dc=test" new_group_dn = "cn=new_developers,cn=groups,dc=md,dc=test" - newrdn_group, new_superior_group = new_group_dn.split(',', maxsplit=1) + newrdn_group, new_superior_group = new_group_dn.split(",", maxsplit=1) response = await http_client.put( "/entry/update/dn", @@ -38,7 +38,7 @@ async def test_api_correct_update_dn(http_client: AsyncClient) -> None: data = response.json() assert isinstance(data, dict) - assert data.get('resultCode') == LDAPCodes.SUCCESS + assert data.get("resultCode") == LDAPCodes.SUCCESS response = await http_client.post( "entry/search", @@ -50,11 +50,11 @@ async def test_api_correct_update_dn(http_client: AsyncClient) -> None: "time_limit": 10, "types_only": False, "filter": "(objectClass=*)", - "attributes": ['*'], + "attributes": ["*"], }, ) data = response.json() - assert data['search_result'][0]['object_name'] == new_user_dn + assert data["search_result"][0]["object_name"] == new_user_dn response = await http_client.put( "/entry/update/dn", @@ -69,7 +69,7 @@ async def test_api_correct_update_dn(http_client: AsyncClient) -> None: data = response.json() assert isinstance(data, dict) - assert data.get('resultCode') == LDAPCodes.SUCCESS + assert data.get("resultCode") == LDAPCodes.SUCCESS response = await http_client.post( "entry/search", @@ -81,32 +81,32 @@ async def test_api_correct_update_dn(http_client: AsyncClient) -> None: "time_limit": 0, "types_only": False, "filter": "(objectClass=*)", - "attributes": ['memberOf'], + "attributes": ["memberOf"], }, ) data = response.json() - assert new_user_dn == data['search_result'][0]['object_name'] + assert new_user_dn == data["search_result"][0]["object_name"] - for attr in data['search_result'][0]['partial_attributes']: - if attr['type'] == 'memberOf': - assert attr['vals'][0] == new_group_dn + for attr in data["search_result"][0]["partial_attributes"]: + if attr["type"] == "memberOf": + assert attr["vals"][0] == new_group_dn break else: - raise Exception('Groups not found') + raise Exception("Groups not found") @pytest.mark.asyncio -@pytest.mark.usefixtures('adding_test_user') -@pytest.mark.usefixtures('setup_session') -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("adding_test_user") +@pytest.mark.usefixtures("setup_session") +@pytest.mark.usefixtures("session") async def test_api_update_dn_with_parent(http_client: AsyncClient) -> None: """Test API for update DN.""" old_user_dn = "cn=user1,ou=moscow,ou=russia,ou=users,dc=md,dc=test" new_user_dn = "cn=new_test2,ou=users,dc=md,dc=test" groups_user = None - newrdn_user, new_superior = new_user_dn.split(',', maxsplit=1) + newrdn_user, new_superior = new_user_dn.split(",", maxsplit=1) response = await http_client.post( "entry/search", @@ -118,18 +118,18 @@ async def test_api_update_dn_with_parent(http_client: AsyncClient) -> None: "time_limit": 0, "types_only": False, "filter": "(objectClass=*)", - "attributes": ['*'], + "attributes": ["*"], }, ) data = response.json() - assert data.get('resultCode') == LDAPCodes.SUCCESS - assert old_user_dn == data['search_result'][0]['object_name'] + assert data.get("resultCode") == LDAPCodes.SUCCESS + assert old_user_dn == data["search_result"][0]["object_name"] - for attr in data['search_result'][0]['partial_attributes']: - if attr['type'] == 'memberOf': - groups_user = attr['vals'] + for attr in data["search_result"][0]["partial_attributes"]: + if attr["type"] == "memberOf": + groups_user = attr["vals"] assert groups_user @@ -145,7 +145,7 @@ async def test_api_update_dn_with_parent(http_client: AsyncClient) -> None: data = response.json() - assert data.get('resultCode') == LDAPCodes.SUCCESS + assert data.get("resultCode") == LDAPCodes.SUCCESS response = await http_client.post( "entry/search", @@ -157,26 +157,26 @@ async def test_api_update_dn_with_parent(http_client: AsyncClient) -> None: "time_limit": 0, "types_only": False, "filter": "(objectClass=*)", - "attributes": ['*'], + "attributes": ["*"], }, ) data = response.json() - assert data.get('resultCode') == LDAPCodes.SUCCESS - assert new_user_dn == data['search_result'][0]['object_name'] + assert data.get("resultCode") == LDAPCodes.SUCCESS + assert new_user_dn == data["search_result"][0]["object_name"] - for attr in data['search_result'][0]['partial_attributes']: - if attr['type'] == 'memberOf': - assert groups_user == attr['vals'] + for attr in data["search_result"][0]["partial_attributes"]: + if attr["type"] == "memberOf": + assert groups_user == attr["vals"] break else: - raise Exception('Groups not found') + raise Exception("Groups not found") @pytest.mark.asyncio -@pytest.mark.usefixtures('setup_session') -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("setup_session") +@pytest.mark.usefixtures("session") async def test_api_update_dn_non_auth_user(http_client: AsyncClient) -> None: """Test API update dn for unauthorized user.""" http_client.cookies.clear() @@ -192,13 +192,13 @@ async def test_api_update_dn_non_auth_user(http_client: AsyncClient) -> None: data = response.json() assert response.status_code == 401 - assert data.get('detail') == 'Could not validate credentials' + assert data.get("detail") == "Could not validate credentials" @pytest.mark.asyncio -@pytest.mark.usefixtures('adding_test_user') -@pytest.mark.usefixtures('setup_session') -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("adding_test_user") +@pytest.mark.usefixtures("setup_session") +@pytest.mark.usefixtures("session") async def test_api_update_dn_non_exist_superior( http_client: AsyncClient) -> None: """Test API update dn with non-existen new_superior.""" @@ -215,13 +215,13 @@ async def test_api_update_dn_non_exist_superior( data = response.json() assert isinstance(data, dict) - assert data.get('resultCode') == LDAPCodes.NO_SUCH_OBJECT + assert data.get("resultCode") == LDAPCodes.NO_SUCH_OBJECT @pytest.mark.asyncio -@pytest.mark.usefixtures('adding_test_user') -@pytest.mark.usefixtures('setup_session') -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("adding_test_user") +@pytest.mark.usefixtures("setup_session") +@pytest.mark.usefixtures("session") async def test_api_update_dn_non_exist_entry(http_client: AsyncClient) -> None: """Test API update dn with non-existen entry.""" response = await http_client.put( @@ -237,13 +237,13 @@ async def test_api_update_dn_non_exist_entry(http_client: AsyncClient) -> None: data = response.json() assert isinstance(data, dict) - assert data.get('resultCode') == LDAPCodes.NO_SUCH_OBJECT + assert data.get("resultCode") == LDAPCodes.NO_SUCH_OBJECT @pytest.mark.asyncio -@pytest.mark.usefixtures('adding_test_user') -@pytest.mark.usefixtures('setup_session') -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("adding_test_user") +@pytest.mark.usefixtures("setup_session") +@pytest.mark.usefixtures("session") async def test_api_update_dn_invalid_entry(http_client: AsyncClient) -> None: """Test API update dn with invalid entry.""" response = await http_client.put( @@ -259,13 +259,13 @@ async def test_api_update_dn_invalid_entry(http_client: AsyncClient) -> None: data = response.json() assert isinstance(data, dict) - assert data.get('resultCode') == LDAPCodes.INVALID_DN_SYNTAX + assert data.get("resultCode") == LDAPCodes.INVALID_DN_SYNTAX @pytest.mark.asyncio -@pytest.mark.usefixtures('adding_test_user') -@pytest.mark.usefixtures('setup_session') -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("adding_test_user") +@pytest.mark.usefixtures("setup_session") +@pytest.mark.usefixtures("session") async def test_api_update_dn_invalid_new_superior( http_client: AsyncClient) -> None: """Test API update dn with invalid new_superior.""" @@ -282,4 +282,4 @@ async def test_api_update_dn_invalid_new_superior( data = response.json() assert isinstance(data, dict) - assert data.get('resultCode') == LDAPCodes.INVALID_DN_SYNTAX + assert data.get("resultCode") == LDAPCodes.INVALID_DN_SYNTAX diff --git a/tests/test_api/test_main/test_router/test_search.py b/tests/test_api/test_main/test_router/test_search.py index 5590866c..1659602c 100644 --- a/tests/test_api/test_main/test_router/test_search.py +++ b/tests/test_api/test_main/test_router/test_search.py @@ -12,7 +12,7 @@ @pytest.mark.asyncio -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("session") async def test_api_root_dse(http_client: AsyncClient) -> None: """Test api root dse.""" response = await http_client.post( @@ -33,26 +33,26 @@ async def test_api_root_dse(http_client: AsyncClient) -> None: data = response.json() attrs = sorted( - data['search_result'][0]['partial_attributes'], - key=lambda x: x['type'], + data["search_result"][0]["partial_attributes"], + key=lambda x: x["type"], ) - aquired_attrs = [attr['type'] for attr in attrs] + aquired_attrs = [attr["type"] for attr in attrs] root_attrs = [ - 'LDAPServiceName', 'currentTime', - 'defaultNamingContext', 'dnsHostName', - 'domainFunctionality', 'dsServiceName', - 'highestCommittedUSN', 'namingContexts', - 'rootDomainNamingContext', 'vendorVersion', - 'schemaNamingContext', 'serverName', - 'serviceName', 'subschemaSubentry', - 'supportedCapabilities', 'supportedControl', - 'supportedLDAPPolicies', 'supportedLDAPVersion', - 'supportedSASLMechanisms', 'vendorName', + "LDAPServiceName", "currentTime", + "defaultNamingContext", "dnsHostName", + "domainFunctionality", "dsServiceName", + "highestCommittedUSN", "namingContexts", + "rootDomainNamingContext", "vendorVersion", + "schemaNamingContext", "serverName", + "serviceName", "subschemaSubentry", + "supportedCapabilities", "supportedControl", + "supportedLDAPPolicies", "supportedLDAPVersion", + "supportedSASLMechanisms", "vendorName", ] - assert data['search_result'][0]['object_name'] == "" + assert data["search_result"][0]["object_name"] == "" assert all( attr in aquired_attrs for attr in root_attrs @@ -60,7 +60,7 @@ async def test_api_root_dse(http_client: AsyncClient) -> None: @pytest.mark.asyncio -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("session") async def test_api_search(http_client: AsyncClient) -> None: """Test api search.""" raw_response = await http_client.post( @@ -80,23 +80,23 @@ async def test_api_search(http_client: AsyncClient) -> None: response = raw_response.json() - assert response['resultCode'] == LDAPCodes.SUCCESS + assert response["resultCode"] == LDAPCodes.SUCCESS sub_dirs = [ "cn=groups,dc=md,dc=test", "ou=users,dc=md,dc=test", ] assert all( - obj['object_name'] in sub_dirs - for obj in response['search_result'] + obj["object_name"] in sub_dirs + for obj in response["search_result"] ) @pytest.mark.asyncio -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("session") async def test_api_search_filter_memberof(http_client: AsyncClient) -> None: """Test api search.""" - member = 'cn=user1,ou=moscow,ou=russia,ou=users,dc=md,dc=test' + member = "cn=user1,ou=moscow,ou=russia,ou=users,dc=md,dc=test" raw_response = await http_client.post( "entry/search", json={ @@ -114,16 +114,16 @@ async def test_api_search_filter_memberof(http_client: AsyncClient) -> None: response = raw_response.json() - assert response['resultCode'] == LDAPCodes.SUCCESS - assert response['search_result'][0]['object_name'] == member + assert response["resultCode"] == LDAPCodes.SUCCESS + assert response["search_result"][0]["object_name"] == member @pytest.mark.asyncio -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("session") async def test_api_search_filter_member(http_client: AsyncClient) -> None: """Test api search.""" - member = 'cn=user1,ou=moscow,ou=russia,ou=users,dc=md,dc=test' - group = 'cn=developers,cn=groups,dc=md,dc=test' + member = "cn=user1,ou=moscow,ou=russia,ou=users,dc=md,dc=test" + group = "cn=developers,cn=groups,dc=md,dc=test" raw_response = await http_client.post( "entry/search", json={ @@ -141,12 +141,12 @@ async def test_api_search_filter_member(http_client: AsyncClient) -> None: response = raw_response.json() - assert response['resultCode'] == LDAPCodes.SUCCESS - assert response['search_result'][0]['object_name'] == group + assert response["resultCode"] == LDAPCodes.SUCCESS + assert response["search_result"][0]["object_name"] == group @pytest.mark.asyncio -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("session") async def test_api_search_filter_objectguid(http_client: AsyncClient) -> None: """Test api search.""" raw_response = await http_client.post( @@ -166,14 +166,14 @@ async def test_api_search_filter_objectguid(http_client: AsyncClient) -> None: data = raw_response.json() hex_guid = None - entry_dn = data['search_result'][3]['object_name'] + entry_dn = data["search_result"][3]["object_name"] - for attr in data['search_result'][3]['partial_attributes']: - if attr['type'] == 'objectGUID': - hex_guid = attr['vals'][0] + for attr in data["search_result"][3]["partial_attributes"]: + if attr["type"] == "objectGUID": + hex_guid = attr["vals"][0] break - assert hex_guid is not None, 'objectGUID attribute is missing' + assert hex_guid is not None, "objectGUID attribute is missing" object_guid = str(uuid.UUID(bytes_le=bytes(bytearray.fromhex(hex_guid)))) @@ -193,12 +193,12 @@ async def test_api_search_filter_objectguid(http_client: AsyncClient) -> None: ) data = raw_response.json() - assert data['search_result'][0]['object_name'] == entry_dn, \ + assert data["search_result"][0]["object_name"] == entry_dn, \ "User with required objectGUID not found" @pytest.mark.asyncio -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("session") async def test_api_search_complex_filter(http_client: AsyncClient) -> None: """Test api search.""" user = "cn=user1,ou=moscow,ou=russia,ou=users,dc=md,dc=test" @@ -232,11 +232,11 @@ async def test_api_search_complex_filter(http_client: AsyncClient) -> None: }, ) data = raw_response.json() - assert data['search_result'][0]['object_name'] == user + assert data["search_result"][0]["object_name"] == user @pytest.mark.asyncio -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("session") async def test_api_search_recursive_memberof(http_client: AsyncClient) -> None: """Test api search.""" group = "cn=domain admins,cn=groups,dc=md,dc=test" @@ -260,15 +260,15 @@ async def test_api_search_recursive_memberof(http_client: AsyncClient) -> None: }, ) data = response.json() - assert len(data['search_result']) == len(members) + assert len(data["search_result"]) == len(members) assert all( - obj['object_name'] in members - for obj in data['search_result'] + obj["object_name"] in members + for obj in data["search_result"] ) @pytest.mark.asyncio -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("session") async def test_api_bytes_to_hex(http_client: AsyncClient) -> None: """Test api search.""" raw_response = await http_client.post( @@ -288,8 +288,8 @@ async def test_api_bytes_to_hex(http_client: AsyncClient) -> None: response = raw_response.json() - assert response['resultCode'] == LDAPCodes.SUCCESS + assert response["resultCode"] == LDAPCodes.SUCCESS - for attr in response['search_result'][0]['partial_attributes']: - if attr['type'] == 'attr_with_bvalue': - assert attr['vals'][0] == b"any".hex() + for attr in response["search_result"][0]["partial_attributes"]: + if attr["type"] == "attr_with_bvalue": + assert attr["vals"][0] == b"any".hex() diff --git a/tests/test_api/test_network/test_router.py b/tests/test_api/test_network/test_router.py index e2aaa36e..e1a01c9c 100644 --- a/tests/test_api/test_network/test_router.py +++ b/tests/test_api/test_network/test_router.py @@ -16,23 +16,23 @@ @pytest.mark.asyncio -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("session") async def test_add_policy( http_client: AsyncClient) -> None: """Test api policy add and read.""" compare_netmasks = [ - '127.0.0.1/32', '172.0.0.2/31', '172.0.0.4/30', - '172.0.0.8/29', '172.0.0.16/28', '172.0.0.32/27', - '172.0.0.64/26', '172.0.0.128/25', '172.0.1.0/24', - '172.0.2.0/23', '172.0.4.0/22', '172.0.8.0/21', - '172.0.16.0/20', '172.0.32.0/19', '172.0.64.0/18', - '172.0.128.0/17', '172.1.0.0/16', '172.2.0.0/15', - '172.4.0.0/14', '172.8.0.0/13', '172.16.0.0/12', - '172.32.0.0/11', '172.64.0.0/10', '172.128.0.0/10', - '172.192.0.0/11', '172.224.0.0/12', '172.240.0.0/13', - '172.248.0.0/14', '172.252.0.0/15', '172.254.0.0/16', - '172.255.0.0/24', '172.255.1.0/30', '172.255.1.4/31', - '172.8.4.0/24', + "127.0.0.1/32", "172.0.0.2/31", "172.0.0.4/30", + "172.0.0.8/29", "172.0.0.16/28", "172.0.0.32/27", + "172.0.0.64/26", "172.0.0.128/25", "172.0.1.0/24", + "172.0.2.0/23", "172.0.4.0/22", "172.0.8.0/21", + "172.0.16.0/20", "172.0.32.0/19", "172.0.64.0/18", + "172.0.128.0/17", "172.1.0.0/16", "172.2.0.0/15", + "172.4.0.0/14", "172.8.0.0/13", "172.16.0.0/12", + "172.32.0.0/11", "172.64.0.0/10", "172.128.0.0/10", + "172.192.0.0/11", "172.224.0.0/12", "172.240.0.0/13", + "172.248.0.0/14", "172.252.0.0/15", "172.254.0.0/16", + "172.255.0.0/24", "172.255.1.0/30", "172.255.1.4/31", + "172.8.4.0/24", ] raw_netmasks = [ @@ -45,12 +45,12 @@ async def test_add_policy( "name": "local seriveses", "netmasks": raw_netmasks, "priority": 2, - 'groups': ['cn=domain admins,cn=groups,dc=md,dc=test'], - 'is_http': True, - 'is_ldap': True, - 'is_kerberos': True, - 'bypass_no_connection': False, - 'bypass_service_failure': False, + "groups": ["cn=domain admins,cn=groups,dc=md,dc=test"], + "is_http": True, + "is_ldap": True, + "is_kerberos": True, + "bypass_no_connection": False, + "bypass_service_failure": False, }) assert raw_response.status_code == 201 @@ -61,120 +61,120 @@ async def test_add_policy( response = raw_response.json() for pol in response: - pol.pop('id') + pol.pop("id") assert response == [ { - 'enabled': True, - 'name': 'Default open policy', - 'netmasks': ['0.0.0.0/0'], - 'raw': ['0.0.0.0/0'], - 'groups': [], - 'priority': 1, - 'mfa_groups': [], - 'mfa_status': 0, - 'is_http': True, - 'is_ldap': True, - 'is_kerberos': True, - 'bypass_no_connection': False, - 'bypass_service_failure': False, + "enabled": True, + "name": "Default open policy", + "netmasks": ["0.0.0.0/0"], + "raw": ["0.0.0.0/0"], + "groups": [], + "priority": 1, + "mfa_groups": [], + "mfa_status": 0, + "is_http": True, + "is_ldap": True, + "is_kerberos": True, + "bypass_no_connection": False, + "bypass_service_failure": False, }, { - 'enabled': True, - 'name': 'local seriveses', - 'netmasks': compare_netmasks, - 'raw': raw_netmasks, - 'groups': ['cn=domain admins,cn=groups,dc=md,dc=test'], - 'priority': 2, - 'mfa_groups': [], - 'mfa_status': 0, - 'is_http': True, - 'is_ldap': True, - 'is_kerberos': True, - 'bypass_no_connection': False, - 'bypass_service_failure': False, + "enabled": True, + "name": "local seriveses", + "netmasks": compare_netmasks, + "raw": raw_netmasks, + "groups": ["cn=domain admins,cn=groups,dc=md,dc=test"], + "priority": 2, + "mfa_groups": [], + "mfa_status": 0, + "is_http": True, + "is_ldap": True, + "is_kerberos": True, + "bypass_no_connection": False, + "bypass_service_failure": False, }, ] @pytest.mark.asyncio -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("session") async def test_update_policy(http_client: AsyncClient) -> None: """Update policy.""" raw_response = await http_client.get("/policy") assert raw_response.status_code == status.HTTP_200_OK response = raw_response.json() - pol_id = response[0].pop('id') + pol_id = response[0].pop("id") assert response == [ { - 'enabled': True, - 'name': 'Default open policy', - 'netmasks': ['0.0.0.0/0'], - 'raw': ['0.0.0.0/0'], - 'priority': 1, - 'mfa_groups': [], - 'mfa_status': 0, - 'groups': [], - 'is_http': True, - 'is_ldap': True, - 'is_kerberos': True, - 'bypass_no_connection': False, - 'bypass_service_failure': False, + "enabled": True, + "name": "Default open policy", + "netmasks": ["0.0.0.0/0"], + "raw": ["0.0.0.0/0"], + "priority": 1, + "mfa_groups": [], + "mfa_status": 0, + "groups": [], + "is_http": True, + "is_ldap": True, + "is_kerberos": True, + "bypass_no_connection": False, + "bypass_service_failure": False, }, ] response = await http_client.put( "/policy", json={ - 'id': pol_id, - 'groups': ['cn=domain admins,cn=groups,dc=md,dc=test'], - 'name': 'Default open policy 2', + "id": pol_id, + "groups": ["cn=domain admins,cn=groups,dc=md,dc=test"], + "name": "Default open policy 2", }) assert response.status_code == status.HTTP_200_OK response = response.json() - response.pop('id') + response.pop("id") assert response == { - 'enabled': True, - 'name': 'Default open policy 2', - 'netmasks': ['0.0.0.0/0'], - 'raw': ['0.0.0.0/0'], - 'groups': ['cn=domain admins,cn=groups,dc=md,dc=test'], - 'mfa_groups': [], - 'mfa_status': 0, - 'priority': 1, - 'is_http': True, - 'is_ldap': True, - 'is_kerberos': True, - 'bypass_no_connection': False, - 'bypass_service_failure': False, + "enabled": True, + "name": "Default open policy 2", + "netmasks": ["0.0.0.0/0"], + "raw": ["0.0.0.0/0"], + "groups": ["cn=domain admins,cn=groups,dc=md,dc=test"], + "mfa_groups": [], + "mfa_status": 0, + "priority": 1, + "is_http": True, + "is_ldap": True, + "is_kerberos": True, + "bypass_no_connection": False, + "bypass_service_failure": False, } response = await http_client.get("/policy") assert response.status_code == status.HTTP_200_OK response = response.json() - response[0].pop('id') + response[0].pop("id") assert response == [ { - 'enabled': True, - 'name': 'Default open policy 2', - 'netmasks': ['0.0.0.0/0'], - 'raw': ['0.0.0.0/0'], - 'mfa_groups': [], - 'mfa_status': 0, - 'priority': 1, - 'groups': ['cn=domain admins,cn=groups,dc=md,dc=test'], - 'is_http': True, - 'is_ldap': True, - 'is_kerberos': True, - 'bypass_no_connection': False, - 'bypass_service_failure': False, + "enabled": True, + "name": "Default open policy 2", + "netmasks": ["0.0.0.0/0"], + "raw": ["0.0.0.0/0"], + "mfa_groups": [], + "mfa_status": 0, + "priority": 1, + "groups": ["cn=domain admins,cn=groups,dc=md,dc=test"], + "is_http": True, + "is_ldap": True, + "is_kerberos": True, + "bypass_no_connection": False, + "bypass_service_failure": False, }, ] @@ -186,9 +186,9 @@ async def test_delete_policy( ) -> None: """Delete policy.""" session.add(NetworkPolicy( - name='Local policy', - netmasks=[IPv4Network('127.100.10.5/32')], - raw=['127.100.10.5/32'], + name="Local policy", + netmasks=[IPv4Network("127.100.10.5/32")], + raw=["127.100.10.5/32"], enabled=True, priority=2, )) @@ -198,40 +198,40 @@ async def test_delete_policy( assert raw_response.status_code == status.HTTP_200_OK response = raw_response.json() - pol_id = response[0].pop('id') - pol_id2 = response[1].pop('id') + pol_id = response[0].pop("id") + pol_id2 = response[1].pop("id") assert response[0] == { - 'enabled': True, - 'name': 'Default open policy', - 'netmasks': ['0.0.0.0/0'], - 'raw': ['0.0.0.0/0'], - 'groups': [], - 'mfa_groups': [], - 'mfa_status': 0, - 'priority': 1, - 'is_http': True, - 'is_ldap': True, - 'is_kerberos': True, - 'bypass_no_connection': False, - 'bypass_service_failure': False, + "enabled": True, + "name": "Default open policy", + "netmasks": ["0.0.0.0/0"], + "raw": ["0.0.0.0/0"], + "groups": [], + "mfa_groups": [], + "mfa_status": 0, + "priority": 1, + "is_http": True, + "is_ldap": True, + "is_kerberos": True, + "bypass_no_connection": False, + "bypass_service_failure": False, } response = await http_client.delete( f"/policy/{pol_id}", follow_redirects=False) assert response.status_code == 303 - assert response.next_request.url.path == '/api/policy' + assert response.next_request.url.path == "/api/policy" response = await http_client.get("/policy") assert response.status_code == status.HTTP_200_OK response = response.json() assert len(response) == 1 - assert response[0]['name'] == "Local policy" - assert response[0]['priority'] == 1 + assert response[0]["name"] == "Local policy" + assert response[0]["priority"] == 1 response = await http_client.delete(f"/policy/{pol_id2}") assert response.status_code == 422 - assert response.json()['detail'] == "At least one policy should be active" + assert response.json()["detail"] == "At least one policy should be active" @pytest.mark.asyncio @@ -241,9 +241,9 @@ async def test_switch_policy( ) -> None: """Switch policy.""" session.add(NetworkPolicy( - name='Local policy', - netmasks=[IPv4Network('127.100.10.5/32')], - raw=['127.100.10.5/32'], + name="Local policy", + netmasks=[IPv4Network("127.100.10.5/32")], + raw=["127.100.10.5/32"], enabled=True, priority=2, )) @@ -253,23 +253,23 @@ async def test_switch_policy( assert raw_response.status_code == status.HTTP_200_OK response = raw_response.json() - pol_id = response[0].pop('id') - pol_id2 = response[1].pop('id') + pol_id = response[0].pop("id") + pol_id2 = response[1].pop("id") assert response[0] == { - 'enabled': True, - 'name': 'Default open policy', - 'netmasks': ['0.0.0.0/0'], - 'raw': ['0.0.0.0/0'], - 'groups': [], - 'mfa_groups': [], - 'mfa_status': 0, - 'priority': 1, - 'is_http': True, - 'is_ldap': True, - 'is_kerberos': True, - 'bypass_no_connection': False, - 'bypass_service_failure': False, + "enabled": True, + "name": "Default open policy", + "netmasks": ["0.0.0.0/0"], + "raw": ["0.0.0.0/0"], + "groups": [], + "mfa_groups": [], + "mfa_status": 0, + "priority": 1, + "is_http": True, + "is_ldap": True, + "is_kerberos": True, + "bypass_no_connection": False, + "bypass_service_failure": False, } response = await http_client.patch( @@ -279,20 +279,20 @@ async def test_switch_policy( assert response.json() is True response = await http_client.get("/policy") - assert response.json()[0]['enabled'] is False + assert response.json()[0]["enabled"] is False response = await http_client.patch(f"/policy/{pol_id2}") assert response.status_code == 422 - assert response.json()['detail'] == "At least one policy should be active" + assert response.json()["detail"] == "At least one policy should be active" @pytest.mark.asyncio -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("session") async def test_404(http_client: AsyncClient) -> None: """Delete policy.""" response = await http_client.get("/policy") assert response.status_code == status.HTTP_200_OK - some_id = response.json()[0]['id'] + 1 + some_id = response.json()[0]["id"] + 1 response = await http_client.delete( f"/policy/{some_id}", @@ -307,15 +307,15 @@ async def test_404(http_client: AsyncClient) -> None: response = await http_client.put( "/policy", json={ - 'id': some_id, - "name": '123', + "id": some_id, + "name": "123", }, ) assert response.status_code == 404 @pytest.mark.asyncio -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("session") async def test_swap(http_client: AsyncClient) -> None: """Test swap policies.""" raw_response = await http_client.post( @@ -331,42 +331,42 @@ async def test_swap(http_client: AsyncClient) -> None: "172.8.4.0/24", ], "priority": 2, - 'groups': ['cn=domain admins,cn=groups,dc=md,dc=test'], - 'is_http': True, - 'is_ldap': True, - 'is_kerberos': True, - 'bypass_no_connection': False, - 'bypass_service_failure': False, + "groups": ["cn=domain admins,cn=groups,dc=md,dc=test"], + "is_http": True, + "is_ldap": True, + "is_kerberos": True, + "bypass_no_connection": False, + "bypass_service_failure": False, }, ) raw_get_response = await http_client.get("/policy") get_response = raw_get_response.json() - assert get_response[0]['priority'] == 1 - assert get_response[0]['name'] == "Default open policy" - assert get_response[1]['priority'] == 2 + assert get_response[0]["priority"] == 1 + assert get_response[0]["name"] == "Default open policy" + assert get_response[1]["priority"] == 2 swap_response = await http_client.post( "/policy/swap", json={ - 'first_policy_id': get_response[0]['id'], - 'second_policy_id': get_response[1]['id'], + "first_policy_id": get_response[0]["id"], + "second_policy_id": get_response[1]["id"], }, ) assert swap_response.json() == { - "first_policy_id": get_response[0]['id'], + "first_policy_id": get_response[0]["id"], "first_policy_priority": 2, - "second_policy_id": get_response[1]['id'], + "second_policy_id": get_response[1]["id"], "second_policy_priority": 1, } raw_response = await http_client.get("/policy") response = raw_response.json() - assert response[0]['priority'] == 1 - assert response[0]['groups'] == [ - 'cn=domain admins,cn=groups,dc=md,dc=test'] - assert response[1]['priority'] == 2 - assert response[1]['name'] == "Default open policy" + assert response[0]["priority"] == 1 + assert response[0]["groups"] == [ + "cn=domain admins,cn=groups,dc=md,dc=test"] + assert response[1]["priority"] == 2 + assert response[1]["name"] == "Default open policy" diff --git a/tests/test_api/test_shadow/conftest.py b/tests/test_api/test_shadow/conftest.py index 1e705a52..ebae802b 100644 --- a/tests/test_api/test_shadow/conftest.py +++ b/tests/test_api/test_shadow/conftest.py @@ -20,6 +20,7 @@ class ProxyRequestModel(BaseModel): Attributes: principal: Unique user identifier ip: IP address from which the request is made + """ principal: str @@ -30,13 +31,13 @@ class ProxyRequestModel(BaseModel): async def adding_mfa_keys(session: AsyncSession) -> None: """Test add user like keycloak.""" session.add( - CatalogueSetting(name='mfa_secret', value='123'), + CatalogueSetting(name="mfa_secret", value="123"), ) - session.add(CatalogueSetting(name='mfa_key', value='123')) + session.add(CatalogueSetting(name="mfa_key", value="123")) session.add( - CatalogueSetting(name='mfa_key_ldap', value='123'), + CatalogueSetting(name="mfa_key_ldap", value="123"), ) - session.add(CatalogueSetting(name='mfa_secret_ldap', value='123')) + session.add(CatalogueSetting(name="mfa_secret_ldap", value="123")) await session.commit() diff --git a/tests/test_api/test_shadow/test_router.py b/tests/test_api/test_shadow/test_router.py index 54326b7b..56c13e2b 100644 --- a/tests/test_api/test_shadow/test_router.py +++ b/tests/test_api/test_shadow/test_router.py @@ -16,14 +16,14 @@ @pytest.mark.asyncio -@pytest.mark.usefixtures('setup_session') +@pytest.mark.usefixtures("setup_session") async def test_shadow_api_non_existent_user(http_client: AsyncClient) -> None: """Test shadow api with non-existent user.""" response = await http_client.post( "/shadow/mfa/push", json=ProxyRequestModel( - principal='non-existent_user', - ip='127.0.0.1', + principal="non-existent_user", + ip="127.0.0.1", ).model_dump(), ) @@ -31,7 +31,7 @@ async def test_shadow_api_non_existent_user(http_client: AsyncClient) -> None: @pytest.mark.asyncio -@pytest.mark.usefixtures('setup_session') +@pytest.mark.usefixtures("setup_session") async def test_shadow_api_without_network_policies( http_client: AsyncClient, adding_mfa_user_and_group: dict, @@ -49,7 +49,7 @@ async def test_shadow_api_without_network_policies( @pytest.mark.asyncio -@pytest.mark.usefixtures('setup_session') +@pytest.mark.usefixtures("setup_session") async def test_shadow_api_without_kerberos_protocol( http_client: AsyncClient, adding_mfa_user_and_group: dict, @@ -70,7 +70,7 @@ async def test_shadow_api_without_kerberos_protocol( @pytest.mark.asyncio -@pytest.mark.usefixtures('setup_session') +@pytest.mark.usefixtures("setup_session") async def test_shadow_api_with_disable_mfa( http_client: AsyncClient, adding_mfa_user_and_group: dict, @@ -85,7 +85,7 @@ async def test_shadow_api_with_disable_mfa( @pytest.mark.asyncio -@pytest.mark.usefixtures('setup_session') +@pytest.mark.usefixtures("setup_session") async def test_shadow_api_whitelist_without_user_group( http_client: AsyncClient, adding_mfa_user_and_group: dict, @@ -106,7 +106,7 @@ async def test_shadow_api_whitelist_without_user_group( @pytest.mark.asyncio -@pytest.mark.usefixtures('setup_session') +@pytest.mark.usefixtures("setup_session") async def test_shadow_api_enable_mfa( http_client: AsyncClient, adding_mfa_user_and_group: dict, diff --git a/tests/test_ldap/test_bind.py b/tests/test_ldap/test_bind.py index d0d5cadb..f61bd406 100644 --- a/tests/test_ldap/test_bind.py +++ b/tests/test_ldap/test_bind.py @@ -285,7 +285,7 @@ async def test_anonymous_bind( bind = BindRequest( version=0, name="", - AuthenticationChoice=SimpleAuthentication(password=""), # noqa + AuthenticationChoice=SimpleAuthentication(password=""), ) async with container(scope=Scope.REQUEST) as container: handler = await resolve_deps(bind.handle, container) diff --git a/tests/test_ldap/test_ldap3_lib.py b/tests/test_ldap/test_ldap3_lib.py index 3d9f3fdb..e4f5fbb6 100644 --- a/tests/test_ldap/test_ldap3_lib.py +++ b/tests/test_ldap/test_ldap3_lib.py @@ -14,8 +14,8 @@ @pytest.mark.asyncio -@pytest.mark.usefixtures('setup_session') -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("setup_session") +@pytest.mark.usefixtures("session") async def test_ldap3_search( ldap_client: Connection, event_loop: BaseEventLoop, @@ -27,7 +27,7 @@ async def test_ldap3_search( result = await event_loop.run_in_executor( None, partial( - ldap_client.search, 'dc=md,dc=test', '(objectclass=*)', + ldap_client.search, "dc=md,dc=test", "(objectclass=*)", )) assert result @@ -35,22 +35,22 @@ async def test_ldap3_search( @pytest.mark.asyncio -@pytest.mark.usefixtures('setup_session') -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("setup_session") +@pytest.mark.usefixtures("session") async def test_ldap3_search_memberof( ldap_client: Connection, event_loop: BaseEventLoop, creds: TestCreds) -> None: """Test ldap3 search memberof.""" - member = 'cn=user1,ou=moscow,ou=russia,ou=users,dc=md,dc=test' + member = "cn=user1,ou=moscow,ou=russia,ou=users,dc=md,dc=test" await event_loop.run_in_executor( None, partial(ldap_client.rebind, user=creds.un, password=creds.pw)) result = await event_loop.run_in_executor( None, partial( - ldap_client.search, 'dc=md,dc=test', - '(memberOf=cn=developers,cn=groups,dc=md,dc=test)', + ldap_client.search, "dc=md,dc=test", + "(memberOf=cn=developers,cn=groups,dc=md,dc=test)", )) assert result diff --git a/tests/test_ldap/test_ldap3_whoami.py b/tests/test_ldap/test_ldap3_whoami.py index ebfef4ff..e7f082cc 100644 --- a/tests/test_ldap/test_ldap3_whoami.py +++ b/tests/test_ldap/test_ldap3_whoami.py @@ -14,7 +14,7 @@ @pytest.mark.asyncio -@pytest.mark.usefixtures('setup_session') +@pytest.mark.usefixtures("setup_session") async def test_anonymous_whoami( event_loop: asyncio.BaseEventLoop, ldap_client: Connection, @@ -30,7 +30,7 @@ async def test_anonymous_whoami( @pytest.mark.asyncio -@pytest.mark.usefixtures('setup_session') +@pytest.mark.usefixtures("setup_session") async def test_bind_whoami( event_loop: asyncio.BaseEventLoop, ldap_client: Connection, @@ -43,4 +43,4 @@ async def test_bind_whoami( result = await event_loop.run_in_executor( None, ldap_client.extend.standard.who_am_i) - assert result == 'u:user0' + assert result == "u:user0" diff --git a/tests/test_ldap/test_passwd_change.py b/tests/test_ldap/test_passwd_change.py index 431d3ac1..b3761ddf 100644 --- a/tests/test_ldap/test_passwd_change.py +++ b/tests/test_ldap/test_passwd_change.py @@ -17,8 +17,8 @@ @pytest.mark.asyncio -@pytest.mark.usefixtures('setup_session') -@pytest.mark.usefixtures('_force_override_tls') +@pytest.mark.usefixtures("setup_session") +@pytest.mark.usefixtures("_force_override_tls") async def test_anonymous_pwd_change( session: AsyncSession, event_loop: asyncio.BaseEventLoop, @@ -33,7 +33,7 @@ async def test_anonymous_pwd_change( result = await event_loop.run_in_executor( None, - partial( # noqa: S106 + partial( ldap_client.extend.standard.modify_password, user_dn, old_password=password, @@ -52,8 +52,8 @@ async def test_anonymous_pwd_change( @pytest.mark.asyncio -@pytest.mark.usefixtures('setup_session') -@pytest.mark.usefixtures('_force_override_tls') +@pytest.mark.usefixtures("setup_session") +@pytest.mark.usefixtures("_force_override_tls") async def test_bind_pwd_change( session: AsyncSession, event_loop: asyncio.BaseEventLoop, @@ -69,7 +69,7 @@ async def test_bind_pwd_change( result = await event_loop.run_in_executor( None, - partial( # noqa: S106 + partial( ldap_client.extend.standard.modify_password, old_password=password, new_password=new_test_password, diff --git a/tests/test_ldap/test_pool_client_handler.py b/tests/test_ldap/test_pool_client_handler.py index b4dab16a..1bbcb7cd 100644 --- a/tests/test_ldap/test_pool_client_handler.py +++ b/tests/test_ldap/test_pool_client_handler.py @@ -15,8 +15,8 @@ @pytest.mark.asyncio -@pytest.mark.usefixtures('setup_session') -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("setup_session") +@pytest.mark.usefixtures("session") async def test_check_policy( ldap_session: LDAPSession, session: AsyncSession) -> None: """Check policy.""" @@ -30,9 +30,9 @@ async def test_specific_policy_ok( ldap_session: LDAPSession, session: AsyncSession) -> None: """Test specific ip.""" session.add(NetworkPolicy( - name='Local policy', - netmasks=[IPv4Network('127.100.10.5/32')], - raw=['127.100.10.5/32'], + name="Local policy", + netmasks=[IPv4Network("127.100.10.5/32")], + raw=["127.100.10.5/32"], enabled=True, priority=1, )) @@ -46,8 +46,8 @@ async def test_specific_policy_ok( @pytest.mark.asyncio -@pytest.mark.usefixtures('setup_session') -@pytest.mark.usefixtures('settings') +@pytest.mark.usefixtures("setup_session") +@pytest.mark.usefixtures("settings") async def test_check_policy_group( ldap_session: LDAPSession, session: AsyncSession) -> None: @@ -55,13 +55,13 @@ async def test_check_policy_group( user = await get_user(session, "user0") assert user - policy = await ldap_session._get_policy(IPv4Address('127.0.0.1'), session) + policy = await ldap_session._get_policy(IPv4Address("127.0.0.1"), session) assert policy assert await is_user_group_valid(user, policy, session) group_dir = await get_group( - 'cn=domain admins,cn=groups,dc=md,dc=test', session) + "cn=domain admins,cn=groups,dc=md,dc=test", session) policy.groups.append(group_dir.group) await session.commit() diff --git a/tests/test_ldap/test_util/test_add.py b/tests/test_ldap/test_util/test_add.py index a56a702c..3dff6b26 100644 --- a/tests/test_ldap/test_util/test_add.py +++ b/tests/test_ldap/test_util/test_add.py @@ -25,11 +25,11 @@ @pytest.mark.asyncio -@pytest.mark.usefixtures('setup_session') +@pytest.mark.usefixtures("setup_session") async def test_ldap_root_add( session: AsyncSession, settings: Settings, user: dict) -> None: """Test ldapadd on server.""" - dn = 'cn=test,dc=md,dc=test' + dn = "cn=test,dc=md,dc=test" search_path = get_search_path(dn) with tempfile.NamedTemporaryFile("w") as file: file.write(( @@ -42,10 +42,10 @@ async def test_ldap_root_add( )) file.seek(0) proc = await asyncio.create_subprocess_exec( - 'ldapadd', - '-vvv', '-H', f'ldap://{settings.HOST}:{settings.PORT}', - '-D', user['sam_accout_name'], '-x', '-w', user['password'], - '-f', file.name, + "ldapadd", + "-vvv", "-H", f"ldap://{settings.HOST}:{settings.PORT}", + "-D", user["sam_accout_name"], "-x", "-w", user["password"], + "-f", file.name, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE) @@ -66,17 +66,17 @@ async def test_ldap_root_add( for attr in new_dir.attributes: attributes[attr.name].append(attr.value) - assert attributes['objectClass'] == ['organization', 'top'] + assert attributes["objectClass"] == ["organization", "top"] @pytest.mark.asyncio -@pytest.mark.usefixtures('setup_session') +@pytest.mark.usefixtures("setup_session") async def test_ldap_user_add_with_group( session: AsyncSession, settings: Settings, user: dict) -> None: """Test ldapadd on server.""" - user_dn = 'cn=test,dc=md,dc=test' + user_dn = "cn=test,dc=md,dc=test" user_search_path = get_search_path(user_dn) - group_dn = 'cn=domain admins,cn=groups,dc=md,dc=test' + group_dn = "cn=domain admins,cn=groups,dc=md,dc=test" with tempfile.NamedTemporaryFile("w") as file: file.write( @@ -94,10 +94,10 @@ async def test_ldap_user_add_with_group( ) file.seek(0) proc = await asyncio.create_subprocess_exec( - 'ldapadd', - '-vvv', '-H', f'ldap://{settings.HOST}:{settings.PORT}', - '-D', user['sam_accout_name'], '-x', '-w', user['password'], - '-f', file.name, + "ldapadd", + "-vvv", "-H", f"ldap://{settings.HOST}:{settings.PORT}", + "-D", user["sam_accout_name"], "-x", "-w", user["password"], + "-f", file.name, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE) @@ -121,13 +121,13 @@ async def test_ldap_user_add_with_group( @pytest.mark.asyncio -@pytest.mark.usefixtures('setup_session') +@pytest.mark.usefixtures("setup_session") async def test_ldap_user_add_group_with_group( session: AsyncSession, settings: Settings, user: dict) -> None: """Test ldapadd on server.""" - child_group_dn = 'cn=twisted,cn=groups,dc=md,dc=test' + child_group_dn = "cn=twisted,cn=groups,dc=md,dc=test" child_group_search_path = get_search_path(child_group_dn) - group_dn = 'cn=domain admins,cn=groups,dc=md,dc=test' + group_dn = "cn=domain admins,cn=groups,dc=md,dc=test" with tempfile.NamedTemporaryFile("w") as file: file.write(( @@ -140,10 +140,10 @@ async def test_ldap_user_add_group_with_group( )) file.seek(0) proc = await asyncio.create_subprocess_exec( - 'ldapadd', - '-vvv', '-H', f'ldap://{settings.HOST}:{settings.PORT}', - '-D', user['sam_accout_name'], '-x', '-w', user['password'], - '-f', file.name, + "ldapadd", + "-vvv", "-H", f"ldap://{settings.HOST}:{settings.PORT}", + "-D", user["sam_accout_name"], "-x", "-w", user["password"], + "-f", file.name, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE) @@ -167,8 +167,8 @@ async def test_ldap_user_add_group_with_group( @pytest.mark.asyncio -@pytest.mark.usefixtures('setup_session') -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("setup_session") +@pytest.mark.usefixtures("session") async def test_add_bvalue_attr( session: AsyncSession, ldap_bound_session: LDAPSession, @@ -185,11 +185,11 @@ async def test_add_bvalue_attr( @pytest.mark.asyncio -@pytest.mark.usefixtures('setup_session') +@pytest.mark.usefixtures("setup_session") async def test_ldap_add_access_control( session: AsyncSession, settings: Settings, creds: TestCreds) -> None: """Test ldapadd on server.""" - dn = 'cn=test,dc=md,dc=test' + dn = "cn=test,dc=md,dc=test" async def try_add() -> int: with tempfile.NamedTemporaryFile("w") as file: @@ -202,10 +202,10 @@ async def try_add() -> int: )) file.seek(0) proc = await asyncio.create_subprocess_exec( - 'ldapadd', - '-vvv', '-H', f'ldap://{settings.HOST}:{settings.PORT}', - '-D', "user_non_admin", '-x', '-w', creds.pw, - '-f', file.name, + "ldapadd", + "-vvv", "-H", f"ldap://{settings.HOST}:{settings.PORT}", + "-D", "user_non_admin", "-x", "-w", creds.pw, + "-f", file.name, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE) @@ -214,7 +214,7 @@ async def try_add() -> int: assert await try_add() == LDAPCodes.INSUFFICIENT_ACCESS_RIGHTS await create_access_policy( - name='DOMAIN Read Access Policy', + name="DOMAIN Read Access Policy", can_add=False, can_modify=False, can_read=True, @@ -227,7 +227,7 @@ async def try_add() -> int: assert await try_add() == LDAPCodes.INSUFFICIENT_ACCESS_RIGHTS await create_access_policy( - name='DOMAIN Add Access Policy', + name="DOMAIN Add Access Policy", can_add=True, can_modify=False, can_read=True, @@ -240,19 +240,19 @@ async def try_add() -> int: assert await try_add() == LDAPCodes.SUCCESS proc = await asyncio.create_subprocess_exec( - 'ldapsearch', - '-vvv', '-x', '-H', f'ldap://{settings.HOST}:{settings.PORT}', - '-D', 'user_non_admin', - '-w', creds.pw, - '-b', 'dc=md,dc=test', 'objectclass=*', + "ldapsearch", + "-vvv", "-x", "-H", f"ldap://{settings.HOST}:{settings.PORT}", + "-D", "user_non_admin", + "-w", creds.pw, + "-b", "dc=md,dc=test", "objectclass=*", stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE) raw_data, _ = await proc.communicate() - data = raw_data.decode().split('\n') + data = raw_data.decode().split("\n") result = await proc.wait() - dn_list = [d.removeprefix("dn: ") for d in data if d.startswith('dn:')] + dn_list = [d.removeprefix("dn: ") for d in data if d.startswith("dn:")] assert result == 0 assert dn in dn_list diff --git a/tests/test_ldap/test_util/test_delete.py b/tests/test_ldap/test_util/test_delete.py index 04ff60d8..1d332b90 100644 --- a/tests/test_ldap/test_util/test_delete.py +++ b/tests/test_ldap/test_util/test_delete.py @@ -19,7 +19,7 @@ @pytest.mark.asyncio -@pytest.mark.usefixtures('setup_session') +@pytest.mark.usefixtures("setup_session") async def test_ldap_delete( session: AsyncSession, settings: Settings, user: dict) -> None: """Test ldapdelete on server.""" @@ -36,10 +36,10 @@ async def test_ldap_delete( )) file.seek(0) proc = await asyncio.create_subprocess_exec( - 'ldapadd', - '-vvv', '-H', f'ldap://{settings.HOST}:{settings.PORT}', - '-D', user['sam_accout_name'], '-x', '-w', user['password'], - '-f', file.name, + "ldapadd", + "-vvv", "-H", f"ldap://{settings.HOST}:{settings.PORT}", + "-D", user["sam_accout_name"], "-x", "-w", user["password"], + "-f", file.name, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE) @@ -48,9 +48,9 @@ async def test_ldap_delete( assert await session.scalar(select(Directory).filter_by(name="test")) proc = await asyncio.create_subprocess_exec( - 'ldapdelete', - '-vvv', '-H', f'ldap://{settings.HOST}:{settings.PORT}', - '-D', user['sam_accout_name'], '-x', '-w', user['password'], + "ldapdelete", + "-vvv", "-H", f"ldap://{settings.HOST}:{settings.PORT}", + "-D", user["sam_accout_name"], "-x", "-w", user["password"], dn, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE) @@ -62,11 +62,11 @@ async def test_ldap_delete( @pytest.mark.asyncio -@pytest.mark.usefixtures('setup_session') +@pytest.mark.usefixtures("setup_session") async def test_ldap_delete_w_access_control( session: AsyncSession, settings: Settings, creds: TestCreds) -> None: """Test ldapadd on server.""" - dn = 'cn=test,dc=md,dc=test' + dn = "cn=test,dc=md,dc=test" with tempfile.NamedTemporaryFile("w") as file: file.write(( @@ -78,10 +78,10 @@ async def test_ldap_delete_w_access_control( )) file.seek(0) proc = await asyncio.create_subprocess_exec( # Add as Admin - 'ldapadd', - '-vvv', '-H', f'ldap://{settings.HOST}:{settings.PORT}', - '-D', creds.un, '-x', '-w', creds.pw, - '-f', file.name, + "ldapadd", + "-vvv", "-H", f"ldap://{settings.HOST}:{settings.PORT}", + "-D", creds.un, "-x", "-w", creds.pw, + "-f", file.name, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE) @@ -89,9 +89,9 @@ async def test_ldap_delete_w_access_control( async def try_delete() -> int: proc = await asyncio.create_subprocess_exec( - 'ldapdelete', - '-vvv', '-H', f'ldap://{settings.HOST}:{settings.PORT}', - '-D', "user_non_admin", '-x', '-w', creds.pw, + "ldapdelete", + "-vvv", "-H", f"ldap://{settings.HOST}:{settings.PORT}", + "-D", "user_non_admin", "-x", "-w", creds.pw, dn, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE) @@ -101,7 +101,7 @@ async def try_delete() -> int: assert await try_delete() == LDAPCodes.NO_SUCH_OBJECT await create_access_policy( - name='TEST Read Access Policy', + name="TEST Read Access Policy", can_add=False, can_modify=False, can_read=True, @@ -114,7 +114,7 @@ async def try_delete() -> int: assert await try_delete() == LDAPCodes.INSUFFICIENT_ACCESS_RIGHTS await create_access_policy( - name='TEST Del Access Policy', + name="TEST Del Access Policy", can_add=False, can_modify=False, can_read=True, @@ -127,19 +127,19 @@ async def try_delete() -> int: assert await try_delete() == LDAPCodes.SUCCESS proc = await asyncio.create_subprocess_exec( - 'ldapsearch', - '-vvv', '-x', '-H', f'ldap://{settings.HOST}:{settings.PORT}', - '-D', 'user_non_admin', - '-w', creds.pw, - '-b', 'dc=md,dc=test', 'objectclass=*', + "ldapsearch", + "-vvv", "-x", "-H", f"ldap://{settings.HOST}:{settings.PORT}", + "-D", "user_non_admin", + "-w", creds.pw, + "-b", "dc=md,dc=test", "objectclass=*", stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE) raw_data, _ = await proc.communicate() - data = raw_data.decode().split('\n') + data = raw_data.decode().split("\n") result = await proc.wait() - dn_list = [d.removeprefix("dn: ") for d in data if d.startswith('dn:')] + dn_list = [d.removeprefix("dn: ") for d in data if d.startswith("dn:")] assert result == 0 assert dn not in dn_list diff --git a/tests/test_ldap/test_util/test_modify.py b/tests/test_ldap/test_util/test_modify.py index 0afc92d9..b03a6074 100644 --- a/tests/test_ldap/test_util/test_modify.py +++ b/tests/test_ldap/test_util/test_modify.py @@ -22,7 +22,7 @@ @pytest.mark.asyncio -@pytest.mark.usefixtures('setup_session') +@pytest.mark.usefixtures("setup_session") async def test_ldap_base_modify( session: AsyncSession, settings: Settings, user: dict) -> None: """Test ldapmodify on server.""" @@ -43,8 +43,8 @@ async def test_ldap_base_modify( for attr in directory.attributes: attributes[attr.name].append(attr.value) - assert 'user' in attributes['objectClass'] - assert attributes['posixEmail'] == ["abctest@mail.com"] + assert "user" in attributes["objectClass"] + assert attributes["posixEmail"] == ["abctest@mail.com"] with tempfile.NamedTemporaryFile("w") as file: file.write(( @@ -67,10 +67,10 @@ async def test_ldap_base_modify( )) file.seek(0) proc = await asyncio.create_subprocess_exec( - 'ldapmodify', - '-vvv', '-H', f'ldap://{settings.HOST}:{settings.PORT}', - '-D', user['sam_accout_name'], '-x', '-w', user['password'], - '-f', file.name, + "ldapmodify", + "-vvv", "-H", f"ldap://{settings.HOST}:{settings.PORT}", + "-D", user["sam_accout_name"], "-x", "-w", user["password"], + "-f", file.name, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE) @@ -85,21 +85,21 @@ async def test_ldap_base_modify( for attr in directory.attributes: attributes[attr.name].append(attr.value) - assert attributes['objectClass'] == [ - 'top', 'person', - 'organizationalPerson', 'posixAccount', 'user'] - assert attributes['title'] == [ + assert attributes["objectClass"] == [ + "top", "person", + "organizationalPerson", "posixAccount", "user"] + assert attributes["title"] == [ "Grand Poobah", "Grand Poobah1", "Grand Poobah2", "Grand Poobah3", ] - assert attributes['jpegPhoto'] == ['modme.jpeg'] + assert attributes["jpegPhoto"] == ["modme.jpeg"] assert directory.user.mail == "modme@student.of.life.edu" - assert 'posixEmail' not in attributes + assert "posixEmail" not in attributes @pytest.mark.asyncio -@pytest.mark.usefixtures('setup_session') +@pytest.mark.usefixtures("setup_session") async def test_ldap_membersip_user_delete( session: AsyncSession, settings: Settings, user: dict) -> None: """Test ldapmodify on server.""" @@ -122,10 +122,10 @@ async def test_ldap_membersip_user_delete( )) file.seek(0) proc = await asyncio.create_subprocess_exec( - 'ldapmodify', - '-vvv', '-H', f'ldap://{settings.HOST}:{settings.PORT}', - '-D', user['sam_accout_name'], '-x', '-w', user['password'], - '-f', file.name, + "ldapmodify", + "-vvv", "-H", f"ldap://{settings.HOST}:{settings.PORT}", + "-D", user["sam_accout_name"], "-x", "-w", user["password"], + "-f", file.name, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE) @@ -139,12 +139,12 @@ async def test_ldap_membersip_user_delete( @pytest.mark.asyncio -@pytest.mark.usefixtures('setup_session') +@pytest.mark.usefixtures("setup_session") async def test_ldap_membersip_user_add( session: AsyncSession, settings: Settings, creds: TestCreds) -> None: """Test ldapmodify on server.""" dn = "cn=user_non_admin,ou=users,dc=md,dc=test" - query = ( # noqa + query = ( select(Directory) .options(selectinload(Directory.groups).selectinload(Group.directory)) .filter(Directory.path == get_search_path(dn))) @@ -166,10 +166,10 @@ async def test_ldap_membersip_user_add( )) file.seek(0) proc = await asyncio.create_subprocess_exec( - 'ldapmodify', - '-vvv', '-H', f'ldap://{settings.HOST}:{settings.PORT}', - '-D', creds.un, '-x', '-w', creds.pw, - '-f', file.name, + "ldapmodify", + "-vvv", "-H", f"ldap://{settings.HOST}:{settings.PORT}", + "-D", creds.un, "-x", "-w", creds.pw, + "-f", file.name, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE) @@ -183,7 +183,7 @@ async def test_ldap_membersip_user_add( @pytest.mark.asyncio -@pytest.mark.usefixtures('setup_session') +@pytest.mark.usefixtures("setup_session") async def test_ldap_membersip_user_replace( session: AsyncSession, settings: Settings, user: dict) -> None: """Test ldapmodify on server.""" @@ -210,10 +210,10 @@ async def test_ldap_membersip_user_replace( )) file.seek(0) proc = await asyncio.create_subprocess_exec( - 'ldapadd', - '-vvv', '-H', f'ldap://{settings.HOST}:{settings.PORT}', - '-D', user['sam_accout_name'], '-x', '-w', user['password'], - '-f', file.name, + "ldapadd", + "-vvv", "-H", f"ldap://{settings.HOST}:{settings.PORT}", + "-D", user["sam_accout_name"], "-x", "-w", user["password"], + "-f", file.name, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE) @@ -231,10 +231,10 @@ async def test_ldap_membersip_user_replace( )) file.seek(0) proc = await asyncio.create_subprocess_exec( - 'ldapmodify', - '-vvv', '-H', f'ldap://{settings.HOST}:{settings.PORT}', - '-D', user['sam_accout_name'], '-x', '-w', user['password'], - '-f', file.name, + "ldapmodify", + "-vvv", "-H", f"ldap://{settings.HOST}:{settings.PORT}", + "-D", user["sam_accout_name"], "-x", "-w", user["password"], + "-f", file.name, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE) @@ -248,13 +248,13 @@ async def test_ldap_membersip_user_replace( @pytest.mark.asyncio -@pytest.mark.usefixtures('setup_session') +@pytest.mark.usefixtures("setup_session") async def test_ldap_membersip_grp_replace( session: AsyncSession, settings: Settings, user: dict) -> None: """Test ldapmodify on server.""" dn = "cn=domain admins,cn=groups,dc=md,dc=test" - query = ( # noqa + query = ( select(Directory) .options( selectinload(Directory.group) @@ -279,10 +279,10 @@ async def test_ldap_membersip_grp_replace( )) file.seek(0) proc = await asyncio.create_subprocess_exec( - 'ldapadd', - '-vvv', '-H', f'ldap://{settings.HOST}:{settings.PORT}', - '-D', user['sam_accout_name'], '-x', '-w', user['password'], - '-f', file.name, + "ldapadd", + "-vvv", "-H", f"ldap://{settings.HOST}:{settings.PORT}", + "-D", user["sam_accout_name"], "-x", "-w", user["password"], + "-f", file.name, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE) @@ -300,10 +300,10 @@ async def test_ldap_membersip_grp_replace( )) file.seek(0) proc = await asyncio.create_subprocess_exec( - 'ldapmodify', - '-vvv', '-H', f'ldap://{settings.HOST}:{settings.PORT}', - '-D', user['sam_accout_name'], '-x', '-w', user['password'], - '-f', file.name, + "ldapmodify", + "-vvv", "-H", f"ldap://{settings.HOST}:{settings.PORT}", + "-D", user["sam_accout_name"], "-x", "-w", user["password"], + "-f", file.name, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE) result = await proc.wait() @@ -317,7 +317,7 @@ async def test_ldap_membersip_grp_replace( @pytest.mark.asyncio -@pytest.mark.usefixtures('setup_session') +@pytest.mark.usefixtures("setup_session") async def test_ldap_modify_dn( session: AsyncSession, settings: Settings, user: dict) -> None: """Test ldapmodify on server.""" @@ -333,10 +333,10 @@ async def test_ldap_modify_dn( )) file.seek(0) proc = await asyncio.create_subprocess_exec( - 'ldapmodify', - '-vvv', '-H', f'ldap://{settings.HOST}:{settings.PORT}', - '-D', user['sam_accout_name'], '-x', '-w', user['password'], - '-f', file.name, + "ldapmodify", + "-vvv", "-H", f"ldap://{settings.HOST}:{settings.PORT}", + "-D", user["sam_accout_name"], "-x", "-w", user["password"], + "-f", file.name, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE) @@ -350,8 +350,8 @@ async def test_ldap_modify_dn( @pytest.mark.asyncio -@pytest.mark.usefixtures('setup_session') -@pytest.mark.usefixtures('_force_override_tls') +@pytest.mark.usefixtures("setup_session") +@pytest.mark.usefixtures("_force_override_tls") async def test_ldap_modify_password_change( settings: Settings, creds: TestCreds) -> None: """Test ldapmodify on server.""" @@ -368,10 +368,10 @@ async def test_ldap_modify_password_change( )) file.seek(0) proc = await asyncio.create_subprocess_exec( - 'ldapmodify', - '-vvv', '-H', f'ldap://{settings.HOST}:{settings.PORT}', - '-D', creds.un, '-x', '-w', creds.pw, - '-f', file.name, + "ldapmodify", + "-vvv", "-H", f"ldap://{settings.HOST}:{settings.PORT}", + "-D", creds.un, "-x", "-w", creds.pw, + "-f", file.name, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE) @@ -380,16 +380,16 @@ async def test_ldap_modify_password_change( assert result == 0 proc = await asyncio.create_subprocess_exec( - 'ldapsearch', - '-vvv', '-H', f'ldap://{settings.HOST}:{settings.PORT}', - '-D', creds.un, '-x', '-w', new_password) + "ldapsearch", + "-vvv", "-H", f"ldap://{settings.HOST}:{settings.PORT}", + "-D", creds.un, "-x", "-w", new_password) result = await proc.wait() assert result == 0 @pytest.mark.asyncio -@pytest.mark.usefixtures('setup_session') +@pytest.mark.usefixtures("setup_session") async def test_ldap_modify_with_ap( session: AsyncSession, settings: Settings, creds: TestCreds) -> None: """Test ldapmodify on server.""" @@ -427,10 +427,10 @@ async def try_modify() -> int: )) file.seek(0) proc = await asyncio.create_subprocess_exec( - 'ldapmodify', - '-vvv', '-H', f'ldap://{settings.HOST}:{settings.PORT}', - '-D', "user_non_admin", '-x', '-w', creds.pw, - '-f', file.name, + "ldapmodify", + "-vvv", "-H", f"ldap://{settings.HOST}:{settings.PORT}", + "-D", "user_non_admin", "-x", "-w", creds.pw, + "-f", file.name, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE) @@ -439,7 +439,7 @@ async def try_modify() -> int: assert await try_modify() == LDAPCodes.INSUFFICIENT_ACCESS_RIGHTS await create_access_policy( - name='TEST read Access Policy', + name="TEST read Access Policy", can_add=False, can_modify=False, can_read=True, @@ -452,7 +452,7 @@ async def try_modify() -> int: assert await try_modify() == LDAPCodes.INSUFFICIENT_ACCESS_RIGHTS await create_access_policy( - name='TEST modify Access Policy', + name="TEST modify Access Policy", can_add=False, can_modify=True, can_read=True, @@ -473,13 +473,13 @@ async def try_modify() -> int: for attr in directory.attributes: attributes[attr.name].append(attr.value) - assert attributes['objectClass'] == [ - 'top', 'container', 'organizationalUnit'] - assert attributes['title'] == [ + assert attributes["objectClass"] == [ + "top", "container", "organizationalUnit"] + assert attributes["title"] == [ "Grand Poobah", "Grand Poobah1", "Grand Poobah2", "Grand Poobah3", ] - assert attributes['jpegPhoto'] == ['modme.jpeg'] + assert attributes["jpegPhoto"] == ["modme.jpeg"] assert directory.user.mail == "modme@student.of.life.edu" - assert 'posixEmail' not in attributes + assert "posixEmail" not in attributes diff --git a/tests/test_ldap/test_util/test_search.py b/tests/test_ldap/test_util/test_search.py index add2f906..b4148976 100644 --- a/tests/test_ldap/test_util/test_search.py +++ b/tests/test_ldap/test_util/test_search.py @@ -25,21 +25,21 @@ @pytest.mark.asyncio -@pytest.mark.usefixtures('setup_session') -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("setup_session") +@pytest.mark.usefixtures("session") async def test_ldap_search(settings: Settings, creds: TestCreds) -> None: """Test ldapsearch on server.""" proc = await asyncio.create_subprocess_exec( - 'ldapsearch', - '-vvv', '-x', '-H', f'ldap://{settings.HOST}:{settings.PORT}', - '-D', creds.un, - '-w', creds.pw, - '-b', 'dc=md,dc=test', 'objectclass=*', + "ldapsearch", + "-vvv", "-x", "-H", f"ldap://{settings.HOST}:{settings.PORT}", + "-D", creds.un, + "-w", creds.pw, + "-b", "dc=md,dc=test", "objectclass=*", stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE) raw_data, _ = await proc.communicate() - data = raw_data.decode().split('\n') + data = raw_data.decode().split("\n") result = await proc.wait() assert result == 0 @@ -49,28 +49,28 @@ async def test_ldap_search(settings: Settings, creds: TestCreds) -> None: @pytest.mark.asyncio -@pytest.mark.usefixtures('setup_session') -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("setup_session") +@pytest.mark.usefixtures("session") async def test_ldap_search_filter( settings: Settings, creds: TestCreds, ) -> None: """Test ldapsearch with filter on server.""" proc = await asyncio.create_subprocess_exec( - 'ldapsearch', - '-vvv', '-x', '-H', f'ldap://{settings.HOST}:{settings.PORT}', - '-D', creds.un, - '-w', creds.pw, - '-b', 'dc=md,dc=test', - '(&' - '(objectClass=user)' - '(memberOf:1.2.840.113556.1.4.1941:=cn=domain admins,cn=groups,dc=md,\ - dc=test)' - ')', + "ldapsearch", + "-vvv", "-x", "-H", f"ldap://{settings.HOST}:{settings.PORT}", + "-D", creds.un, + "-w", creds.pw, + "-b", "dc=md,dc=test", + "(&" + "(objectClass=user)" + "(memberOf:1.2.840.113556.1.4.1941:=cn=domain admins,cn=groups,dc=md,\ + dc=test)" + ")", stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE) raw_data, _ = await proc.communicate() - data = raw_data.decode().split('\n') + data = raw_data.decode().split("\n") result = await proc.wait() assert result == 0 @@ -79,24 +79,24 @@ async def test_ldap_search_filter( @pytest.mark.asyncio -@pytest.mark.usefixtures('setup_session') -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("setup_session") +@pytest.mark.usefixtures("session") async def test_ldap_search_filter_prefix( settings: Settings, creds: TestCreds, ) -> None: """Test ldapsearch with filter on server.""" proc = await asyncio.create_subprocess_exec( - 'ldapsearch', - '-vvv', '-x', '-H', f'ldap://{settings.HOST}:{settings.PORT}', - '-D', creds.un, - '-w', creds.pw, - '-b', 'dc=md,dc=test', - '(description=*desc)', + "ldapsearch", + "-vvv", "-x", "-H", f"ldap://{settings.HOST}:{settings.PORT}", + "-D", creds.un, + "-w", creds.pw, + "-b", "dc=md,dc=test", + "(description=*desc)", stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE) raw_data, _ = await proc.communicate() - data = raw_data.decode().split('\n') + data = raw_data.decode().split("\n") result = await proc.wait() assert result == 0 @@ -104,7 +104,7 @@ async def test_ldap_search_filter_prefix( @pytest.mark.asyncio -@pytest.mark.usefixtures('setup_session') +@pytest.mark.usefixtures("setup_session") async def test_bind_policy( session: AsyncSession, settings: Settings, @@ -112,32 +112,32 @@ async def test_bind_policy( ldap_session: LDAPSession, ) -> None: """Bind with policy.""" - policy = await ldap_session._get_policy(IPv4Address('127.0.0.1'), session) + policy = await ldap_session._get_policy(IPv4Address("127.0.0.1"), session) assert policy group_dir = await get_group( - 'cn=domain admins,cn=groups,dc=md,dc=test', session) + "cn=domain admins,cn=groups,dc=md,dc=test", session) policy.groups.append(group_dir.group) await session.commit() proc = await asyncio.create_subprocess_exec( - 'ldapsearch', - '-vvv', '-H', f'ldap://{settings.HOST}:{settings.PORT}', - '-D', creds.un, '-x', '-w', creds.pw) + "ldapsearch", + "-vvv", "-H", f"ldap://{settings.HOST}:{settings.PORT}", + "-D", creds.un, "-x", "-w", creds.pw) result = await proc.wait() assert result == 0 @pytest.mark.asyncio -@pytest.mark.usefixtures('setup_session') +@pytest.mark.usefixtures("setup_session") async def test_bind_policy_missing_group( session: AsyncSession, ldap_session: LDAPSession, settings: Settings, creds: TestCreds) -> None: """Bind policy fail.""" - policy = await ldap_session._get_policy(IPv4Address('127.0.0.1'), session) + policy = await ldap_session._get_policy(IPv4Address("127.0.0.1"), session) assert policy @@ -146,7 +146,7 @@ async def test_bind_policy_missing_group( .options(selectinload(User.groups)))).one() policy.groups = await get_groups( - ['cn=domain admins,cn=groups,dc=md,dc=test'], + ["cn=domain admins,cn=groups,dc=md,dc=test"], session, ) user.groups.clear() @@ -155,24 +155,24 @@ async def test_bind_policy_missing_group( assert not await is_user_group_valid(user, policy, session) proc = await asyncio.create_subprocess_exec( - 'ldapsearch', - '-vvv', '-H', f'ldap://{settings.HOST}:{settings.PORT}', - '-D', creds.un, '-x', '-w', creds.pw) + "ldapsearch", + "-vvv", "-H", f"ldap://{settings.HOST}:{settings.PORT}", + "-D", creds.un, "-x", "-w", creds.pw) result = await proc.wait() assert result == 49 @pytest.mark.asyncio -@pytest.mark.usefixtures('setup_session') -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("setup_session") +@pytest.mark.usefixtures("session") async def test_ldap_bind(settings: Settings, creds: TestCreds) -> None: """Test ldapsearch on server.""" proc = await asyncio.create_subprocess_exec( - 'ldapsearch', - '-vvv', '-x', '-H', f'ldap://{settings.HOST}:{settings.PORT}', - '-D', creds.un, - '-w', creds.pw, + "ldapsearch", + "-vvv", "-x", "-H", f"ldap://{settings.HOST}:{settings.PORT}", + "-D", creds.un, + "-w", creds.pw, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE) @@ -181,8 +181,8 @@ async def test_ldap_bind(settings: Settings, creds: TestCreds) -> None: @pytest.mark.asyncio -@pytest.mark.usefixtures('setup_session') -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("setup_session") +@pytest.mark.usefixtures("session") async def test_bvalue_in_search_request( session: AsyncSession, ldap_bound_session: LDAPSession, @@ -206,13 +206,13 @@ async def test_bvalue_in_search_request( assert result for attr in result.partial_attributes: - if attr.type == 'attr_with_bvalue': + if attr.type == "attr_with_bvalue": assert isinstance(attr.vals[0], bytes) @pytest.mark.asyncio -@pytest.mark.usefixtures('setup_session') -@pytest.mark.usefixtures('session') +@pytest.mark.usefixtures("setup_session") +@pytest.mark.usefixtures("session") async def test_ldap_search_access_control_denied( settings: Settings, creds: TestCreds, @@ -223,19 +223,19 @@ async def test_ldap_search_access_control_denied( Default user can read himself and parent containers. """ proc = await asyncio.create_subprocess_exec( - 'ldapsearch', - '-vvv', '-x', '-H', f'ldap://{settings.HOST}:{settings.PORT}', - '-D', 'user_non_admin', - '-w', creds.pw, - '-b', 'dc=md,dc=test', 'objectclass=*', + "ldapsearch", + "-vvv", "-x", "-H", f"ldap://{settings.HOST}:{settings.PORT}", + "-D", "user_non_admin", + "-w", creds.pw, + "-b", "dc=md,dc=test", "objectclass=*", stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE) raw_data, _ = await proc.communicate() - data = raw_data.decode().split('\n') + data = raw_data.decode().split("\n") result = await proc.wait() - dn_list = [d for d in data if d.startswith('dn:')] + dn_list = [d for d in data if d.startswith("dn:")] assert result == 0 assert dn_list == [ @@ -245,7 +245,7 @@ async def test_ldap_search_access_control_denied( ] await create_access_policy( - name='Groups Read Access Policy', + name="Groups Read Access Policy", can_add=False, can_modify=False, can_read=True, @@ -257,27 +257,27 @@ async def test_ldap_search_access_control_denied( await session.commit() proc = await asyncio.create_subprocess_exec( - 'ldapsearch', - '-vvv', '-x', '-H', f'ldap://{settings.HOST}:{settings.PORT}', - '-D', 'user_non_admin', - '-w', creds.pw, - '-b', 'dc=md,dc=test', 'objectclass=*', + "ldapsearch", + "-vvv", "-x", "-H", f"ldap://{settings.HOST}:{settings.PORT}", + "-D", "user_non_admin", + "-w", creds.pw, + "-b", "dc=md,dc=test", "objectclass=*", stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE) raw_data, _ = await proc.communicate() - data = raw_data.decode().split('\n') + data = raw_data.decode().split("\n") result = await proc.wait() - dn_list = [d for d in data if d.startswith('dn:')] + dn_list = [d for d in data if d.startswith("dn:")] assert result == 0 assert sorted(dn_list) == sorted([ - 'dn: dc=md,dc=test', - 'dn: ou=users,dc=md,dc=test', - 'dn: cn=groups,dc=md,dc=test', - 'dn: cn=domain admins,cn=groups,dc=md,dc=test', - 'dn: cn=developers,cn=groups,dc=md,dc=test', - 'dn: cn=domain users,cn=groups,dc=md,dc=test', - 'dn: cn=user_non_admin,ou=users,dc=md,dc=test', + "dn: dc=md,dc=test", + "dn: ou=users,dc=md,dc=test", + "dn: cn=groups,dc=md,dc=test", + "dn: cn=domain admins,cn=groups,dc=md,dc=test", + "dn: cn=developers,cn=groups,dc=md,dc=test", + "dn: cn=domain users,cn=groups,dc=md,dc=test", + "dn: cn=user_non_admin,ou=users,dc=md,dc=test", ]) diff --git a/tests/test_ldap/test_util/test_whoami.py b/tests/test_ldap/test_util/test_whoami.py index 75eb0e79..b9ff524e 100644 --- a/tests/test_ldap/test_util/test_whoami.py +++ b/tests/test_ldap/test_util/test_whoami.py @@ -13,24 +13,24 @@ @pytest.mark.asyncio -@pytest.mark.usefixtures('setup_session') +@pytest.mark.usefixtures("setup_session") async def test_anonymous_whoami(settings: Settings) -> None: """Test anonymous whoami.""" proc = await asyncio.create_subprocess_exec( - 'ldapwhoami', '-x', '-H', f'ldap://{settings.HOST}:{settings.PORT}') + "ldapwhoami", "-x", "-H", f"ldap://{settings.HOST}:{settings.PORT}") assert await proc.wait() == 0 @pytest.mark.asyncio -@pytest.mark.usefixtures('setup_session') +@pytest.mark.usefixtures("setup_session") async def test_binded_whoami(settings: Settings, creds: TestCreds) -> None: """Test anonymous whoami.""" proc = await asyncio.create_subprocess_exec( - 'ldapwhoami', '-x', - '-H', f'ldap://{settings.HOST}:{settings.PORT}', - '-D', creds.un, - '-w', creds.pw, + "ldapwhoami", "-x", + "-H", f"ldap://{settings.HOST}:{settings.PORT}", + "-D", creds.un, + "-w", creds.pw, ) assert await proc.wait() == 0 From 62c6faad8f7540f37c141ebd183487e9f2dd5e7a Mon Sep 17 00:00:00 2001 From: Milov Dmitriy Date: Tue, 18 Feb 2025 14:49:45 +0300 Subject: [PATCH 03/11] fix: ruff github ci --- .github/workflows/checks.yml | 22 +--------------------- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 22 deletions(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 44e4021f..8b271051 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -26,27 +26,7 @@ jobs: - name: Run linters env: NEW_TAG: linter - run: docker run $NEW_TAG ruff check - - flake8: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Login to GitHub Container Registry - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - name: build linters - env: - TAG: ghcr.io/${{ env.REPO }}_linters:latest - NEW_TAG: linter - run: docker build --target=runtime -f .docker/lint.Dockerfile . -t $NEW_TAG --cache-to type=gha,mode=max --cache-from $TAG --build-arg BUILDKIT_INLINE_CACHE=1 - - name: Run linters - env: - NEW_TAG: linter - run: docker run $NEW_TAG flake8 + run: docker run $NEW_TAG ruff check --output-format=github . mypy: runs-on: ubuntu-latest diff --git a/pyproject.toml b/pyproject.toml index 9a036a42..507bda82 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -204,7 +204,7 @@ unfixable = [] [tool.ruff.lint.per-file-ignores] "tests/*.py" = ["S101"] # Ignore `Flake8-bandit S101` rule for the `tests/` directory. -# "alembic/*.py" = ["I001"] # Ignore `Flake8-isort IO01` rule for the `alembic/` directory. It works incorrect in CI ruff test. +"alembic/*.py" = ["I001"] # Ignore `Flake8-isort IO01` rule for the `alembic/` directory. It works incorrect in CI ruff test. [tool.ruff.lint.mccabe] # 30 Complexity level is too high, need to reduce this level or ignore it `# noqa: C901`. From c0232ccb60415aaec0c5d51c66317c7bb92595c7 Mon Sep 17 00:00:00 2001 From: Milov Dmitriy Date: Tue, 18 Feb 2025 14:58:27 +0300 Subject: [PATCH 04/11] fix: ruff linters --- app/ldap_protocol/asn1parser.py | 2 +- app/ldap_protocol/filter_interpreter.py | 9 ++-- app/ldap_protocol/ldap_requests/add.py | 23 +++++---- app/ldap_protocol/ldap_requests/bind.py | 63 ++++++++++++----------- app/ldap_protocol/ldap_requests/search.py | 18 ++++--- tests/conftest.py | 4 +- 6 files changed, 65 insertions(+), 54 deletions(-) diff --git a/app/ldap_protocol/asn1parser.py b/app/ldap_protocol/asn1parser.py index 166fdfd2..20729d9f 100644 --- a/app/ldap_protocol/asn1parser.py +++ b/app/ldap_protocol/asn1parser.py @@ -139,7 +139,7 @@ def _handle_substring(self) -> str: return substring_tag_map[substring_tag] - def serialize(self, obj: "ASN1Row" | T | None = None) -> str: + def serialize(self, obj: "ASN1Row | T | None" = None) -> str: """Serialize an ASN.1 object or list into a string. Recursively processes ASN.1 structures to construct a valid LDAP diff --git a/app/ldap_protocol/filter_interpreter.py b/app/ldap_protocol/filter_interpreter.py index 36077f05..dcaf6276 100644 --- a/app/ldap_protocol/filter_interpreter.py +++ b/app/ldap_protocol/filter_interpreter.py @@ -152,9 +152,12 @@ def _cast_item(item: ASN1Row) -> UnaryExpression | ColumnElement: return Attribute.name.ilike(item.value.lower()) - if len(item.value) == 3 and isinstance(item.value[1].value, bytes): - if item.value[1].value.decode("utf-8").lower() in MEMBERS_ATTRS: - return _ldap_filter_by_attribute(*item.value) # NOTE: oid + if ( + len(item.value) == 3 + and isinstance(item.value[1].value, bytes) + and item.value[1].value.decode("utf-8").lower() in MEMBERS_ATTRS + ): + return _ldap_filter_by_attribute(*item.value) # NOTE: oid left, right = item.value attr = left.value.lower().replace("objectcategory", "objectclass") diff --git a/app/ldap_protocol/ldap_requests/add.py b/app/ldap_protocol/ldap_requests/add.py index 146d7405..6a7704a5 100644 --- a/app/ldap_protocol/ldap_requests/add.py +++ b/app/ldap_protocol/ldap_requests/add.py @@ -86,7 +86,7 @@ def from_data(cls, data: ASN1Row) -> "AddRequest": ] return cls(entry=entry.value, attributes=attributes) # type: ignore - async def handle( + async def handle( # noqa: C901 self, session: AsyncSession, ldap_session: LDAPSession, @@ -104,8 +104,7 @@ async def handle( root_dn = get_search_path(self.entry) exists_q = select( - select(Directory) - .filter(get_path_filter(root_dn)).exists(), + select(Directory).filter(get_path_filter(root_dn)).exists(), ) if await session.scalar(exists_q) is True: @@ -187,8 +186,9 @@ async def handle( user_attributes: dict[str, str] = {} group_attributes: list[str] = [] user_fields = User.search_fields.keys() | User.fields.keys() - attributes.append(Attribute( - name=new_dn, value=name, directory=new_dir)) + attributes.append( + Attribute(name=new_dn, value=name, directory=new_dir) + ) for attr in self.attributes: lname = attr.type.lower() @@ -239,10 +239,12 @@ async def handle( ) sam_accout_name = user_attributes.get( - "sAMAccountName", create_user_name(new_dir.id), + "sAMAccountName", + create_user_name(new_dir.id), ) user_principal_name = user_attributes.get( - "userPrincipalName", f"{sam_accout_name!r}@{base_dn.name}", + "userPrincipalName", + f"{sam_accout_name!r}@{base_dn.name}", ) user = User( sam_accout_name=sam_accout_name, @@ -316,9 +318,7 @@ async def handle( if (is_user or is_group) and "gidnumber" not in self.attr_names: reverse_d_name = new_dir.name[::-1] value = ( - "513" - if is_user - else str(create_integer_hash(reverse_d_name)) + "513" if is_user else str(create_integer_hash(reverse_d_name)) ) attributes.append( Attribute( @@ -348,7 +348,8 @@ async def handle( await kadmin.add_principal(user.get_upn_prefix(), pw) if is_computer: await kadmin.add_principal( - f"{new_dir.host_principal}.{base_dn.name}", None, + f"{new_dir.host_principal}.{base_dn.name}", + None, ) await kadmin.add_principal(new_dir.host_principal, None) except KRBAPIError: diff --git a/app/ldap_protocol/ldap_requests/bind.py b/app/ldap_protocol/ldap_requests/bind.py index 7848df2e..dd70de7d 100644 --- a/app/ldap_protocol/ldap_requests/bind.py +++ b/app/ldap_protocol/ldap_requests/bind.py @@ -138,14 +138,15 @@ async def handle( yield BindResponse(result_code=LDAPCodes.SUCCESS) return - if isinstance(self.authentication_choice, SaslGSSAPIAuthentication): - if response := await self.authentication_choice.step( - session, - ldap_session, - settings, - ): - yield response - return + if isinstance( + self.authentication_choice, SaslGSSAPIAuthentication + ) and ( + response := await self.authentication_choice.step( + session, ldap_session, settings + ) + ): + yield response + return user = await self.authentication_choice.get_user(session, self.name) @@ -163,11 +164,13 @@ async def handle( yield get_bad_response(LDAPBindErrors.LOGON_FAILURE) return - policy = await PasswordPolicySchema.get_policy_settings( - session, kadmin, + policy_pwd = await PasswordPolicySchema.get_policy_settings( + session, kadmin + ) + p_last_set = await policy_pwd.get_pwd_last_set( + session, user.directory_id ) - p_last_set = await policy.get_pwd_last_set(session, user.directory_id) - pwd_expired = policy.validate_max_age(p_last_set) + pwd_expired = policy_pwd.validate_max_age(p_last_set) is_krb_user = await check_kerberos_group(user, session) @@ -183,24 +186,24 @@ async def handle( yield get_bad_response(LDAPBindErrors.PASSWORD_MUST_CHANGE) return - if policy := getattr(ldap_session, "policy", None): # type: ignore - if policy.mfa_status in (MFAFlags.ENABLED, MFAFlags.WHITELIST): - - request_2fa = True - if policy.mfa_status == MFAFlags.WHITELIST: - request_2fa = await check_mfa_group(policy, user, session) - - if request_2fa: - mfa_status = await self.check_mfa( - mfa, - user.user_principal_name, - self.authentication_choice.otpassword, - policy, - ) - - if mfa_status is False: - yield get_bad_response(LDAPBindErrors.LOGON_FAILURE) - return + if ( + policy := getattr(ldap_session, "policy", None) + ) and policy.mfa_status in (MFAFlags.ENABLED, MFAFlags.WHITELIST): + request_2fa = True + if policy.mfa_status == MFAFlags.WHITELIST: + request_2fa = await check_mfa_group(policy, user, session) + + if request_2fa: + mfa_status = await self.check_mfa( + mfa, + user.user_principal_name, + self.authentication_choice.otpassword, + policy, + ) + + if mfa_status is False: + yield get_bad_response(LDAPBindErrors.LOGON_FAILURE) + return with contextlib.suppress(KRBAPIError, httpx.TimeoutException): await kadmin.add_principal( diff --git a/app/ldap_protocol/ldap_requests/search.py b/app/ldap_protocol/ldap_requests/search.py index fcefc0ee..5ee7465c 100644 --- a/app/ldap_protocol/ldap_requests/search.py +++ b/app/ldap_protocol/ldap_requests/search.py @@ -440,10 +440,13 @@ async def tree_view( str(get_windows_timestamp(directory.user.last_logon))) attrs["authTimestamp"].append(directory.user.last_logon) - if self.member_of: - if "group" in obj_classes or "user" in obj_classes: - for group in directory.groups: - attrs["memberOf"].append(group.directory.path_dn) + if ( + self.member_of + and "group" in obj_classes + or "user" in obj_classes + ): + for group in directory.groups: + attrs["memberOf"].append(group.directory.path_dn) if self.token_groups and "user" in obj_classes: attrs["tokenGroups"].append( @@ -458,10 +461,9 @@ async def tree_view( attrs["tokenGroups"].append( str(string_to_sid(directory_.object_sid))) - if self.member: - if "group" in obj_classes and directory.group: - for member in directory.group.members: - attrs["member"].append(member.path_dn) + if self.member and "group" in obj_classes and directory.group: + for member in directory.group.members: + attrs["member"].append(member.path_dn) if directory.user: if self.all_attrs: diff --git a/tests/conftest.py b/tests/conftest.py index e73381be..24769cfb 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -78,7 +78,9 @@ async def get_kadmin(self) -> AsyncIterator[AsyncMock]: ok_response = Mock() ok_response.status_code = 200 - ok_response.aiter_bytes.return_value = map(bytes, zip(b"test_string", strict=False)) + ok_response.aiter_bytes.return_value = map( + bytes, zip(b"test_string", strict=False) + ) kadmin.setup = AsyncMock() kadmin.ktadd = AsyncMock(return_value=ok_response) From 7e6c4d1f273f8d3c9fb42c150ac17083bcd04473 Mon Sep 17 00:00:00 2001 From: Milov Dmitriy Date: Tue, 18 Feb 2025 15:44:31 +0300 Subject: [PATCH 05/11] fix: ruff config --- app/api/auth/router.py | 2 +- app/api/auth/router_mfa.py | 29 +++++++++++++++----------- app/api/main/krb5_router.py | 5 +++-- app/api/network/router.py | 12 +++++------ app/ldap_protocol/ldap_requests/add.py | 2 +- pyproject.toml | 26 ++--------------------- 6 files changed, 30 insertions(+), 46 deletions(-) diff --git a/app/api/auth/router.py b/app/api/auth/router.py index c89637d6..7cc8c873 100644 --- a/app/api/auth/router.py +++ b/app/api/auth/router.py @@ -82,7 +82,7 @@ async def login( :raises HTTPException: 403 if user not part of network policy :raises HTTPException: 426 if mfa required :return None: None - """ # noqa: D301 + """ user = await authenticate_user(session, form.username, form.password) if not user: diff --git a/app/api/auth/router_mfa.py b/app/api/auth/router_mfa.py index f8f1b611..c4ead5ba 100644 --- a/app/api/auth/router_mfa.py +++ b/app/api/auth/router_mfa.py @@ -62,11 +62,12 @@ async def setup_mfa( session: FromDishka[AsyncSession], ) -> bool: """Set mfa credentials, rewrites if exists. + \f :param MFACreateRequest mfa: MuliFactor credentials :param FromDishka[AsyncSession] session: db - :return bool: status. - """ # noqa: D301 + :return bool: status + """ async with session.begin_nested(): await session.execute( ( @@ -100,8 +101,7 @@ async def remove_mfa( keys = ["mfa_key_ldap", "mfa_secret_ldap"] await session.execute( - delete(CatalogueSetting) - .filter(CatalogueSetting.name.in_(keys)), + delete(CatalogueSetting).filter(CatalogueSetting.name.in_(keys)), ) await session.commit() @@ -114,7 +114,7 @@ async def get_mfa( """Get MFA creds. \f :return MFAGetResponse: response. - """ # noqa: D301 + """ if not mfa_creds: mfa_creds = MFA_HTTP_Creds(Creds(None, None)) if not mfa_creds_ldap: @@ -130,8 +130,9 @@ async def get_mfa( @mfa_router.post("/create", name="callback_mfa", include_in_schema=True) async def callback_mfa( - access_token: Annotated[str, Form( - alias="accessToken", validation_alias="accessToken")], + access_token: Annotated[ + str, Form(alias="accessToken", validation_alias="accessToken") + ], session: FromDishka[AsyncSession], storage: FromDishka[SessionStorage], settings: FromDishka[Settings], @@ -152,7 +153,7 @@ async def callback_mfa( :param Annotated[str, Form access_token: token from multifactor callback :raises HTTPException: if mfa not set up :return RedirectResponse: on bypass or success - """ # noqa: D301 + """ if not mfa_creds: raise HTTPException(status.HTTP_404_NOT_FOUND) @@ -174,7 +175,8 @@ async def callback_mfa( response = RedirectResponse("/", status.HTTP_302_FOUND) await create_and_set_session_key( - user, session, settings, response, storage, ip, user_agent) + user, session, settings, response, storage, ip, user_agent + ) return response @@ -238,7 +240,8 @@ async def two_factor_protocol( except MultifactorAPI.MFAConnectError: if network_policy.bypass_no_connection: await create_and_set_session_key( - user, session, settings, response, storage, ip, user_agent) + user, session, settings, response, storage, ip, user_agent + ) return MFAChallengeResponse(status="bypass", message="") logger.critical(f"API error {traceback.format_exc()}") @@ -249,13 +252,15 @@ async def two_factor_protocol( except MultifactorAPI.MFAMissconfiguredError: await create_and_set_session_key( - user, session, settings, response, storage, ip, user_agent) + user, session, settings, response, storage, ip, user_agent + ) return MFAChallengeResponse(status="bypass", message="") except MultifactorAPI.MultifactorError: if network_policy.bypass_service_failure: await create_and_set_session_key( - user, session, settings, response, storage, ip, user_agent) + user, session, settings, response, storage, ip, user_agent + ) return MFAChallengeResponse(status="bypass", message="") logger.critical(f"API error {traceback.format_exc()}") diff --git a/app/api/main/krb5_router.py b/app/api/main/krb5_router.py index e98b24c3..080fd8db 100644 --- a/app/api/main/krb5_router.py +++ b/app/api/main/krb5_router.py @@ -349,11 +349,12 @@ async def delete_principal( principal_name: Annotated[LIMITED_STR, Body(embed=True)], kadmin: FromDishka[AbstractKadmin], ) -> None: - r"""Delete principal in kerberos with given name. + """Delete principal in kerberos with given name. + \f :param Annotated[str, Body principal_name: upn :param FromDishka[AbstractKadmin] kadmin: _description_ - :raises HTTPException: on failed kamin request. + :raises HTTPException: on failed kamin request """ try: await kadmin.del_principal(principal_name) diff --git a/app/api/network/router.py b/app/api/network/router.py index 61ce3967..cc92a8f2 100644 --- a/app/api/network/router.py +++ b/app/api/network/router.py @@ -48,7 +48,7 @@ async def add_network_policy( :raises HTTPException: 422 invalid group DN :raises HTTPException: 422 Entry already exists :return PolicyResponse: Ready policy - """ # noqa: D301 + """ new_policy = NetworkPolicy( name=policy.name, netmasks=policy.complete_netmasks, @@ -110,7 +110,7 @@ async def get_list_network_policies( \f :return list[PolicyResponse]: all policies - """ # noqa: D301 + """ groups = selectinload(NetworkPolicy.groups).selectinload(Group.directory) mfa_groups = ( selectinload(NetworkPolicy.mfa_groups) @@ -162,7 +162,7 @@ async def delete_network_policy( :raises HTTPException: 422 On last active policy, at least 1 should be in database. :return bool: status of delete - """ # noqa: D301 + """ policy = await session.get(NetworkPolicy, policy_id, with_for_update=True) if not policy: @@ -204,7 +204,7 @@ async def switch_network_policy( :raises HTTPException: 422 On last active policy, at least 1 should be active :return bool: status of update - """ # noqa: D301 + """ policy = await session.get(NetworkPolicy, policy_id, with_for_update=True) if not policy: @@ -231,7 +231,7 @@ async def update_network_policy( :raises HTTPException: 422 Invalid group DN :raises HTTPException: 422 Entry already exists :return PolicyResponse: Policy from database - """ # noqa: D301 + """ selected_policy = await session.get( NetworkPolicy, request.id, @@ -328,7 +328,7 @@ async def swap_network_policy( :param int second_policy_id: policy to swap :raises HTTPException: 404 :return SwapResponse: policy new priorities - """ # noqa: D301 + """ policy1 = await session.get( NetworkPolicy, swap.first_policy_id, with_for_update=True, ) diff --git a/app/ldap_protocol/ldap_requests/add.py b/app/ldap_protocol/ldap_requests/add.py index 6a7704a5..dc40b799 100644 --- a/app/ldap_protocol/ldap_requests/add.py +++ b/app/ldap_protocol/ldap_requests/add.py @@ -86,7 +86,7 @@ def from_data(cls, data: ASN1Row) -> "AddRequest": ] return cls(entry=entry.value, attributes=attributes) # type: ignore - async def handle( # noqa: C901 + async def handle( self, session: AsyncSession, ldap_session: LDAPSession, diff --git a/pyproject.toml b/pyproject.toml index 507bda82..eba314a6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -87,7 +87,6 @@ show_missing = true [tool.coverage.run] concurrency = ["thread", "gevent"] - # RUFF # Ruff is a linter, not a type checker. # @@ -109,27 +108,6 @@ line-ending = "lf" skip-magic-trailing-comma = false # default: false [tool.ruff.lint] -# Flake8 S101, I900, G004, IF100, S311, D301, E231 -# Для alembic предлагаю оставить включенным, только некоторые правила можно вырубить. -# S101 - done. Использование assert в коде. Вырубил только в тестах -# I900 - done. Это аналог RUF100. Проверка использования комментариев # noqa -# G004 - done. Аналог S102. Предлагаю врубить. Проверка использования небезопасных методов и функций (например, eval, exec) -# IF100 - done. Это аналог SIM101. Предлагаю врубить. Проверка использования if условий, которые могут быть упрощены -# S311 - done. Это аналог S311. Вырубил. -# D301 - done. Вырублены все D-rules. Мб включим когда-нибудь, а пока добавлю и закомментирую. -# E231 - done. Это не надо вырубать. - -# Ruff contains flake8-awesome. -# flake8-annotations -> ANN (Ruff) -# flake8-bandit -> S (Ruff) -# flake8-bugbear -> B (Ruff) -# flake8-commas -> COM (Ruff) -# flake8-docstrings -> D (Ruff) -# flake8-isort -> I (Ruff) -# flake8-pytest -> PT (Ruff) -# flake8-quotes -> Q (Ruff) -# flake8-simplify -> SIM (Ruff) -# flake8-type-checking -> TC (Ruff) select = [ "F", # Pyflakes. Must have "E", # pycodestyle (Error), check tool.ruff.lint.pycodestyle. Must have @@ -174,8 +152,8 @@ ignore = [ "D106", # TODO delete that and fix all errors "D107", # TODO delete that and fix all errors "D203", # this is necessary. Conflict with `D211` - "D205", # TODO delete that and fix all errors "D213", # this is necessary. Conflict with `D212` + "D301", # this is necessary. "UP007", # TODO delete that and fix all errors "UP015", # TODO delete that and fix all errors "UP017", # TODO delete that and fix all errors @@ -208,7 +186,7 @@ unfixable = [] [tool.ruff.lint.mccabe] # 30 Complexity level is too high, need to reduce this level or ignore it `# noqa: C901`. -max-complexity = 30 +max-complexity = 34 [tool.ruff.lint.isort] known-first-party = [ From 70610005303ccf27b0bd5616f895343fddbe7201 Mon Sep 17 00:00:00 2001 From: Milov Dmitriy Date: Tue, 18 Feb 2025 15:53:46 +0300 Subject: [PATCH 06/11] add: ruff config --- app/alembic/versions/6f8fe2548893_fix_read_only.py | 1 - app/alembic/versions/bv546ccd35fa_fix_krbadmin_attrs.py | 1 - app/alembic/versions/dafg3a4b22ab_add_preauth_princ.py | 1 - pyproject.toml | 8 +++++--- tests/test_ldap/test_util/test_add.py | 1 - 5 files changed, 5 insertions(+), 7 deletions(-) diff --git a/app/alembic/versions/6f8fe2548893_fix_read_only.py b/app/alembic/versions/6f8fe2548893_fix_read_only.py index e94d9ac1..f6627444 100644 --- a/app/alembic/versions/6f8fe2548893_fix_read_only.py +++ b/app/alembic/versions/6f8fe2548893_fix_read_only.py @@ -74,4 +74,3 @@ def upgrade() -> None: def downgrade() -> None: """Downgrade.""" - pass diff --git a/app/alembic/versions/bv546ccd35fa_fix_krbadmin_attrs.py b/app/alembic/versions/bv546ccd35fa_fix_krbadmin_attrs.py index cfa08334..dc3d467a 100644 --- a/app/alembic/versions/bv546ccd35fa_fix_krbadmin_attrs.py +++ b/app/alembic/versions/bv546ccd35fa_fix_krbadmin_attrs.py @@ -73,4 +73,3 @@ def upgrade() -> None: def downgrade() -> None: """Downgrade.""" - pass diff --git a/app/alembic/versions/dafg3a4b22ab_add_preauth_princ.py b/app/alembic/versions/dafg3a4b22ab_add_preauth_princ.py index 94c7b510..ca516218 100644 --- a/app/alembic/versions/dafg3a4b22ab_add_preauth_princ.py +++ b/app/alembic/versions/dafg3a4b22ab_add_preauth_princ.py @@ -75,4 +75,3 @@ def upgrade() -> None: def downgrade() -> None: """Downgrade.""" - pass diff --git a/pyproject.toml b/pyproject.toml index eba314a6..7ca4b4a0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -119,12 +119,12 @@ select = [ "D", # pydocstyle, check tool.ruff.lint.pydocstyle "UP", # pyupgrade, check tool.ruff.lint.pyupgrade. Must have "ANN", # flake8-annotations, check tool.ruff.lint.flake8-annotations - # "ASYNC", # flake8-async TODO uncomment, ruff fix and fix error + "ASYNC", # flake8-async "S", # flake8-bandit "B", # flake8-bugbear. Must have "COM", # flake8-commas # "CPY", # flake8-copyright TODO uncomment, ruff fix and fix error - # "PIE", # flake8-pie TODO uncomment, ruff fix and fix error + "PIE", # flake8-pie # "PYI", # flake8-pyi TODO uncomment, ruff fix and fix error "PT", # flake8-pytest "Q", # flake8-quotes @@ -134,7 +134,7 @@ select = [ "TC", # flake8-type-checking, check flake8-type-checking # "ARG", # flake8-unused-arguments TODO uncomment, ruff fix and fix error # "TD", # flake8-todos TODO uncomment, ruff fix and fix error - # "ERA", # eradicate TODO uncomment, ruff fix and fix error + "ERA", # eradicate # "PGH", # pygrep-hooks TODO does we need it? uncomment, ruff fix and fix error # "PL", # Pylint TODO uncomment, ruff fix and fix error # "DOC", # pydoclint TODO uncomment, ruff fix and fix error @@ -166,6 +166,8 @@ ignore = [ "ANN002", # this is necessary. "ANN003", # this is necessary. "ANN401", # TODO delete that and fix all errors + "ASYNC109", + "ASYNC230", "S311", # this is necessary. "B904", # this is necessary. "COM812", # this is necessary. Cause conflicts when used with the formatter diff --git a/tests/test_ldap/test_util/test_add.py b/tests/test_ldap/test_util/test_add.py index 3dff6b26..623668f5 100644 --- a/tests/test_ldap/test_util/test_add.py +++ b/tests/test_ldap/test_util/test_add.py @@ -49,7 +49,6 @@ async def test_ldap_root_add( stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE) - # print(await proc.communicate()) result = await proc.wait() assert result == 0 From 2e44ef3354f20f8d139f4534310cabd4f979f2b2 Mon Sep 17 00:00:00 2001 From: Milov Dmitriy Date: Tue, 18 Feb 2025 15:57:53 +0300 Subject: [PATCH 07/11] fix: ruff linter D205 --- app/api/auth/router.py | 2 +- app/api/auth/router_mfa.py | 4 +++- app/api/main/ap_router.py | 3 ++- app/api/main/krb5_router.py | 11 +++++++---- 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/app/api/auth/router.py b/app/api/auth/router.py index 7cc8c873..aa4b91d3 100644 --- a/app/api/auth/router.py +++ b/app/api/auth/router.py @@ -169,7 +169,7 @@ async def password_reset( session: FromDishka[AsyncSession], kadmin: FromDishka[AbstractKadmin], ) -> None: - r"""Reset user's (entry) password. + """Reset user's (entry) password. - **identity**: user identity, any `userPrincipalName`, `saMAccountName` or `DN` diff --git a/app/api/auth/router_mfa.py b/app/api/auth/router_mfa.py index c4ead5ba..e945fa9e 100644 --- a/app/api/auth/router_mfa.py +++ b/app/api/auth/router_mfa.py @@ -112,6 +112,7 @@ async def get_mfa( mfa_creds_ldap: FromDishka[MFA_LDAP_Creds], ) -> MFAGetResponse: """Get MFA creds. + \f :return MFAGetResponse: response. """ @@ -192,7 +193,8 @@ async def two_factor_protocol( ip: Annotated[IPv4Address | IPv6Address, Depends(get_ip_from_request)], user_agent: Annotated[str, Depends(get_user_agent_from_request)], ) -> MFAChallengeResponse: - r"""Initiate two factor protocol with app. + """Initiate two factor protocol with app. + \f :param Annotated[OAuth2Form, Depends form: password form :param Request request: FastAPI request diff --git a/app/api/main/ap_router.py b/app/api/main/ap_router.py index 4f9a8d14..b8e3e220 100644 --- a/app/api/main/ap_router.py +++ b/app/api/main/ap_router.py @@ -23,7 +23,8 @@ async def get_access_policies( session: FromDishka[AsyncSession], ) -> list[MaterialAccessPolicySchema]: - r"""Get APs. + """Get APs. + \f :param AccessPolicySchema policy: ap :param FromDishka[AsyncSession] session: db. diff --git a/app/api/main/krb5_router.py b/app/api/main/krb5_router.py index 080fd8db..9ed0dece 100644 --- a/app/api/main/krb5_router.py +++ b/app/api/main/krb5_router.py @@ -152,7 +152,7 @@ async def setup_kdc( settings: FromDishka[Settings], kadmin: FromDishka[AbstractKadmin], ) -> None: - r"""Set up KDC server. + """Set up KDC server. Create data structure in catalogue, generate config files, trigger commands @@ -291,7 +291,8 @@ async def add_principal( instance: Annotated[LIMITED_STR, Body()], kadmin: FromDishka[AbstractKadmin], ) -> None: - r"""Create principal in kerberos with given name. + """Create principal in kerberos with given name. + \f :param Annotated[str, Body principal_name: upn :param Annotated[LDAPSession, Depends ldap_session: ldap @@ -310,7 +311,8 @@ async def rename_principal( principal_new_name: Annotated[LIMITED_STR, Body()], kadmin: FromDishka[AbstractKadmin], ) -> None: - r"""Rename principal in kerberos with given name. + """Rename principal in kerberos with given name. + \f :param Annotated[str, Body principal_name: upn :param Annotated[LIMITED_STR, Body principal_new_name: _description_ @@ -330,7 +332,8 @@ async def reset_principal_pw( new_password: Annotated[LIMITED_STR, Body()], kadmin: FromDishka[AbstractKadmin], ) -> None: - r"""Reset principal password in kerberos with given name. + """Reset principal password in kerberos with given name. + \f :param Annotated[str, Body principal_name: upn :param Annotated[LIMITED_STR, Body new_password: _description_ From 7f8f22a27fe942a8b58f3f69953d33c29daa5afd Mon Sep 17 00:00:00 2001 From: Milov Dmitriy Date: Tue, 18 Feb 2025 16:09:09 +0300 Subject: [PATCH 08/11] refacrtor: ruff --- app/ldap_protocol/asn1parser.py | 2 +- app/ldap_protocol/ldap_requests/add.py | 2 +- app/ldap_protocol/ldap_requests/base.py | 13 +++---------- app/ldap_protocol/ldap_requests/modify.py | 2 +- app/ldap_protocol/ldap_requests/search.py | 2 +- app/schedule.py | 4 ++-- pyproject.toml | 6 +++--- 7 files changed, 12 insertions(+), 19 deletions(-) diff --git a/app/ldap_protocol/asn1parser.py b/app/ldap_protocol/asn1parser.py index 20729d9f..512bfff2 100644 --- a/app/ldap_protocol/asn1parser.py +++ b/app/ldap_protocol/asn1parser.py @@ -139,7 +139,7 @@ def _handle_substring(self) -> str: return substring_tag_map[substring_tag] - def serialize(self, obj: "ASN1Row | T | None" = None) -> str: + def serialize(self, obj: "ASN1Row | T | None" = None) -> str: # noqa: C901 """Serialize an ASN.1 object or list into a string. Recursively processes ASN.1 structures to construct a valid LDAP diff --git a/app/ldap_protocol/ldap_requests/add.py b/app/ldap_protocol/ldap_requests/add.py index dc40b799..6a7704a5 100644 --- a/app/ldap_protocol/ldap_requests/add.py +++ b/app/ldap_protocol/ldap_requests/add.py @@ -86,7 +86,7 @@ def from_data(cls, data: ASN1Row) -> "AddRequest": ] return cls(entry=entry.value, attributes=attributes) # type: ignore - async def handle( + async def handle( # noqa: C901 self, session: AsyncSession, ldap_session: LDAPSession, diff --git a/app/ldap_protocol/ldap_requests/base.py b/app/ldap_protocol/ldap_requests/base.py index ddbedf4d..f0f8529e 100644 --- a/app/ldap_protocol/ldap_requests/base.py +++ b/app/ldap_protocol/ldap_requests/base.py @@ -5,14 +5,7 @@ """ from abc import ABC, abstractmethod -from typing import ( - TYPE_CHECKING, - AsyncGenerator, - Callable, - ClassVar, - Protocol, - TypeAlias, -) +from typing import TYPE_CHECKING, AsyncGenerator, Callable, ClassVar, Protocol from dishka import AsyncContainer from loguru import logger @@ -34,8 +27,8 @@ colorize=False, ) -handler: TypeAlias = Callable[..., AsyncGenerator[BaseResponse, None]] -serializer: TypeAlias = Callable[..., "BaseRequest"] +type handler = Callable[..., AsyncGenerator[BaseResponse, None]] +type serializer = Callable[..., "BaseRequest"] if TYPE_CHECKING: diff --git a/app/ldap_protocol/ldap_requests/modify.py b/app/ldap_protocol/ldap_requests/modify.py index 6b9c313e..323871f9 100644 --- a/app/ldap_protocol/ldap_requests/modify.py +++ b/app/ldap_protocol/ldap_requests/modify.py @@ -337,7 +337,7 @@ async def _add_group_attrs( await session.commit() - async def _add( + async def _add( # noqa: C901 self, change: Changes, directory: Directory, diff --git a/app/ldap_protocol/ldap_requests/search.py b/app/ldap_protocol/ldap_requests/search.py index 5ee7465c..026ba521 100644 --- a/app/ldap_protocol/ldap_requests/search.py +++ b/app/ldap_protocol/ldap_requests/search.py @@ -398,7 +398,7 @@ async def paginate_query( return query, int(ceil(count / float(self.size_limit))), count - async def tree_view( + async def tree_view( # noqa: C901 self, query: Select, session: AsyncSession, ) -> AsyncGenerator[SearchResultEntry, None]: """Yield all resulted directories.""" diff --git a/app/schedule.py b/app/schedule.py index 34255de7..1a1d554d 100644 --- a/app/schedule.py +++ b/app/schedule.py @@ -1,7 +1,7 @@ """Simple scheduler for tasks.""" import asyncio -from typing import Callable, Coroutine, TypeAlias +from typing import Callable, Coroutine import uvloop from dishka import AsyncContainer, Scope, make_async_container @@ -16,7 +16,7 @@ from ioc import MainProvider from ldap_protocol.dependency import resolve_deps -task_type: TypeAlias = Callable[..., Coroutine] +type task_type = Callable[..., Coroutine] _TASKS: set[tuple[task_type, float]] = { (read_and_save_krb_pwds, 1.5), diff --git a/pyproject.toml b/pyproject.toml index 7ca4b4a0..3b5dda38 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -161,7 +161,7 @@ ignore = [ "UP034", # TODO delete that and fix all errors "UP035", # this is necessary. We allowed deprecated import "UP037", # TODO delete that and fix all errors - "UP040", # TODO delete that and fix all errors + # "UP040", # TODO delete that and fix all errors "ANN001", # TODO delete that and fix all errors "ANN002", # this is necessary. "ANN003", # this is necessary. @@ -187,8 +187,8 @@ unfixable = [] "alembic/*.py" = ["I001"] # Ignore `Flake8-isort IO01` rule for the `alembic/` directory. It works incorrect in CI ruff test. [tool.ruff.lint.mccabe] -# 30 Complexity level is too high, need to reduce this level or ignore it `# noqa: C901`. -max-complexity = 34 +# 22 Complexity level is too high, need to reduce this level or ignore it `# noqa: C901`. +max-complexity = 15 [tool.ruff.lint.isort] known-first-party = [ From f65162758651a33a83f377791ecff24e16533401 Mon Sep 17 00:00:00 2001 From: Milov Dmitriy Date: Tue, 18 Feb 2025 16:12:56 +0300 Subject: [PATCH 09/11] fix: ruff D***-rules linter --- app/api/auth/schema.py | 2 ++ app/api/main/schema.py | 2 ++ app/ldap_protocol/ldap_requests/bind_methods/base.py | 1 + app/ldap_protocol/ldap_responses.py | 2 ++ pyproject.toml | 6 ------ tests/conftest.py | 1 + 6 files changed, 8 insertions(+), 6 deletions(-) diff --git a/app/api/auth/schema.py b/app/api/auth/schema.py index cb283b59..c04f5abf 100644 --- a/app/api/auth/schema.py +++ b/app/api/auth/schema.py @@ -42,6 +42,8 @@ def __init__( username: str = Form(), password: str = Form(), ): + """Initialize form.""" + self.username = username self.password = password diff --git a/app/api/main/schema.py b/app/api/main/schema.py index 3df2741c..6fac17ff 100644 --- a/app/api/main/schema.py +++ b/app/api/main/schema.py @@ -36,6 +36,8 @@ async def handle_api( # type: ignore class SearchResponse(SearchResultDone): + """Search response for web api.""" + search_result: list[SearchResultEntry] diff --git a/app/ldap_protocol/ldap_requests/bind_methods/base.py b/app/ldap_protocol/ldap_requests/bind_methods/base.py index f8fab6bd..756cdf61 100644 --- a/app/ldap_protocol/ldap_requests/bind_methods/base.py +++ b/app/ldap_protocol/ldap_requests/bind_methods/base.py @@ -48,6 +48,7 @@ class LDAPBindErrors(StrEnum): ACCOUNT_LOCKED_OUT = "775" def __str__(self) -> str: + """Return the error message as a string.""" return ( "80090308: LdapErr: DSID-0C09030B, " "comment: AcceptSecurityContext error, " diff --git a/app/ldap_protocol/ldap_responses.py b/app/ldap_protocol/ldap_responses.py index c4f6bd94..f4c28542 100644 --- a/app/ldap_protocol/ldap_responses.py +++ b/app/ldap_protocol/ldap_responses.py @@ -34,6 +34,8 @@ class LDAPResult(BaseModel): error_message: str = Field("", alias="errorMessage") class Config: + """Allow class to use property.""" + populate_by_name = True arbitrary_types_allowed = True json_encoders = { diff --git a/pyproject.toml b/pyproject.toml index 3b5dda38..7e396832 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -144,13 +144,8 @@ select = [ # Gradually remove all values marked 'TODO' and fix errors. ignore = [ - "D101", # TODO delete that and fix all errors "D102", # TODO delete that and fix all errors - "D103", # TODO delete that and fix all errors "D104", # TODO delete that and fix all errors - "D105", # TODO delete that and fix all errors - "D106", # TODO delete that and fix all errors - "D107", # TODO delete that and fix all errors "D203", # this is necessary. Conflict with `D211` "D213", # this is necessary. Conflict with `D212` "D301", # this is necessary. @@ -161,7 +156,6 @@ ignore = [ "UP034", # TODO delete that and fix all errors "UP035", # this is necessary. We allowed deprecated import "UP037", # TODO delete that and fix all errors - # "UP040", # TODO delete that and fix all errors "ANN001", # TODO delete that and fix all errors "ANN002", # this is necessary. "ANN003", # this is necessary. diff --git a/tests/conftest.py b/tests/conftest.py index 24769cfb..04f8baac 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -262,6 +262,7 @@ async def kadmin(container: AsyncContainer) -> AsyncIterator[AbstractKadmin]: @pytest.fixture(scope="session") def event_loop() -> Generator: # noqa: indirect usage + """Create uvloop event loop.""" loop = uvloop.new_event_loop() yield loop with suppress(asyncio.CancelledError, RuntimeError): From 38a6073fbadfae84a14175b7d3569daa7eb46536 Mon Sep 17 00:00:00 2001 From: Milov Dmitriy Date: Tue, 18 Feb 2025 16:17:12 +0300 Subject: [PATCH 10/11] fix: ruff UP*** rules --- app/api/auth/schema.py | 1 - app/api/main/schema.py | 12 ++++++------ app/extra/scripts/krb_pass_sync.py | 2 +- app/ldap_protocol/dialogue.py | 6 +++--- app/ldap_protocol/dns.py | 2 +- app/ldap_protocol/utils/helpers.py | 4 ++-- app/models.py | 4 ++-- app/multidirectory.py | 2 +- pyproject.toml | 4 ---- tests/conftest.py | 2 +- 10 files changed, 17 insertions(+), 22 deletions(-) diff --git a/app/api/auth/schema.py b/app/api/auth/schema.py index c04f5abf..e8b2f332 100644 --- a/app/api/auth/schema.py +++ b/app/api/auth/schema.py @@ -43,7 +43,6 @@ def __init__( password: str = Form(), ): """Initialize form.""" - self.username = username self.password = password diff --git a/app/api/main/schema.py b/app/api/main/schema.py index 6fac17ff..4c77429d 100644 --- a/app/api/main/schema.py +++ b/app/api/main/schema.py @@ -3,7 +3,7 @@ Copyright (c) 2024 MultiFactor License: https://github.com/MultiDirectoryLab/MultiDirectory/blob/main/LICENSE """ -from typing import Optional, final +from typing import final from dishka import AsyncContainer from pydantic import BaseModel, Field, SecretStr @@ -75,8 +75,8 @@ class DNSServiceSetupRequest(BaseModel): dns_status: DNSManagerState domain: str - dns_ip_address: Optional[str] = Field(None) - tsig_key: Optional[str] = Field(None) + dns_ip_address: str | None = Field(None) + tsig_key: str | None = Field(None) class DNSServiceRecordBaseRequest(BaseModel): @@ -90,7 +90,7 @@ class DNSServiceRecordCreateRequest(DNSServiceRecordBaseRequest): """DNS create request schema.""" record_value: str - ttl: Optional[int] = Field(None) + ttl: int | None = Field(None) class DNSServiceRecordDeleteRequest(DNSServiceRecordBaseRequest): @@ -102,5 +102,5 @@ class DNSServiceRecordDeleteRequest(DNSServiceRecordBaseRequest): class DNSServiceRecordUpdateRequest(DNSServiceRecordBaseRequest): """DNS update request schema.""" - record_value: Optional[str] = Field(None) - ttl: Optional[int] = Field(None) + record_value: str | None = Field(None) + ttl: int | None = Field(None) diff --git a/app/extra/scripts/krb_pass_sync.py b/app/extra/scripts/krb_pass_sync.py index 9b1edb6d..45562786 100644 --- a/app/extra/scripts/krb_pass_sync.py +++ b/app/extra/scripts/krb_pass_sync.py @@ -42,7 +42,7 @@ async def read_and_save_krb_pwds(session: AsyncSession) -> None: domain = domains[0].name for path in files: - with open(path, "r") as file: + with open(path) as file: data = file.read().split("\n") username, password = data[0], data[3] diff --git a/app/ldap_protocol/dialogue.py b/app/ldap_protocol/dialogue.py index 3aab232d..93679ef6 100644 --- a/app/ldap_protocol/dialogue.py +++ b/app/ldap_protocol/dialogue.py @@ -48,7 +48,7 @@ async def from_db( cls, user: User, session_id: str, - ) -> "UserSchema": + ) -> UserSchema: """Create model from db model.""" return cls( id=user.id, @@ -76,7 +76,7 @@ class LDAPSession: gssapi_authenticated: bool = False gssapi_security_context: gssapi.SecurityContext | None = None - gssapi_security_layer: "GSSAPISL" + gssapi_security_layer: GSSAPISL def __init__( self, *, user: UserSchema | None = None, @@ -85,7 +85,7 @@ def __init__( """Set lock.""" self._lock = asyncio.Lock() self._user: UserSchema | None = user - self.queue: asyncio.Queue["LDAPRequestMessage"] = asyncio.Queue() + self.queue: asyncio.Queue[LDAPRequestMessage] = asyncio.Queue() self.id = uuid.uuid4() self.storage = storage diff --git a/app/ldap_protocol/dns.py b/app/ldap_protocol/dns.py index bccc2be9..e123bcfd 100644 --- a/app/ldap_protocol/dns.py +++ b/app/ldap_protocol/dns.py @@ -163,7 +163,7 @@ async def setup( with open(settings.DNS_SERVER_NAMED_CONF, "a") as f: f.write('\ninclude "/opt/zone.key";') - with open(settings.DNS_TSIG_KEY, "r") as f: + with open(settings.DNS_TSIG_KEY) as f: key_file_content = f.read() tsig_key = re.findall(r"\ssecret \"(\S+)\"", key_file_content)[0] diff --git a/app/ldap_protocol/utils/helpers.py b/app/ldap_protocol/utils/helpers.py index 1f213f8f..687886d8 100644 --- a/app/ldap_protocol/utils/helpers.py +++ b/app/ldap_protocol/utils/helpers.py @@ -173,7 +173,7 @@ def get_attribute_types() -> list[str]: :return list[list[str]]: attrs """ - with open("extra/adTypes.txt", "r") as file: + with open("extra/adTypes.txt") as file: return [line.replace(")\n", " )") for line in file] @@ -182,7 +182,7 @@ def get_object_classes() -> list[str]: :return list[list[str]]: attrs """ - with open("extra/adClasses.txt", "r") as file: + with open("extra/adClasses.txt") as file: return list(file) diff --git a/app/models.py b/app/models.py index d1abdc89..29c131e4 100644 --- a/app/models.py +++ b/app/models.py @@ -143,7 +143,7 @@ class Directory(Base): nullable=True, ) - parent: Mapped["Directory | None"] = relationship( + parent: Mapped[Directory | None] = relationship( lambda: Directory, remote_side="Directory.id", backref=backref("directories", cascade="all,delete", viewonly=True), @@ -277,7 +277,7 @@ def path_dn(self) -> str: def create_path( self, - parent: "Directory | None" = None, + parent: Directory | None = None, dn: str = "cn", ) -> None: """Create path from a new directory.""" diff --git a/app/multidirectory.py b/app/multidirectory.py index 377f958e..a1903e82 100644 --- a/app/multidirectory.py +++ b/app/multidirectory.py @@ -59,7 +59,7 @@ async def proc_time_header_middleware( start_time = time.perf_counter() response = await call_next(request) process_time = time.perf_counter() - start_time - response.headers["X-Process-Time"] = "{:.4f}".format(process_time) + response.headers["X-Process-Time"] = f"{process_time:.4f}" return response diff --git a/pyproject.toml b/pyproject.toml index 7e396832..0f737b63 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -149,13 +149,9 @@ ignore = [ "D203", # this is necessary. Conflict with `D211` "D213", # this is necessary. Conflict with `D212` "D301", # this is necessary. - "UP007", # TODO delete that and fix all errors - "UP015", # TODO delete that and fix all errors "UP017", # TODO delete that and fix all errors - "UP032", # TODO delete that and fix all errors "UP034", # TODO delete that and fix all errors "UP035", # this is necessary. We allowed deprecated import - "UP037", # TODO delete that and fix all errors "ANN001", # TODO delete that and fix all errors "ANN002", # this is necessary. "ANN003", # this is necessary. diff --git a/tests/conftest.py b/tests/conftest.py index 04f8baac..47f68a35 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -261,7 +261,7 @@ async def kadmin(container: AsyncContainer) -> AsyncIterator[AbstractKadmin]: @pytest.fixture(scope="session") -def event_loop() -> Generator: # noqa: indirect usage +def event_loop() -> Generator: """Create uvloop event loop.""" loop = uvloop.new_event_loop() yield loop From acc4a56112912193eb9e374a90f9b185fe25d489 Mon Sep 17 00:00:00 2001 From: Milov Dmitriy Date: Thu, 20 Feb 2025 11:31:36 +0300 Subject: [PATCH 11/11] add: ignore B905 ruff rule --- app/ldap_protocol/session_storage.py | 2 +- pyproject.toml | 1 + tests/conftest.py | 4 +--- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/app/ldap_protocol/session_storage.py b/app/ldap_protocol/session_storage.py index 38c4f6bc..eb70d86a 100644 --- a/app/ldap_protocol/session_storage.py +++ b/app/ldap_protocol/session_storage.py @@ -264,7 +264,7 @@ async def get_user_sessions(self, uid: int) -> dict: retval = {} - for k, v in zip(keys, data, strict=False): + for k, v in zip(keys, data): if v is not None: tmp = json.loads(v) if k.startswith("ldap"): diff --git a/pyproject.toml b/pyproject.toml index 0f737b63..0b9b1b28 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -165,6 +165,7 @@ ignore = [ "TC002", # this is necessary. "TC003", # this is necessary. "SIM101", # analogue simplify-boolean-expressions IF100 + "B905", # this is necessary. get-attr-with-constant ] extend-select = [] diff --git a/tests/conftest.py b/tests/conftest.py index 47f68a35..cf9769a4 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -78,9 +78,7 @@ async def get_kadmin(self) -> AsyncIterator[AsyncMock]: ok_response = Mock() ok_response.status_code = 200 - ok_response.aiter_bytes.return_value = map( - bytes, zip(b"test_string", strict=False) - ) + ok_response.aiter_bytes.return_value = map(bytes, zip(b"test_string")) kadmin.setup = AsyncMock() kadmin.ktadd = AsyncMock(return_value=ok_response)