Skip to content

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

Draft
jakub-vavra-cz wants to merge 1 commit into
SSSD:masterfrom
jakub-vavra-cz:adforest
Draft

tests: Add tests for AD Forest multidomain and gpo tests#9045
jakub-vavra-cz wants to merge 1 commit into
SSSD:masterfrom
jakub-vavra-cz:adforest

Conversation

@jakub-vavra-cz

Copy link
Copy Markdown
Contributor

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

Not ported (Different topology):
sssd/src/tests/multihost/admultidomain/test_multiforest.py

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

Not ported (Different topology):
sssd/src/tests/multihost/admultidomain/test_multiforest.py

Co-authored-by: Cursor <cursoragent@cursor.com>
continue
try:
gpo.unlink()
except ProcessError:
pass
try:
gpo.delete()
except ProcessError:
"""Move the computer object back under CN=Computers if needed."""
try:
ad.computer(computer).move(f"CN=Computers,{ad.naming_context}")
except ProcessError:
@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!"
    )

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants