def test_multiput_existing_resource_through_main_data(self):
        res = self.client.put('/zoo_employee/',
                              data=jsondumps({
                                  'data': [{
                                      'id': self.zoo_employee.id,
                                      'name': 'add okay',
                                  }]
                              }),
                              content_type='application/json')
        self.assertEqual(res.status_code, 403)
        response_data = jsonloads(res.content)
        assert_json(
            response_data,
            {
                'code':
                'Forbidden',
                # We actually get a different error: "you do not have a scope that allows..."
                #'required_permission': 'testapp.change_zooemployee',
                EXTRA():
                None,
            })

        res = self.client.put('/zoo_employee/',
                              data=jsondumps({
                                  'data': [{
                                      'id': self.zoo_employee.id,
                                      'name': 'change okay',
                                  }]
                              }),
                              content_type='application/json')
        self.assertEqual(res.status_code, 200)
    def test_post_new_resource(self):
        res = self.client.post('/zoo_employee/',
                               data=jsondumps({
                                   'zoo': self.zoo.id,
                                   'name': 'change okay',
                               }),
                               content_type='application/json')
        self.assertEqual(res.status_code, 403)
        response_data = jsonloads(res.content)
        assert_json(
            response_data,
            {
                'code':
                'Forbidden',
                # We actually get a different error: "you do not have a scope that allows..."
                #'required_permission': 'testapp.add_zooemployee',
                EXTRA():
                None,
            })

        res = self.client.post('/zoo_employee/',
                               data=jsondumps({
                                   'zoo': self.zoo.id,
                                   'name': 'add okay',
                               }),
                               content_type='application/json')
        self.assertEqual(res.status_code, 200)
示例#3
0
    def test_writes_to_write_once_fields_are_blocked_for_updates(self):
        res = self.client.post('/caretaker/',
                               data=jsondumps({
                                   'name':
                                   'Fabby',
                                   'first_seen':
                                   '2020-01-01T00:00:00Z'
                               }),
                               content_type='application/json')
        self.assertEqual(res.status_code, 200)

        data = jsonloads(res.content)

        self.assertEqual([], data['_meta']['ignored_fields'])
        dt = parse_datetime('2020-01-01T00:00:00Z')
        self.assertEqual(dt, parse_datetime(data['first_seen']))
        caretaker = Caretaker.objects.get(id=data['id'])
        self.assertEqual(dt, caretaker.first_seen)
        self.assertEqual('Fabby', caretaker.name)

        res = self.client.put('/caretaker/%s/' % caretaker.id,
                              data=jsondumps({
                                  'name':
                                  'Mr Fabby',
                                  'first_seen':
                                  '2020-02-01T00:00:00Z'
                              }),
                              content_type='application/json')
        self.assertEqual(res.status_code, 200)

        data = jsonloads(res.content)

        self.assertEqual(['first_seen'], data['_meta']['ignored_fields'])
        self.assertEqual(dt, parse_datetime(data['first_seen']))
        caretaker.refresh_from_db()
        self.assertEqual(dt, caretaker.first_seen)
        self.assertEqual('Mr Fabby', caretaker.name)

        # A put without the field means it's not in ignored_fields
        res = self.client.put('/caretaker/%s/' % caretaker.id,
                              data=jsondumps({'name': 'Mrs Fabby'}),
                              content_type='application/json')
        self.assertEqual(res.status_code, 200)

        data = jsonloads(res.content)

        self.assertEqual([], data['_meta']['ignored_fields'])
        self.assertEqual(dt, parse_datetime(data['first_seen']))
        caretaker.refresh_from_db()
        self.assertEqual(dt, caretaker.first_seen)
        self.assertEqual('Mrs Fabby', caretaker.name)
    def test_cannot_delete_on_multiput_without_delete_permission(self):
        country = Country.objects.create(name='Nederland')
        city1 = City.objects.create(country=country, name='Amsterdam')
        city2 = City.objects.create(country=country, name='Leeuwarden')

        # Now suppose Friesland is finally going to separate

        res = self.client.put('/country/',
                              data=jsondumps({
                                  'data': [{
                                      'id': country.pk,
                                      'name': 'Nederland',
                                      'cities': [city1.pk]
                                  }]
                              }))

        # This is not ok
        self.assertEquals(403, res.status_code)

        content = jsonloads(res.content)
        self.assertEquals('testapp.delete_city',
                          content['required_permission'])

        # City 2 still exists!
        city2.refresh_from_db()
        self.assertEquals(country,
                          city2.country)  # And belongs to the nederlands
示例#5
0
    def test_transaction(self):
        requests = [
            # This request should work fine
            {
                'method': 'POST',
                'path': '/animal/',
                'body': {
                    'name': 'Foo',
                },
            },
            # This one should error
            {
                'method': 'POST',
                'path': '/animal/',
                'body': {
                    'NONEXISTINGFIELD': 'Bar',
                },
            },
        ]

        response = self.client.post(
            '/multi/',
            data=jsondumps(requests),
            content_type='application/json',
        )
        self.assertEqual(response.status_code, 418)
        response = jsonloads(response.content)

        with self.assertRaises(Animal.DoesNotExist):
            Animal.objects.get(pk=response[0]['body']['id'])
    def test_multiput_deletions_no_perm(self):
        country = Country.objects.create(name='Netherlands')
        res = self.client.put('/country/'.format(country.pk),
                              data=jsondumps({
                                  'deletions': [country.pk],
                              }))

        self.assertEquals(403, res.status_code)

        country.refresh_from_db()
    def test_multiput_deletions(self):
        country = Country.objects.create(name='Netherlands')
        res = self.client.put('/country/'.format(country.pk),
                              data=jsondumps({
                                  'deletions': [country.pk],
                              }))

        self.assertEquals(200, res.status_code)

        with self.assertRaises(Country.DoesNotExist):
            country.refresh_from_db()
示例#8
0
    def identify(self, request):
        """
		Simple endpoint, to identify own user

		:param request:
		:return:
		"""
        me = self.get_queryset(request).get(pk=request.user.pk)
        return HttpResponse(
            jsondumps({
                'username': me.username,
                'email': me.email
            }))
    def test_related_object_nullable_on_delete_is_set_to_null(self):
        country = Country.objects.create(name='Belgium')
        city1 = CityState.objects.create(country=country, name='Brussels')

        res = self.client.put('/country/{}/'.format(country.pk),
                              data=jsondumps({
                                  'id': country.pk,
                                  'name': 'Belgium',
                                  'city_states': []
                              }))
        self.assertEquals(200, res.status_code)

        city1.refresh_from_db()

        self.assertIsNone(city1.country)
    def test_softdelete_on_put_with_delete_permission_softdeletable(self):
        country = Country.objects.create(name='Nederland')
        city1 = PermanentCity.objects.create(country=country,
                                             name='Rotterdam',
                                             deleted=False)

        res = self.client.put('/country/{}/'.format(country.pk),
                              data=jsondumps({
                                  'id': country.pk,
                                  'name': 'Nederland',
                                  'permanent_cities': []
                              }))

        self.assertEquals(200, res.status_code)
        city1.refresh_from_db()
        self.assertTrue(city1.deleted)
