예제 #1
0
def test_graph_desc_to_ances(session, graph, users, groups):  # noqa: F811
    """ Test adding members where all descendants already exist."""

    setup_desc_to_ances(session, users, groups)
    session.commit()
    graph.update_from_db(session)

    assert get_users(graph, "team-sre") == set(["*****@*****.**", "*****@*****.**"])
    assert get_users(graph, "tech-ops") == set(["*****@*****.**", "*****@*****.**"])

    assert get_users(graph, "team-infra") == set(["*****@*****.**", "*****@*****.**"])
    assert get_users(graph, "team-infra", cutoff=1) == set(["*****@*****.**"])

    assert get_users(graph, "all-teams") == set(
        ["*****@*****.**", "*****@*****.**", "*****@*****.**"])
    assert get_users(graph, "all-teams", cutoff=1) == set(["*****@*****.**"])

    assert get_groups(graph, "*****@*****.**") == set(
        ["team-sre", "all-teams", "tech-ops", "team-infra"])
    assert get_groups(graph, "*****@*****.**",
                      cutoff=1) == set(["team-sre", "tech-ops", "team-infra"])

    assert get_groups(graph, "*****@*****.**") == set(
        ["team-sre", "all-teams", "tech-ops", "team-infra"])
    assert get_groups(graph, "*****@*****.**",
                      cutoff=1) == set(["team-sre", "tech-ops"])

    assert get_groups(graph, "*****@*****.**") == set(["all-teams"])
    assert get_groups(graph, "*****@*****.**", cutoff=1) == set(["all-teams"])
예제 #2
0
def test_graph_desc_to_ances(session, graph, users, groups):  # noqa: F811
    """ Test adding members where all descendants already exist."""

    setup_desc_to_ances(session, users, groups)
    session.commit()
    graph.update_from_db(session)

    assert get_users(graph, "team-sre") == set(["*****@*****.**", "*****@*****.**"])
    assert get_users(graph, "tech-ops") == set(["*****@*****.**", "*****@*****.**"])

    assert get_users(graph, "team-infra") == set(["*****@*****.**", "*****@*****.**"])
    assert get_users(graph, "team-infra", cutoff=1) == set(["*****@*****.**"])

    assert get_users(graph, "all-teams") == set(["*****@*****.**", "*****@*****.**", "*****@*****.**"])
    assert get_users(graph, "all-teams", cutoff=1) == set(["*****@*****.**"])

    assert get_groups(graph, "*****@*****.**") == set(
        ["team-sre", "all-teams", "tech-ops", "team-infra"]
    )
    assert get_groups(graph, "*****@*****.**", cutoff=1) == set(["team-sre", "tech-ops", "team-infra"])

    assert get_groups(graph, "*****@*****.**") == set(
        ["team-sre", "all-teams", "tech-ops", "team-infra"]
    )
    assert get_groups(graph, "*****@*****.**", cutoff=1) == set(["team-sre", "tech-ops"])

    assert get_groups(graph, "*****@*****.**") == set(["all-teams"])
    assert get_groups(graph, "*****@*****.**", cutoff=1) == set(["all-teams"])
예제 #3
0
def test_graph_cycle_direct(session, graph, users, groups):  # noqa: F811
    """ Test adding members where all descendants already exist."""

    add_member(groups["team-sre"], users["*****@*****.**"])
    add_member(groups["tech-ops"], users["*****@*****.**"])

    add_member(groups["team-sre"], groups["tech-ops"])
    add_member(groups["tech-ops"], groups["team-sre"])

    session.commit()
    graph.update_from_db(session)
    assert get_users(graph, "team-sre") == set(["*****@*****.**", "*****@*****.**"])
    assert get_users(graph, "team-sre", cutoff=1) == set(["*****@*****.**"])

    assert get_users(graph, "tech-ops") == set(["*****@*****.**", "*****@*****.**"])
    assert get_users(graph, "tech-ops", cutoff=1) == set(["*****@*****.**"])

    assert get_groups(graph, "*****@*****.**") == set(["team-sre", "tech-ops"])
    assert get_groups(graph, "*****@*****.**", cutoff=1) == set(["team-sre"])

    assert get_groups(graph, "*****@*****.**") == set(["team-sre", "tech-ops"])
    assert get_groups(graph, "*****@*****.**", cutoff=1) == set(["tech-ops"])
예제 #4
0
def test_graph_cycle_direct(session, graph, users, groups):  # noqa: F811
    """ Test adding members where all descendants already exist."""

    add_member(groups["team-sre"], users["*****@*****.**"])
    add_member(groups["tech-ops"], users["*****@*****.**"])

    add_member(groups["team-sre"], groups["tech-ops"])
    add_member(groups["tech-ops"], groups["team-sre"])

    session.commit()
    graph.update_from_db(session)
    assert get_users(graph, "team-sre") == set(["*****@*****.**", "*****@*****.**"])
    assert get_users(graph, "team-sre", cutoff=1) == set(["*****@*****.**"])

    assert get_users(graph, "tech-ops") == set(["*****@*****.**", "*****@*****.**"])
    assert get_users(graph, "tech-ops", cutoff=1) == set(["*****@*****.**"])

    assert get_groups(graph, "*****@*****.**") == set(["team-sre", "tech-ops"])
    assert get_groups(graph, "*****@*****.**", cutoff=1) == set(["team-sre"])

    assert get_groups(graph, "*****@*****.**") == set(["team-sre", "tech-ops"])
    assert get_groups(graph, "*****@*****.**", cutoff=1) == set(["tech-ops"])
예제 #5
0
def test_graph_cycle_indirect(session, graph, users, groups):  # noqa: F811
    """ Test adding a member that will create a cycle.

        gary         zay            testuser
         |            |                |
        sre <----- tech-ops <----- team-infra <--
         |                                       |
         |                                       |
          --------> all-teams --------------------

    """

    add_member(groups["team-sre"], users["*****@*****.**"])
    add_member(groups["tech-ops"], users["*****@*****.**"])
    add_member(groups["team-infra"], users["*****@*****.**"])

    add_member(groups["team-sre"], groups["tech-ops"])
    add_member(groups["tech-ops"], groups["team-infra"])
    add_member(groups["team-infra"], groups["all-teams"])

    add_member(groups["all-teams"], groups["team-sre"])

    session.commit()
    graph.update_from_db(session)
    all_users = set(["*****@*****.**", "*****@*****.**", "*****@*****.**"])
    all_groups = set(["team-sre", "all-teams", "tech-ops", "team-infra"])

    assert get_users(graph, "team-sre") == all_users
    assert get_users(graph, "team-sre", cutoff=1) == set(["*****@*****.**"])

    assert get_users(graph, "tech-ops") == all_users
    assert get_users(graph, "tech-ops", cutoff=1) == set(["*****@*****.**"])

    assert get_users(graph, "team-infra") == all_users
    assert get_users(graph, "team-infra", cutoff=1) == set(["*****@*****.**"])

    assert get_users(graph, "all-teams") == all_users
    assert get_users(graph, "all-teams", cutoff=1) == set([])

    assert get_groups(graph, "*****@*****.**") == all_groups
    assert get_groups(graph, "*****@*****.**", cutoff=1) == set(["team-sre"])

    assert get_groups(graph, "*****@*****.**") == all_groups
    assert get_groups(graph, "*****@*****.**", cutoff=1) == set(["tech-ops"])

    assert get_groups(graph, "*****@*****.**") == all_groups
    assert get_groups(graph, "*****@*****.**", cutoff=1) == set(["team-infra"])
