コード例 #1
0
class TestReviewNotification(TestCase):
    """Test notification on Review status change."""
    def setUp(self):
        super(TestReviewNotification, self).setUp()
        self.client.get("/login")
        self.generator = generator.ObjectGenerator()
        self.api = Api()

    @ddt.data(
        (all_models.Review.NotificationTypes.EMAIL_TYPE, 1),
        (all_models.Review.NotificationTypes.ISSUE_TRACKER, 0),
    )
    @ddt.unpack
    def test_notification_add_new_review(self, notification_type,
                                         expected_notifications):
        """After creation of new review notification should be created"""
        program = factories.ProgramFactory()
        resp, _ = self.generator.generate_object(
            all_models.Review,
            {
                "reviewable": {
                    "type": program.type,
                    "id": program.id,
                },
                "context": None,
                "notification_type": notification_type,
                "status": all_models.Review.STATES.UNREVIEWED,
                "access_control_list": build_reviewer_acl(),
            },
        )
        self.assertEqual(201, resp.status_code)
        self.assertEqual(expected_notifications,
                         len(all_models.Notification.query.all()))

    @ddt.data(
        (all_models.Review.NotificationTypes.EMAIL_TYPE, 1),
        (all_models.Review.NotificationTypes.ISSUE_TRACKER, 0),
    )
    @ddt.unpack
    def test_reviewable_attributes(self, notification_type,
                                   expected_notifications):
        """Review change state to Unreviewed

     Notification with notification type STATUS_UNREVIEWED created
    """
        with factories.single_commit():
            program = factories.ProgramFactory()
            review = factories.ReviewFactory(
                status=all_models.Review.STATES.REVIEWED,
                reviewable=program,
                notification_type=notification_type)
        review_id = review.id
        reviewable = review.reviewable

        self.api.modify_object(reviewable, {"title": "new title"})
        review = all_models.Review.query.get(review_id)
        self.assertEqual(review.status, all_models.Review.STATES.UNREVIEWED)

        review_notif_types = all_models.Review.NotificationObjectTypes

        notyf_unreviewed_type = all_models.Notification.query.join(
            all_models.NotificationType).filter(
                all_models.NotificationType.name ==
                review_notif_types.STATUS_UNREVIEWED).all()
        self.assertEqual(expected_notifications, len(notyf_unreviewed_type))

    @ddt.data(
        (all_models.Review.NotificationTypes.EMAIL_TYPE, 1),
        (all_models.Review.NotificationTypes.ISSUE_TRACKER, 0),
    )
    @ddt.unpack
    def test_map_snapshotable_notification(self, notification_type,
                                           expected_notifications):
        """Map snapshotable should change review status and add notification"""
        with factories.single_commit():
            program = factories.ProgramFactory()
            review = factories.ReviewFactory(
                status=all_models.Review.STATES.REVIEWED,
                reviewable=program,
                notification_type=notification_type)
            review_id = review.id

        self.generator.generate_relationship(
            source=program,
            destination=factories.ProductFactory(),
            context=None,
        )

        review = all_models.Review.query.get(review_id)
        self.assertEqual(review.status, all_models.Review.STATES.UNREVIEWED)
        review_notif_types = all_models.Review.NotificationObjectTypes

        notyf_unreviewed_type = all_models.Notification.query.join(
            all_models.NotificationType).filter(
                all_models.NotificationType.name ==
                review_notif_types.STATUS_UNREVIEWED).all()
        self.assertEqual(expected_notifications, len(notyf_unreviewed_type))

    @ddt.data(
        (all_models.Review.NotificationTypes.EMAIL_TYPE, 1),
        (all_models.Review.NotificationTypes.ISSUE_TRACKER, 0),
    )
    @ddt.unpack
    def test_proposal_apply_notification(self, notification_type,
                                         expected_notifications):
        """Reviewable object changed via proposal -> notification created"""
        with factories.single_commit():
            program = factories.ProgramFactory()
            review = factories.ReviewFactory(
                status=all_models.Review.STATES.REVIEWED,
                reviewable=program,
                notification_type=notification_type)
            review_id = review.id

            proposal_content = {
                "fields": {
                    "title": "new title"
                },
            }
            proposal = factories.ProposalFactory(instance=program,
                                                 content=proposal_content,
                                                 agenda="agenda content")
        self.api.modify_object(proposal, {"status": proposal.STATES.APPLIED})

        review = all_models.Review.query.get(review_id)
        self.assertEqual(review.status, all_models.Review.STATES.UNREVIEWED)
        review_notif_types = all_models.Review.NotificationObjectTypes

        notyf_unreviewed_type = all_models.Notification.query.join(
            all_models.NotificationType).filter(
                all_models.NotificationType.name ==
                review_notif_types.STATUS_UNREVIEWED).all()
        self.assertEqual(expected_notifications, len(notyf_unreviewed_type))

    @patch("google.appengine.api.mail.send_mail")
    def test_reviewer_notification_on_create_review(self, _):
        """Reviewer should receive email notification"""
        reviewer = factories.PersonFactory()
        reviewer_role_id = all_models.AccessControlRole.query.filter_by(
            name="Reviewers",
            object_type="Review",
        ).one().id
        program = factories.ProgramFactory()
        email_message = "email email_message"
        self.generator.generate_object(
            all_models.Review,
            {
                "reviewable": {
                    "type": program.type,
                    "id": program.id,
                },
                "context":
                None,
                "notification_type":
                all_models.Review.NotificationTypes.EMAIL_TYPE,
                "status":
                all_models.Review.STATES.UNREVIEWED,
                "email_message":
                email_message,
                "access_control_list": [{
                    "ac_role_id": reviewer_role_id,
                    "person": {
                        "id": reviewer.id
                    },
                }],
            },
        )
        with mock.patch.object(fast_digest.DIGEST_TMPL,
                               "render") as bodybuilder_mock:
            fast_digest.send_notification()
            template_args = bodybuilder_mock.call_args[1]
            self.assertListEqual([], template_args["proposals"])
            self.assertListEqual([], template_args["review_owners"])
            self.assertEqual(1, len(template_args["review_reviewers"]))
            self.assertEqual(
                program.id, template_args["review_reviewers"][0].reviewable.id)
            self.assertEqual(
                email_message,
                template_args["review_reviewers"][0].email_message)

    @patch("google.appengine.api.mail.send_mail")
    def test_self_reviewer_notification_on_create_review(self, _):
        """Auto assigned Reviewer should NOT receive email notification"""
        current_user = all_models.Person.query.filter_by(
            email="*****@*****.**").one()
        reviewer_role_id = all_models.AccessControlRole.query.filter_by(
            name="Reviewers",
            object_type="Review",
        ).one().id
        program = factories.ProgramFactory()
        email_message = "email email_message"
        self.generator.generate_object(
            all_models.Review,
            {
                "reviewable": {
                    "type": program.type,
                    "id": program.id,
                },
                "context":
                None,
                "notification_type":
                all_models.Review.NotificationTypes.EMAIL_TYPE,
                "status":
                all_models.Review.STATES.REVIEWED,
                "email_message":
                email_message,
                "access_control_list": [{
                    "ac_role_id": reviewer_role_id,
                    "person": {
                        "id": current_user.id
                    },
                }],
            },
        )
        with mock.patch.object(fast_digest.DIGEST_TMPL,
                               "render") as bodybuilder_mock:
            fast_digest.send_notification()

            # assert no email sent
            bodybuilder_mock.assert_not_called()

    @patch("google.appengine.api.mail.send_mail")
    def test_reviewer_owner_notification(self, _):
        """Object owners should receive notifications

    System should send notification(s) to object's managers,
    primary contacts, secondary contacts,
    if object is reverted to 'Unreviewed'.
    """
        reviewer = factories.PersonFactory(name='reviewers')
        reviewer_role_id = all_models.AccessControlRole.query.filter_by(
            name="Reviewers",
            object_type="Review",
        ).one().id

        with factories.single_commit():
            program_primary_contact = factories.PersonFactory(name='primary')
            program_secondary_contact = factories.PersonFactory(
                name='secondary')
            program = factories.ProgramFactory()
            program.add_person_with_role_name(program_primary_contact,
                                              "Primary Contacts")
            program.add_person_with_role_name(program_secondary_contact,
                                              "Secondary Contacts")
        email_message = "email email_message"
        self.generator.generate_object(
            all_models.Review,
            {
                "reviewable": {
                    "type": program.type,
                    "id": program.id,
                },
                "context":
                None,
                "notification_type":
                all_models.Review.NotificationTypes.EMAIL_TYPE,
                "status":
                all_models.Review.STATES.REVIEWED,
                "email_message":
                email_message,
                "access_control_list": [{
                    "ac_role_id": reviewer_role_id,
                    "person": {
                        "id": reviewer.id
                    },
                }],
            },
        )

        self.api.put(program, {"title": "new title"})

        with mock.patch.object(fast_digest.DIGEST_TMPL,
                               "render") as bodybuilder_mock:
            fast_digest.send_notification()

            # 4 emails to each user
            self.assertEqual(3, bodybuilder_mock.call_count)
            call_count = _call_counter(bodybuilder_mock)
            # 1 email to reviewer -> need to review
            self.assertEqual(1, call_count["review_reviewers"])

            # 1 emails to owners -> object state updated
            self.assertEqual(2, call_count["review_owners"])

    @patch("google.appengine.api.mail.send_mail")
    @freezegun.freeze_time("2019-01-15 12:00:00")
    def test_notification_subject(self, send_mail_mock):
        """Test that emails are sent with proper subject."""
        expected_subject = "GGRC Change requests review digest " \
                           "for 01/15/2019 04:00:00 PST"

        reviewer = factories.PersonFactory()
        reviewer_role_id = all_models.AccessControlRole.query.filter_by(
            name="Reviewers",
            object_type="Review",
        ).one().id

        with factories.single_commit():
            program_manager = factories.PersonFactory()
            program = factories.ProgramFactory()
            program.add_person_with_role_name(program_manager,
                                              "Program managers")

        email_message = "email email_message"
        _, review = self.generator.generate_object(
            all_models.Review,
            {
                "reviewable": {
                    "type": program.type,
                    "id": program.id,
                },
                "context":
                None,
                "notification_type":
                all_models.Review.NotificationTypes.EMAIL_TYPE,
                "status":
                all_models.Review.STATES.REVIEWED,
                "email_message":
                email_message,
                "access_control_list": [{
                    "ac_role_id": reviewer_role_id,
                    "person": {
                        "id": reviewer.id
                    },
                }],
            },
        )

        self.api.modify_object(review.reviewable, {"title": "new title"})
        with mock.patch.object(fast_digest.DIGEST_TMPL, "render"):
            fast_digest.send_notification()
            for call_item in send_mail_mock.call_args_list:
                self.assertEqual(expected_subject, call_item[1]["subject"])
