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"])
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)
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"))
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)
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))
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") )
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"])
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)
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"])
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) )