示例#11
0
    def test_json_datetimes_dump_and_load_correctly(self):
        encoded = {
            # Include a few non-datetime objects to check
            # we defer to standard json encoding for them.
            'arr': ['matey'],
            'nest': {
                'num':
                1234,
                'some_datetime':
                datetime.strptime('2016-01-01 12:00:00.123456+0000',
                                  '%Y-%m-%d %H:%M:%S.%f%z'),
            },
            'the_datetime':
            datetime.strptime('2016-06-21 01:02:03+0000',
                              '%Y-%m-%d %H:%M:%S%z'),
            'timezoned_datetime':
            datetime.strptime('2016-10-04 11:28:20+0200',
                              '%Y-%m-%d %H:%M:%S%z'),
            'plain_date':
            date(1998, 2, 3),
        }

        # We can't check directly against the serialized
        # string because dicts have no ordering, and space
        # usage might differ.  So instead, we load with the
        # core JSON parser and compare the raw string values.
        plain = {
            'arr': ['matey'],
            'nest': {
                'num': 1234,
                'some_datetime': '2016-01-01T12:00:00.123456+0000',
            },
            'the_datetime': '2016-06-21T01:02:03.000000+0000',
            'timezoned_datetime': '2016-10-04T11:28:20.000000+0200',
            'plain_date': '1998-02-03',
        }

        # Dumping to json
        self.assertEqual(
            plain, python_core_json.loads(binder_json.jsondumps(encoded)))

        # Loading from json, currently just defers to plain
        # JSON (after decoding).
        self.assertEqual(
            plain,
            binder_json.jsonloads(bytes(python_core_json.dumps(plain),
                                        'utf-8')))
    def test_can_delete_on_put_with_delete_permission(self):
        country = Country.objects.create(name='Nederland')
        city1 = City.objects.create(country=country, name='Amsterdam')
        city2 = City.objects.create(country=country, name='Leeuwarden')

        res = self.client.put('/country/{}/'.format(country.pk),
                              data=jsondumps({
                                  'id': country.pk,
                                  'name': 'Nederland',
                                  'cities': [city1.pk]
                              }))

        self.assertEquals(200, res.status_code)

        with self.assertRaises(City.DoesNotExist):
            city2.refresh_from_db()
        self.assertEquals(1, country.cities.count())
    def test_softdelete_on_put_without_softdelete_permission_fails(self):
        country = Country.objects.create(name='Nederland')
        city1 = PermanentCity.objects.create(country=country,
                                             name='Rotterdam',
                                             deleted=False)

        res = self.client.put('/country/{}/'.format(country.pk),
                              data=jsondumps({
                                  'id': country.pk,
                                  'name': 'Nederland',
                                  'permanent_cities': []
                              }))

        self.assertEquals(res.status_code, 403)
        content = jsonloads(res.content)
        self.assertEquals('testapp.delete_permanentcity',
                          content['required_permission'])
示例#14
0
    def test_create_and_update(self):
        requests = [
            {
                'method': 'POST',
                'path': '/zoo/',
                'body': {
                    'name': 'Zoo',
                },
            },
            {
                'method':
                'POST',
                'path':
                '/animal/',
                'body': {
                    'name': 'Foo',
                },
                'transforms': [{
                    'source': [0, 'body', 'id'],
                    'target': ['body', 'zoo'],
                }],
            },
            {
                'method':
                'PUT',
                'path':
                '/animal/{id}/',
                'body': {
                    'name': 'Bar',
                },
                'transforms': [{
                    'source': [1, 'body', 'id'],
                    'target': ['path', 'id'],
                }],
            },
        ]

        response = self.client.post(
            '/multi/',
            data=jsondumps(requests),
            content_type='application/json',
        )
        self.assertEqual(response.status_code, 200)
 def test_multiput_new_resource_through_with(self):
     res = self.client.put('/zoo/',
                           data=jsondumps({
                               'data': [],
                               'with': {
                                   'zoo_employee': [{
                                       'id': -1,
                                   }]
                               }
                           }),
                           content_type='application/json')
     self.assertEqual(res.status_code, 403)
     response_data = jsonloads(res.content)
     assert_json(
         response_data, {
             'code': 'Forbidden',
             'required_permission': 'testapp.add_zooemployee',
             EXTRA(): None,
         })
    def test_related_object_nullable_on_delete_no_change_permission_not_allowed(
            self):
        country = Country.objects.create(name='Belgium')
        city1 = CityState.objects.create(country=country, name='Brussels')

        res = self.client.put('/country/{}/'.format(country.pk),
                              data=jsondumps({
                                  'id': country.pk,
                                  'name': 'Belgium',
                                  'city_states': []
                              }))

        self.assertEquals(403, res.status_code)

        content = jsonloads(res.content)
        self.assertEquals('testapp.change_citystate',
                          content['required_permission'])

        city1.refresh_from_db()

        self.assertEquals(country.pk, country.pk)
    def test_delete_scoping_on_multiput_with_delete_permission(self):
        country = Country.objects.create(name='Nederland')
        city1 = City.objects.create(country=country, name='Amsterdam')
        city2 = City.objects.create(country=country, name='Leeuwarden')

        # Now suppose Friesland is finally going to separate

        res = self.client.put('/country/',
                              data=jsondumps({
                                  'data': [{
                                      'id': country.pk,
                                      'name': 'Nederland',
                                      'cities': [city1.pk]
                                  }]
                              }))

        self.assertEquals(200, res.status_code)

        # City 2 must not exist.
        with self.assertRaises(City.DoesNotExist):
            city2.refresh_from_db()
        self.assertEquals(1, country.cities.count())
    def test_cannot_change_on_multiput_without_change_permission(self):
        country = Country.objects.create(name='Nederland')
        city1 = City.objects.create(country=country, name='Amsterdam')

        res = self.client.put('/country/',
                              data=jsondumps({
                                  'data': [{
                                      'id': country.pk,
                                      'name': 'Nederland',
                                      'cities': [city1.pk]
                                  }],
                                  'with': {
                                      'city': [{
                                          'id': city1.pk,
                                          'name': 'Rotterdam',
                                      }]
                                  }
                              }))

        # This is not ok
        self.assertEquals(403, res.status_code)
        content = jsonloads(res.content)
        self.assertEquals('testapp.change_city',
                          content['required_permission'])
示例#19
0
 def test_uuids_dump_correctly(self):
     u = UUID('{12345678-1234-5678-1234-567812345678}')
     self.assertEqual('["12345678-1234-5678-1234-567812345678"]',
                      binder_json.jsondumps([u]))
示例#20
0
 def test_json_datetimes_dump_correctly_tz_us(self):
     t = datetime(2016, 1, 1, 1, 2, 3, 313337, tzinfo=timezone.utc)
     self.assertEqual('["2016-01-01T01:02:03.313337+0000"]',
                      binder_json.jsondumps([t]))
示例#21
0
 def test_json_datetimes_dump_correctly_notz_us(self):
     t = datetime(2016, 1, 1, 1, 2, 3, 313337)
     self.assertEqual('["2016-01-01T01:02:03.313337"]',
                      binder_json.jsondumps([t]))
示例#22
0
    def test_writes_to_unwritable_fields_are_blocked_for_new_objects_and_updates(
            self):
        res = self.client.post('/caretaker/',
                               data=jsondumps({
                                   'name':
                                   'Fabby',
                                   'last_seen':
                                   '2020-01-01T00:00:00Z'
                               }),
                               content_type='application/json')
        self.assertEqual(res.status_code, 200)

        data = jsonloads(res.content)

        self.assertEqual(['last_seen'], data['_meta']['ignored_fields'])
        self.assertIsNone(data['last_seen'])
        caretaker = Caretaker.objects.get(id=data['id'])
        self.assertIsNone(caretaker.last_seen)
        self.assertEqual('Fabby', caretaker.name)

        res = self.client.put('/caretaker/%s/' % caretaker.id,
                              data=jsondumps({
                                  'name':
                                  'Mr Fabby',
                                  'last_seen':
                                  '2020-01-01T00:00:00Z'
                              }),
                              content_type='application/json')
        self.assertEqual(res.status_code, 200)

        data = jsonloads(res.content)

        self.assertEqual(['last_seen'], data['_meta']['ignored_fields'])
        self.assertIsNone(data['last_seen'])
        caretaker.refresh_from_db()
        self.assertIsNone(caretaker.last_seen)
        self.assertEqual('Mr Fabby', caretaker.name)

        # Just a paranoid check that we can't overwrite it, even if it has a value
        dt = parse_datetime('2020-07-31T12:34:56Z')
        caretaker.last_seen = dt
        caretaker.save()

        res = self.client.put('/caretaker/%s/' % caretaker.id,
                              data=jsondumps({
                                  'name':
                                  'Mrs Fabby',
                                  'last_seen':
                                  '2020-01-01T00:00:00Z'
                              }),
                              content_type='application/json')
        self.assertEqual(res.status_code, 200)

        data = jsonloads(res.content)

        self.assertEqual(['last_seen'], data['_meta']['ignored_fields'])
        self.assertEqual(dt, parse_datetime(data['last_seen']))
        caretaker.refresh_from_db()
        self.assertEqual(dt, caretaker.last_seen)
        self.assertEqual('Mrs Fabby', caretaker.name)

        # A put without the field means it's not in ignored_fields
        res = self.client.put('/caretaker/%s/' % caretaker.id,
                              data=jsondumps({'name': 'just Fabby'}),
                              content_type='application/json')
        self.assertEqual(res.status_code, 200)

        data = jsonloads(res.content)

        self.assertEqual([], data['_meta']['ignored_fields'])
        self.assertEqual(dt, parse_datetime(data['last_seen']))
        caretaker.refresh_from_db()
        self.assertEqual(dt, caretaker.last_seen)
        self.assertEqual('just Fabby', caretaker.name)
示例#23
0
			def foo(self, request, obj):
				return jsondumps({})