コード例 #2
0
class TestEvidenceRolePropagation(TestCase):
    """Evidence role propagation test case"""

    # pylint: disable=invalid-name

    def setUp(self):
        super(TestEvidenceRolePropagation, self).setUp()
        self.api = Api()
        self.generator = ObjectGenerator()

    # Propagation isn't work for 'Primary Contacts', 'Secondary Contacts'
    # just add them to data list to check if fix works.

    @ddt.data("Assignees", "Creators", "Verifiers")
    def test_assessment_role_propagation_edit(self, role_name):
        """Asses user with role '{0}' should be able to edit related evidence"""

        _, reader = self.generator.generate_person(user_role="Creator")
        with factories.single_commit():
            assessment = factories.AssessmentFactory()
            assessment.add_person_with_role_name(reader, role_name)
            evidence = factories.EvidenceFactory()
            evidence_id = evidence.id
            factories.RelationshipFactory(source=assessment,
                                          destination=evidence)

        self.api.set_user(reader)

        evidence = all_models.Evidence.query.get(evidence_id)
        new_description = 'new description'
        resp = self.api.modify_object(evidence,
                                      {'description': new_description})
        evidence = self.refresh_object(evidence)
        self.assert200(resp)
        self.assertEquals(new_description, evidence.description)
        self.assertEquals(reader.id, evidence.modified_by_id)

    @ddt.data(
        ("Creator", "Audit Captains", 200),
        ("Creator", "Auditors", 403),
        ("Reader", "Audit Captains", 200),
        ("Reader", "Auditors", 403),
        ("Editor", "Audit Captains", 200),
        ("Editor", "Auditors", 200),
    )
    @ddt.unpack
    def test_audit_role_propagation_edit(self, user_role, audit_role,
                                         status_code):
        """'{0}' assigned as '{1}' should get '{2}' when editing audit evidence"""
        _, user = self.generator.generate_person(user_role=user_role)
        with factories.single_commit():
            audit = factories.AuditFactory()
            audit.add_person_with_role_name(user, audit_role)
            evidence = factories.EvidenceFactory()
            evidence_id = evidence.id

        factories.RelationshipFactory(source=audit, destination=evidence)

        self.api.set_user(user)

        evidence = all_models.Evidence.query.get(evidence_id)
        new_description = 'new description'
        resp = self.api.modify_object(evidence,
                                      {'description': new_description})
        evidence = self.refresh_object(evidence)

        if status_code == 200:
            self.assert200(resp)
            self.assertEquals(new_description, evidence.description)
            self.assertEquals(user.id, evidence.modified_by_id)
        else:
            self.assertStatus(resp, status_code)

    def test_audit_role_propagation_not_delete(self):
        """Audit user with role Auditors can NOT delete related evidence"""
        role_name = "Auditors"
        _, reader = self.generator.generate_person(user_role="Reader")
        with factories.single_commit():
            audit = factories.AuditFactory()
            audit.add_person_with_role_name(reader, role_name)
            evidence = factories.EvidenceFactory()
            evidence_id = evidence.id

        factories.RelationshipFactory(source=audit, destination=evidence)

        self.api.set_user(reader)
        evidence = all_models.Evidence.query.get(evidence_id)

        resp = self.api.delete(evidence)
        self.assertStatus(resp, 403)
        evidence = all_models.Evidence.query.get(evidence_id)
        self.assertTrue(evidence)
コード例 #3
0
ファイル: audit.py プロジェクト: vjsavo4324/ggrc-core
class AuditRBACFactory(base.BaseRBACFactory):
    """Audit RBAC factory class."""
    def __init__(self, user_id, acr, parent=None):
        """Set up objects for Audit permission tests.

    Args:
        user_id: Id of user under which all operations will be run.
        acr: Instance of ACR that should be assigned for tested user.
        parent: Model name in scope of which objects should be set up.
    """
        # pylint: disable=unused-argument
        self.setup_program_scope(user_id, acr, "Audit")

        self.api = Api()
        self.objgen = generator.ObjectGenerator()
        self.objgen.api = self.api

        self.admin_control_id = {
            name: id
            for id, name in access_control.role.get_custom_roles_for(
                "Control").items()
        }["Admin"]
        if user_id:
            self.user_id = user_id
            user = all_models.Person.query.get(user_id)
            self.api.set_user(user)

    def create(self):
        """Create new Audit object."""
        return self.api.post(
            all_models.Audit, {
                "audit": {
                    "title": "New audit",
                    "program": {
                        "id": self.program_id
                    },
                    "context": None,
                    "access_control_list": [],
                }
            })

    def read(self):
        """Read existing Audit object."""
        return self.api.get(all_models.Audit, self.audit_id)

    def update(self):
        """Update title of existing Audit object."""
        audit = all_models.Audit.query.get(self.audit_id)
        return self.api.put(audit, {"title": factories.random_str()})

    def delete(self):
        """Delete Audit object."""
        audit = all_models.Audit.query.get(self.audit_id)
        return self.api.delete(audit)

    def clone(self):
        """Clone existing Audit with Assessment Templates."""
        return self.api.post(
            all_models.Audit, {
                "audit": {
                    "program": {
                        "id": self.program_id,
                        "type": "Program"
                    },
                    "context": None,
                    "operation": "clone",
                    "cloneOptions": {
                        "sourceObjectId": self.audit_id,
                        "mappedObjects": "AssessmentTemplate"
                    }
                }
            })

    def read_revisions(self):
        """Read revisions for Audit object."""
        model_class = get_model("Audit")
        responses = []
        for query in [
                "source_type={}&source_id={}",
                "destination_type={}&destination_id={}",
                "resource_type={}&resource_id={}"
        ]:
            responses.append(
                self.api.get_query(model_class,
                                   query.format("audit", self.audit_id)))
        return responses

    def map_external_control(self):
        """Map Control (on which current user don't have any rights) to Audit."""
        control = factories.ControlFactory()
        audit = all_models.Audit.query.get(self.audit_id)

        return self.objgen.generate_relationship(
            source=audit,
            destination=control,
        )[0]

    def map_control(self):
        """Map new snapshot of Control to Audit."""
        with factories.single_commit():
            control = factories.ControlFactory()
            acl = [
                acl for acl in control._access_control_list
                if acl.ac_role_id == self.admin_control_id
            ][0]
            factories.AccessControlPersonFactory(person_id=self.user_id,
                                                 ac_list=acl)
        audit = all_models.Audit.query.get(self.audit_id)

        return self.objgen.generate_relationship(
            source=audit,
            destination=control,
        )[0]

    def deprecate(self):
        """Set status 'Deprecated' for Audit."""
        audit = all_models.Audit.query.get(self.audit_id)
        return self.api.modify_object(audit, {"status": "Deprecated"})

    def archive(self):
        """Move Audit into archived state."""
        audit = all_models.Audit.query.get(self.audit_id)
        return self.api.modify_object(audit, {"archived": True})

    def unarchive(self):
        """Move Audit into unarchived state."""
        audit = all_models.Audit.query.get(self.audit_id)
        return self.api.modify_object(audit, {"archived": False})

    def summary(self):
        """Get Audit summary information."""
        return self.api.client.get("api/audits/{}/{}".format(
            self.audit_id, "summary"))
