Skip to content

tests: Add tests for AD Forest multidomain and gpo tests - #9045

Merged
thalman merged 2 commits into
SSSD:masterfrom
jakub-vavra-cz:adforest
Oct 7, 2026
Merged

thalman merged 2 commits into
SSSD:masterfrom
jakub-vavra-cz:adforest

Conversation

@jakub-vavra-cz

@jakub-vavra-cz jakub-vavra-cz commented Aug 5, 2026 •

Copy link
Copy Markdown
Contributor

Re-design and rewrite of legacy sssd-qe ad_forest and admultidomain suites into test_adforest.py and test_adforest_gpo.py. Covers lookup, auth, simple access, ad_access_filter, join other DC scenarios.

These suites are downstream (idmci) only.

Deprecates:
sssd-qe/client/ad_provider/ad_forest
sssd-qe/client/ad_provider/ad_gpo_hbac_multidomain
sssd/src/tests/multihost/admultidomain/test_multidomain.py

Not covered (different topology):
sssd/src/tests/multihost/admultidomain/test_multiforest.py

Depends on SSSD/sssd-test-framework#263

Comment thread src/tests/system/tests/test_adforest_gpo.py Fixed
Comment thread src/tests/system/tests/test_adforest_gpo.py Fixed
Comment thread src/tests/system/tests/test_adforest_gpo.py Fixed
@danlavu
danlavu self-requested a review August 5, 2026 22:22
@danlavu danlavu self-assigned this Aug 5, 2026
@danlavu

danlavu commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

I know this is in a draft, I went ahead and took the liberty of making the multidomain gpo tests closer to the current gpo tests.

"""
AD Forest Multi-Domain GPO Tests

Covers GPO HBAC enforcement for users in trusted child/tree domains while
the client is joined to the root domain.

Requires ``KnownTopology.ADForest`` (three Windows AD hosts: root, child, tree)
and the companion sssd-test-framework PR:
https://github.com/SSSD/sssd-test-framework/pull/263

:requirement: AD Forest GPO
"""

from __future__ import annotations

import pytest
from sssd_test_framework.roles.ad import AD
from sssd_test_framework.roles.client import Client
from sssd_test_framework.topology import KnownTopology


@pytest.mark.importance("high")
@pytest.mark.parametrize("trusted_name", ["child", "tree"])
@pytest.mark.topology(KnownTopology.ADForest)
def test_gpo_multidomain__is_set_to_disabled_and_all_users_are_allowed(
    client: Client, root: AD, child: AD, tree: AD, trusted_name: str
):
    """
    :title: GPO disabled allows all trusted-domain users regardless of linked policy
    :setup:
        1. Create 'user1' and 'deny_user1' in the trusted domain.
        2. Create and link GPO 'site policy' with 'user1' in allow and
           'deny_user1' in deny; add 'Domain Admins' to SeInteractiveLogonRight.
        3. Configure SSSD with 'ad_gpo_access_control = disabled'. Start SSSD.
    :steps:
        1. Authenticate 'user1' and 'deny_user1' via SSH.
    :expectedresults:
        1. Both users can log in.
    :customerscenario: False
    :requirement: AD Forest GPO
    """
    domain = child if trusted_name == "child" else tree
    user1 = domain.user("user1").add()
    deny_user1 = domain.user("deny_user1").add()

    root.gpo("site policy").add().policy(
        {
            "SeInteractiveLogonRight": [user1, root.group("Domain Admins")],
            "SeDenyInteractiveLogonRight": [deny_user1],
        }
    ).link()

    client.sssd.import_domain(root.domain, root)
    client.sssd.domain["access_provider"] = "ad"
    client.sssd.domain["ad_gpo_access_control"] = "disabled"
    client.sssd.start()

    assert client.auth.ssh.password(f"user1@{domain.domain}", "Secret123"), (
        "Allowed user failed login!"
    )
    assert client.auth.ssh.password(f"deny_user1@{domain.domain}", "Secret123"), (
        "Denied user failed login with GPO disabled!"
    )


@pytest.mark.importance("high")
@pytest.mark.parametrize("trusted_name", ["child", "tree"])
@pytest.mark.topology(KnownTopology.ADForest)
def test_gpo_multidomain__is_set_to_enforcing(
    client: Client, root: AD, child: AD, tree: AD, trusted_name: str
):
    """
    :title: GPO enforcing permits only listed trusted-domain users and group members
    :setup:
        1. Create 'user1', 'user2' and 'deny_user1' in the trusted domain.
        2. Create group 'group' with 'user2' as member.
        3. Create and link GPO 'site policy' with 'user1' and 'group' in allow,
           'deny_user1' in deny, 'Domain Admins' in SeInteractiveLogonRight.
        4. Configure SSSD with 'ad_gpo_access_control = enforcing'. Start SSSD.
    :steps:
        1. Authenticate 'user1' and 'user2' via SSH.
        2. Authenticate 'deny_user1' via SSH.
    :expectedresults:
        1. Allowed user and group member can log in.
        2. Denied user is rejected.
    :customerscenario: False
    :requirement: AD Forest GPO
    """
    domain = child if trusted_name == "child" else tree
    user1 = domain.user("user1").add()
    user2 = domain.user("user2").add()
    deny_user1 = domain.user("deny_user1").add()
    group = domain.group("group").add().add_member(user2)

    root.gpo("site policy").add().policy(
        {
            "SeInteractiveLogonRight": [user1, group, root.group("Domain Admins")],
            "SeDenyInteractiveLogonRight": [deny_user1],
        }
    ).link()

    client.sssd.import_domain(root.domain, root)
    client.sssd.domain["access_provider"] = "ad"
    client.sssd.domain["ad_gpo_access_control"] = "enforcing"
    client.sssd.start()

    assert client.auth.ssh.password(f"user1@{domain.domain}", "Secret123"), (
        "Allowed user failed login!"
    )
    assert client.auth.ssh.password(f"user2@{domain.domain}", "Secret123"), (
        "Allowed group member failed login!"
    )
    assert not client.auth.ssh.password(f"deny_user1@{domain.domain}", "Secret123"), (
        "Denied user logged in!"
    )


@pytest.mark.importance("high")
@pytest.mark.parametrize("trusted_name", ["child", "tree"])
@pytest.mark.topology(KnownTopology.ADForest)
def test_gpo_multidomain__ou_and_domain_inheritance(
    client: Client, root: AD, child: AD, tree: AD, trusted_name: str
):
    """
    :title: GPO OU policy takes precedence over domain policy for trusted-domain users
    :description:
        Policies can be applied to sites, domains and OUs. OUs have the highest priority.
        A user denied by the domain policy but allowed by the OU policy can log in when the
        computer is placed in that OU.
    :setup:
        1. Create 'user1' and 'user2' in the trusted domain.
        2. Create OU 'test' on the root domain.
        3. Create and link 'domain policy' to the root domain naming context with 'user1'
           in allow and 'user2' in deny.
        4. Create and link 'ou policy' to the test OU with 'user2' in allow and 'user1' in deny.
        5. Move the computer object to the test OU.
        6. Configure SSSD with 'ad_gpo_access_control = enforcing'. Start SSSD.
    :steps:
        1. Authenticate 'user1' via SSH.
        2. Authenticate 'user2' via SSH.
    :expectedresults:
        1. 'user1' authentication is unsuccessful.
        2. 'user2' authentication is successful.
    :customerscenario: True
    :requirement: AD Forest GPO
    """
    domain = child if trusted_name == "child" else tree
    user1 = domain.user("user1").add()
    user2 = domain.user("user2").add()
    ou = root.ou("test").add().dn

    root.gpo("domain policy").add().policy(
        {
            "SeInteractiveLogonRight": [user1, root.group("Domain Admins")],
            "SeDenyInteractiveLogonRight": [user2],
        }
    ).link(target=root.host.naming_context)

    root.gpo("ou policy").add().policy(
        {
            "SeInteractiveLogonRight": [user2, root.group("Domain Admins")],
            "SeDenyInteractiveLogonRight": [user1],
        }
    ).link(target=ou)

    root.computer(client.host.hostname.split(".")[0]).move(ou)

    client.sssd.import_domain(root.domain, root)
    client.sssd.domain["access_provider"] = "ad"
    client.sssd.domain["ad_gpo_access_control"] = "enforcing"
    client.sssd.start()

    assert not client.auth.ssh.password(f"user1@{domain.domain}", "Secret123"), (
        "Domain-policy user authenticated successfully!"
    )
    assert client.auth.ssh.password(f"user2@{domain.domain}", "Secret123"), (
        "OU-policy user authentication failed!"
    )


