Example #1
0
    def get(self):
        user = self.get_current_user()
        if not (user_has_permission(self.session, user, AUDIT_VIEWER) or
                user_has_permission(self.session, user, AUDIT_MANAGER)):
            return self.forbidden()

        offset = int(self.get_argument("offset", 0))
        limit = int(self.get_argument("limit", 50))
        if limit > 200:
            limit = 200

        open_filter = self.get_argument("filter", "Open Audits")
        audits = get_audits(self.session, only_open=(open_filter == "Open Audits"))

        open_audits = any([not audit.complete for audit in audits])
        total = audits.count()
        audits = audits.offset(offset).limit(limit).all()

        open_audits = self.session.query(Audit).filter(
            Audit.complete == False).all()
        can_start = user_has_permission(self.session, user, AUDIT_MANAGER)

        # FIXME(herb): make limit selected from ui
        audit_log_entries = AuditLog.get_entries(self.session, category=AuditLogCategory.audit,
                limit=100)

        self.render(
            "audits.html", audits=audits, open_filter=open_filter, can_start=can_start,
            offset=offset, limit=limit, total=total, open_audits=open_audits,
            audit_log_entries=audit_log_entries,
        )
Example #2
0
    def get(self):
        user = self.get_current_user()
        if not (user_has_permission(self.session, user, AUDIT_VIEWER) or
                user_has_permission(self.session, user, AUDIT_MANAGER)):
            return self.forbidden()

        offset = int(self.get_argument("offset", 0))
        limit = int(self.get_argument("limit", 50))
        if limit > 200:
            limit = 200

        open_filter = self.get_argument("filter", "Open Audits")
        audits = get_audits(self.session, only_open=(open_filter == "Open Audits"))

        open_audits = any([not audit.complete for audit in audits])
        total = audits.count()
        audits = audits.offset(offset).limit(limit).all()

        open_audits = self.session.query(Audit).filter(
            Audit.complete == False).all()
        can_start = user_has_permission(self.session, user, AUDIT_MANAGER)

        # FIXME(herb): make limit selected from ui
        audit_log_entries = AuditLog.get_entries(self.session, category=AuditLogCategory.audit,
                limit=100)

        self.render(
            "audits.html", audits=audits, open_filter=open_filter, can_start=can_start,
            offset=offset, limit=limit, total=total, open_audits=open_audits,
            audit_log_entries=audit_log_entries,
        )
Example #3
0
    def post(self, audit_id):
        user = self.get_current_user()
        if not user_has_permission(self.session, user, PERMISSION_AUDITOR):
            return self.forbidden()

        audit = self.session.query(Audit).filter(Audit.id == audit_id).one()

        # only owners can complete
        owner_ids = {member.id for member in audit.group.my_owners().values()}
        if user.id not in owner_ids:
            return self.forbidden()

        if audit.complete:
            return self.redirect("/groups/{}".format(audit.group.name))

        edges = {}
        for argument in self.request.arguments:
            if argument.startswith('audit_'):
                edges[int(argument.split('_')[1])] = self.request.arguments[argument][0]

        for member in audit.my_members():
            if member.id in edges:
                # You can only approve yourself (otherwise you can remove yourself
                # from the group and leave it ownerless)
                if member.member.id == user.id:
                    member.status = "approved"
                elif edges[member.id] in AUDIT_STATUS_CHOICES:
                    member.status = edges[member.id]

        self.session.commit()

        # Now if it's completable (no pendings) then mark it complete, else redirect them
        # to the group page.
        if not audit.completable:
            return self.redirect('/groups/{}'.format(audit.group.name))

        # Complete audits have to be "enacted" now. This means anybody marked as remove has to
        # be removed from the group now.
        for member in audit.my_members():
            if member.status == "remove":
                audit.group.revoke_member(self.current_user, member.member,
                                          "Revoked as part of audit.")
                AuditLog.log(self.session, self.current_user.id, 'remove_member',
                             'Removed membership in audit: {}'.format(member.member.name),
                             on_group_id=audit.group.id, on_user_id=member.member.id,
                             category=AuditLogCategory.audit)

        audit.complete = True
        self.session.commit()

        # Now cancel pending emails
        cancel_async_emails(self.session, 'audit-{}'.format(audit.group.id))

        AuditLog.log(self.session, self.current_user.id, 'complete_audit',
                     'Completed group audit.', on_group_id=audit.group.id,
                     category=AuditLogCategory.audit)

        # check if all audits are complete
        if get_audits(self.session, only_open=True).count() == 0:
            AuditLog.log(self.session, self.current_user.id, 'complete_global_audit',
                    'last open audit have been completed', category=AuditLogCategory.audit)

        return self.redirect('/groups/{}'.format(audit.group.name))
Example #4
0
def test_audit_end_to_end(session, users, groups, http_client, base_url, graph):  # noqa: F811
    """ Tests an end-to-end audit cycle. """
    groupname = "audited-team"

    gary_id = users["*****@*****.**"].id

    # make everyone an auditor or global audit will have issues
    add_member(groups["auditors"], users["*****@*****.**"])
    add_member(groups["auditors"], users["*****@*****.**"])
    add_member(groups["auditors"], users["*****@*****.**"])
    add_member(groups["auditors"], users["*****@*****.**"])

    # add some users to test removal
    add_member(groups[groupname], users["*****@*****.**"])
    add_member(groups[groupname], users["*****@*****.**"])

    graph.update_from_db(session)

    # start the audit
    end_at_str = (datetime.now() + timedelta(days=10)).strftime("%m/%d/%Y")
    fe_url = url(base_url, "/audits/create")
    resp = yield http_client.fetch(
        fe_url,
        method="POST",
        body=urlencode({"ends_at": end_at_str}),
        headers={"X-Grouper-User": "******"},
    )
    assert resp.code == 200

    open_audits = get_audits(session, only_open=True).all()
    assert len(open_audits) == 4, "audits created"

    assert groupname in [x.group.name for x in open_audits], "group we expect also gets audit"

    # pull all the info we need to resolve audits, avoids detached sqlalchemy sessions
    AuditMember = namedtuple("AuditMember", "am_id, edge_type, edge_id")
    Audit = namedtuple("Audit", "audit_id, owner_name, group_name, audit_members")
    all_group_ids = [x.group.id for x in open_audits]
    open_audits = [
        Audit(
            x.id,
            next(iter(x.group.my_owners())),
            x.group.name,
            [AuditMember(am.id, am.edge.member_type, am.edge_id) for am in x.my_members()],
        )
        for x in open_audits
    ]

    # approve everything but the one we added members to
    for one_audit in open_audits:
        fe_url = url(base_url, "/audits/{}/complete".format(one_audit.audit_id))

        if one_audit.group_name == groupname:
            continue

        # blanket approval
        body = urlencode(
            {"audit_{}".format(am.am_id): "approved" for am in one_audit.audit_members}
        )

        resp = yield http_client.fetch(
            fe_url, method="POST", body=body, headers={"X-Grouper-User": one_audit.owner_name}
        )
        assert resp.code == 200

    open_audits = get_audits(session, only_open=True).all()
    assert len(open_audits) == 1, "only our test group remaining"

    one_audit = open_audits[0]
    one_audit.id

    body_dict = {}
    for am in one_audit.my_members():
        if gary_id == am.member.id:
            # deny
            body_dict["audit_{}".format(am.id)] = "remove"
        else:
            # approve
            body_dict["audit_{}".format(am.id)] = "approved"

    owner_name = next(iter(one_audit.group.my_owners()))
    fe_url = url(base_url, "/audits/{}/complete".format(one_audit.id))
    resp = yield http_client.fetch(
        fe_url, method="POST", body=urlencode(body_dict), headers={"X-Grouper-User": owner_name}
    )
    assert resp.code == 200

    # check all the logs
    assert len(AuditLog.get_entries(session, action="start_audit")) == 1, "global start is logged"
    assert (
        len(AuditLog.get_entries(session, action="complete_global_audit")) == 1
    ), "global complete is logged"

    for group_id in all_group_ids:
        assert (
            len(
                AuditLog.get_entries(
                    session,
                    on_group_id=group_id,
                    action="complete_audit",
                    category=AuditLogCategory.audit,
                )
            )
            == 1
        ), "complete entry for each group"

    assert (
        len(AuditLog.get_entries(session, on_user_id=gary_id, category=AuditLogCategory.audit))
        == 1
    ), "removal AuditLog entry on user"