コード例 #4
0
class TestEvidenceRolePropagation(TestCase):
  """Evidence role propagation test case"""
  # pylint: disable=invalid-name

  def setUp(self):
    super(TestEvidenceRolePropagation, self).setUp()
    self.api = Api()
    self.generator = ObjectGenerator()

  # Propagation isn't work for 'Primary Contacts', 'Secondary Contacts'
  # just add them to data list to check if fix works.

  @ddt.data("Assignees", "Creators", "Verifiers")
  def test_assessment_role_propagation_edit(self, role_name):
    """Asses user with role '{0}' should be able to edit related evidence"""

    _, reader = self.generator.generate_person(user_role="Creator")
    with factories.single_commit():
      assessment = factories.AssessmentFactory()
      assessment.add_person_with_role_name(reader, role_name)
      evidence = factories.EvidenceFactory()
      evidence_id = evidence.id
      factories.RelationshipFactory(source=assessment, destination=evidence)

    self.api.set_user(reader)

    evidence = all_models.Evidence.query.get(evidence_id)
    new_description = 'new description'
    resp = self.api.modify_object(evidence, {'description': new_description})
    evidence = self.refresh_object(evidence)
    self.assert200(resp)
    self.assertEquals(new_description, evidence.description)
    self.assertEquals(reader.id, evidence.modified_by_id)

  @ddt.data(
      ("Creator", "Audit Captains", 200),
      ("Creator", "Auditors", 403),
      ("Reader", "Audit Captains", 200),
      ("Reader", "Auditors", 403),
      ("Editor", "Audit Captains", 200),
      ("Editor", "Auditors", 200),
  )
  @ddt.unpack
  def test_audit_role_propagation_edit(self, user_role, audit_role,
                                       status_code):
    """'{0}' assigned as '{1}' should get '{2}' when editing audit evidence"""
    _, user = self.generator.generate_person(user_role=user_role)
    with factories.single_commit():
      audit = factories.AuditFactory()
      audit.add_person_with_role_name(user, audit_role)
      evidence = factories.EvidenceFactory()
      evidence_id = evidence.id

    factories.RelationshipFactory(source=audit, destination=evidence)

    self.api.set_user(user)

    evidence = all_models.Evidence.query.get(evidence_id)
    new_description = 'new description'
    resp = self.api.modify_object(evidence, {'description': new_description})
    evidence = self.refresh_object(evidence)

    if status_code == 200:
      self.assert200(resp)
      self.assertEquals(new_description, evidence.description)
      self.assertEquals(user.id, evidence.modified_by_id)
    else:
      self.assertStatus(resp, status_code)

  def test_audit_role_propagation_not_delete(self):
    """Audit user with role Auditors can NOT delete related evidence"""
    role_name = "Auditors"
    _, reader = self.generator.generate_person(user_role="Reader")
    with factories.single_commit():
      audit = factories.AuditFactory()
      audit.add_person_with_role_name(reader, role_name)
      evidence = factories.EvidenceFactory()
      evidence_id = evidence.id

    factories.RelationshipFactory(source=audit, destination=evidence)

    self.api.set_user(reader)
    evidence = all_models.Evidence.query.get(evidence_id)

    resp = self.api.delete(evidence)
    self.assertStatus(resp, 403)
    evidence = all_models.Evidence.query.get(evidence_id)
    self.assertTrue(evidence)
コード例 #5
0
class TestEvidenceRolePropagation(TestCase):
    """Evidence role propagation test case"""

    # pylint: disable=invalid-name

    def setUp(self):
        super(TestEvidenceRolePropagation, self).setUp()
        self.api = Api()
        self.generator = ObjectGenerator()

    # Propagation isn't work for 'Primary Contacts', 'Secondary Contacts'
    # just add them to data list to check if fix works.

    @ddt.data("Assignees", "Creators", "Verifiers")
    def test_assessment_role_propagation_edit(self, role):
        """Asses user with role '{0}' should be able to edit related evidence"""

        _, reader = self.generator.generate_person(user_role="Creator")
        assignees_role = all_models.AccessControlRole.query.filter_by(
            object_type=all_models.Assessment.__name__, name=role).first()

        with factories.single_commit():
            assessment = factories.AssessmentFactory()
            factories.AccessControlListFactory(ac_role=assignees_role,
                                               object=assessment,
                                               person=reader)
            evidence = factories.EvidenceFactory()
            evidence_id = evidence.id
            factories.RelationshipFactory(source=assessment,
                                          destination=evidence)

        self.api.set_user(reader)

        evidence = all_models.Evidence.query.get(evidence_id)
        new_description = 'new description'
        resp = self.api.modify_object(evidence,
                                      {'description': new_description})
        evidence = self.refresh_object(evidence)
        self.assert200(resp)
        self.assertEquals(new_description, evidence.description)
        self.assertEquals(reader.id, evidence.modified_by_id)

    @ddt.data("Audit Captains", "Auditors")
    def test_audit_role_propagation_edit(self, role):
        """Audit user with role '{0}' should be able to edit related evidence"""
        _, reader = self.generator.generate_person(user_role="Reader")
        assignees_role = all_models.AccessControlRole.query.filter_by(
            object_type=all_models.Audit.__name__, name=role).first()
        with factories.single_commit():
            audit = factories.AuditFactory()
            factories.AccessControlListFactory(ac_role=assignees_role,
                                               object=audit,
                                               person=reader)
            evidence = factories.EvidenceFactory()
            evidence_id = evidence.id

        factories.RelationshipFactory(source=audit, destination=evidence)

        self.api.set_user(reader)

        evidence = all_models.Evidence.query.get(evidence_id)
        new_description = 'new description'
        resp = self.api.modify_object(evidence,
                                      {'description': new_description})
        evidence = self.refresh_object(evidence)
        self.assert200(resp)
        self.assertEquals(new_description, evidence.description)
        self.assertEquals(reader.id, evidence.modified_by_id)

    def test_audit_role_propagation_not_delete(self, role="Auditors"):
        """Audit user with role Auditors can NOT delete related evidence"""

        _, reader = self.generator.generate_person(user_role="Reader")
        assignees_role = all_models.AccessControlRole.query.filter_by(
            object_type=all_models.Audit.__name__, name=role).first()
        with factories.single_commit():
            audit = factories.AuditFactory()
            factories.AccessControlListFactory(ac_role=assignees_role,
                                               object=audit,
                                               person=reader)
            evidence = factories.EvidenceFactory()
            evidence_id = evidence.id

        factories.RelationshipFactory(source=audit, destination=evidence)

        self.api.set_user(reader)
        evidence = all_models.Evidence.query.get(evidence_id)

        resp = self.api.delete(evidence)
        self.assertStatus(resp, 403)
        evidence = all_models.Evidence.query.get(evidence_id)
        self.assertTrue(evidence)
コード例 #6
0
class AssessmentRBACFactory(base.BaseRBACFactory):
    """Assessment RBAC factory class."""
    def __init__(self, user_id, acr, parent=None):
        """Set up objects for Assessment permission tests.

    Args:
        user_id: Id of user under which all operations will be run.
        acr: Instance of ACR that should be assigned for tested user.
        parent: Model name in scope of which objects should be set up.
    """
        self.setup_program_scope(user_id, acr, parent)

        self.api = Api()
        self.objgen = generator.ObjectGenerator()
        self.objgen.api = self.api

        if user_id:
            user = all_models.Person.query.get(user_id)
            self.api.set_user(user)

    def create(self):
        """Create new Assessment object."""
        return self.api.post(
            all_models.Assessment, {
                "assessment": {
                    "title": "New Assessment",
                    "context": None,
                    "audit": {
                        "id": self.audit_id
                    },
                },
            })

    def generate(self):
        """Generate new Assessment object."""
        with factories.single_commit():
            control = factories.ControlFactory()
            template_id = factories.AssessmentTemplateFactory().id
        audit = all_models.Audit.query.get(self.audit_id)
        # pylint: disable=protected-access
        snapshot = TestCase._create_snapshots(audit, [control])[0]
        snapshot_id = snapshot.id
        factories.RelationshipFactory(source=snapshot, destination=audit)

        responses = []
        asmnt_data = {
            "assessment": {
                "_generated": True,
                "audit": {
                    "id": self.audit_id,
                    "type": "Audit"
                },
                "object": {
                    "id": snapshot_id,
                    "type": "Snapshot"
                },
                "context": None,
                "title": "New assessment",
            }
        }
        responses.append(self.api.post(all_models.Assessment, asmnt_data))

        asmnt_data["assessment"]["template"] = {
            "id": template_id,
            "type": "AssessmentTemplate"
        }
        responses.append(self.api.post(all_models.Assessment, asmnt_data))
        return responses

    def read(self):
        """Read existing Assessment object."""
        return self.api.get(all_models.Assessment, self.assessment_id)

    def update(self):
        """Update title of existing Assessment object."""
        asmnt = all_models.Assessment.query.get(self.assessment_id)
        return self.api.put(asmnt, {"title": factories.random_str()})

    def delete(self):
        """Delete Assessment object."""
        asmnt = all_models.Assessment.query.get(self.assessment_id)
        return self.api.delete(asmnt)

    def read_revisions(self):
        """Read revisions for Assessment object."""
        model_class = get_model("Assessment")
        responses = []
        for query in [
                "source_type={}&source_id={}",
                "destination_type={}&destination_id={}",
                "resource_type={}&resource_id={}"
        ]:
            responses.append(
                self.api.get_query(
                    model_class, query.format("assessment",
                                              self.assessment_id)))
        return responses

    def map_snapshot(self):
        """Map snapshot to assessment."""
        audit = all_models.Audit.query.get(self.audit_id)
        assessment = all_models.Assessment.query.get(self.assessment_id)
        control = factories.ControlFactory()
        # pylint: disable=protected-access
        snapshot = TestCase._create_snapshots(audit, [control])[0]
        factories.RelationshipFactory(source=snapshot, destination=audit)

        return self.objgen.generate_relationship(
            source=assessment,
            destination=snapshot,
        )[0]

    def deprecate(self):
        """Set status 'Deprecated' for Assessment."""
        assessment = all_models.Assessment.query.get(self.assessment_id)
        return self.api.modify_object(assessment, {"status": "Deprecated"})

    def complete(self):
        """Set status 'Completed' for Assessment."""
        assessment = all_models.Assessment.query.get(self.assessment_id)
        return self.api.put(assessment, {"status": "Completed"})

    def in_progress(self):
        """Set status 'In Progress' for Assessment."""
        assessment = all_models.Assessment.query.get(self.assessment_id)
        return self.api.put(assessment, {"status": "In Progress"})

    def not_started(self):
        """Set status 'Not Started' for Assessment."""
        assessment = all_models.Assessment.query.get(self.assessment_id)
        return self.api.put(assessment, {"status": "Not Started"})

    def decline(self):
        """Set status 'Rework Needed' for Assessment."""
        assessment = all_models.Assessment.query.get(self.assessment_id)
        return self.api.put(assessment, {"status": "Rework Needed"})

    def verify(self):
        """Set status 'Verified' for Assessment."""
        assessment = all_models.Assessment.query.get(self.assessment_id)
        return self.api.put(assessment, {
            "status": "Completed",
            "verified": True
        })

    def map_comment(self):
        """Map new comment to assessment."""
        assessment = all_models.Assessment.query.get(self.assessment_id)

        return self.api.put(
            assessment, {
                "actions": {
                    "add_related": [{
                        "id": None,
                        "type": "Comment",
                        "description": factories.random_str(),
                        "custom_attribute_definition_id": None,
                    }]
                }
            })

    def map_evidence(self):
        """Map new evidence to assessment."""
        assessment = all_models.Assessment.query.get(self.assessment_id)
        return self.api.put(
            assessment, {
                "actions": {
                    "add_related": [{
                        "id": None,
                        "type": "Evidence",
                        "kind": "URL",
                        "title": factories.random_str(),
                        "link": factories.random_str(),
                    }]
                }
            })

    def related_assessments(self):
        """Get related Assessments for existing object."""
        audit = all_models.Audit.query.get(self.audit_id)
        assessment = all_models.Assessment.query.get(self.assessment_id)
        with factories.single_commit():
            control = factories.ControlFactory()
            # pylint: disable=protected-access
            snapshot = TestCase._create_snapshots(audit, [control])[0]
            factories.RelationshipFactory(source=audit, destination=snapshot)
            factories.RelationshipFactory(source=assessment,
                                          destination=snapshot)
            assessment2 = factories.AssessmentFactory(
                assessment_type=assessment.assessment_type)
            factories.RelationshipFactory(source=assessment2,
                                          destination=snapshot)

        return self.api.client.get(
            "/api/related_assessments?object_id={}&object_type=Assessment".
            format(self.assessment_id))

    def related_objects(self):
        """Get related objects for existing Assessment."""
        return self.api.client.get(
            "/api/assessments/{}/related_objects".format(self.assessment_id))
コード例 #7
0
class AuditRBACFactory(base.BaseRBACFactory):
  """Audit RBAC factory class."""

  def __init__(self, user_id, acr, parent=None):
    """Set up objects for Audit permission tests.

    Args:
        user_id: Id of user under which all operations will be run.
        acr: Instance of ACR that should be assigned for tested user.
        parent: Model name in scope of which objects should be set up.
    """
    # pylint: disable=unused-argument
    self.setup_program_scope(user_id, acr, "Audit")

    self.api = Api()
    self.objgen = generator.ObjectGenerator()
    self.objgen.api = self.api

    self.admin_control_id = {
        name: id for id, name
        in access_control.role.get_custom_roles_for("Control").items()
    }["Admin"]
    if user_id:
      self.user_id = user_id
      user = all_models.Person.query.get(user_id)
      self.api.set_user(user)

  def create(self):
    """Create new Audit object."""
    return self.api.post(all_models.Audit, {
        "audit": {
            "title": "New audit",
            "program": {"id": self.program_id},
            "context": None,
            "access_control_list": [],
        }
    })

  def read(self):
    """Read existing Audit object."""
    return self.api.get(all_models.Audit, self.audit_id)

  def update(self):
    """Update title of existing Audit object."""
    audit = all_models.Audit.query.get(self.audit_id)
    return self.api.put(audit, {"title": factories.random_str()})

  def delete(self):
    """Delete Audit object."""
    audit = all_models.Audit.query.get(self.audit_id)
    return self.api.delete(audit)

  def clone(self):
    """Clone existing Audit with Assessment Templates."""
    return self.api.post(all_models.Audit, {
        "audit": {
            "program": {"id": self.program_id, "type": "Program"},
            # workaround - title is required for validation
            "title": "",
            "context": None,
            "operation": "clone",
            "cloneOptions": {
                "sourceObjectId": self.audit_id,
                "mappedObjects": "AssessmentTemplate"
            }
        }
    })

  def read_revisions(self):
    """Read revisions for Audit object."""
    model_class = get_model("Audit")
    responses = []
    for query in ["source_type={}&source_id={}",
                  "destination_type={}&destination_id={}",
                  "resource_type={}&resource_id={}"]:
      responses.append(
          self.api.get_query(model_class, query.format("audit", self.audit_id))
      )
    return responses

  def map_external_control(self):
    """Map Control (on which current user don't have any rights) to Audit."""
    control = factories.ControlFactory()
    audit = all_models.Audit.query.get(self.audit_id)

    return self.objgen.generate_relationship(
        source=audit,
        destination=control,
    )[0]

  def map_control(self):
    """Map new snapshot of Control to Audit."""
    with factories.single_commit():
      control = factories.ControlFactory()
      acl = [
          acl
          for acl in control._access_control_list
          if acl.ac_role_id == self.admin_control_id
      ][0]
      factories.AccessControlPersonFactory(
          person_id=self.user_id,
          ac_list=acl
      )
    audit = all_models.Audit.query.get(self.audit_id)

    return self.objgen.generate_relationship(
        source=audit,
        destination=control,
    )[0]

  def deprecate(self):
    """Set status 'Deprecated' for Audit."""
    audit = all_models.Audit.query.get(self.audit_id)
    return self.api.modify_object(audit, {
        "status": "Deprecated"
    })

  def archive(self):
    """Move Audit into archived state."""
    audit = all_models.Audit.query.get(self.audit_id)
    return self.api.modify_object(audit, {
        "archived": True
    })

  def unarchive(self):
    """Move Audit into unarchived state."""
    audit = all_models.Audit.query.get(self.audit_id)
    return self.api.modify_object(audit, {
        "archived": False
    })

  def summary(self):
    """Get Audit summary information."""
    return self.api.client.get(
        "api/audits/{}/{}".format(self.audit_id, "summary")
    )
コード例 #8
0
class TestReviewNotification(TestCase):
  """Test notification on Review status change."""

  def setUp(self):
    super(TestReviewNotification, self).setUp()
    self.client.get("/login")
    self.generator = generator.ObjectGenerator()
    self.api = Api()

  @ddt.data(
      (all_models.Review.NotificationTypes.EMAIL_TYPE, 1),
      (all_models.Review.NotificationTypes.ISSUE_TRACKER, 0),
  )
  @ddt.unpack
  def test_notification_add_new_review(
      self, notification_type, expected_notifications
  ):
    """After creation of new review notification should be created"""
    program = factories.ProgramFactory()
    resp, _ = self.generator.generate_object(
        all_models.Review,
        {
            "reviewable": {
                "type": program.type,
                "id": program.id,
            },
            "context": None,
            "notification_type": notification_type,
            "status": all_models.Review.STATES.UNREVIEWED,
            "access_control_list": build_reviewer_acl(),
        },
    )
    self.assertEqual(201, resp.status_code)
    self.assertEqual(
        expected_notifications, len(all_models.Notification.query.all())
    )

  @ddt.data(
      (all_models.Review.NotificationTypes.EMAIL_TYPE, 1),
      (all_models.Review.NotificationTypes.ISSUE_TRACKER, 0),
  )
  @ddt.unpack
  def test_reviewable_attributes(self, notification_type,
                                 expected_notifications):
    """Review change state to Unreviewed

     Notification with notification type STATUS_UNREVIEWED created
    """
    with factories.single_commit():
      program = factories.ProgramFactory()
      review = factories.ReviewFactory(
          status=all_models.Review.STATES.REVIEWED,
          reviewable=program,
          notification_type=notification_type
      )
    review_id = review.id
    reviewable = review.reviewable

    self.api.modify_object(reviewable, {"title": "new title"})
    review = all_models.Review.query.get(review_id)
    self.assertEqual(review.status, all_models.Review.STATES.UNREVIEWED)

    review_notif_types = all_models.Review.NotificationObjectTypes

    notyf_unreviewed_type = all_models.Notification.query.join(
        all_models.NotificationType
    ).filter(
        all_models.NotificationType.name ==
        review_notif_types.STATUS_UNREVIEWED
    ).all()
    self.assertEqual(expected_notifications, len(notyf_unreviewed_type))

  @ddt.data(
      (all_models.Review.NotificationTypes.EMAIL_TYPE, 1),
      (all_models.Review.NotificationTypes.ISSUE_TRACKER, 0),
  )
  @ddt.unpack
  def test_map_snapshotable_notification(self, notification_type,
                                         expected_notifications):
    """Map snapshotable should change review status and add notification"""
    with factories.single_commit():
      program = factories.ProgramFactory()
      review = factories.ReviewFactory(
          status=all_models.Review.STATES.REVIEWED,
          reviewable=program,
          notification_type=notification_type
      )
      review_id = review.id

    self.generator.generate_relationship(
        source=program,
        destination=factories.ProductFactory(),
        context=None,
    )

    review = all_models.Review.query.get(review_id)
    self.assertEqual(review.status, all_models.Review.STATES.UNREVIEWED)
    review_notif_types = all_models.Review.NotificationObjectTypes

    notyf_unreviewed_type = all_models.Notification.query.join(
        all_models.NotificationType
    ).filter(
        all_models.NotificationType.name ==
        review_notif_types.STATUS_UNREVIEWED
    ).all()
    self.assertEqual(expected_notifications, len(notyf_unreviewed_type))

  @ddt.data(
      (all_models.Review.NotificationTypes.EMAIL_TYPE, 1),
      (all_models.Review.NotificationTypes.ISSUE_TRACKER, 0),
  )
  @ddt.unpack
  def test_proposal_apply_notification(self, notification_type,
                                       expected_notifications):
    """Reviewable object changed via proposal -> notification created"""
    with factories.single_commit():
      program = factories.ProgramFactory()
      review = factories.ReviewFactory(
          status=all_models.Review.STATES.REVIEWED,
          reviewable=program,
          notification_type=notification_type
      )
      review_id = review.id

      proposal_content = {
          "fields": {
              "title": "new title"
          },
      }
      proposal = factories.ProposalFactory(
          instance=program, content=proposal_content, agenda="agenda content"
      )
    self.api.modify_object(proposal, {"status": proposal.STATES.APPLIED})

    review = all_models.Review.query.get(review_id)
    self.assertEqual(review.status, all_models.Review.STATES.UNREVIEWED)
    review_notif_types = all_models.Review.NotificationObjectTypes

    notyf_unreviewed_type = all_models.Notification.query.join(
        all_models.NotificationType
    ).filter(
        all_models.NotificationType.name ==
        review_notif_types.STATUS_UNREVIEWED
    ).all()
    self.assertEqual(expected_notifications, len(notyf_unreviewed_type))

  @ddt.data(
      (all_models.Review.NotificationTypes.EMAIL_TYPE, 0),
      (all_models.Review.NotificationTypes.ISSUE_TRACKER, 0),
  )
  @ddt.unpack
  def test_proposal_apply_review_status(self, notification_type,
                                        num_notifications_expected):
    """Change via proposal with ignorable attrs review status not change"""
    with factories.single_commit():
      risk = factories.RiskFactory()
      review = factories.ReviewFactory(
          status=all_models.Review.STATES.REVIEWED,
          reviewable=risk,
          notification_type=notification_type
      )
      review_id = review.id

      user = factories.PersonFactory()
      acl = factories.AccessControlListFactory(
          ac_role=factories.AccessControlRoleFactory(object_type="Risk"),
          object=risk
      )

      proposal_content = {
          "access_control_list": {
              acl.ac_role_id: {
                  "added": [{"id": user.id, "email": user.email}],
                  "deleted": []
              }
          }
      }

      proposal = factories.ProposalFactory(
          instance=risk,
          content=proposal_content,
          agenda="agenda content"
      )

    self.api.modify_object(proposal, {"status": proposal.STATES.APPLIED})
    review = all_models.Review.query.get(review_id)
    self.assertEqual(review.status, all_models.Review.STATES.REVIEWED)

    review_notif_types = all_models.Review.NotificationObjectTypes
    notif_unreviewed_type = all_models.Notification.query.join(
        all_models.NotificationType
    ).filter(
        all_models.NotificationType.name ==
        review_notif_types.STATUS_UNREVIEWED
    ).all()

    self.assertEqual(num_notifications_expected, len(notif_unreviewed_type))

  @patch("google.appengine.api.mail.send_mail")
  def test_reviewer_notification_on_create_review(self, _):
    """Reviewer should receive email notification"""
    reviewer = factories.PersonFactory()
    reviewer_role_id = all_models.AccessControlRole.query.filter_by(
        name="Reviewers",
        object_type="Review",
    ).one().id
    program = factories.ProgramFactory()
    email_message = "email email_message"
    self.generator.generate_object(
        all_models.Review,
        {
            "reviewable": {
                "type": program.type,
                "id": program.id,
            },
            "context":
            None,
            "notification_type":
            all_models.Review.NotificationTypes.EMAIL_TYPE,
            "status":
            all_models.Review.STATES.UNREVIEWED,
            "email_message":
            email_message,
            "access_control_list":
            [{
                "ac_role_id": reviewer_role_id,
                "person": {
                    "id": reviewer.id
                },
            }],
        },
    )
    with mock.patch.object(
        fast_digest.DIGEST_TMPL, "render"
    ) as bodybuilder_mock:
      fast_digest.send_notification()
      template_args = bodybuilder_mock.call_args[1]
      self.assertListEqual([], template_args["proposals"])
      self.assertListEqual([], template_args["review_owners"])
      self.assertEqual(1, len(template_args["review_reviewers"]))
      self.assertEqual(
          program.id, template_args["review_reviewers"][0].reviewable.id
      )
      self.assertEqual(
          email_message, template_args["review_reviewers"][0].email_message
      )

  @patch("google.appengine.api.mail.send_mail")
  def test_self_reviewer_notification_on_create_review(self, _):
    """Auto assigned Reviewer should NOT receive email notification"""
    current_user = all_models.Person.query.filter_by(
        email="*****@*****.**"
    ).one()
    reviewer_role_id = all_models.AccessControlRole.query.filter_by(
        name="Reviewers",
        object_type="Review",
    ).one().id
    program = factories.ProgramFactory()
    email_message = "email email_message"
    self.generator.generate_object(
        all_models.Review,
        {
            "reviewable": {
                "type": program.type,
                "id": program.id,
            },
            "context":
            None,
            "notification_type":
            all_models.Review.NotificationTypes.EMAIL_TYPE,
            "status":
            all_models.Review.STATES.REVIEWED,
            "email_message":
            email_message,
            "access_control_list": [
                {
                    "ac_role_id": reviewer_role_id,
                    "person": {
                        "id": current_user.id
                    },
                }
            ],
        },
    )
    with mock.patch.object(
        fast_digest.DIGEST_TMPL, "render"
    ) as bodybuilder_mock:
      fast_digest.send_notification()

      # assert no email sent
      bodybuilder_mock.assert_not_called()

  @patch("google.appengine.api.mail.send_mail")
  def test_reviewer_owner_notification(self, _):
    """Object owners should receive notifications

    System should send notification(s) to object's managers,
    primary contacts, secondary contacts,
    if object is reverted to 'Unreviewed'.
    """
    reviewer = factories.PersonFactory(name='reviewers')
    reviewer_role_id = all_models.AccessControlRole.query.filter_by(
        name="Reviewers",
        object_type="Review",
    ).one().id

    with factories.single_commit():
      program_primary_contact = factories.PersonFactory(name='primary')
      program_secondary_contact = factories.PersonFactory(name='secondary')
      program = factories.ProgramFactory()
      program.add_person_with_role_name(program_primary_contact,
                                        "Primary Contacts")
      program.add_person_with_role_name(program_secondary_contact,
                                        "Secondary Contacts")
    email_message = "email email_message"
    self.generator.generate_object(
        all_models.Review,
        {
            "reviewable": {
                "type": program.type,
                "id": program.id,
            },
            "context":
            None,
            "notification_type":
            all_models.Review.NotificationTypes.EMAIL_TYPE,
            "status":
            all_models.Review.STATES.REVIEWED,
            "email_message":
            email_message,
            "access_control_list":
            [{
                "ac_role_id": reviewer_role_id,
                "person": {
                    "id": reviewer.id
                },
            }],
        },
    )

    self.api.put(program, {"title": "new title"})

    with mock.patch.object(
        fast_digest.DIGEST_TMPL, "render"
    ) as bodybuilder_mock:
      fast_digest.send_notification()

      # 4 emails to each user
      self.assertEqual(3, bodybuilder_mock.call_count)
      call_count = _call_counter(bodybuilder_mock)
      # 1 email to reviewer -> need to review
      self.assertEqual(1, call_count["review_reviewers"])

      # 1 emails to owners -> object state updated
      self.assertEqual(2, call_count["review_owners"])

  @patch("google.appengine.api.mail.send_mail")
  @freezegun.freeze_time("2019-01-15 12:00:00")
  def test_notification_subject(self, send_mail_mock):
    """Test that emails are sent with proper subject."""
    expected_subject = "GGRC Change requests review digest " \
                       "for 01/15/2019 04:00:00 PST"

    reviewer = factories.PersonFactory()
    reviewer_role_id = all_models.AccessControlRole.query.filter_by(
        name="Reviewers",
        object_type="Review",
    ).one().id

    with factories.single_commit():
      risk_admin = factories.PersonFactory()
      risk = factories.RiskFactory()
      risk.add_person_with_role_name(risk_admin, "Admin")

    email_message = "email email_message"
    _, review = self.generator.generate_object(
        all_models.Review,
        {
            "reviewable": {
                "type": risk.type,
                "id": risk.id,
            },
            "context": None,
            "notification_type":
                all_models.Review.NotificationTypes.EMAIL_TYPE,
            "status": all_models.Review.STATES.REVIEWED,
            "email_message": email_message,
            "access_control_list": [{
                "ac_role_id": reviewer_role_id,
                "person": {
                    "id": reviewer.id
                },
            }],
        },
    )

    self.api.modify_object(review.reviewable, {"title": "new title"})
    with mock.patch.object(fast_digest.DIGEST_TMPL, "render"):
      fast_digest.send_notification()
      for call_item in send_mail_mock.call_args_list:
        self.assertEqual(expected_subject, call_item[1]["subject"])