@pytest.mark.importance("high")
@pytest.mark.parametrize("trusted_name", ["child", "tree"])
@pytest.mark.ticket(bz=1316164)
@pytest.mark.topology(KnownTopology.ADForest)
def test_gpo_multidomain__ignores_invalid_and_unnecessary_keys_and_values(
    client: Client, root: AD, child: AD, tree: AD, trusted_name: str
):
    """
    :title: GPO ignores invalid and unnecessary keys and values for trusted-domain users
    :description:
        The GPO security database can contain additional keys and keys with empty values for
        other applications. SSSD should only process the relevant keys and ignore the rest.
    :setup:
        1. Create 'user1' and 'deny_user1' in the trusted domain.
        2. Create and link GPO 'policy invalid keys and values' with 'user1' in allow and
           'deny_user1' in deny; additionally add bogus 'Service General Setting' keys.
        3. Configure SSSD with 'ad_gpo_access_control = enforcing'. Start SSSD.
    :steps:
        1. Authenticate 'user1' via SSH.
        2. Authenticate 'deny_user1' via SSH.
    :expectedresults:
        1. 'user1' authentication is successful.
        2. 'deny_user1' authentication is unsuccessful.
    :customerscenario: True
    :requirement: AD Forest GPO
    """
    domain = child if trusted_name == "child" else tree
    user1 = domain.user("user1").add()
    deny_user1 = domain.user("deny_user1").add()

    root.gpo("policy invalid keys and values").add().policy(
        {
            "SeInteractiveLogonRight": [user1, root.group("Domain Admins")],
            "SeDenyInteractiveLogonRight": [deny_user1],
        },
        cfg={"Service General Setting": {"BITS": "2", "wuaserv": "2", "MpsSvc": "2"}},
    ).link()

    client.sssd.import_domain(root.domain, root)
    client.sssd.domain["access_provider"] = "ad"
    client.sssd.domain["ad_gpo_access_control"] = "enforcing"
    client.sssd.start()

    assert client.auth.ssh.password(f"user1@{domain.domain}", "Secret123"), (
        "Allowed user authentication failed!"
    )
    assert not client.auth.ssh.password(f"deny_user1@{domain.domain}", "Secret123"), (
        "Denied user authenticated successfully!"
    )


@pytest.mark.importance("high")
@pytest.mark.parametrize("trusted_name", ["child", "tree"])
@pytest.mark.topology(KnownTopology.ADForest)
def test_gpo_multidomain__is_set_to_permissive(
    client: Client, root: AD, child: AD, tree: AD, trusted_name: str
):
    """
    :title: GPO permissive allows all trusted users even when a restrictive policy is linked
    :setup:
        1. Create 'user1' and 'deny_user1' in the trusted domain.
        2. Create and link GPO 'site policy' with 'user1' in allow and 'deny_user1' in deny.
        3. Configure SSSD with 'ad_gpo_access_control = permissive'. Start SSSD.
    :steps:
        1. Authenticate 'user1' and 'deny_user1' via SSH.
    :expectedresults:
        1. Both users can log in.
    :customerscenario: False
    :requirement: AD Forest GPO
    """
    domain = child if trusted_name == "child" else tree
    user1 = domain.user("user1").add()
    deny_user1 = domain.user("deny_user1").add()

    root.gpo("site policy").add().policy(
        {
            "SeInteractiveLogonRight": [user1, root.group("Domain Admins")],
            "SeDenyInteractiveLogonRight": [deny_user1],
        }
    ).link()

    client.sssd.import_domain(root.domain, root)
    client.sssd.domain["access_provider"] = "ad"
    client.sssd.domain["ad_gpo_access_control"] = "permissive"
    client.sssd.start()

    assert client.auth.ssh.password(f"user1@{domain.domain}", "Secret123"), (
        "Allowed user failed login!"
    )
    assert client.auth.ssh.password(f"deny_user1@{domain.domain}", "Secret123"), (
        "Denied user failed login with GPO permissive!"
    )

