Exemplo n.º 1
0
def test_list_grants(setup):
    # type: (SetupTest) -> None
    with setup.transaction():
        create_graph(setup)

    mock_ui = MockUI()
    usecase = setup.usecase_factory.create_list_grants_usecase(mock_ui)
    usecase.list_grants()

    expected = {
        "not-gary":
        UniqueGrantsOfPermission(users={"*****@*****.**": ["foo"]},
                                 role_users={},
                                 service_accounts={}),
        "other-permission":
        UniqueGrantsOfPermission(users={"*****@*****.**": [""]},
                                 role_users={},
                                 service_accounts={}),
        "some-permission":
        UniqueGrantsOfPermission(
            users={
                "*****@*****.**": ["bar", "foo"],
                "*****@*****.**": ["*"]
            },
            role_users={"*****@*****.**": ["foo", "role"]},
            service_accounts={"*****@*****.**": ["*"]},
        ),
        "twice":
        UniqueGrantsOfPermission(users={"*****@*****.**": ["*"]},
                                 role_users={},
                                 service_accounts={}),
    }
    assert mock_ui.grants == expected
    assert mock_ui.grants_of_permission == {}
Exemplo n.º 2
0
def test_broken_service_account_grants(setup):
    # type: (SetupTest) -> None
    """Test correct handling of a bug in service account membership.

    It's currently possible to add the user underlying a service account directly to a group.  This
    was not intended behavior, but unfortunately some code depends on this behavior, so we can't
    fix it (yet).  Until then, we want to suppress any permissions derived from such membership
    from the graph underlying /grants to maintain separation between service account permissions
    and user permissions.  This tests that we do so correctly.

    TODO(rra): Remove this test once we've cleaned up service account membership handling.
    """
    with setup.transaction():
        setup.create_service_account("*****@*****.**", "some-group")
        setup.grant_permission_to_service_account("service", "bar",
                                                  "*****@*****.**")
        setup.grant_permission_to_group("some-permission", "foo",
                                        "another-group")
        setup.add_user_to_group("*****@*****.**", "another-group")

    mock_ui = MockUI()
    usecase = setup.usecase_factory.create_list_grants_usecase(mock_ui)
    usecase.list_grants()
    assert mock_ui.grants == {
        "service":
        UniqueGrantsOfPermission(
            users={},
            role_users={},
            service_accounts={"*****@*****.**": ["bar"]})
    }
Exemplo n.º 3
0
def test_np_owner_grants(setup):
    # type: (SetupTest) -> None
    """Test special behavior of np-owner.

    np-owner roles should not cause permission grants to pass on to the user with that role, either
    as a direct member or as np-owner of a group that in turn is a member of the group with the
    permission.  To make things even trickier, the user who is an np-owner on the shortest path
    should still get the permission if there is some other path that doesn't involve np-owner.
    This sets up a graph to test this behavior.
    """
    with setup.transaction():
        setup.add_user_to_group("*****@*****.**", "group")
        setup.add_user_to_group("*****@*****.**", "np-group", "np-owner")
        setup.grant_permission_to_group("permission", "direct", "np-group")
        setup.add_group_to_group("np-group", "parent-group")
        setup.grant_permission_to_group("permission", "parent", "parent-group")
        setup.add_group_to_group("group", "intermediate-group")
        setup.add_group_to_group("intermediate-group", "grandparent-group")
        setup.add_group_to_group("np-group", "grandparent-group")
        setup.grant_permission_to_group("permission", "grandparent",
                                        "grandparent-group")

    mock_ui = MockUI()
    usecase = setup.usecase_factory.create_list_grants_usecase(mock_ui)
    usecase.list_grants()
    assert mock_ui.grants == {
        "permission":
        UniqueGrantsOfPermission(users={"*****@*****.**": ["grandparent"]},
                                 role_users={},
                                 service_accounts={})
    }
Exemplo n.º 4
0
def test_unknown_permission(setup):
    # type: (SetupTest) -> None
    mock_ui = MockUI()
    usecase = setup.usecase_factory.create_list_grants_usecase(mock_ui)
    usecase.list_grants_of_permission("unknown-permission")
    assert mock_ui.grants == {}
    assert mock_ui.grants_of_permission == {
        "unknown-permission":
        UniqueGrantsOfPermission(users={}, role_users={}, service_accounts={})
    }
Exemplo n.º 5
0
 def all_grants_of_permission(self, permission):
     # type: (str) -> UniqueGrantsOfPermission
     empty_grants = UniqueGrantsOfPermission(users={},
                                             role_users={},
                                             service_accounts={})
     return self._grants_by_permission.get(permission, empty_grants)
Exemplo n.º 6
0
    def _get_grants_by_permission(
            permission_graph,  # type: DiGraph
            group_grants,  # type: Dict[str, List[GroupPermissionGrant]]
            service_account_grants,  # type: Dict[str, List[ServiceAccountPermissionGrant]]
            user_metadata,  # type: Dict[str, Any]
    ):
        # type: (...) -> Dict[str, UniqueGrantsOfPermission]
        """Build a map of permissions to users and service accounts with grants."""
        service_grants = defaultdict(
            lambda: defaultdict(set))  # type: Dict[str, Dict[str, Set[str]]]
        for account, service_grant_list in iteritems(service_account_grants):
            for service_grant in service_grant_list:
                service_grants[service_grant.permission][account].add(
                    service_grant.argument)

        # For each group that has a permission grant, determine all of its users from the graph,
        # and then record each permission grant of that group as a grant to all of those users.
        # Use a set for the arguments in our intermediate data structure to handle uniqueness.  We
        # have to separate role users from non-role users here, since they're otherwise identical
        # and are both handled by the same graph.
        #
        # TODO(rra): We currently have a bug that erroneously allows service accounts to be added
        # as regular members of groups, causing them to show up in the user graph.  Work around
        # this by skipping such users based on user metadata.  This special-case can be removed
        # once we enforce that the user underlying service accounts cannot be added as a member of
        # groups.  (A better place to put this is to remove service accounts from the nodes in the
        # graph, but this will break some arguably broken software that (ab)used the membership of
        # service accounts in groups, so we'll do that later when we fix that bug.)
        role_user_grants = defaultdict(
            lambda: defaultdict(set))  # type: Dict[str, Dict[str, Set[str]]]
        user_grants = defaultdict(
            lambda: defaultdict(set))  # type: Dict[str, Dict[str, Set[str]]]
        for group, grant_list in iteritems(group_grants):
            members = set()  # type: Set[str]
            paths = single_source_shortest_path(permission_graph,
                                                ("Group", group))
            for member, path in iteritems(paths):
                member_type, member_name = member
                if member_type != "User":
                    continue
                if "service_account" in user_metadata[member_name]:
                    continue
                members.add(member_name)
            for grant in grant_list:
                for member in members:
                    if user_metadata[member]["role_user"]:
                        role_user_grants[grant.permission][member].add(
                            grant.argument)
                    else:
                        user_grants[grant.permission][member].add(
                            grant.argument)

        # Now, assemble the service_grants, role_user_grants, and user_grants dicts into a single
        # dictionary of permission names to UniqueGrantsOfPermission named tuples.  defaultdicts
        # don't compare easily to dicts and the API server wants to return lists, so convert to a
        # regular dict with list values for ease of testing.  (The performance loss should be
        # insignificant.)
        all_grants = {}  # type: Dict[str, UniqueGrantsOfPermission]
        for permission in set(user_grants.keys()) | set(service_grants.keys()):
            grants = UniqueGrantsOfPermission(
                users={
                    k: sorted(v)
                    for k, v in iteritems(user_grants[permission])
                },
                role_users={
                    k: sorted(v)
                    for k, v in iteritems(role_user_grants[permission])
                },
                service_accounts={
                    k: sorted(v)
                    for k, v in iteritems(service_grants[permission])
                },
            )
            all_grants[permission] = grants

        return all_grants