Ejemplo n.º 1
0
    def test_redaction_private_user_level(self):
        team_id = self.response_params['team_id']
        user = User.create(email='*****@*****.**')

        own_params = dict(
            self.response_params,
            type=Response.USER_LEVEL_SYMBOL,
            private=True,
            user_id=user.uid,
        )
        own_private_response = Response.create(**own_params)
        own_private_response.put()

        other_params = dict(
            self.response_params,
            type=Response.USER_LEVEL_SYMBOL,
            private=True,
            user_id='User_other',
        )
        other_private_response = Response.create(**other_params)
        other_private_response.put()

        responses = Response.get_for_teams(user, [team_id])

        self.assertEqual(len(responses), 2)
        own_fetched = next(r for r in responses if r.user_id == user.uid)
        other_fetched = next(r for r in responses if r.user_id != user.uid)

        # Own response is not redacted, despite being private.
        self.assertGreater(len(own_fetched.body), 0)

        # Other's private response is redacted.
        self.assertEqual(len(other_fetched.body), 0)
Ejemplo n.º 2
0
    def test_task_creates_backup_for_user(self):
        user_response_params = dict(
            self.response_params,
            type=Response.USER_LEVEL_SYMBOL,
            private=True,
            user_id='User_foo',
        )
        r = Response.create(**user_response_params)
        r.put()
        payload = json.dumps(
            r.to_dict(),
            default=util.json_dumps_default,
        )

        self.testapp.post(
            '/task/backup_response',
            payload,
            headers={'Content-Type': 'application/json'},
        )

        with mysql_connection.connect() as sql:
            rows = sql.select_star_where(ResponseBackup.table)

        backup = rows[0]
        backup['body'] = json.loads(backup['body'])
        expected = dict(r.to_dict(), backup_id=1),
        self.assertEqual(len(rows), 1)
        self.assertEqual(
            rows[0],
            dict(r.to_dict(), backup_id=1),
        )
Ejemplo n.º 3
0
    def create_for_dashboard(self, org, x=0):
        x_label = str(x).rjust(2, '0')
        team = Team.create(
            name='Team {}'.format(x_label),
            captain_id='User_captain_{}'.format(x_label),
            organization_ids=[org.uid],
            program_id=self.program.uid,
        )
        user = User.create(name='User {}'.format(x_label),
                           email='foo.{}@bar.com'.format(x_label),
                           owned_teams=[team.uid])
        cycle = Cycle.create(
            team_id=team.uid,
            ordinal=1,
            start_date=datetime.date.today() - datetime.timedelta(days=1),
            end_date=datetime.date.today() + datetime.timedelta(days=1),
        )
        response = Response.create(
            type=Response.USER_LEVEL_SYMBOL,
            user_id=user.uid,
            team_id=team.uid,
            parent_id=cycle.uid,
            module_label='DemoModule',
            progress=50,
            page=1,
            body={
                'question1': {
                    'modified': '2019-01-01T00:00:00Z',
                    'value': 'foo',
                },
            },
        )

        return (team, user, cycle, response)
Ejemplo n.º 4
0
 def team_module_response(self, cycle, module_label, progress=100):
     return Response.create(
         type=Response.TEAM_LEVEL_SYMBOL,
         user_id="",
         team_id=cycle.team_id,
         parent_id=cycle.uid,
         module_label=module_label,
         progress=progress,
         body={},
     )
Ejemplo n.º 5
0
    def test_redaction_public_team_level(self):
        user = User.create(email='*****@*****.**')
        team_params = dict(
            self.response_params,
            type=Response.TEAM_LEVEL_SYMBOL,
            private=False,
        )
        r = Response.create(**team_params)
        r.put()

        responses = Response.get_for_teams(user, [team_params['team_id']])

        # Not redacted.
        self.assertGreater(len(responses[0].body), 0)
Ejemplo n.º 6
0
    def test_redaction_public_user_level(self):
        user = User.create(email='*****@*****.**')
        user_params = dict(
            self.response_params,
            type=Response.USER_LEVEL_SYMBOL,
            private=False,
            user_id='User_foo',
        )
        r = Response.create(**user_params)
        r.put()

        responses = Response.get_for_teams(user, [user_params['team_id']])

        # Not redacted, even though user doesn't own the response.
        self.assertGreater(len(responses[0].body), 0)