@jakub-vavra-cz
jakub-vavra-cz force-pushed the adforest branch 2 times, most recently from 0b88058 to 9be0bc6 Compare September 9, 2026 12:46
Comment thread src/tests/system/tests/test_adforest_gpo.py Dismissed
@jakub-vavra-cz

jakub-vavra-cz commented Sep 9, 2026 •

Copy link
Copy Markdown
Contributor Author

I know this is in a draft, I went ahead and took the liberty of making the multidomain gpo tests closer to the current gpo tests.

I applied some changes to make it more uniform with test_gpo:
Summary:

1 — SSSD setup via _ad_forest_configure_sssd
_configure_gpo() now delegates to the framework helper and only sets GPO-specific options (access_provider, ad_gpo_access_control, plus per-test extras).

2 — Inlined GPO creation
Removed _link_site_gpo().
Simple tests use ad.gpo("site policy").add().policy({...}).link() like test_gpo.py.
Multi-GPO tests (inheritance, link order, unreadable) keep distinct names where needed.

3 — Aligned object names with test_gpo.py
gpo-ok / gpo-deny → user1 / deny_user1
gpo-ok-g / gpo-ok-group → user2 / group
gpo-nop → user
Inheritance test: user1, user2, user3
Link order / unreadable: user1, user2

I collapsed some parametrized @pytest.mark.parametrize("method", ["su", "ssh"]) test together and saved ~20minutes of execution compared to original design. (ad forest including gpo is now ~80minutes).

Comment thread src/tests/system/tests/test_adforest.py Outdated
Comment thread src/tests/system/tests/test_adforest.py Outdated
Comment thread src/tests/system/tests/test_adforest.py Outdated
Comment thread src/tests/system/tests/test_adforest.py Outdated
Comment thread src/tests/system/tests/test_adforest.py Outdated
@jakub-vavra-cz
jakub-vavra-cz force-pushed the adforest branch 2 times, most recently from ecaf14b to 36e28bb Compare October 5, 2026 11:27

@danlavu danlavu left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't go through all the test names, but you should get the idea from the comments. I'd like the test name to be as close to the respective generic/ad provider test name.

Comment thread src/tests/system/tests/test_adforest.py Outdated
Comment thread src/tests/system/tests/test_adforest.py Outdated
Comment thread src/tests/system/tests/test_adforest.py Outdated
Comment thread src/tests/system/tests/test_adforest.py Outdated
Comment thread src/tests/system/tests/test_adforest.py Outdated
Comment thread src/tests/system/tests/test_adforest.py Outdated
Comment thread src/tests/system/tests/test_adforest.py Outdated
Comment thread src/tests/system/tests/test_adforest.py Outdated
Comment thread src/tests/system/tests/test_adforest.py Outdated
Comment thread src/tests/system/tests/test_adforest.py Outdated

@danlavu danlavu left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me, thank you!

Re-design and rewrite of legacy sssd-qe ad_forest
and admultidomain suites into test_adforest.py
and test_adforest_gpo.py.

These suites are downstream (idmci) only.

Deprecates:
sssd-qe/client/ad_provider/ad_forest
sssd-qe/client/ad_provider/ad_gpo_hbac_multidomain
sssd/src/tests/multihost/admultidomain/test_multidomain.py

Not covered (different topology):
sssd/src/tests/multihost/admultidomain/test_multiforest.py

Assisted-by: Cursor
Reviewed-by: Dan Lavu <dlavu@redhat.com>
Reviewed-by: Tomáš Halman <thalman@redhat.com>
Match remaining test names to generic suites, group cases by feature, and
break up the initgroups helper for readability.
Move fixtures so passkey tests are not broken.