Example #5
0
    def post(self, audit_id):
        user = self.get_current_user()
        if not user_has_permission(self.session, user, PERMISSION_AUDITOR):
            return self.forbidden()

        audit = self.session.query(Audit).filter(Audit.id == audit_id).one()

        # only owners can complete
        owner_ids = {member.id for member in audit.group.my_owners().values()}
        if user.id not in owner_ids:
            return self.forbidden()

        if audit.complete:
            return self.redirect("/groups/{}".format(audit.group.name))

        edges = {}
        for argument in self.request.arguments:
            if argument.startswith('audit_'):
                edges[int(argument.split('_')
                          [1])] = self.request.arguments[argument][0]

        for member in audit.my_members():
            if member.id in edges:
                # You can only approve yourself (otherwise you can remove yourself
                # from the group and leave it ownerless)
                if member.member.id == user.id:
                    member.status = "approved"
                elif edges[member.id] in AUDIT_STATUS_CHOICES:
                    member.status = edges[member.id]

        self.session.commit()

        # Now if it's completable (no pendings) then mark it complete, else redirect them
        # to the group page.
        if not audit.completable:
            return self.redirect('/groups/{}'.format(audit.group.name))

        # Complete audits have to be "enacted" now. This means anybody marked as remove has to
        # be removed from the group now.
        try:
            for member in audit.my_members():
                if member.status == "remove":
                    audit.group.revoke_member(self.current_user, member.member,
                                              "Revoked as part of audit.")
                    AuditLog.log(self.session,
                                 self.current_user.id,
                                 'remove_member',
                                 'Removed membership in audit: {}'.format(
                                     member.member.name),
                                 on_group_id=audit.group.id,
                                 on_user_id=member.member.id,
                                 category=AuditLogCategory.audit)
        except PluginRejectedGroupMembershipUpdate as e:
            alert = Alert("danger", str(e))
            return self.redirect('/groups/{}'.format(audit.group.name),
                                 alerts=[alert])

        audit.complete = True
        self.session.commit()

        # Now cancel pending emails
        cancel_async_emails(self.session, 'audit-{}'.format(audit.group.id))

        AuditLog.log(self.session,
                     self.current_user.id,
                     'complete_audit',
                     'Completed group audit.',
                     on_group_id=audit.group.id,
                     category=AuditLogCategory.audit)

        # check if all audits are complete
        if get_audits(self.session, only_open=True).count() == 0:
            AuditLog.log(self.session,
                         self.current_user.id,
                         'complete_global_audit',
                         'last open audit have been completed',
                         category=AuditLogCategory.audit)

        return self.redirect('/groups/{}'.format(audit.group.name))
Example #6
0
def test_audit_end_to_end(session, users, groups, http_client, base_url,
                          graph):  # noqa: F811
    """ Tests an end-to-end audit cycle. """
    groupname = "audited-team"

    gary_id = users["*****@*****.**"].id

    # make everyone an auditor or global audit will have issues
    add_member(groups["auditors"], users["*****@*****.**"])
    add_member(groups["auditors"], users["*****@*****.**"])
    add_member(groups["auditors"], users["*****@*****.**"])
    add_member(groups["auditors"], users["*****@*****.**"])

    # add some users to test removal
    add_member(groups[groupname], users["*****@*****.**"])
    add_member(groups[groupname], users["*****@*****.**"])

    graph.update_from_db(session)

    # start the audit
    end_at_str = (datetime.now() + timedelta(days=10)).strftime("%m/%d/%Y")
    fe_url = url(base_url, "/audits/create")
    resp = yield http_client.fetch(
        fe_url,
        method="POST",
        body=urlencode({"ends_at": end_at_str}),
        headers={"X-Grouper-User": "******"},
    )
    assert resp.code == 200

    open_audits = get_audits(session, only_open=True).all()
    assert len(open_audits) == 4, "audits created"

    assert groupname in [x.group.name for x in open_audits
                         ], "group we expect also gets audit"

    # pull all the info we need to resolve audits, avoids detached sqlalchemy sessions
    # (DetachedInstanceError)
    all_group_ids = [x.group.id for x in open_audits]
    open_audits = [
        Audit(
            x.id,
            next(iter(x.group.my_owners())),
            x.group.name,
            [
                MyAuditMemberInfo(
                    ami.audit_member_obj.id,
                    ami.audit_member_obj.edge.member_type,
                    ami.audit_member_obj.edge_id,
                ) for ami in get_group_audit_members_infos(session, x.group)
            ],
        ) for x in open_audits
    ]

    # approve everything but the one we added members to
    for one_audit in open_audits:
        fe_url = url(base_url,
                     "/audits/{}/complete".format(one_audit.audit_id))

        if one_audit.group_name == groupname:
            continue

        # blanket approval
        body = urlencode({
            "audit_{}".format(ami.am_id): "approved"
            for ami in one_audit.audit_members_infos
        })

        resp = yield http_client.fetch(
            fe_url,
            method="POST",
            body=body,
            headers={"X-Grouper-User": one_audit.owner_name})
        assert resp.code == 200

    open_audits = get_audits(session, only_open=True).all()
    assert len(open_audits) == 1, "only our test group remaining"

    one_audit = open_audits[0]
    one_audit.id

    body_dict = {}
    for ami in get_group_audit_members_infos(session, one_audit.group):
        if gary_id == ami.member_obj.id:
            # deny
            body_dict["audit_{}".format(ami.audit_member_obj.id)] = "remove"
        else:
            # approve
            body_dict["audit_{}".format(ami.audit_member_obj.id)] = "approved"

    owner_name = next(iter(one_audit.group.my_owners()))
    fe_url = url(base_url, "/audits/{}/complete".format(one_audit.id))
    resp = yield http_client.fetch(fe_url,
                                   method="POST",
                                   body=urlencode(body_dict),
                                   headers={"X-Grouper-User": owner_name})
    assert resp.code == 200

    # check all the logs
    assert len(AuditLog.get_entries(
        session, action="start_audit")) == 1, "global start is logged"
    assert (len(AuditLog.get_entries(
        session,
        action="complete_global_audit")) == 1), "global complete is logged"

    for group_id in all_group_ids:
        assert (len(
            AuditLog.get_entries(
                session,
                on_group_id=group_id,
                action="complete_audit",
                category=AuditLogCategory.audit,
            )) == 1), "complete entry for each group"

    assert (len(
        AuditLog.get_entries(session,
                             on_user_id=gary_id,
                             category=AuditLogCategory.audit)) == 1
            ), "removal AuditLog entry on user"
Example #7
0
def test_audit_end_to_end(session, users, groups, http_client, base_url,
                          graph):  # noqa
    """ Tests an end-to-end audit cycle. """
    groupname = 'audited-team'

    zay_id = users["*****@*****.**"].id
    gary_id = users["*****@*****.**"].id

    # make everyone an auditor or global audit will have issues
    add_member(groups["auditors"], users["*****@*****.**"])
    add_member(groups["auditors"], users["*****@*****.**"])
    add_member(groups["auditors"], users["*****@*****.**"])
    add_member(groups["auditors"], users["*****@*****.**"])

    # add some users to test removal
    add_member(groups[groupname], users["*****@*****.**"])
    add_member(groups[groupname], users["*****@*****.**"])

    graph.update_from_db(session)

    # start the audit
    end_at_str = (datetime.now() + timedelta(days=10)).strftime('%m/%d/%Y')
    fe_url = url(base_url, '/audits/create')
    resp = yield http_client.fetch(fe_url,
                                   method="POST",
                                   body=urlencode({'ends_at': end_at_str}),
                                   headers={'X-Grouper-User': '******'})
    assert resp.code == 200

    open_audits = get_audits(session, only_open=True).all()
    assert len(open_audits) == 4, 'audits created'

    assert groupname in [x.group.name for x in open_audits
                         ], 'group we expect also gets audit'

    # pull all the info we need to resolve audits, avoids detached sqlalchemy sessions
    AuditMember = namedtuple('AuditMember', 'am_id, edge_type, edge_id')
    Audit = namedtuple('Audit',
                       'audit_id, owner_name, group_name, audit_members')
    all_group_ids = [x.group.id for x in open_audits]
    open_audits = [
        Audit(x.id,
              x.group.my_owners().iterkeys().next(), x.group.name, [
                  AuditMember(am.id, am.edge.member_type, am.edge_id)
                  for am in x.my_members()
              ]) for x in open_audits
    ]

    # approve everything but the one we added members to
    for one_audit in open_audits:
        fe_url = url(base_url,
                     '/audits/{}/complete'.format(one_audit.audit_id))

        if one_audit.group_name == groupname:
            continue

        # blanket approval
        body = urlencode({
            "audit_{}".format(am.am_id): "approved"
            for am in one_audit.audit_members
        })

        resp = yield http_client.fetch(
            fe_url,
            method="POST",
            body=body,
            headers={'X-Grouper-User': one_audit.owner_name})
        assert resp.code == 200

    open_audits = get_audits(session, only_open=True).all()
    assert len(open_audits) == 1, 'only our test group remaining'

    one_audit = open_audits[0]
    one_audit.id

    body_dict = {}
    for am in one_audit.my_members():
        if gary_id == am.member.id:
            # deny
            body_dict["audit_{}".format(am.id)] = "remove"
        else:
            # approve
            body_dict["audit_{}".format(am.id)] = "approved"

    owner_name = one_audit.group.my_owners().iterkeys().next()
    fe_url = url(base_url, '/audits/{}/complete'.format(one_audit.id))
    resp = yield http_client.fetch(fe_url,
                                   method="POST",
                                   body=urlencode(body_dict),
                                   headers={'X-Grouper-User': owner_name})
    assert resp.code == 200

    # check all the logs
    assert len(AuditLog.get_entries(
        session, action='start_audit')) == 1, 'global start is logged'
    assert len(AuditLog.get_entries(
        session,
        action='complete_global_audit')) == 1, 'global complete is logged'

    for group_id in all_group_ids:
        assert len(
            AuditLog.get_entries(session,
                                 on_group_id=group_id,
                                 action='complete_audit',
                                 category=AuditLogCategory.audit)
        ) == 1, 'complete entry for each group'

    assert len(
        AuditLog.get_entries(session,
                             on_user_id=gary_id,
                             category=AuditLogCategory.audit)
    ) == 1, 'removal AuditLog entry on user'
Example #8
0
    def post(self, audit_id):
        if not user_has_permission(self.session, self.current_user,
                                   PERMISSION_AUDITOR):
            return self.forbidden()

        audit = self.session.query(Audit).filter(Audit.id == audit_id).one()

        # only owners can complete
        owner_ids = {member.id for member in audit.group.my_owners().values()}
        if self.current_user.id not in owner_ids:
            return self.forbidden()

        if audit.complete:
            return self.redirect("/groups/{}".format(audit.group.name))

        edges = {}
        for argument in self.request.arguments:
            if argument.startswith("audit_"):
                edges[int(argument.split("_")
                          [1])] = self.request.arguments[argument][0].decode()

        for audit_member_info in get_group_audit_members_infos(
                self.session, audit.group):
            if audit_member_info.audit_member_obj.id in edges:
                # You can only approve yourself (otherwise you can remove yourself
                # from the group and leave it ownerless)
                if audit_member_info.member_obj.id == self.current_user.id:
                    audit_member_info.audit_member_obj.status = "approved"
                elif edges[audit_member_info.audit_member_obj.
                           id] in AUDIT_STATUS_CHOICES:
                    audit_member_info.audit_member_obj.status = edges[
                        audit_member_info.audit_member_obj.id]

        self.session.commit()

        # If there are still pending statuses, then redirect to the group page.
        if group_has_pending_audit_members(self.session, audit.group):
            return self.redirect("/groups/{}".format(audit.group.name))

        # Complete audits have to be "enacted" now. This means anybody marked as remove has to
        # be removed from the group now.
        try:
            for audit_member_info in get_group_audit_members_infos(
                    self.session, audit.group):
                member_obj = audit_member_info.member_obj
                if audit_member_info.audit_member_obj.status == "remove":
                    audit.group.revoke_member(self.current_user, member_obj,
                                              "Revoked as part of audit.")
                    AuditLog.log(
                        self.session,
                        self.current_user.id,
                        "remove_member",
                        "Removed membership in audit: {}".format(
                            member_obj.name),
                        on_group_id=audit.group.id,
                        on_user_id=member_obj.id,
                        category=AuditLogCategory.audit,
                    )
        except PluginRejectedGroupMembershipUpdate as e:
            alert = Alert("danger", str(e))
            return self.redirect("/groups/{}".format(audit.group.name),
                                 alerts=[alert])

        audit.complete = True
        self.session.commit()

        # Now cancel pending emails
        cancel_async_emails(self.session, "audit-{}".format(audit.group.id))

        AuditLog.log(
            self.session,
            self.current_user.id,
            "complete_audit",
            "Completed group audit.",
            on_group_id=audit.group.id,
            category=AuditLogCategory.audit,
        )

        # check if all audits are complete
        if get_audits(self.session, only_open=True).count() == 0:
            AuditLog.log(
                self.session,
                self.current_user.id,
                "complete_global_audit",
                "last open audit have been completed",
                category=AuditLogCategory.audit,
            )

        return self.redirect("/groups/{}".format(audit.group.name))