Ejemplo n.º 7
0
    def test_update_creates_task(self):
        # **Don't** use .put() here, because responses are saved with a
        # custom transaction via insert_or_conflict() and update_or_conflict().

        user_response_params = dict(
            self.response_params,
            type=Response.USER_LEVEL_SYMBOL,
            private=True,
            user_id='User_foo',
        )
        r = Response.create(**user_response_params)
        r.put() # Creates one task.

        # Modifying should create a (second) task.
        new_params = dict(user_response_params, progress = 100)
        updated = Response.update_or_conflict(r.uid, new_params, False)
        expected_payload = json.dumps(
            updated.to_dict(),
            default=util.json_dumps_default,
        )
        tasks = self.taskqueue_stub.get_filtered_tasks()
        self.assertEqual(len(tasks), 2)
        self.assertEqual(tasks[1].payload, expected_payload)
Ejemplo n.º 8
0
    def test_atomic_updates(self):
        """Not exactly a test; more of a proof of concept that mysql
        row locking works."""
        r = Response.create(
            user_id='User_foo',
            team_id='Team_foo',
            parent_id='Cycle_foo',
            module_label='ModuleFoo',
            body={'foo': 'bar'},
            progress=0,
        )
        r.put()

        table = 'response'

        with mysql_connection.connect(retry_on_error=False) as sql:
            # This simulates one user clicking "submit" to a module.
            sql.select_row_for_update(table, 'uid', r.uid)  # locks

            with self.assertRaises(MySQLdb.OperationalError):
                with mysql_connection.connect(retry_on_error=False) as sql:
                    # This default to 50, which is too long to wait.
                    sql.query('SET innodb_lock_wait_timeout = 1', tuple())

                    # This simulates another user clicking submit on their
                    # client at the exact same time, which if it weren't for the
                    # lock would be a race condition. Here it should just wait,
                    # and then reach the timeout and raise.
                    sql.update_row(table, 'uid', r.uid, progress=1)

                    # Unfortunately exiting here will close _both_ connections,
                    # so we'll have to let that happen and open a third.

        # If lock succeeded, the data should be unchanged.
        fetched = Response.get_by_id(r.uid)
        self.assertEqual(fetched.progress, 0)
Ejemplo n.º 9
0
    def create(self):
        org = Organization.create(name='Foo Community',
                                  program_id=self.program.uid)
        org.put()
        team = Team.create(
            name='foo',
            captain_id='User_captain',
            program_id=self.program.uid,
            organization_ids=[org.uid],
        )
        teammate = User.create(name='teammate',
                               email='*****@*****.**',
                               owned_teams=[team.uid])
        other = User.create(name='other', email='*****@*****.**')
        User.put_multi((other, teammate))
        team.put()

        cycles = (
            Cycle.create(
                team_id=team.uid,
                ordinal=1,
                start_date=datetime.date(2000, 1, 1),
                end_date=datetime.date(2000, 2, 1),
            ),
            Cycle.create(
                team_id=team.uid,
                ordinal=2,
                start_date=datetime.date.today(),
                end_date=datetime.date.today() + datetime.timedelta(weeks=4),
            ),
        )
        Cycle.put_multi(cycles)

        responses = {
            # User-level, not related to our team or user, inaccessible.
            'user_other_other':
            Response.create(
                user_id=other.uid,
                team_id='Team_other',
                parent_id='Cycle_other',
                module_label='ModuleFoo',
                body=self.default_body(),
            ),
            # Related to our user but not our team.
            'user_other_user':
            Response.create(
                user_id=teammate.uid,
                team_id='Team_other',
                parent_id='Cycle_isolated',
                module_label='ModuleFoo',
                body=self.default_body(),
            ),
            # Related to both our team and user; two different cycles.
            'user_team_user1':
            Response.create(
                user_id=teammate.uid,
                team_id=team.uid,
                parent_id=cycles[0].uid,
                module_label='ModuleFoo',
                body=self.default_body(),
            ),
            'user_team_user2':
            Response.create(
                user_id=teammate.uid,
                team_id=team.uid,
                parent_id=cycles[1].uid,
                module_label='ModuleFoo',
                body=self.default_body(),
            ),
            # Related to our team but not our user; body should stay secret
            'user_team_other':
            Response.create(
                user_id='User_other-teammate',
                team_id=team.uid,
                parent_id=cycles[0].uid,
                module_label='ModuleFoo',
                body=self.default_body(),
            ),
            # Team-level response, readable for all team members
            'team_team':
            Response.create(
                type='Team',
                user_id='',
                team_id=team.uid,
                parent_id='launch-step',
                module_label='ModuleFoo',
                body=self.default_body(),
            ),
            # Team-level, but for a different team.
            'team_other':
            Response.create(
                type='Team',
                user_id='',
                team_id='Team_other',
                parent_id='launch-step',
                module_label='ModuleFoo',
                body=self.default_body(),
            ),
        }
        Response.put_multi(responses.values())

        return (other, teammate, team, cycles, responses)