Assisted-by: Cursor
Reviewed-by: Dan Lavu <dlavu@redhat.com>
Reviewed-by: Tomáš Halman <thalman@redhat.com>
@sssd-bot

sssd-bot commented Oct 7, 2026

Copy link
Copy Markdown
Contributor

The pull request was accepted by @thalman with the following PR CI status:


🟢 CodeQL (success)
🟢 osh-diff-scan:fedora-rawhide-x86_64:upstream (success)
🟢 rpm-build:centos-stream-10-x86_64:upstream (success)
🟢 rpm-build:fedora-43-x86_64:upstream (success)
🟢 rpm-build:fedora-44-x86_64:upstream (success)
🟢 rpm-build:fedora-45-x86_64:upstream (success)
🟢 rpm-build:fedora-rawhide-x86_64:upstream (success)
🟢 testing-farm:centos-stream-10-x86_64:upstream (success)
🟢 testing-farm:fedora-43-x86_64:upstream (success)
🟢 testing-farm:fedora-44-x86_64:upstream (success)
🟢 testing-farm:fedora-45-x86_64:upstream (success)
🟢 testing-farm:fedora-rawhide-x86_64:upstream (success)
🟢 ci / intgcheck (centos-10) (success)
🔴 ci / intgcheck (fedora-43) (failure)
🟢 ci / intgcheck (fedora-44) (success)
🟢 ci / intgcheck (fedora-45) (success)
🟢 ci / intgcheck (fedora-46) (success)
🟢 ci / prepare (success)
🔴 ci / system (centos-10) (failure)
🟢 ci / system (fedora-43) (success)
🔴 ci / system (fedora-44) (failure)
🟢 ci / system (fedora-45) (success)
🔴 ci / system (fedora-46) (failure)
➖ Coverity scan / coverity (skipped)
🟢 Static code analysis / codeql (success)
🟢 Static code analysis / files-changed (success)
🟢 Static code analysis / pre-commit (success)
🟢 Static code analysis / python-system-tests (success)


There are unsuccessful or unfinished checks. Make sure that the failures are not related to this pull request before merging.

@danlavu

danlavu commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

I collapsed some parametrized @pytest.mark.parametrize("method", ["su", "ssh"]) test together and saved ~20minutes of execution compared to original design. (ad forest including gpo is now ~80minutes).

I missed this comment. #9401 ;)

@jakub-vavra-cz
jakub-vavra-cz deleted the adforest branch October 8, 2026 08:36
danlavu pushed a commit to danlavu/sssd that referenced this pull request Oct 8, 2026
Drop parametrization of su and ssh and calls in the gpo tests. The setup and teardown
in AD is costly. This is the same approach Jakub used in PR SSSD#9045 shaving 20 minutes
on runtime.

AI: Claude Sonnet 5
Co-authored-by: Cursor <cursoragent@cursor.com>
danlavu pushed a commit to danlavu/sssd that referenced this pull request Oct 8, 2026
Drop parametrization of su and ssh and calls in the gpo tests. The setup and teardown
in AD is costly. This is the same approach Jakub used in PR SSSD#9045 shaving 20 minutes
on runtime.

AI: Claude Sonnet 5
Co-authored-by: Cursor <cursoragent@cursor.com>
sssd-bot pushed a commit to danlavu/sssd that referenced this pull request Oct 9, 2026
Drop parametrization of su and ssh and calls in the gpo tests. The setup and teardown
in AD is costly. This is the same approach Jakub used in PR SSSD#9045 shaving 20 minutes
on runtime.

AI: Claude Sonnet 5
Co-authored-by: Cursor <cursoragent@cursor.com>
Reviewed-by: Jakub Vávra <jvavra@redhat.com>
danlavu pushed a commit that referenced this pull request Oct 11, 2026
Drop parametrization of su and ssh and calls in the gpo tests. The setup and teardown
in AD is costly. This is the same approach Jakub used in PR #9045 shaving 20 minutes
on runtime.

AI: Claude Sonnet 5
Co-authored-by: Cursor <cursoragent@cursor.com>
Reviewed-by: Jakub Vávra <jvavra@redhat.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants