<filename>tests/service_accounts_test.py
import pytest
from six.moves.urllib.parse import urlencode
from tornado.httpclient import HTTPError

from grouper.group_service_account import get_service_accounts
from grouper.models.service_account import ServiceAccount
from grouper.permissions import grant_permission_to_service_account
from grouper.plugin import get_plugin_proxy
from grouper.plugin.base import BasePlugin
from grouper.plugin.exceptions import PluginRejectedMachineSet
from grouper.service_account import (
    can_manage_service_account,
    create_service_account,
    disable_service_account,
    DuplicateServiceAccount,
    enable_service_account,
    is_service_account,
    service_account_permissions,
)
from tests.fixtures import (  # noqa: F401
    fe_app as app,
    graph,
    groups,
    permissions,
    service_accounts,
    session,
    standard_graph,
    users,
)
from tests.url_util import url


def test_service_accounts(
    session, standard_graph, graph, users, groups, permissions  # noqa: F811
):
    # Create a service account.
    service_account = ServiceAccount.get(session, name="<EMAIL>")
    assert service_account.description == "some service account"
    assert service_account.machine_set == "some machines"
    assert service_account.user.name == "<EMAIL>"
    assert service_account.user.enabled == True
    assert service_account.user.is_service_account == True
    accounts = get_service_accounts(session, groups["team-sre"])
    assert len(accounts) == 1
    assert accounts[0].user.name == "<EMAIL>"
    assert is_service_account(session, service_account.user)

    # Duplicates should raise an exception.
    with pytest.raises(DuplicateServiceAccount):
        create_service_account(
            session, users["<EMAIL>"], "<EMAIL>", "dup", "dup", groups["team-sre"]
        )

    # zorkian should be able to manage the account, as should gary, but oliver (not a member of the
    # group) should not.
    assert can_manage_service_account(session, service_account, users["<EMAIL>"])
    assert can_manage_service_account(session, service_account, users["<EMAIL>"])
    assert not can_manage_service_account(session, service_account, users["<EMAIL>"])

    # Check that the user appears in the graph.
    graph.update_from_db(session)
    metadata = graph.user_metadata["<EMAIL>"]
    assert metadata["enabled"]
    assert metadata["service_account"]["description"] == "some service account"
    assert metadata["service_account"]["machine_set"] == "some machines"
    assert metadata["service_account"]["owner"] == "team-sre"
    group_details = graph.get_group_details("team-sre")
    assert group_details["service_accounts"] == ["<EMAIL>"]

    # Grant a permission to the service account and check it in the graph.
    grant_permission_to_service_account(session, service_account, permissions["team-sre"], "*")
    graph.update_from_db(session)
    user_details = graph.get_user_details("<EMAIL>")
    assert user_details["permissions"][0]["permission"] == "team-sre"
    assert user_details["permissions"][0]["argument"] == "*"

    # Diabling the service account should remove the link to the group.
    disable_service_account(session, users["<EMAIL>"], service_account)
    assert service_account.user.enabled == False
    assert get_service_accounts(session, groups["team-sre"]) == []

    # The user should also be gone from the graph and have its permissions removed.
    graph.update_from_db(session)
    group_details = graph.get_group_details("team-sre")
    assert "service_accounts" not in group_details
    metadata = graph.user_metadata["<EMAIL>"]
    assert not metadata["enabled"]
    assert "owner" not in metadata["service_account"]
    user_details = graph.get_user_details("<EMAIL>")
    assert user_details["permissions"] == []

    # We can re-enable and attach to a different group.
    new_group = groups["security-team"]
    enable_service_account(session, users["<EMAIL>"], service_account, new_group)
    assert service_account.user.enabled == True
    assert get_service_accounts(session, groups["team-sre"]) == []
    accounts = get_service_accounts(session, new_group)
    assert len(accounts) == 1
    assert accounts[0].user.name == "<EMAIL>"

    # Check that this is reflected in the graph and the user has no permissions.
    graph.update_from_db(session)
    group_details = graph.get_group_details("security-team")
    assert group_details["service_accounts"] == ["<EMAIL>"]
    metadata = graph.user_metadata["<EMAIL>"]
    assert metadata["service_account"]["owner"] == "security-team"
    user_details = graph.get_user_details("<EMAIL>")
    assert user_details["permissions"] == []


@pytest.mark.gen_test
def test_service_account_fe_disable(
    session, standard_graph, graph, http_client, base_url  # noqa: F811
):
    admin = "<EMAIL>"
    owner = "<EMAIL>"
    plebe = "<EMAIL>"

    # Unrelated people cannot disable the service account.
    fe_url = url(base_url, "/groups/security-team/service/service@a.co/disable")
    with pytest.raises(HTTPError):
        yield http_client.fetch(
            fe_url, method="POST", headers={"X-Grouper-User": plebe}, body=urlencode({})
        )

    # Group members can disable the service account.
    resp = yield http_client.fetch(
        fe_url, method="POST", headers={"X-Grouper-User": owner}, body=urlencode({})
    )
    assert resp.code == 200
    graph.update_from_db(session)
    metadata = graph.user_metadata["<EMAIL>"]
    assert not metadata["enabled"]
    group_details = graph.get_group_details("team-sre")
    assert "service_accounts" not in group_details

    # The group owner cannot enable the account, since the group ownership has been lost
    fe_url = url(base_url, "/service/service@a.co/enable")
    with pytest.raises(HTTPError):
        yield http_client.fetch(
            fe_url,
            method="POST",
            headers={"X-Grouper-User": owner},
            body=urlencode({"owner": "team-sre"}),
        )

    # A global admin can enable the account.
    resp = yield http_client.fetch(
        fe_url,
        method="POST",
        headers={"X-Grouper-User": admin},
        body=urlencode({"owner": "team-sre"}),
    )
    assert resp.code == 200
    graph.update_from_db(session)
    metadata = graph.user_metadata["<EMAIL>"]
    assert metadata["enabled"]
    assert metadata["service_account"]["owner"] == "team-sre"
    group_details = graph.get_group_details("team-sre")
    assert group_details["service_accounts"] == ["<EMAIL>"]

    # And can also disable the account even though they're not a member of the group.
    fe_url = url(base_url, "/groups/security-team/service/service@a.co/disable")
    resp = yield http_client.fetch(
        fe_url, method="POST", headers={"X-Grouper-User": admin}, body=urlencode({})
    )
    assert resp.code == 200
    graph.update_from_db(session)
    metadata = graph.user_metadata["<EMAIL>"]
    assert not metadata["enabled"]


@pytest.mark.gen_test
def test_service_account_fe_edit(
    session, standard_graph, graph, http_client, base_url  # noqa: F811
):
    owner = "<EMAIL>"
    plebe = "<EMAIL>"

    # Unrelated people cannot edit the service account.
    fe_url = url(base_url, "/groups/security-team/service/service@a.co/edit")
    update = {"description": "desc", "machine_set": "machines"}
    with pytest.raises(HTTPError):
        yield http_client.fetch(
            fe_url, method="POST", headers={"X-Grouper-User": plebe}, body=urlencode(update)
        )

    # A group member can.
    resp = yield http_client.fetch(
        fe_url, method="POST", headers={"X-Grouper-User": owner}, body=urlencode(update)
    )
    assert resp.code == 200
    graph.update_from_db(session)
    metadata = graph.user_metadata["<EMAIL>"]
    assert metadata["service_account"]["description"] == "desc"
    assert metadata["service_account"]["machine_set"] == "machines"

    # A user admin also can.
    update["description"] = "done by admin"
    resp = yield http_client.fetch(
        fe_url, method="POST", headers={"X-Grouper-User": owner}, body=urlencode(update)
    )
    assert resp.code == 200
    graph.update_from_db(session)
    metadata = graph.user_metadata["<EMAIL>"]
    assert metadata["service_account"]["description"] == "done by admin"


@pytest.mark.gen_test
def test_service_account_fe_perms(
    session, standard_graph, graph, http_client, base_url  # noqa: F811
):
    admin = "<EMAIL>"
    owner = "<EMAIL>"
    plebe = "<EMAIL>"

    # Unrelated people cannot create a service account
    fe_url = url(base_url, "/groups/team-sre/service/create")
    with pytest.raises(HTTPError):
        yield http_client.fetch(
            fe_url,
            method="POST",
            headers={"X-Grouper-User": plebe},
            body=urlencode({"name": "service_account", "description": "*", "machine_set": "*"}),
        )
    # But group members can create service accounts
    resp = yield http_client.fetch(
        fe_url,
        method="POST",
        headers={"X-Grouper-User": owner},
        body=urlencode({"name": "service_account", "description": "*", "machine_set": "*"}),
    )
    assert resp.code == 200

    # Unrelated people cannot grant a permission.
    fe_url = url(base_url, "/groups/team-sre/service/service@a.co/grant")
    with pytest.raises(HTTPError):
        yield http_client.fetch(
            fe_url,
            method="POST",
            headers={"X-Grouper-User": plebe},
            body=urlencode({"permission": "team-sre", "argument": "*"}),
        )

    # Even group owners cannot grant an unrelated permission.
    resp = yield http_client.fetch(
        fe_url,
        method="POST",
        headers={"X-Grouper-User": owner},
        body=urlencode({"permission": "other-perm", "argument": "*"}),
    )
    assert resp.code == 200
    graph.update_from_db(session)
    metadata = graph.get_user_details("<EMAIL>")
    assert metadata["permissions"] == []

    # Group owners can delegate a team permission.
    resp = yield http_client.fetch(
        fe_url,
        method="POST",
        headers={"X-Grouper-User": owner},
        body=urlencode({"permission": "team-sre", "argument": "*"}),
    )
    assert resp.code == 200

    # Global user admins still cannot grant an unrelated permission.
    resp = yield http_client.fetch(
        fe_url,
        method="POST",
        headers={"X-Grouper-User": admin},
        body=urlencode({"permission": "other-perm", "argument": "*"}),
    )
    assert resp.code == 200
    graph.update_from_db(session)
    metadata = graph.get_user_details("<EMAIL>")
    assert len(metadata["permissions"]) == 1

    # But can delegate a team permission.
    resp = yield http_client.fetch(
        fe_url,
        method="POST",
        headers={"X-Grouper-User": admin},
        body=urlencode({"permission": "ssh", "argument": "*"}),
    )
    assert resp.code == 200

    # Check that the permissions are reflected in the graph.
    graph.update_from_db(session)
    metadata = graph.get_user_details("<EMAIL>")
    perms = [(p["permission"], p["argument"]) for p in metadata["permissions"]]
    assert sorted(perms) == [("ssh", "*"), ("team-sre", "*")]

    # Find the mapping IDs of the two permissions.
    service_account = ServiceAccount.get(session, name="<EMAIL>")
    perms = service_account_permissions(session, service_account)

    # Unrelated people cannot revoke a permission.
    fe_url = url(
        base_url, "/groups/team-sre/service/service@a.co/revoke/{}".format(perms[0].grant_id)
    )
    with pytest.raises(HTTPError):
        yield http_client.fetch(
            fe_url, method="POST", headers={"X-Grouper-User": plebe}, body=urlencode({})
        )

    # But the group owner and a global admin can.
    resp = yield http_client.fetch(
        fe_url, method="POST", headers={"X-Grouper-User": admin}, body=urlencode({})
    )
    assert resp.code == 200
    fe_url = url(
        base_url, "/groups/team-sre/service/service@a.co/revoke/{}".format(perms[1].grant_id)
    )
    resp = yield http_client.fetch(
        fe_url, method="POST", headers={"X-Grouper-User": owner}, body=urlencode({})
    )
    assert resp.code == 200

    # This should have removed all the permissions.
    graph.update_from_db(session)
    metadata = graph.get_user_details("<EMAIL>")
    assert metadata["permissions"] == []


class MachineSetPlugin(BasePlugin):
    """Test plugin that rejects some machine sets."""

    def check_machine_set(self, name, machine_set):
        # type: (str, str) -> None
        if "okay" not in machine_set:
            raise PluginRejectedMachineSet("{} has invalid machine set".format(name))


@pytest.mark.gen_test
def test_machine_set_plugin(session, standard_graph, graph, http_client, base_url):  # noqa: F811
    get_plugin_proxy().add_plugin(MachineSetPlugin())
    admin = "<EMAIL>"

    # Edit the metadata of an existing service account.  This should fail (although return 200)
    # including the appropriate error.
    update = {"description": "some service account", "machine_set": "not valid"}
    fe_url = url(base_url, "/groups/team-sre/service/service@a.co/edit")
    resp = yield http_client.fetch(
        fe_url, method="POST", headers={"X-Grouper-User": admin}, body=urlencode(update)
    )
    assert resp.code == 200
    assert b"<EMAIL> has invalid machine set" in resp.body
    graph.update_from_db(session)
    metadata = graph.user_metadata["<EMAIL>"]
    assert metadata["service_account"]["machine_set"] == "some machines"

    # Use a valid machine set, and then this should go through.
    update["machine_set"] = "is okay"
    resp = yield http_client.fetch(
        fe_url, method="POST", headers={"X-Grouper-User": admin}, body=urlencode(update)
    )
    assert resp.code == 200
    graph.update_from_db(session)
    metadata = graph.user_metadata["<EMAIL>"]
    assert metadata["service_account"]["machine_set"] == "is okay"

<filename>tests/service_accounts_test.py
import pytest
from six.moves.urllib.parse import urlencode
from tornado.httpclient import HTTPError

from grouper.group_service_account import get_service_accounts
from grouper.models.service_account import ServiceAccount
from grouper.permissions import grant_permission_to_service_account
from grouper.plugin import get_plugin_proxy
from grouper.plugin.base import BasePlugin
from grouper.plugin.exceptions import PluginRejectedMachineSet
from grouper.service_account import (
    can_manage_service_account,
    create_service_account,
    disable_service_account,
    DuplicateServiceAccount,
    enable_service_account,
    is_service_account,
    service_account_permissions,
)
from tests.fixtures import (  # noqa: F401
    fe_app as app,
    graph,
    groups,
    permissions,
    service_accounts,
    session,
    standard_graph,
    users,
)
from tests.url_util import url


def test_service_accounts(
    session, standard_graph, graph, users, groups, permissions  # noqa: F811
):
    # Create a service account.
    service_account = ServiceAccount.get(session, name="<EMAIL>")
    assert service_account.description == "some service account"
    assert service_account.machine_set == "some machines"
    assert service_account.user.name == "<EMAIL>"
    assert service_account.user.enabled == True
    assert service_account.user.is_service_account == True
    accounts = get_service_accounts(session, groups["team-sre"])
    assert len(accounts) == 1
    assert accounts[0].user.name == "<EMAIL>"
    assert is_service_account(session, service_account.user)

    # Duplicates should raise an exception.
    with pytest.raises(DuplicateServiceAccount):
        create_service_account(
            session, users["<EMAIL>"], "<EMAIL>", "dup", "dup", groups["team-sre"]
        )

    # zorkian should be able to manage the account, as should gary, but oliver (not a member of the
    # group) should not.
    assert can_manage_service_account(session, service_account, users["<EMAIL>"])
    assert can_manage_service_account(session, service_account, users["<EMAIL>"])
    assert not can_manage_service_account(session, service_account, users["<EMAIL>"])

    # Check that the user appears in the graph.
    graph.update_from_db(session)
    metadata = graph.user_metadata["<EMAIL>"]
    assert metadata["enabled"]
    assert metadata["service_account"]["description"] == "some service account"
    assert metadata["service_account"]["machine_set"] == "some machines"
    assert metadata["service_account"]["owner"] == "team-sre"
    group_details = graph.get_group_details("team-sre")
    assert group_details["service_accounts"] == ["<EMAIL>"]

    # Grant a permission to the service account and check it in the graph.
    grant_permission_to_service_account(session, service_account, permissions["team-sre"], "*")
    graph.update_from_db(session)
    user_details = graph.get_user_details("<EMAIL>")
    assert user_details["permissions"][0]["permission"] == "team-sre"
    assert user_details["permissions"][0]["argument"] == "*"

    # Diabling the service account should remove the link to the group.
    disable_service_account(session, users["<EMAIL>"], service_account)
    assert service_account.user.enabled == False
    assert get_service_accounts(session, groups["team-sre"]) == []

    # The user should also be gone from the graph and have its permissions removed.
    graph.update_from_db(session)
    group_details = graph.get_group_details("team-sre")
    assert "service_accounts" not in group_details
    metadata = graph.user_metadata["<EMAIL>"]
    assert not metadata["enabled"]
    assert "owner" not in metadata["service_account"]
    user_details = graph.get_user_details("<EMAIL>")
    assert user_details["permissions"] == []

    # We can re-enable and attach to a different group.
    new_group = groups["security-team"]
    enable_service_account(session, users["<EMAIL>"], service_account, new_group)
    assert service_account.user.enabled == True
    assert get_service_accounts(session, groups["team-sre"]) == []
    accounts = get_service_accounts(session, new_group)
    assert len(accounts) == 1
    assert accounts[0].user.name == "<EMAIL>"

    # Check that this is reflected in the graph and the user has no permissions.
    graph.update_from_db(session)
    group_details = graph.get_group_details("security-team")
    assert group_details["service_accounts"] == ["<EMAIL>"]
    metadata = graph.user_metadata["<EMAIL>"]
    assert metadata["service_account"]["owner"] == "security-team"
    user_details = graph.get_user_details("<EMAIL>")
    assert user_details["permissions"] == []


@pytest.mark.gen_test
def test_service_account_fe_disable(
    session, standard_graph, graph, http_client, base_url  # noqa: F811
):
    admin = "<EMAIL>"
    owner = "<EMAIL>"
    plebe = "<EMAIL>"

    # Unrelated people cannot disable the service account.
    fe_url = url(base_url, "/groups/security-team/service/service@a.co/disable")
    with pytest.raises(HTTPError):
        yield http_client.fetch(
            fe_url, method="POST", headers={"X-Grouper-User": plebe}, body=urlencode({})
        )

    # Group members can disable the service account.
    resp = yield http_client.fetch(
        fe_url, method="POST", headers={"X-Grouper-User": owner}, body=urlencode({})
    )
    assert resp.code == 200
    graph.update_from_db(session)
    metadata = graph.user_metadata["<EMAIL>"]
    assert not metadata["enabled"]
    group_details = graph.get_group_details("team-sre")
    assert "service_accounts" not in group_details

    # The group owner cannot enable the account, since the group ownership has been lost
    fe_url = url(base_url, "/service/service@a.co/enable")
    with pytest.raises(HTTPError):
        yield http_client.fetch(
            fe_url,
            method="POST",
            headers={"X-Grouper-User": owner},
            body=urlencode({"owner": "team-sre"}),
        )

    # A global admin can enable the account.
    resp = yield http_client.fetch(
        fe_url,
        method="POST",
        headers={"X-Grouper-User": admin},
        body=urlencode({"owner": "team-sre"}),
    )
    assert resp.code == 200
    graph.update_from_db(session)
    metadata = graph.user_metadata["<EMAIL>"]
    assert metadata["enabled"]
    assert metadata["service_account"]["owner"] == "team-sre"
    group_details = graph.get_group_details("team-sre")
    assert group_details["service_accounts"] == ["<EMAIL>"]

    # And can also disable the account even though they're not a member of the group.
    fe_url = url(base_url, "/groups/security-team/service/service@a.co/disable")
    resp = yield http_client.fetch(
        fe_url, method="POST", headers={"X-Grouper-User": admin}, body=urlencode({})
    )
    assert resp.code == 200
    graph.update_from_db(session)
    metadata = graph.user_metadata["<EMAIL>"]
    assert not metadata["enabled"]


@pytest.mark.gen_test
def test_service_account_fe_edit(
    session, standard_graph, graph, http_client, base_url  # noqa: F811
):
    owner = "<EMAIL>"
    plebe = "<EMAIL>"

    # Unrelated people cannot edit the service account.
    fe_url = url(base_url, "/groups/security-team/service/service@a.co/edit")
    update = {"description": "desc", "machine_set": "machines"}
    with pytest.raises(HTTPError):
        yield http_client.fetch(
            fe_url, method="POST", headers={"X-Grouper-User": plebe}, body=urlencode(update)
        )

    # A group member can.
    resp = yield http_client.fetch(
        fe_url, method="POST", headers={"X-Grouper-User": owner}, body=urlencode(update)
    )
    assert resp.code == 200
    graph.update_from_db(session)
    metadata = graph.user_metadata["<EMAIL>"]
    assert metadata["service_account"]["description"] == "desc"
    assert metadata["service_account"]["machine_set"] == "machines"

    # A user admin also can.
    update["description"] = "done by admin"
    resp = yield http_client.fetch(
        fe_url, method="POST", headers={"X-Grouper-User": owner}, body=urlencode(update)
    )
    assert resp.code == 200
    graph.update_from_db(session)
    metadata = graph.user_metadata["<EMAIL>"]
    assert metadata["service_account"]["description"] == "done by admin"


@pytest.mark.gen_test
def test_service_account_fe_perms(
    session, standard_graph, graph, http_client, base_url  # noqa: F811
):
    admin = "<EMAIL>"
    owner = "<EMAIL>"
    plebe = "<EMAIL>"

    # Unrelated people cannot create a service account
    fe_url = url(base_url, "/groups/team-sre/service/create")
    with pytest.raises(HTTPError):
        yield http_client.fetch(
            fe_url,
            method="POST",
            headers={"X-Grouper-User": plebe},
            body=urlencode({"name": "service_account", "description": "*", "machine_set": "*"}),
        )
    # But group members can create service accounts
    resp = yield http_client.fetch(
        fe_url,
        method="POST",
        headers={"X-Grouper-User": owner},
        body=urlencode({"name": "service_account", "description": "*", "machine_set": "*"}),
    )
    assert resp.code == 200

    # Unrelated people cannot grant a permission.
    fe_url = url(base_url, "/groups/team-sre/service/service@a.co/grant")
    with pytest.raises(HTTPError):
        yield http_client.fetch(
            fe_url,
            method="POST",
            headers={"X-Grouper-User": plebe},
            body=urlencode({"permission": "team-sre", "argument": "*"}),
        )

    # Even group owners cannot grant an unrelated permission.
    resp = yield http_client.fetch(
        fe_url,
        method="POST",
        headers={"X-Grouper-User": owner},
        body=urlencode({"permission": "other-perm", "argument": "*"}),
    )
    assert resp.code == 200
    graph.update_from_db(session)
    metadata = graph.get_user_details("<EMAIL>")
    assert metadata["permissions"] == []

    # Group owners can delegate a team permission.
    resp = yield http_client.fetch(
        fe_url,
        method="POST",
        headers={"X-Grouper-User": owner},
        body=urlencode({"permission": "team-sre", "argument": "*"}),
    )
    assert resp.code == 200

    # Global user admins still cannot grant an unrelated permission.
    resp = yield http_client.fetch(
        fe_url,
        method="POST",
        headers={"X-Grouper-User": admin},
        body=urlencode({"permission": "other-perm", "argument": "*"}),
    )
    assert resp.code == 200
    graph.update_from_db(session)
    metadata = graph.get_user_details("<EMAIL>")
    assert len(metadata["permissions"]) == 1

    # But can delegate a team permission.
    resp = yield http_client.fetch(
        fe_url,
        method="POST",
        headers={"X-Grouper-User": admin},
        body=urlencode({"permission": "ssh", "argument": "*"}),
    )
    assert resp.code == 200

    # Check that the permissions are reflected in the graph.
    graph.update_from_db(session)
    metadata = graph.get_user_details("<EMAIL>")
    perms = [(p["permission"], p["argument"]) for p in metadata["permissions"]]
    assert sorted(perms) == [("ssh", "*"), ("team-sre", "*")]

    # Find the mapping IDs of the two permissions.
    service_account = ServiceAccount.get(session, name="<EMAIL>")
    perms = service_account_permissions(session, service_account)

    # Unrelated people cannot revoke a permission.
    fe_url = url(
        base_url, "/groups/team-sre/service/service@a.co/revoke/{}".format(perms[0].grant_id)
    )
    with pytest.raises(HTTPError):
        yield http_client.fetch(
            fe_url, method="POST", headers={"X-Grouper-User": plebe}, body=urlencode({})
        )

    # But the group owner and a global admin can.
    resp = yield http_client.fetch(
        fe_url, method="POST", headers={"X-Grouper-User": admin}, body=urlencode({})
    )
    assert resp.code == 200
    fe_url = url(
        base_url, "/groups/team-sre/service/service@a.co/revoke/{}".format(perms[1].grant_id)
    )
    resp = yield http_client.fetch(
        fe_url, method="POST", headers={"X-Grouper-User": owner}, body=urlencode({})
    )
    assert resp.code == 200

    # This should have removed all the permissions.
    graph.update_from_db(session)
    metadata = graph.get_user_details("<EMAIL>")
    assert metadata["permissions"] == []


