Esempio n. 1
0
def get_audited_groups(session):
    # type: (Session) -> List[Group]
    """Returns all audited enabled groups.

    At present, this is not cached at all and returns the full list of
    groups from the database each time it's called.

    Args:
        session (Session): Session to load data on.

    Returns:
        a list of all enabled and audited Group objects in the database
    """
    audited_groups = []
    graph = Graph()
    for group in get_all_groups(session):
        try:
            group_md = graph.get_group_details(group.name)
        except NoSuchGroup:
            # Very new group with no metadata yet, or it has been disabled and
            # excluded from in-memory cache.
            continue

        if group_md.get('audited', False):
            audited_groups.append(group)

    return audited_groups
Esempio n. 2
0
def get_audited_groups(session):
    # type: (Session) -> List[Group]
    """Returns all audited enabled groups.

    At present, this is not cached at all and returns the full list of
    groups from the database each time it's called.

    Args:
        session (Session): Session to load data on.

    Returns:
        a list of all enabled and audited Group objects in the database
    """
    audited_groups = []
    graph = Graph()
    for group in get_all_groups(session):
        try:
            group_md = graph.get_group_details(group.name)
        except NoSuchGroup:
            # Very new group with no metadata yet, or it has been disabled and
            # excluded from in-memory cache.
            continue

        if group_md.get('audited', False):
            audited_groups.append(group)

    return audited_groups
Esempio n. 3
0
    def promote_nonauditors(self, session):
        # type: (Session) -> None
        """Checks all enabled audited groups and ensures that all approvers for that group have
        the PERMISSION_AUDITOR permission. All non-auditor approvers of audited groups will be
        promoted to be auditors, i.e., added to the auditors group.

        Args:
            session (Session): database session
        """
        graph = Graph()
        # Hack to ensure the graph is loaded before we access it
        graph.update_from_db(session)
        # map from user object to names of audited groups in which
        # user is a nonauditor approver
        nonauditor_approver_to_groups = defaultdict(
            set)  # type: Dict[User, Set[str]]
        user_is_auditor = {}  # type: Dict[str, bool]
        for group_tuple in graph.get_groups(audited=True,
                                            directly_audited=False):
            group_md = graph.get_group_details(group_tuple.groupname,
                                               expose_aliases=False)
            for username, user_md in iteritems(group_md["users"]):
                if username not in user_is_auditor:
                    user_perms = graph.get_user_details(
                        username)["permissions"]
                    user_is_auditor[username] = any([
                        p["permission"] == PERMISSION_AUDITOR
                        for p in user_perms
                    ])
                if user_is_auditor[username]:
                    # user is already auditor so can skip
                    continue
                if user_md["role"] in APPROVER_ROLE_INDICES:
                    # non-auditor approver. BAD!
                    nonauditor_approver_to_groups[username].add(
                        group_tuple.groupname)

        if nonauditor_approver_to_groups:
            auditors_group = get_auditors_group(self.settings, session)
            for username, group_names in iteritems(
                    nonauditor_approver_to_groups):
                reason = "auto-added due to having approver role(s) in group(s): {}".format(
                    ", ".join(group_names))
                user = User.get(session, name=username)
                assert user
                auditors_group.add_member(user,
                                          user,
                                          reason,
                                          status="actioned")
                notify_nonauditor_promoted(self.settings, session, user,
                                           auditors_group, group_names)

        session.commit()
Esempio n. 4
0
def assert_controllers_are_auditors(group):
    # type: (Group) -> bool
    """Return whether not all owners/np-owners/managers in a group (and below) are auditors

    This is used to ensure that all of the people who can control a group
    (owners, np-owners, managers) and all subgroups (all the way down the tree)
    have audit permissions.

    Raises:
        UserNotAuditor: If a user is found that violates the audit training policy, then this
            exception is raised.

    Returns:
        bool: True if the tree is completely controlled by auditors, else it will raise as above.
    """
    graph = Graph()
    checked = set()  # type: Set[str]
    queue = [group.name]
    while queue:
        cur_group = queue.pop()
        if cur_group in checked:
            continue
        details = graph.get_group_details(cur_group)
        for chk_user, info in iteritems(details["users"]):
            if chk_user in checked:
                continue
            # Only examine direct members of this group, because then the role is accurate.
            if info["distance"] == 1:
                if info["rolename"] == "member":
                    continue
                if user_is_auditor(chk_user):
                    checked.add(chk_user)
                else:
                    raise UserNotAuditor(
                        "User {} has role '{}' in the group {} but lacks the auditing "
                        "permission ('{}').".format(
                            chk_user, info["rolename"], cur_group, PERMISSION_AUDITOR
                        )
                    )
        # Now put subgroups into the queue to examine.
        for chk_group, info in iteritems(details["subgroups"]):
            if info["distance"] == 1:
                queue.append(chk_group)

    # If we didn't raise, we're valid.
    return True
Esempio n. 5
0
def assert_can_join(group, user_or_group, role="member"):
    # type: (Group, Union[Group, User], str) -> bool
    """Enforce audit rules on joining a group

    This applies the auditing rules to determine whether or not a given user can join the given
    group with the given role.

    Args:
        group (models.Group): The group to test against.
        user (models.User): The user attempting to join.
        role (str): The role being tested.

    Raises:
        UserNotAuditor: If a user is found that violates the audit training policy, then this
            exception is raised.

    Returns:
        bool: True if the user should be allowed per policy, else it will raise as above.
    """
    # By definition, any user can join as a member to any group.
    if user_or_group.type == "User" and role == "member":
        return True

    # Else, we have to check if the group is audited. If not, anybody can join.
    graph = Graph()
    group_md = graph.get_group_details(group.name)
    if not group_md["audited"]:
        return True

    # Audited group. Easy case, let's see if we're checking a user. If so, the user must be
    # considered an auditor.
    if user_or_group.type == "User":
        if user_is_auditor(user_or_group.name):
            return True
        raise UserNotAuditor(
            "User {} lacks the auditing permission ('{}') so may only have the "
            "'member' role in this audited group.".format(user_or_group.name, PERMISSION_AUDITOR)
        )

    # No, this is a group-joining-group case. In this situation we must walk the entire group
    # subtree and ensure that all owners/np-owners/managers are considered auditors. This data
    # is contained in the group metadetails, which contains all eventual members.
    #
    # We have to fetch each group's details individually though to figure out what someone's role
    # is in that particular group.
    return assert_controllers_are_auditors(user_or_group)
Esempio n. 6
0
def assert_can_join(group, user_or_group, role="member"):
    # type: (Group, Union[Group, User], str) -> bool
    """Enforce audit rules on joining a group

    This applies the auditing rules to determine whether or not a given user can join the given
    group with the given role.

    Args:
        group (models.Group): The group to test against.
        user (models.User): The user attempting to join.
        role (str): The role being tested.

    Raises:
        UserNotAuditor: If a user is found that violates the audit training policy, then this
            exception is raised.

    Returns:
        bool: True if the user should be allowed per policy, else it will raise as above.
    """
    # By definition, any user can join as a member to any group.
    if user_or_group.type == "User" and role == "member":
        return True

    # Else, we have to check if the group is audited. If not, anybody can join.
    graph = Graph()
    group_md = graph.get_group_details(group.name)
    if not group_md["audited"]:
        return True

    # Audited group. Easy case, let's see if we're checking a user. If so, the user must be
    # considered an auditor.
    if user_or_group.type == "User":
        if user_is_auditor(user_or_group.name):
            return True
        raise UserNotAuditor(
            "User {} lacks the auditing permission ('{}') so may only have the "
            "'member' role in this audited group.".format(user_or_group.name, PERMISSION_AUDITOR)
        )

    # No, this is a group-joining-group case. In this situation we must walk the entire group
    # subtree and ensure that all owners/np-owners/managers are considered auditors. This data
    # is contained in the group metadetails, which contains all eventual members.
    #
    # We have to fetch each group's details individually though to figure out what someone's role
    # is in that particular group.
    return assert_controllers_are_auditors(user_or_group)
Esempio n. 7
0
def assert_controllers_are_auditors(group):
    # type: (Group) -> bool
    """Return whether not all owners/np-owners/managers in a group (and below) are auditors

    This is used to ensure that all of the people who can control a group
    (owners, np-owners, managers) and all subgroups (all the way down the tree)
    have audit permissions.

    Raises:
        UserNotAuditor: If a user is found that violates the audit training policy, then this
            exception is raised.

    Returns:
        bool: True if the tree is completely controlled by auditors, else it will raise as above.
    """
    graph = Graph()
    checked = set()  # type: Set[str]
    queue = [group.name]
    while queue:
        cur_group = queue.pop()
        if cur_group in checked:
            continue
        details = graph.get_group_details(cur_group)
        for chk_user, info in iteritems(details["users"]):
            if chk_user in checked:
                continue
            # Only examine direct members of this group, because then the role is accurate.
            if info["distance"] == 1:
                if info["rolename"] == "member":
                    continue
                if user_is_auditor(chk_user):
                    checked.add(chk_user)
                else:
                    raise UserNotAuditor(
                        "User {} has role '{}' in the group {} but lacks the auditing "
                        "permission ('{}').".format(
                            chk_user, info["rolename"], cur_group, PERMISSION_AUDITOR
                        )
                    )
        # Now put subgroups into the queue to examine.
        for chk_group, info in iteritems(details["subgroups"]):
            if info["distance"] == 1:
                queue.append(chk_group)

    # If we didn't raise, we're valid.
    return True
Esempio n. 8
0
    def promote_nonauditors(self, session):
        # type: (Session) -> None
        """Checks all enabled audited groups and ensures that all approvers for that group have
        the PERMISSION_AUDITOR permission. All non-auditor approvers of audited groups will be
        promoted to be auditors, i.e., added to the auditors group.

        Args:
            session (Session): database session
        """
        graph = Graph()
        # Hack to ensure the graph is loaded before we access it
        graph.update_from_db(session)
        # map from user object to names of audited groups in which
        # user is a nonauditor approver
        nonauditor_approver_to_groups = defaultdict(set)  # type: Dict[User, Set[str]]
        user_is_auditor = {}  # type: Dict[str, bool]
        for group_tuple in graph.get_groups(audited=True, directly_audited=False):
            group_md = graph.get_group_details(group_tuple.name, expose_aliases=False)
            for username, user_md in iteritems(group_md["users"]):
                if username not in user_is_auditor:
                    user_perms = graph.get_user_details(username)["permissions"]
                    user_is_auditor[username] = any(
                        [p["permission"] == PERMISSION_AUDITOR for p in user_perms]
                    )
                if user_is_auditor[username]:
                    # user is already auditor so can skip
                    continue
                if user_md["role"] in APPROVER_ROLE_INDICES:
                    # non-auditor approver. BAD!
                    nonauditor_approver_to_groups[username].add(group_tuple.name)

        if nonauditor_approver_to_groups:
            auditors_group = get_auditors_group(self.settings, session)
            for username, group_names in iteritems(nonauditor_approver_to_groups):
                reason = "auto-added due to having approver role(s) in group(s): {}".format(
                    ", ".join(group_names)
                )
                user = User.get(session, name=username)
                assert user
                auditors_group.add_member(user, user, reason, status="actioned")
                notify_nonauditor_promoted(
                    self.settings, session, user, auditors_group, group_names
                )

        session.commit()
Esempio n. 9
0
def assert_controllers_are_auditors(group: Group) -> None:
    """Return whether not all owners/np-owners/managers in a group (and below) are auditors

    This is used to ensure that all of the people who can control a group (owners, np-owners,
    managers) and all subgroups (all the way down the tree) have audit permissions.

    Raises:
        UserNotAuditor: If a user is found that violates the audit training policy
    """
    graph = Graph()
    checked: Set[str] = set()
    queue = [group.name]
    while queue:
        cur_group = queue.pop()
        if cur_group in checked:
            continue
        checked.add(cur_group)
        details = graph.get_group_details(cur_group)
        for chk_user, info in details["users"].items():
            if chk_user in checked:
                continue
            # Only examine direct members of this group, because then the role is accurate.
            if info["distance"] == 1:
                if info["rolename"] == "member":
                    continue
                if user_is_auditor(chk_user):
                    checked.add(chk_user)
                else:
                    raise UserNotAuditor(
                        "User {} has role '{}' in the group {} but lacks the auditing "
                        "permission ('{}').".format(
                            chk_user, info["rolename"], cur_group, PERMISSION_AUDITOR
                        )
                    )
        # Now put subgroups into the queue to examine.
        for chk_group, info in details["subgroups"].items():
            if info["distance"] == 1:
                queue.append(chk_group)