예제 #6
0
def test_graph_cycle_indirect(session, graph, users, groups):  # noqa: F811
    """ Test adding a member that will create a cycle.

        gary         zay            testuser
         |            |                |
        sre <----- tech-ops <----- team-infra <--
         |                                       |
         |                                       |
          --------> all-teams --------------------

    """

    add_member(groups["team-sre"], users["*****@*****.**"])
    add_member(groups["tech-ops"], users["*****@*****.**"])
    add_member(groups["team-infra"], users["*****@*****.**"])

    add_member(groups["team-sre"], groups["tech-ops"])
    add_member(groups["tech-ops"], groups["team-infra"])
    add_member(groups["team-infra"], groups["all-teams"])

    add_member(groups["all-teams"], groups["team-sre"])

    session.commit()
    graph.update_from_db(session)
    all_users = set(["*****@*****.**", "*****@*****.**", "*****@*****.**"])
    all_groups = set(["team-sre", "all-teams", "tech-ops", "team-infra"])

    assert get_users(graph, "team-sre") == all_users
    assert get_users(graph, "team-sre", cutoff=1) == set(["*****@*****.**"])

    assert get_users(graph, "tech-ops") == all_users
    assert get_users(graph, "tech-ops", cutoff=1) == set(["*****@*****.**"])

    assert get_users(graph, "team-infra") == all_users
    assert get_users(graph, "team-infra", cutoff=1) == set(["*****@*****.**"])

    assert get_users(graph, "all-teams") == all_users
    assert get_users(graph, "all-teams", cutoff=1) == set([])

    assert get_groups(graph, "*****@*****.**") == all_groups
    assert get_groups(graph, "*****@*****.**", cutoff=1) == set(["team-sre"])

    assert get_groups(graph, "*****@*****.**") == all_groups
    assert get_groups(graph, "*****@*****.**", cutoff=1) == set(["tech-ops"])

    assert get_groups(graph, "*****@*****.**") == all_groups
    assert get_groups(graph, "*****@*****.**", cutoff=1) == set(["team-infra"])
예제 #7
0
def test_graph_add_member_existing(session, graph, users,
                                   groups):  # noqa: F811
    """ Test adding members to an existing relationship."""

    add_member(groups["team-sre"], users["*****@*****.**"], role="owner")
    add_member(groups["tech-ops"], users["*****@*****.**"], role="owner")

    add_member(groups["team-infra"], users["*****@*****.**"], role="owner")
    add_member(groups["team-infra"], groups["team-sre"])
    add_member(groups["team-infra"], groups["tech-ops"])

    add_member(groups["all-teams"], users["*****@*****.**"], role="owner")
    add_member(groups["all-teams"], groups["team-infra"])

    add_member(groups["team-sre"], users["*****@*****.**"])
    add_member(groups["tech-ops"], users["*****@*****.**"])

    session.commit()
    graph.update_from_db(session)

    assert get_users(graph, "team-sre") == set(["*****@*****.**", "*****@*****.**"])
    assert get_users(graph, "tech-ops") == set(["*****@*****.**", "*****@*****.**"])

    assert get_users(graph, "team-infra") == set(["*****@*****.**", "*****@*****.**"])
    assert get_users(graph, "team-infra", cutoff=1) == set(["*****@*****.**"])

    assert get_users(graph, "all-teams") == set(
        ["*****@*****.**", "*****@*****.**", "*****@*****.**"])
    assert get_users(graph, "all-teams", cutoff=1) == set(["*****@*****.**"])

    assert get_groups(graph, "*****@*****.**") == set(
        ["team-sre", "all-teams", "tech-ops", "team-infra"])
    assert get_groups(graph, "*****@*****.**",
                      cutoff=1) == set(["team-sre", "tech-ops", "team-infra"])

    assert get_groups(graph, "*****@*****.**") == set(
        ["team-sre", "all-teams", "tech-ops", "team-infra"])
    assert get_groups(graph, "*****@*****.**",
                      cutoff=1) == set(["team-sre", "tech-ops"])

    assert get_groups(graph, "*****@*****.**") == set(["all-teams"])
    assert get_groups(graph, "*****@*****.**", cutoff=1) == set(["all-teams"])