示例#24
0
			def foo(self, request, obj):
				that.assertTrue(isinstance(obj, Country))
				that.assertEqual(country.pk, obj.pk)
				return jsondumps({})
示例#25
0
def parse_request(data, allowed_methods, responses, request):
    if not isinstance(data, dict):
        raise BinderRequestError('requests should be dicts')

    # Transform data
    transforms = data.pop('transforms', [])
    str_params = defaultdict(dict)

    if not isinstance(transforms, list):
        raise BinderRequestError('transforms should be a list')

    for transform in transforms:
        if 'source' not in transform:
            raise BinderRequestError('transforms require the field source')
        if 'target' not in transform:
            raise BinderRequestError('transforms require the field target')
        if (not isinstance(transform['source'], list)
                or len(transform['source']) < 1):
            raise BinderRequestError('source must be a non empty list')
        if (not isinstance(transform['target'], list)
                or len(transform['target']) < 2):
            raise BinderRequestError('target must be a non empty list')

        # Get value through source
        value = responses
        for key in transform['source']:
            if not isinstance(value, (list, dict)):
                raise BinderRequestError(
                    'source can only iterate through lists and dicts')
            try:
                value = value[key]
            except (KeyError, IndexError):
                raise BinderRequestError(
                    'invalid source {}, error at key {}'.format(
                        transform['source'], key))

        # Set value according to target
        target = data
        target_key = transform['target'][0]
        for i, key in enumerate(transform['target'][1:]):
            if isinstance(target, (list, dict)):
                try:
                    target = target[target_key]
                except (KeyError, IndexError):
                    raise BinderRequestError(
                        'invalid target {}, error at key {}'.format(
                            transform['target'], target_key))
                target_key = key
            else:
                raise BinderRequestError(
                    'target can only iterate through lists and dicts')

        if isinstance(target, (list, dict)):
            try:
                target[target_key] = value
            except IndexError:
                raise BinderRequestError(
                    'invalid target {}, error at key {}'.format(
                        transform['target'], target_key))
        elif isinstance(target, str):
            str_params[tuple(
                transform['target'][:-1])][transform['target'][-1]] = value
        else:
            raise BinderRequestError(
                'target can only modify lists, dicts and strs')

    try:
        for keys, params in str_params.items():
            target = data
            target_key = keys[0]
            for key in keys[1:]:
                target = target[target_key]
                target_key = key
            s = target[target_key]
            try:
                s = s.format(**params)
            except KeyError as e:
                raise BinderRequestError(
                    'str formatting at {}, missing key: {}'.format(
                        keys, e.args[0]))
            target[target_key] = target[target_key].format(**params)
    except Exception:
        # All kind of things can go wrong when the data is altered through
        # a transform that occured after the str_params where determined
        # causing the target not to exist anymore or not be a str
        raise BinderRequestError(
            'transforms altered data in such a way that str params became '
            'invalid')

    # Validate request
    if 'method' not in data:
        raise BinderRequestError('requests require the field method')
    if 'path' not in data:
        raise BinderRequestError('requests require the field path')

    # Validate method is allowed
    if data['method'] not in allowed_methods:
        raise BinderMethodNotAllowed()

    # Create request
    req = HttpRequest()
    req.method = data['method']
    req.path = data['path']
    req.path_info = req.path
    req.COOKIES = request.COOKIES
    req.META = request.META
    req.content_type = 'application/json'

    if 'body' in data:
        req._body = jsondumps(data['body']).encode()
    else:
        req._body = b''

    return req
示例#26
0
 def test_decimals_dump_correctly(self):
     u = Decimal('1.1')
     self.assertEqual('["1.1"]', binder_json.jsondumps([u]))
示例#27
0
 def test_json_datetimerange_dump_correctly(self):
     t = datetime(2016, 1, 1, 1, 2, 3, 313337, tzinfo=timezone.utc)
     d = DateTimeTZRange(t, t)
     self.assertEqual(
         '["2016-01-01T01:02:03.313337+0000", "2016-01-01T01:02:03.313337+0000"]',
         binder_json.jsondumps(d))
示例#28
0
 def test_nontimezoned_json_datetimes_dump_correctly(self):
     t = datetime.strptime('2016-01-01 01:02:03', '%Y-%m-%d %H:%M:%S')
     self.assertEqual('["2016-01-01T01:02:03.000000+0000"]',
                      binder_json.jsondumps([t]))