コード例 #9
0
class TestEvidenceRolePropagation(TestCase):
  """Evidence role propagation test case"""
  # pylint: disable=invalid-name

  def setUp(self):
    super(TestEvidenceRolePropagation, self).setUp()
    self.api = Api()
    self.generator = ObjectGenerator()

  # Propagation isn't work for 'Primary Contacts', 'Secondary Contacts'
  # just add them to data list to check if fix works.

  @ddt.data("Assignees", "Creators", "Verifiers")
  def test_assessment_role_propagation_edit(self, role):
    """Asses user with role '{0}' should be able to edit related evidence"""

    _, reader = self.generator.generate_person(user_role="Creator")
    assignees_role = all_models.AccessControlRole.query.filter_by(
        object_type=all_models.Assessment.__name__, name=role
    ).first()

    with factories.single_commit():
      assessment = factories.AssessmentFactory()
      factories.AccessControlListFactory(
          ac_role=assignees_role,
          object=assessment,
          person=reader
      )
      evidence = factories.EvidenceFactory()
      evidence_id = evidence.id
      factories.RelationshipFactory(source=assessment, destination=evidence)

    self.api.set_user(reader)

    evidence = all_models.Evidence.query.get(evidence_id)
    new_description = 'new description'
    resp = self.api.modify_object(evidence, {'description': new_description})
    evidence = self.refresh_object(evidence)
    self.assert200(resp)
    self.assertEquals(new_description, evidence.description)
    self.assertEquals(reader.id, evidence.modified_by_id)

  @ddt.data("Audit Captains", "Auditors")
  def test_audit_role_propagation_edit(self, role):
    """Audit user with role '{0}' should be able to edit related evidence"""
    _, reader = self.generator.generate_person(user_role="Reader")
    assignees_role = all_models.AccessControlRole.query.filter_by(
        object_type=all_models.Audit.__name__, name=role
    ).first()
    with factories.single_commit():
      audit = factories.AuditFactory()
      factories.AccessControlListFactory(
          ac_role=assignees_role,
          object=audit,
          person=reader
      )
      evidence = factories.EvidenceFactory()
      evidence_id = evidence.id

    factories.RelationshipFactory(source=audit, destination=evidence)

    self.api.set_user(reader)

    evidence = all_models.Evidence.query.get(evidence_id)
    new_description = 'new description'
    resp = self.api.modify_object(evidence, {'description': new_description})
    evidence = self.refresh_object(evidence)
    self.assert200(resp)
    self.assertEquals(new_description, evidence.description)
    self.assertEquals(reader.id, evidence.modified_by_id)

  def test_audit_role_propagation_not_delete(self, role="Auditors"):
    """Audit user with role Auditors can NOT delete related evidence"""

    _, reader = self.generator.generate_person(user_role="Reader")
    assignees_role = all_models.AccessControlRole.query.filter_by(
        object_type=all_models.Audit.__name__, name=role
    ).first()
    with factories.single_commit():
      audit = factories.AuditFactory()
      factories.AccessControlListFactory(
          ac_role=assignees_role,
          object=audit,
          person=reader
      )
      evidence = factories.EvidenceFactory()
      evidence_id = evidence.id

    factories.RelationshipFactory(source=audit, destination=evidence)

    self.api.set_user(reader)
    evidence = all_models.Evidence.query.get(evidence_id)

    resp = self.api.delete(evidence)
    self.assertStatus(resp, 403)
    evidence = all_models.Evidence.query.get(evidence_id)
    self.assertTrue(evidence)
コード例 #10
0
class TestReviewNotification(TestCase):
    """Test notification on Review status change."""
    def setUp(self):
        super(TestReviewNotification, self).setUp()
        self.client.get("/login")
        self.generator = generator.ObjectGenerator()
        self.api = Api()

    @ddt.data(
        (all_models.Review.NotificationTypes.EMAIL_TYPE, 1),
        (all_models.Review.NotificationTypes.ISSUE_TRACKER, 0),
    )
    @ddt.unpack
    def test_notification_add_new_review(self, notification_type,
                                         expected_notifications):
        """After creation of new review notification should be created"""
        control = factories.ControlFactory()
        resp, _ = self.generator.generate_object(
            all_models.Review,
            {
                "reviewable": {
                    "type": control.type,
                    "id": control.id,
                },
                "context": None,
                "notification_type": notification_type,
                "status": all_models.Review.STATES.UNREVIEWED,
                "access_control_list": build_reviewer_acl(),
            },
        )
        self.assertEqual(201, resp.status_code)
        self.assertEqual(expected_notifications,
                         len(all_models.Notification.query.all()))

    @ddt.data(
        (all_models.Review.NotificationTypes.EMAIL_TYPE, 1),
        (all_models.Review.NotificationTypes.ISSUE_TRACKER, 0),
    )
    @ddt.unpack
    def test_reviewable_attributes(self, notification_type,
                                   expected_notifications):
        """Review change state to Unreviewed

     Notification with notification type STATUS_UNREVIEWED created
    """
        with factories.single_commit():
            control = factories.ControlFactory()
            review = factories.ReviewFactory(
                status=all_models.Review.STATES.REVIEWED,
                reviewable=control,
                notification_type=notification_type)
        review_id = review.id
        reviewable = review.reviewable

        self.api.modify_object(reviewable, {"title": "new title"})
        review = all_models.Review.query.get(review_id)
        self.assertEqual(review.status, all_models.Review.STATES.UNREVIEWED)

        review_notif_types = all_models.Review.NotificationObjectTypes

        notyf_unreviewed_type = all_models.Notification.query.join(
            all_models.NotificationType).filter(
                all_models.NotificationType.name ==
                review_notif_types.STATUS_UNREVIEWED).all()
        self.assertEqual(expected_notifications, len(notyf_unreviewed_type))

    @ddt.data(
        (all_models.Review.NotificationTypes.EMAIL_TYPE, 1),
        (all_models.Review.NotificationTypes.ISSUE_TRACKER, 0),
    )
    @ddt.unpack
    def test_map_snapshotable_notification(self, notification_type,
                                           expected_notifications):
        """Map snapshotable should change review status and add notification"""
        with factories.single_commit():
            control = factories.ControlFactory()
            review = factories.ReviewFactory(
                status=all_models.Review.STATES.REVIEWED,
                reviewable=control,
                notification_type=notification_type)
            review_id = review.id

        self.generator.generate_relationship(
            source=control,
            destination=factories.ProductFactory(),
            context=None,
        )

        review = all_models.Review.query.get(review_id)
        self.assertEqual(review.status, all_models.Review.STATES.UNREVIEWED)
        review_notif_types = all_models.Review.NotificationObjectTypes

        notyf_unreviewed_type = all_models.Notification.query.join(
            all_models.NotificationType).filter(
                all_models.NotificationType.name ==
                review_notif_types.STATUS_UNREVIEWED).all()
        self.assertEqual(expected_notifications, len(notyf_unreviewed_type))

    @ddt.data(
        (all_models.Review.NotificationTypes.EMAIL_TYPE, 1),
        (all_models.Review.NotificationTypes.ISSUE_TRACKER, 0),
    )
    @ddt.unpack
    def test_proposal_apply_notification(self, notification_type,
                                         expected_notifications):
        """Reviewable object changed via proposal -> notification created"""
        with factories.single_commit():
            control = factories.ControlFactory()
            review = factories.ReviewFactory(
                status=all_models.Review.STATES.REVIEWED,
                reviewable=control,
                notification_type=notification_type)
            review_id = review.id

            proposal_content = {
                "fields": {
                    "title": "new title"
                },
            }
            proposal = factories.ProposalFactory(instance=control,
                                                 content=proposal_content,
                                                 agenda="agenda content")
        self.api.modify_object(proposal, {"status": proposal.STATES.APPLIED})

        review = all_models.Review.query.get(review_id)
        self.assertEqual(review.status, all_models.Review.STATES.UNREVIEWED)
        review_notif_types = all_models.Review.NotificationObjectTypes

        notyf_unreviewed_type = all_models.Notification.query.join(
            all_models.NotificationType).filter(
                all_models.NotificationType.name ==
                review_notif_types.STATUS_UNREVIEWED).all()
        self.assertEqual(expected_notifications, len(notyf_unreviewed_type))

    @ddt.data(
        (all_models.Review.NotificationTypes.EMAIL_TYPE, 0),
        (all_models.Review.NotificationTypes.ISSUE_TRACKER, 0),
    )
    @ddt.unpack
    def test_proposal_apply_review_status(self, notification_type,
                                          num_notifications_expected):
        """Change via proposal with ignorable attrs review status not change"""
        with factories.single_commit():
            control = factories.ControlFactory()
            review = factories.ReviewFactory(
                status=all_models.Review.STATES.REVIEWED,
                reviewable=control,
                notification_type=notification_type)
            review_id = review.id

            user = factories.PersonFactory()
            acl = factories.AccessControlListFactory(
                ac_role=factories.AccessControlRoleFactory(
                    object_type="Control"),
                object=control)

            proposal_content = {
                "access_control_list": {
                    acl.ac_role_id: {
                        "added": [{
                            "id": user.id,
                            "email": user.email
                        }],
                        "deleted": []
                    }
                }
            }

            proposal = factories.ProposalFactory(instance=control,
                                                 content=proposal_content,
                                                 agenda="agenda content")

        self.api.modify_object(proposal, {"status": proposal.STATES.APPLIED})
        review = all_models.Review.query.get(review_id)
        self.assertEqual(review.status, all_models.Review.STATES.REVIEWED)

        review_notif_types = all_models.Review.NotificationObjectTypes
        notif_unreviewed_type = all_models.Notification.query.join(
            all_models.NotificationType).filter(
                all_models.NotificationType.name ==
                review_notif_types.STATUS_UNREVIEWED).all()

        self.assertEqual(num_notifications_expected,
                         len(notif_unreviewed_type))

    @patch("google.appengine.api.mail.send_mail")
    def test_reviewer_notification_on_create_review(self, _):
        """Reviewer should receive email notification"""
        reviewer = factories.PersonFactory()
        reviewer_role_id = all_models.AccessControlRole.query.filter_by(
            name="Reviewer",
            object_type="Review",
        ).one().id
        control = factories.ControlFactory()
        email_message = "email email_message"
        self.generator.generate_object(
            all_models.Review,
            {
                "reviewable": {
                    "type": control.type,
                    "id": control.id,
                },
                "context":
                None,
                "notification_type":
                all_models.Review.NotificationTypes.EMAIL_TYPE,
                "status":
                all_models.Review.STATES.UNREVIEWED,
                "email_message":
                email_message,
                "access_control_list": [{
                    "ac_role_id": reviewer_role_id,
                    "person": {
                        "id": reviewer.id
                    },
                }],
            },
        )
        with mock.patch.object(fast_digest.DIGEST_TMPL,
                               "render") as bodybuilder_mock:
            fast_digest.send_notification()
            template_args = bodybuilder_mock.call_args[1]
            self.assertListEqual([], template_args["proposals"])
            self.assertListEqual([], template_args["review_owners"])
            self.assertEqual(1, len(template_args["review_reviewers"]))
            self.assertEqual(
                control.id, template_args["review_reviewers"][0].reviewable.id)
            self.assertEqual(
                email_message,
                template_args["review_reviewers"][0].email_message)

    @patch("google.appengine.api.mail.send_mail")
    def test_self_reviewer_notification_on_create_review(self, _):
        """Auto assigned Reviewer should NOT receive email notification"""
        current_user = all_models.Person.query.filter_by(
            email="*****@*****.**").one()
        reviewer_role_id = all_models.AccessControlRole.query.filter_by(
            name="Reviewer",
            object_type="Review",
        ).one().id
        control = factories.ControlFactory()
        email_message = "email email_message"
        self.generator.generate_object(
            all_models.Review,
            {
                "reviewable": {
                    "type": control.type,
                    "id": control.id,
                },
                "context":
                None,
                "notification_type":
                all_models.Review.NotificationTypes.EMAIL_TYPE,
                "status":
                all_models.Review.STATES.REVIEWED,
                "email_message":
                email_message,
                "access_control_list": [{
                    "ac_role_id": reviewer_role_id,
                    "person": {
                        "id": current_user.id
                    },
                }],
            },
        )
        with mock.patch.object(fast_digest.DIGEST_TMPL,
                               "render") as bodybuilder_mock:
            fast_digest.send_notification()

            # assert no email sent
            bodybuilder_mock.assert_not_called()

    @patch("google.appengine.api.mail.send_mail")
    def test_reviewer_owner_notification(self, _):
        """Object owners should receive notifications

    System should send notification(s) to object's admins,
    primary contacts, secondary contacts,
    if object is reverted to 'Unreviewed'.
    """
        reviewer = factories.PersonFactory()
        reviewer_role_id = all_models.AccessControlRole.query.filter_by(
            name="Reviewer",
            object_type="Review",
        ).one().id

        with factories.single_commit():
            control_admin = factories.PersonFactory()
            control_primary_contact = factories.PersonFactory()
            control_secondary_contact = factories.PersonFactory()
            control = factories.ControlFactory()
            control.add_person_with_role_name(control_admin, "Admin")
            control.add_person_with_role_name(control_primary_contact,
                                              "Control Operators")
            control.add_person_with_role_name(control_secondary_contact,
                                              "Control Owners")
        email_message = "email email_message"
        _, review = self.generator.generate_object(
            all_models.Review,
            {
                "reviewable": {
                    "type": control.type,
                    "id": control.id,
                },
                "context":
                None,
                "notification_type":
                all_models.Review.NotificationTypes.EMAIL_TYPE,
                "status":
                all_models.Review.STATES.REVIEWED,
                "email_message":
                email_message,
                "access_control_list": [{
                    "ac_role_id": reviewer_role_id,
                    "person": {
                        "id": reviewer.id
                    },
                }],
            },
        )

        self.api.modify_object(review.reviewable, {"title": "new title"})

        with mock.patch.object(fast_digest.DIGEST_TMPL,
                               "render") as bodybuilder_mock:
            fast_digest.send_notification()

            # 4 emails to each user
            self.assertEqual(4, bodybuilder_mock.call_count)
            call_count = _call_counter(bodybuilder_mock)
            # 1 email to reviewer -> need to review
            self.assertEqual(1, call_count["review_reviewers"])

            # 1 emails to owners -> object state updated
            self.assertEqual(3, call_count["review_owners"])