class MachineSetPlugin(BasePlugin):
    """Test plugin that rejects some machine sets."""

    def check_machine_set(self, name, machine_set):
        # type: (str, str) -> None
        if "okay" not in machine_set:
            raise PluginRejectedMachineSet("{} has invalid machine set".format(name))


@pytest.mark.gen_test
def test_machine_set_plugin(session, standard_graph, graph, http_client, base_url):  # noqa: F811
    get_plugin_proxy().add_plugin(MachineSetPlugin())
    admin = "<EMAIL>"

    # Edit the metadata of an existing service account.  This should fail (although return 200)
    # including the appropriate error.
    update = {"description": "some service account", "machine_set": "not valid"}
    fe_url = url(base_url, "/groups/team-sre/service/service@a.co/edit")
    resp = yield http_client.fetch(
        fe_url, method="POST", headers={"X-Grouper-User": admin}, body=urlencode(update)
    )
    assert resp.code == 200
    assert b"<EMAIL> has invalid machine set" in resp.body
    graph.update_from_db(session)
    metadata = graph.user_metadata["<EMAIL>"]
    assert metadata["service_account"]["machine_set"] == "some machines"

    # Use a valid machine set, and then this should go through.
    update["machine_set"] = "is okay"
    resp = yield http_client.fetch(
        fe_url, method="POST", headers={"X-Grouper-User": admin}, body=urlencode(update)
    )
    assert resp.code == 200
    graph.update_from_db(session)
    metadata = graph.user_metadata["<EMAIL>"]
    assert metadata["service_account"]["machine_set"] == "is okay"

# noqa: F401 # noqa: F811 # Create a service account. # Duplicates should raise an exception. # zorkian should be able to manage the account, as should gary, but oliver (not a member of the # group) should not. # Check that the user appears in the graph. # Grant a permission to the service account and check it in the graph. # Diabling the service account should remove the link to the group. # The user should also be gone from the graph and have its permissions removed. # We can re-enable and attach to a different group. # Check that this is reflected in the graph and the user has no permissions. # noqa: F811 # Unrelated people cannot disable the service account. # Group members can disable the service account. # The group owner cannot enable the account, since the group ownership has been lost # A global admin can enable the account. # And can also disable the account even though they're not a member of the group. # noqa: F811 # Unrelated people cannot edit the service account. # A group member can. # A user admin also can. # noqa: F811 # Unrelated people cannot create a service account # But group members can create service accounts # Unrelated people cannot grant a permission. # Even group owners cannot grant an unrelated permission. # Group owners can delegate a team permission. # Global user admins still cannot grant an unrelated permission. # But can delegate a team permission. # Check that the permissions are reflected in the graph. # Find the mapping IDs of the two permissions. # Unrelated people cannot revoke a permission. # But the group owner and a global admin can. # This should have removed all the permissions. Test plugin that rejects some machine sets. # type: (str, str) -> None # noqa: F811 # Edit the metadata of an existing service account.  This should fail (although return 200) # including the appropriate error. # Use a valid machine set, and then this should go through.