예제 #8
0
def test_graph_add_member_existing(session, graph, users, groups):  # noqa: F811
    """ Test adding members to an existing relationship."""

    add_member(groups["team-sre"], users["*****@*****.**"], role="owner")
    add_member(groups["tech-ops"], users["*****@*****.**"], role="owner")

    add_member(groups["team-infra"], users["*****@*****.**"], role="owner")
    add_member(groups["team-infra"], groups["team-sre"])
    add_member(groups["team-infra"], groups["tech-ops"])

    add_member(groups["all-teams"], users["*****@*****.**"], role="owner")
    add_member(groups["all-teams"], groups["team-infra"])

    add_member(groups["team-sre"], users["*****@*****.**"])
    add_member(groups["tech-ops"], users["*****@*****.**"])

    session.commit()
    graph.update_from_db(session)

    assert get_users(graph, "team-sre") == set(["*****@*****.**", "*****@*****.**"])
    assert get_users(graph, "tech-ops") == set(["*****@*****.**", "*****@*****.**"])

    assert get_users(graph, "team-infra") == set(["*****@*****.**", "*****@*****.**"])
    assert get_users(graph, "team-infra", cutoff=1) == set(["*****@*****.**"])

    assert get_users(graph, "all-teams") == set(["*****@*****.**", "*****@*****.**", "*****@*****.**"])
    assert get_users(graph, "all-teams", cutoff=1) == set(["*****@*****.**"])

    assert get_groups(graph, "*****@*****.**") == set(
        ["team-sre", "all-teams", "tech-ops", "team-infra"]
    )
    assert get_groups(graph, "*****@*****.**", cutoff=1) == set(["team-sre", "tech-ops", "team-infra"])

    assert get_groups(graph, "*****@*****.**") == set(
        ["team-sre", "all-teams", "tech-ops", "team-infra"]
    )
    assert get_groups(graph, "*****@*****.**", cutoff=1) == set(["team-sre", "tech-ops"])

    assert get_groups(graph, "*****@*****.**") == set(["all-teams"])
    assert get_groups(graph, "*****@*****.**", cutoff=1) == set(["all-teams"])
예제 #9
0
def test_promote_nonauditors(
        mock_gagn,
        standard_graph,
        graph,
        users,
        groups,
        session,
        permissions  # noqa: F811
):
    """ Test expiration auditing and notification. """

    assert graph.get_group_details("audited-team")["audited"]

    #
    # Ensure auditors promotion for all approvers
    #
    approver_roles = ["owner", "np-owner", "manager"]

    affected_users = set(
        ["*****@*****.**", "*****@*****.**", "*****@*****.**", "*****@*****.**"])
    for idx, role in enumerate(approver_roles):

        # Add non-auditor as an approver to an audited group
        add_member(groups["audited-team"], users["*****@*****.**"], role=role)
        graph.update_from_db(session)
        assert not affected_users.intersection(get_users(graph, "auditors"))

        # do the promotion logic
        background = BackgroundProcessor(settings, None)
        background.promote_nonauditors(session)

        # Check that the users now added to auditors group
        graph.update_from_db(session)
        assert affected_users.intersection(get_users(
            graph, "auditors")) == affected_users
        unsent_emails = _get_unsent_emails_and_send(session)
        assert any([
            'Subject: Added as member to group "auditors"' in email.body
            and "To: [email protected]" in email.body for email in unsent_emails
        ])
        assert any([
            'Subject: Added as member to group "auditors"' in email.body
            and "To: [email protected]" in email.body for email in unsent_emails
        ])
        assert any([
            'Subject: Added as member to group "auditors"' in email.body
            and "To: [email protected]" in email.body for email in unsent_emails
        ])

        audits = AuditLog.get_entries(session, action="nonauditor_promoted")
        assert len(audits) == len(affected_users) * (idx + 1)

        # reset for next iteration
        revoke_member(groups["audited-team"], users["*****@*****.**"])
        for username in affected_users:
            revoke_member(groups["auditors"], users[username])

    #
    # Ensure nonauditor, nonapprovers in audited groups do not get promoted
    #

    # first, run a promotion to get any other promotion that we don't
    # care about out of the way
    background = BackgroundProcessor(settings, None)
    background.promote_nonauditors(session)

    prev_audit_log_count = len(
        AuditLog.get_entries(session, action="nonauditor_promoted"))

    member_roles = ["member"]
    for idx, role in enumerate(member_roles):

        # Add non-auditor as a non-approver to an audited group
        add_member(groups["audited-team"], users["*****@*****.**"], role=role)

        # do the promotion logic
        background = BackgroundProcessor(settings, None)
        background.promote_nonauditors(session)

        # Check that the user is not added to auditors group
        graph.update_from_db(session)
        assert "*****@*****.**" not in get_users(graph, "auditors")

        assert not any([
            'Subject: Added as member to group "auditors"' in email.body
            and "To: [email protected]" in email.body
            for email in _get_unsent_emails_and_send(session)
        ])

        audits = AuditLog.get_entries(session, action="nonauditor_promoted")
        assert len(audits) == prev_audit_log_count

        revoke_member(groups["audited-team"], users["*****@*****.**"])
예제 #10
0
def test_auditor_promotion(mock_nnp, mock_gagn, session, graph, permissions, users):  # noqa: F811
    """Test automatic promotion of non-auditor approvers

    We set up our own group/user/permission for testing instead of
    using the `standard_graph` fixture---retrofitting it to work for
    us and also not break existing tests is too cumbersome.

    So here are our groups:

    very-special-auditors:
      * user14

    group-1:
      * user11 (o)
      * user12
      * user13 (np-o)
      * user14 (o, a)

    group-2:
      * user13 (np-o)
      * user21 (o)
      * user22

    group-3:
      * user22 (o)
      * user12 (o)

    group-4:
      * user21 (np-o)
      * user41
      * user42 (o)
      * user43 (np-o)

    o: owner, np-o: no-permission owner, a: auditor

    group-1 and group-2 have the permission that we will enable
    auditing. group-4 will be a subgroup of group-1 and thus will
    inherit the audited permission from group-1.

    The expected outcome is: user11, user13, user21, user42, and
    user43 will be added to the auditors group.

    """
    settings = BackgroundSettings()
    set_global_settings(settings)

    #
    # set up our test part of the graph
    #

    # create groups
    AUDITED_GROUP = "audited"
    AUDITORS_GROUP = mock_gagn.return_value = "very-special-auditors"
    PERMISSION_NAME = "test-permission"
    all_groups = {
        groupname: Group.get_or_create(session, groupname=groupname)[0]
        for groupname in ("group-1", "group-2", "group-3", "group-4", AUDITORS_GROUP)
    }
    # create users
    users.update(
        {
            username + "@a.co": User.get_or_create(session, username=username + "@a.co")[0]
            for username in (
                "user11",
                "user12",
                "user13",
                "user14",
                "user21",
                "user22",
                "user23",
                "user41",
                "user42",
                "user43",
            )
        }
    )
    # create permissions
    permissions.update(
        {
            permission: get_or_create_permission(
                session, permission, description="{} permission".format(permission)
            )[0]
            for permission in [PERMISSION_NAME]
        }
    )
    # add users to groups
    for (groupname, username, role) in (
        ("group-1", "user11", "owner"),
        ("group-1", "user12", "member"),
        ("group-1", "user13", "np-owner"),
        ("group-1", "user14", "owner"),
        ("group-2", "user13", "np-owner"),
        ("group-2", "user21", "owner"),
        ("group-2", "user22", "member"),
        ("group-3", "user12", "owner"),
        ("group-3", "user22", "owner"),
        ("group-4", "user21", "np-owner"),
        ("group-4", "user41", "member"),
        ("group-4", "user42", "owner"),
        ("group-4", "user43", "np-owner"),
    ):
        add_member(all_groups[groupname], users[username + "@a.co"], role=role)
    # add group-4 as member of group-1
    add_member(all_groups["group-1"], all_groups["group-4"])
    # add user14 to auditors group
    add_member(all_groups[AUDITORS_GROUP], users["*****@*****.**"])
    # grant permissions to groups
    #
    # give the test permission to groups 1 and 2, and group 4 should
    # also inherit from group 1
    grant_permission(all_groups["group-1"], permissions[PERMISSION_NAME])
    grant_permission(all_groups["group-2"], permissions[PERMISSION_NAME])
    grant_permission(all_groups[AUDITORS_GROUP], permissions[PERMISSION_AUDITOR])

    graph.update_from_db(session)
    # done setting up

    # now a few pre-op checks
    assert not graph.get_group_details("group-1").get(AUDITED_GROUP)
    assert not graph.get_group_details("group-4").get(AUDITED_GROUP)
    assert get_users(graph, AUDITORS_GROUP) == set(["*****@*****.**"])
    assert get_users(graph, "group-3") == set(["*****@*****.**", "*****@*****.**"])

    #
    # run the promotion logic -> nothing should happen because the
    # test-permission is not yet audited
    #
    background = BackgroundProcessor(settings, None)
    background.promote_nonauditors(session)
    graph.update_from_db(session)

    # nothing should have happened
    assert not graph.get_group_details("group-1").get(AUDITED_GROUP)
    assert not graph.get_group_details("group-4").get(AUDITED_GROUP)
    assert get_users(graph, AUDITORS_GROUP) == set(["*****@*****.**"])
    assert mock_nnp.call_count == 0

    #
    # now enable auditing for the permission and run the promotion
    # logic again
    #
    enable_permission_auditing(session, PERMISSION_NAME, users["*****@*****.**"].id)
    graph.update_from_db(session)
    assert graph.get_group_details("group-1").get(AUDITED_GROUP)
    assert graph.get_group_details("group-4").get(AUDITED_GROUP)

    background = BackgroundProcessor(settings, None)
    background.promote_nonauditors(session)
    graph.update_from_db(session)

    # check that stuff happened
    assert get_users(graph, AUDITORS_GROUP) == set(
        ["*****@*****.**", "*****@*****.**", "*****@*****.**", "*****@*****.**", "*****@*****.**", "*****@*****.**"]
    )
    expected_calls = [
        call(
            settings, session, users["*****@*****.**"], all_groups[AUDITORS_GROUP], set(["group-1"])
        ),
        call(
            settings,
            session,
            users["*****@*****.**"],
            all_groups[AUDITORS_GROUP],
            set(["group-1", "group-2"]),
        ),
        call(
            settings,
            session,
            users["*****@*****.**"],
            all_groups[AUDITORS_GROUP],
            set(["group-2", "group-4"]),
        ),
        call(
            settings, session, users["*****@*****.**"], all_groups[AUDITORS_GROUP], set(["group-4"])
        ),
        call(
            settings, session, users["*****@*****.**"], all_groups[AUDITORS_GROUP], set(["group-4"])
        ),
    ]
    assert mock_nnp.call_count == len(expected_calls)
    mock_nnp.assert_has_calls(expected_calls, any_order=True)

    #
    # run the background promotion logic again, and nothing should
    # happen
    #
    mock_nnp.reset_mock()
    background = BackgroundProcessor(settings, None)
    background.promote_nonauditors(session)
    assert mock_nnp.call_count == 0
예제 #11
0
def test_auditor_promotion(mock_nnp, mock_gagn, session, graph, permissions,
                           users):  # noqa: F811
    """Test automatic promotion of non-auditor approvers

    We set up our own group/user/permission for testing instead of
    using the `standard_graph` fixture---retrofitting it to work for
    us and also not break existing tests is too cumbersome.

    So here are our groups:

    very-special-auditors:
      * user14

    group-1:
      * user11 (o)
      * user12
      * user13 (np-o)
      * user14 (o, a)

    group-2:
      * user13 (np-o)
      * user21 (o)
      * user22

    group-3:
      * user22 (o)
      * user12 (o)

    group-4:
      * user21 (np-o)
      * user41
      * user42 (o)
      * user43 (np-o)

    o: owner, np-o: no-permission owner, a: auditor

    group-1 and group-2 have the permission that we will enable
    auditing. group-4 will be a subgroup of group-1 and thus will
    inherit the audited permission from group-1.

    The expected outcome is: user11, user13, user21, user42, and
    user43 will be added to the auditors group.

    """
    settings = BackgroundSettings()
    set_global_settings(settings)

    #
    # set up our test part of the graph
    #

    # create groups
    AUDITED_GROUP = "audited"
    AUDITORS_GROUP = mock_gagn.return_value = "very-special-auditors"
    PERMISSION_NAME = "test-permission"
    all_groups = {
        groupname: Group.get_or_create(session, groupname=groupname)[0]
        for groupname in ("group-1", "group-2", "group-3", "group-4",
                          AUDITORS_GROUP)
    }
    # create users
    users.update({
        username + "@a.co": User.get_or_create(session,
                                               username=username + "@a.co")[0]
        for username in (
            "user11",
            "user12",
            "user13",
            "user14",
            "user21",
            "user22",
            "user23",
            "user41",
            "user42",
            "user43",
        )
    })
    # create permissions
    permissions.update({
        permission: get_or_create_permission(
            session,
            permission,
            description="{} permission".format(permission))[0]
        for permission in [PERMISSION_NAME]
    })
    # add users to groups
    for (groupname, username, role) in (
        ("group-1", "user11", "owner"),
        ("group-1", "user12", "member"),
        ("group-1", "user13", "np-owner"),
        ("group-1", "user14", "owner"),
        ("group-2", "user13", "np-owner"),
        ("group-2", "user21", "owner"),
        ("group-2", "user22", "member"),
        ("group-3", "user12", "owner"),
        ("group-3", "user22", "owner"),
        ("group-4", "user21", "np-owner"),
        ("group-4", "user41", "member"),
        ("group-4", "user42", "owner"),
        ("group-4", "user43", "np-owner"),
    ):
        add_member(all_groups[groupname], users[username + "@a.co"], role=role)
    # add group-4 as member of group-1
    add_member(all_groups["group-1"], all_groups["group-4"])
    # add user14 to auditors group
    add_member(all_groups[AUDITORS_GROUP], users["*****@*****.**"])
    # grant permissions to groups
    #
    # give the test permission to groups 1 and 2, and group 4 should
    # also inherit from group 1
    grant_permission(all_groups["group-1"], permissions[PERMISSION_NAME])
    grant_permission(all_groups["group-2"], permissions[PERMISSION_NAME])
    grant_permission(all_groups[AUDITORS_GROUP],
                     permissions[PERMISSION_AUDITOR])

    graph.update_from_db(session)
    # done setting up

    # now a few pre-op checks
    assert not graph.get_group_details("group-1").get(AUDITED_GROUP)
    assert not graph.get_group_details("group-4").get(AUDITED_GROUP)
    assert get_users(graph, AUDITORS_GROUP) == set(["*****@*****.**"])
    assert get_users(graph, "group-3") == set(["*****@*****.**", "*****@*****.**"])

    #
    # run the promotion logic -> nothing should happen because the
    # test-permission is not yet audited
    #
    background = BackgroundProcessor(settings, None)
    background.promote_nonauditors(session)
    graph.update_from_db(session)

    # nothing should have happened
    assert not graph.get_group_details("group-1").get(AUDITED_GROUP)
    assert not graph.get_group_details("group-4").get(AUDITED_GROUP)
    assert get_users(graph, AUDITORS_GROUP) == set(["*****@*****.**"])
    assert mock_nnp.call_count == 0

    #
    # now enable auditing for the permission and run the promotion
    # logic again
    #
    enable_permission_auditing(session, PERMISSION_NAME,
                               users["*****@*****.**"].id)
    graph.update_from_db(session)
    assert graph.get_group_details("group-1").get(AUDITED_GROUP)
    assert graph.get_group_details("group-4").get(AUDITED_GROUP)

    background = BackgroundProcessor(settings, None)
    background.promote_nonauditors(session)
    graph.update_from_db(session)

    # check that stuff happened
    assert get_users(graph, AUDITORS_GROUP) == set([
        "*****@*****.**", "*****@*****.**", "*****@*****.**", "*****@*****.**",
        "*****@*****.**", "*****@*****.**"
    ])
    expected_calls = [
        call(settings, session, users["*****@*****.**"],
             all_groups[AUDITORS_GROUP], set(["group-1"])),
        call(
            settings,
            session,
            users["*****@*****.**"],
            all_groups[AUDITORS_GROUP],
            set(["group-1", "group-2"]),
        ),
        call(
            settings,
            session,
            users["*****@*****.**"],
            all_groups[AUDITORS_GROUP],
            set(["group-2", "group-4"]),
        ),
        call(settings, session, users["*****@*****.**"],
             all_groups[AUDITORS_GROUP], set(["group-4"])),
        call(settings, session, users["*****@*****.**"],
             all_groups[AUDITORS_GROUP], set(["group-4"])),
    ]
    assert mock_nnp.call_count == len(expected_calls)
    mock_nnp.assert_has_calls(expected_calls, any_order=True)

    #
    # run the background promotion logic again, and nothing should
    # happen
    #
    mock_nnp.reset_mock()
    background = BackgroundProcessor(settings, None)
    background.promote_nonauditors(session)
    assert mock_nnp.call_count == 0