コード例 #11
0
ファイル: assessment.py プロジェクト: egorhm/ggrc-core
class AssessmentRBACFactory(base.BaseRBACFactory):
  """Assessment RBAC factory class."""

  def __init__(self, user_id, acr, parent=None):
    """Set up objects for Assessment permission tests.

    Args:
        user_id: Id of user under which all operations will be run.
        acr: Instance of ACR that should be assigned for tested user.
        parent: Model name in scope of which objects should be set up.
    """
    self.setup_program_scope(user_id, acr, parent)

    self.api = Api()
    self.objgen = generator.ObjectGenerator()
    self.objgen.api = self.api

    if user_id:
      user = all_models.Person.query.get(user_id)
      self.api.set_user(user)

  def create(self):
    """Create new Assessment object."""
    return self.api.post(all_models.Assessment, {
        "assessment": {
            "title": "New Assessment",
            "context": None,
            "audit": {"id": self.audit_id},
        },
    })

  def generate(self):
    """Generate new Assessment object."""
    with factories.single_commit():
      control = factories.ControlFactory()
      template_id = factories.AssessmentTemplateFactory().id
    audit = all_models.Audit.query.get(self.audit_id)
    # pylint: disable=protected-access
    snapshot = TestCase._create_snapshots(audit, [control])[0]
    snapshot_id = snapshot.id
    factories.RelationshipFactory(source=snapshot, destination=audit)

    responses = []
    asmnt_data = {
        "assessment": {
            "_generated": True,
            "audit": {
                "id": self.audit_id,
                "type": "Audit"
            },
            "object": {
                "id": snapshot_id,
                "type": "Snapshot"
            },
            "context": None,
            "title": "New assessment",
        }
    }
    responses.append(self.api.post(all_models.Assessment, asmnt_data))

    asmnt_data["assessment"]["template"] = {
        "id": template_id,
        "type": "AssessmentTemplate"
    }
    responses.append(self.api.post(all_models.Assessment, asmnt_data))
    return responses

  def read(self):
    """Read existing Assessment object."""
    return self.api.get(all_models.Assessment, self.assessment_id)

  def update(self):
    """Update title of existing Assessment object."""
    asmnt = all_models.Assessment.query.get(self.assessment_id)
    return self.api.put(asmnt, {"title": factories.random_str()})

  def delete(self):
    """Delete Assessment object."""
    asmnt = all_models.Assessment.query.get(self.assessment_id)
    return self.api.delete(asmnt)

  def read_revisions(self):
    """Read revisions for Assessment object."""
    model_class = get_model("Assessment")
    responses = []
    for query in ["source_type={}&source_id={}",
                  "destination_type={}&destination_id={}",
                  "resource_type={}&resource_id={}"]:
      responses.append(
          self.api.get_query(
              model_class,
              query.format("assessment", self.assessment_id)
          )
      )
    return responses

  def map_snapshot(self):
    """Map snapshot to assessment."""
    audit = all_models.Audit.query.get(self.audit_id)
    assessment = all_models.Assessment.query.get(self.assessment_id)
    control = factories.ControlFactory()
    # pylint: disable=protected-access
    snapshot = TestCase._create_snapshots(audit, [control])[0]
    factories.RelationshipFactory(source=snapshot, destination=audit)

    return self.objgen.generate_relationship(
        source=assessment,
        destination=snapshot,
    )[0]

  def deprecate(self):
    """Set status 'Deprecated' for Assessment."""
    assessment = all_models.Assessment.query.get(self.assessment_id)
    return self.api.modify_object(assessment, {"status": "Deprecated"})

  def complete(self):
    """Set status 'Completed' for Assessment."""
    assessment = all_models.Assessment.query.get(self.assessment_id)
    return self.api.put(assessment, {"status": "Completed"})

  def in_progress(self):
    """Set status 'In Progress' for Assessment."""
    assessment = all_models.Assessment.query.get(self.assessment_id)
    return self.api.put(assessment, {"status": "In Progress"})

  def not_started(self):
    """Set status 'Not Started' for Assessment."""
    assessment = all_models.Assessment.query.get(self.assessment_id)
    return self.api.put(assessment, {"status": "Not Started"})

  def decline(self):
    """Set status 'Rework Needed' for Assessment."""
    assessment = all_models.Assessment.query.get(self.assessment_id)
    return self.api.put(assessment, {"status": "Rework Needed"})

  def verify(self):
    """Set status 'Verified' for Assessment."""
    assessment = all_models.Assessment.query.get(self.assessment_id)
    return self.api.put(assessment, {"status": "Completed", "verified": True})

  def map_comment(self):
    """Map new comment to assessment."""
    assessment = all_models.Assessment.query.get(self.assessment_id)

    return self.api.put(assessment, {
        "actions": {
            "add_related": [{
                "id": None,
                "type": "Comment",
                "description": factories.random_str(),
                "custom_attribute_definition_id": None,
            }]
        }
    })

  def map_evidence(self):
    """Map new evidence to assessment."""
    assessment = all_models.Assessment.query.get(self.assessment_id)
    return self.api.put(assessment, {
        "actions": {
            "add_related": [{
                "id": None,
                "type": "Evidence",
                "kind": "URL",
                "title": factories.random_str(),
                "link": factories.random_str(),
            }]
        }
    })

  def related_assessments(self):
    """Get related Assessments for existing object."""
    audit = all_models.Audit.query.get(self.audit_id)
    assessment = all_models.Assessment.query.get(self.assessment_id)
    with factories.single_commit():
      control = factories.ControlFactory()
      # pylint: disable=protected-access
      snapshot = TestCase._create_snapshots(audit, [control])[0]
      factories.RelationshipFactory(source=audit, destination=snapshot)
      factories.RelationshipFactory(source=assessment, destination=snapshot)
      assessment2 = factories.AssessmentFactory(
          assessment_type=assessment.assessment_type
      )
      factories.RelationshipFactory(source=assessment2, destination=snapshot)

    return self.api.client.get(
        "/api/related_assessments?object_id={}&object_type=Assessment".format(
            self.assessment_id
        )
    )

  def related_objects(self):
    """Get related objects for existing Assessment."""
    return self.api.client.get(
        "/api/assessments/{}/related_objects".format(self.assessment_id)
    )