mirror of
https://github.com/mozilla-services/syncstorage-rs.git
synced 2025-08-06 20:06:57 +02:00
Some checks failed
Glean probe-scraper / glean-probe-scraper (push) Has been cancelled
refactor: unified python imports for consistency in local, production, and test environments
578 lines
27 KiB
Python
578 lines
27 KiB
Python
# This Source Code Form is subject to the terms of the Mozilla Public
|
|
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
# You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
import pytest
|
|
import unittest
|
|
from integration_tests.tokenserver.test_support import TestCase
|
|
|
|
|
|
@pytest.mark.usefixtures('setup_server_local_testing_with_oauth')
|
|
class TestAuthorization(TestCase, unittest.TestCase):
|
|
def setUp(self):
|
|
super(TestAuthorization, self).setUp()
|
|
|
|
def tearDown(self):
|
|
super(TestAuthorization, self).tearDown()
|
|
|
|
def test_unauthorized_error_status(self):
|
|
# Totally busted auth -> generic error.
|
|
headers = {'Authorization': 'Unsupported-Auth-Scheme IHACKYOU'}
|
|
res = self.app.get('/1.0/sync/1.5', headers=headers, status=401)
|
|
|
|
expected_error_response = {
|
|
'errors': [
|
|
{
|
|
'description': 'Unsupported',
|
|
'location': 'body',
|
|
'name': ''
|
|
}
|
|
],
|
|
'status': 'error'
|
|
}
|
|
self.assertEqual(res.json, expected_error_response)
|
|
|
|
def test_no_auth(self):
|
|
res = self.app.get('/1.0/sync/1.5', status=401)
|
|
|
|
expected_error_response = {
|
|
'status': 'error',
|
|
'errors': [
|
|
{
|
|
'location': 'body',
|
|
'name': '',
|
|
'description': 'Unauthorized'
|
|
}
|
|
]
|
|
}
|
|
self.assertEqual(res.json, expected_error_response)
|
|
|
|
def test_invalid_client_state_in_key_id(self):
|
|
additional_headers = {
|
|
'X-KeyID': "1234-state!"
|
|
}
|
|
headers = self._build_auth_headers(
|
|
keys_changed_at=1234,
|
|
client_state='aaaa',
|
|
**additional_headers)
|
|
res = self.app.get('/1.0/sync/1.5', headers=headers, status=401)
|
|
|
|
expected_error_response = {
|
|
'status': 'invalid-credentials',
|
|
'errors': [
|
|
{
|
|
'location': 'body',
|
|
'name': '',
|
|
'description': 'Unauthorized'
|
|
}
|
|
]
|
|
}
|
|
self.assertEqual(res.json, expected_error_response)
|
|
|
|
def test_invalid_client_state_in_x_client_state(self):
|
|
additional_headers = {'X-Client-State': 'state!'}
|
|
headers = self._build_auth_headers(generation=1234,
|
|
keys_changed_at=1234,
|
|
client_state='aaaa',
|
|
**additional_headers)
|
|
|
|
res = self.app.get('/1.0/sync/1.5', headers=headers, status=400)
|
|
|
|
expected_error_response = {
|
|
'status': 'error',
|
|
'errors': [
|
|
{
|
|
'location': 'header',
|
|
'name': 'X-Client-State',
|
|
'description': 'Invalid client state value'
|
|
}
|
|
]
|
|
}
|
|
self.assertEqual(res.json, expected_error_response)
|
|
|
|
def test_keys_changed_at_less_than_equal_to_generation(self):
|
|
self._add_user(generation=1232, keys_changed_at=1234)
|
|
# If keys_changed_at changes, that change must be less than or equal
|
|
# to the new generation
|
|
headers = self._build_auth_headers(generation=1235,
|
|
keys_changed_at=1236,
|
|
client_state='aaaa')
|
|
res = self.app.get('/1.0/sync/1.5', headers=headers, status=401)
|
|
expected_error_response = {
|
|
'status': 'invalid-keysChangedAt',
|
|
'errors': [
|
|
{
|
|
'location': 'body',
|
|
'name': '',
|
|
'description': 'Unauthorized'
|
|
}
|
|
]
|
|
}
|
|
self.assertEqual(res.json, expected_error_response)
|
|
# If the keys_changed_at on the request matches that currently stored
|
|
# on the user record, it does not need to be less than or equal to the
|
|
# generation on the request
|
|
headers = self._build_auth_headers(generation=1233,
|
|
keys_changed_at=1234,
|
|
client_state='aaaa')
|
|
self.app.get('/1.0/sync/1.5', headers=headers)
|
|
# A request with no generation is acceptable
|
|
headers = self._build_auth_headers(generation=None,
|
|
keys_changed_at=1235,
|
|
client_state='aaaa')
|
|
self.app.get('/1.0/sync/1.5', headers=headers)
|
|
# A request with a keys_changed_at less than the new generation
|
|
# is acceptable
|
|
headers = self._build_auth_headers(generation=1236,
|
|
keys_changed_at=1235,
|
|
client_state='aaaa')
|
|
self.app.get('/1.0/sync/1.5', headers=headers)
|
|
|
|
def test_disallow_reusing_old_client_state(self):
|
|
# Add a user record that has already been replaced
|
|
self._add_user(client_state='aaaa', replaced_at=1200)
|
|
# Add the most up-to-date user record
|
|
self._add_user(client_state='bbbb')
|
|
# A request cannot use a client state associated with a replaced user
|
|
headers = self._build_auth_headers(generation=1234,
|
|
keys_changed_at=1234,
|
|
client_state='aaaa')
|
|
res = self.app.get('/1.0/sync/1.5', headers=headers, status=401)
|
|
expected_error_response = {
|
|
'status': 'invalid-client-state',
|
|
'errors': [
|
|
{
|
|
'location': 'header',
|
|
'name': 'X-Client-State',
|
|
'description': 'Unacceptable client-state value stale '
|
|
'value'
|
|
}
|
|
]
|
|
}
|
|
self.assertEqual(res.json, expected_error_response)
|
|
# Using the last-seen client state is okay
|
|
headers = self._build_auth_headers(generation=1234,
|
|
keys_changed_at=1234,
|
|
client_state='bbbb')
|
|
res1 = self.app.get('/1.0/sync/1.5', headers=headers)
|
|
# Using a new client state (with an updated generation and
|
|
# keys_changed_at) is okay
|
|
headers = self._build_auth_headers(generation=1235,
|
|
keys_changed_at=1235,
|
|
client_state='cccc')
|
|
res2 = self.app.get('/1.0/sync/1.5', headers=headers)
|
|
# This results in the creation of a new user record
|
|
self.assertNotEqual(res1.json['uid'], res2.json['uid'])
|
|
|
|
def test_generation_change_must_accompany_client_state_change(self):
|
|
self._add_user(generation=1234, client_state='aaaa')
|
|
# A request with a new client state must also contain a new generation
|
|
headers = self._build_auth_headers(generation=1234,
|
|
keys_changed_at=1234,
|
|
client_state='bbbb')
|
|
res = self.app.get('/1.0/sync/1.5', headers=headers, status=401)
|
|
expected_error_response = {
|
|
'status': 'invalid-client-state',
|
|
'errors': [
|
|
{
|
|
'location': 'header',
|
|
'name': 'X-Client-State',
|
|
'description': 'Unacceptable client-state value new '
|
|
'value with no generation change'
|
|
}
|
|
]
|
|
}
|
|
self.assertEqual(res.json, expected_error_response)
|
|
# A request with no generation is acceptable
|
|
headers = self._build_auth_headers(generation=None,
|
|
keys_changed_at=1235,
|
|
client_state='bbbb')
|
|
self.app.get('/1.0/sync/1.5', headers=headers)
|
|
# We can't use a generation of 1235 when setting a new client state
|
|
# because the generation was set to be equal to the keys_changed_at
|
|
# in the previous request, which was 1235
|
|
headers = self._build_auth_headers(generation=1235,
|
|
keys_changed_at=1235,
|
|
client_state='cccc')
|
|
expected_error_response = {
|
|
'status': 'invalid-client-state',
|
|
'errors': [
|
|
{
|
|
'location': 'header',
|
|
'name': 'X-Client-State',
|
|
'description': 'Unacceptable client-state value new '
|
|
'value with no generation change'
|
|
}
|
|
]
|
|
}
|
|
res = self.app.get('/1.0/sync/1.5', headers=headers, status=401)
|
|
self.assertEqual(res.json, expected_error_response)
|
|
# A change in client state is acceptable only with a change in
|
|
# generation (if it is present)
|
|
headers = self._build_auth_headers(generation=1236,
|
|
keys_changed_at=1236,
|
|
client_state='cccc')
|
|
self.app.get('/1.0/sync/1.5', headers=headers)
|
|
|
|
def test_keys_changed_at_change_must_accompany_client_state_change(self):
|
|
self._add_user(generation=1234, keys_changed_at=1234,
|
|
client_state='aaaa')
|
|
# A request with a new client state must also contain a new
|
|
# keys_changed_at
|
|
headers = self._build_auth_headers(generation=1235,
|
|
keys_changed_at=1234,
|
|
client_state='bbbb')
|
|
res = self.app.get('/1.0/sync/1.5', headers=headers, status=401)
|
|
expected_error_response = {
|
|
'status': 'invalid-client-state',
|
|
'errors': [
|
|
{
|
|
'location': 'header',
|
|
'name': 'X-Client-State',
|
|
'description': 'Unacceptable client-state value new '
|
|
'value with no keys_changed_at change'
|
|
}
|
|
]
|
|
}
|
|
self.assertEqual(res.json, expected_error_response)
|
|
# A request with a new keys_changed_at is acceptable
|
|
headers = self._build_auth_headers(generation=1235,
|
|
keys_changed_at=1235,
|
|
client_state='bbbb')
|
|
self.app.get('/1.0/sync/1.5', headers=headers)
|
|
|
|
def test_generation_must_not_be_less_than_last_seen_value(self):
|
|
uid = self._add_user(generation=1234)
|
|
# The generation in the request cannot be less than the generation
|
|
# currently stored on the user record
|
|
headers = self._build_auth_headers(generation=1233,
|
|
keys_changed_at=1234,
|
|
client_state='aaaa')
|
|
res = self.app.get('/1.0/sync/1.5', headers=headers, status=401)
|
|
expected_error_response = {
|
|
'status': 'invalid-generation',
|
|
'errors': [
|
|
{
|
|
'location': 'body',
|
|
'name': '',
|
|
'description': 'Unauthorized',
|
|
}
|
|
]
|
|
}
|
|
self.assertEqual(res.json, expected_error_response)
|
|
# A request with no generation is acceptable
|
|
headers = self._build_auth_headers(generation=None,
|
|
keys_changed_at=1234,
|
|
client_state='aaaa')
|
|
self.app.get('/1.0/sync/1.5', headers=headers)
|
|
# A request with a generation equal to the last-seen generation is
|
|
# acceptable
|
|
headers = self._build_auth_headers(generation=1234,
|
|
keys_changed_at=1234,
|
|
client_state='aaaa')
|
|
self.app.get('/1.0/sync/1.5', headers=headers)
|
|
# A request with a generation greater than the last-seen generation is
|
|
# acceptable
|
|
headers = self._build_auth_headers(generation=1235,
|
|
keys_changed_at=1234,
|
|
client_state='aaaa')
|
|
res = self.app.get('/1.0/sync/1.5', headers=headers)
|
|
# This should not result in the creation of a new user
|
|
self.assertEqual(res.json['uid'], uid)
|
|
|
|
def test_set_generation_unchanged_without_keys_changed_at_update(self):
|
|
# Add a user who has never sent us a generation
|
|
uid = self._add_user(generation=0, keys_changed_at=1234,
|
|
client_state='aaaa')
|
|
# Send a request without a generation that doesn't update
|
|
# keys_changed_at
|
|
headers = self._build_auth_headers(generation=None,
|
|
keys_changed_at=1234,
|
|
client_state='aaaa')
|
|
self.app.get('/1.0/sync/1.5', headers=headers)
|
|
user = self._get_user(uid)
|
|
# This should not have set the user's generation
|
|
self.assertEqual(user['generation'], 0)
|
|
# Send a request without a generation that updates keys_changed_at
|
|
headers = self._build_auth_headers(generation=None,
|
|
keys_changed_at=1235,
|
|
client_state='aaaa')
|
|
self.app.get('/1.0/sync/1.5', headers=headers)
|
|
user = self._get_user(uid)
|
|
# This should have set the user's generation
|
|
self.assertEqual(user['generation'], 1235)
|
|
|
|
def test_set_generation_with_keys_changed_at_initialization(self):
|
|
# Add a user who has never sent us a generation or a keys_changed_at
|
|
uid = self._add_user(generation=0, keys_changed_at=None,
|
|
client_state='aaaa')
|
|
|
|
# Send a request without a generation that updates keys_changed_at
|
|
headers = self._build_auth_headers(generation=None,
|
|
keys_changed_at=1234,
|
|
client_state='aaaa')
|
|
self.app.get('/1.0/sync/1.5', headers=headers)
|
|
user = self._get_user(uid)
|
|
# This should have set the user's generation
|
|
self.assertEqual(user['generation'], 1234)
|
|
|
|
def test_fxa_kid_change(self):
|
|
self._add_user(generation=1234, keys_changed_at=None,
|
|
client_state='aaaa')
|
|
# An OAuth client shows up, setting keys_changed_at.
|
|
# (The value matches generation number above, beause in this scenario
|
|
# FxA hasn't been updated to track and report keysChangedAt yet).
|
|
headers = self._build_auth_headers(generation=1234,
|
|
keys_changed_at=1234,
|
|
client_state='aaaa')
|
|
res = self.app.get('/1.0/sync/1.5', headers=headers)
|
|
token0 = self.unsafelyParseToken(res.json['id'])
|
|
# Reject keys_changed_at lower than the value previously seen
|
|
headers = self._build_auth_headers(generation=1234,
|
|
keys_changed_at=1233,
|
|
client_state='aaaa')
|
|
res = self.app.get('/1.0/sync/1.5', headers=headers, status=401)
|
|
expected_error_response = {
|
|
'status': 'invalid-keysChangedAt',
|
|
'errors': [
|
|
{
|
|
'location': 'body',
|
|
'name': '',
|
|
'description': 'Unauthorized',
|
|
}
|
|
]
|
|
}
|
|
self.assertEqual(res.json, expected_error_response)
|
|
# Reject greater keys_changed_at with no corresponding update to
|
|
# generation
|
|
headers = self._build_auth_headers(generation=1234,
|
|
keys_changed_at=2345,
|
|
client_state='bbbb')
|
|
res = self.app.get('/1.0/sync/1.5', headers=headers, status=401)
|
|
self.assertEqual(res.json, expected_error_response)
|
|
# Accept equal keys_changed_at
|
|
headers = self._build_auth_headers(generation=1234,
|
|
keys_changed_at=1234,
|
|
client_state='aaaa')
|
|
self.app.get('/1.0/sync/1.5', headers=headers)
|
|
# Accept greater keys_changed_at with new generation
|
|
headers = self._build_auth_headers(generation=2345,
|
|
keys_changed_at=2345,
|
|
client_state='bbbb')
|
|
res = self.app.get('/1.0/sync/1.5', headers=headers)
|
|
token = self.unsafelyParseToken(res.json['id'])
|
|
self.assertEqual(token['fxa_kid'], '0000000002345-u7s')
|
|
self.assertNotEqual(token['uid'], token0['uid'])
|
|
self.assertEqual(token['node'], token0['node'])
|
|
|
|
def test_client_specified_duration(self):
|
|
self._add_user(generation=1234, keys_changed_at=1234,
|
|
client_state='aaaa')
|
|
headers = self._build_auth_headers(generation=1234,
|
|
keys_changed_at=1234,
|
|
client_state='aaaa')
|
|
# It's ok to request a shorter-duration token.
|
|
res = self.app.get('/1.0/sync/1.5?duration=12', headers=headers)
|
|
self.assertEqual(res.json['duration'], 12)
|
|
# But you can't exceed the server's default value.
|
|
res = self.app.get('/1.0/sync/1.5?duration=4000', headers=headers)
|
|
self.assertEqual(res.json['duration'], 3600)
|
|
# And nonsense values are ignored.
|
|
res = self.app.get('/1.0/sync/1.5?duration=lolwut', headers=headers)
|
|
self.assertEqual(res.json['duration'], 3600)
|
|
res = self.app.get('/1.0/sync/1.5?duration=-1', headers=headers)
|
|
self.assertEqual(res.json['duration'], 3600)
|
|
|
|
# Although all servers are now writing keys_changed_at, we still need this
|
|
# case to be handled. See this PR for more information:
|
|
# https://github.com/mozilla-services/tokenserver/pull/176
|
|
def test_kid_change_during_gradual_tokenserver_rollout(self):
|
|
# Let's start with a user already in the db, with no keys_changed_at.
|
|
uid = self._add_user(generation=1234, client_state='aaaa',
|
|
keys_changed_at=None)
|
|
user1 = self._get_user(uid)
|
|
# User hits updated tokenserver node, writing keys_changed_at to db.
|
|
headers = self._build_auth_headers(generation=1234,
|
|
keys_changed_at=1200,
|
|
client_state='aaaa')
|
|
res = self.app.get('/1.0/sync/1.5', headers=headers)
|
|
# That should not have triggered a node re-assignment.
|
|
user2 = self._get_user(res.json['uid'])
|
|
self.assertEqual(user1['uid'], user2['uid'])
|
|
self.assertEqual(user1['nodeid'], user2['nodeid'])
|
|
# That should have written keys_changed_at into the db.
|
|
self.assertEqual(user2['generation'], 1234)
|
|
self.assertEqual(user2['keys_changed_at'], 1200)
|
|
# User does a password reset on their Firefox Account.
|
|
headers = self._build_auth_headers(generation=2345,
|
|
keys_changed_at=2345,
|
|
client_state='bbbb')
|
|
# They sync again, but hit a tokenserver node that isn't updated yet.
|
|
# This would trigger the allocation of a new user, so we simulate this
|
|
# by adding a new user. We set keys_changed_at to be the last-used
|
|
# value, since we are simulating a server that doesn't pay attention
|
|
# to keys_changed_at.
|
|
uid = self._add_user(generation=2345, keys_changed_at=1200,
|
|
client_state='bbbb')
|
|
user2 = self._get_user(uid)
|
|
self.assertNotEqual(user1['uid'], user2['uid'])
|
|
self.assertEqual(user1['nodeid'], user2['nodeid'])
|
|
# They sync again, hitting an updated tokenserver node.
|
|
# This should succeed, despite keys_changed_at appearing to have
|
|
# changed without any corresponding change in generation number.
|
|
res = self.app.get('/1.0/sync/1.5', headers=headers)
|
|
# That should not have triggered a second user allocation.
|
|
user1 = user2
|
|
user2 = self._get_user(res.json['uid'])
|
|
self.assertEqual(user2['uid'], user1['uid'])
|
|
self.assertEqual(user2['nodeid'], user1['nodeid'])
|
|
|
|
def test_update_client_state(self):
|
|
uid = self._add_user(generation=0, keys_changed_at=None,
|
|
client_state='')
|
|
user1 = self._get_user(uid)
|
|
# The user starts out with no client_state
|
|
self.assertEqual(user1['generation'], 0)
|
|
self.assertEqual(user1['client_state'], '')
|
|
seen_uids = set((uid,))
|
|
orig_node = user1['nodeid']
|
|
# Changing client_state allocates a new user, resulting in a new uid
|
|
headers = self._build_auth_headers(generation=1234,
|
|
keys_changed_at=1234,
|
|
client_state='bbbb')
|
|
res = self.app.get('/1.0/sync/1.5', headers=headers)
|
|
user2 = self._get_user(res.json['uid'])
|
|
self.assertTrue(user2['uid'] not in seen_uids)
|
|
self.assertEqual(user2['nodeid'], orig_node)
|
|
self.assertEqual(user2['generation'], 1234)
|
|
self.assertEqual(user2['keys_changed_at'], 1234)
|
|
self.assertEqual(user2['client_state'], 'bbbb')
|
|
seen_uids.add(user2['uid'])
|
|
# We can change the client state even if no generation is present on
|
|
# the request
|
|
headers = self._build_auth_headers(generation=None,
|
|
keys_changed_at=1235,
|
|
client_state='cccc')
|
|
res = self.app.get('/1.0/sync/1.5', headers=headers)
|
|
user3 = self._get_user(res.json['uid'])
|
|
self.assertTrue(user3['uid'] not in seen_uids)
|
|
self.assertEqual(user3['nodeid'], orig_node)
|
|
# When keys_changed_at changes and generation is not present on the
|
|
# request, generation is set to be the same as keys_changed_at
|
|
self.assertEqual(user3['generation'], 1235)
|
|
self.assertEqual(user3['keys_changed_at'], 1235)
|
|
self.assertEqual(user3['client_state'], 'cccc')
|
|
seen_uids.add(user3['uid'])
|
|
# We cannot change client_state without a change in keys_changed_at
|
|
headers = self._build_auth_headers(generation=None,
|
|
keys_changed_at=1235,
|
|
client_state='dddd')
|
|
res = self.app.get('/1.0/sync/1.5', headers=headers, status=401)
|
|
expected_error_response = {
|
|
'status': 'invalid-client-state',
|
|
'errors': [
|
|
{
|
|
'location': 'header',
|
|
'name': 'X-Client-State',
|
|
'description': 'Unacceptable client-state value new '
|
|
'value with no keys_changed_at change'
|
|
}
|
|
]
|
|
}
|
|
self.assertEqual(expected_error_response, res.json)
|
|
# We cannot use a previously-used client_state
|
|
headers = self._build_auth_headers(generation=1236,
|
|
keys_changed_at=1236,
|
|
client_state='bbbb')
|
|
res = self.app.get('/1.0/sync/1.5', headers=headers, status=401)
|
|
expected_error_response = {
|
|
'status': 'invalid-client-state',
|
|
'errors': [
|
|
{
|
|
'location': 'header',
|
|
'name': 'X-Client-State',
|
|
'description': 'Unacceptable client-state value stale '
|
|
'value'
|
|
}
|
|
]
|
|
}
|
|
self.assertEqual(expected_error_response, res.json)
|
|
|
|
def test_set_generation_from_no_generation(self):
|
|
# Add a user that has no generation set
|
|
uid = self._add_user(generation=0, keys_changed_at=None,
|
|
client_state='aaaa')
|
|
headers = self._build_auth_headers(generation=1234,
|
|
keys_changed_at=1234,
|
|
client_state='aaaa')
|
|
# Send a request to set the generation
|
|
self.app.get('/1.0/sync/1.5', headers=headers)
|
|
user = self._get_user(uid)
|
|
# Ensure that the user had the correct generation set
|
|
self.assertEqual(user['generation'], 1234)
|
|
|
|
def test_set_keys_changed_at_from_no_keys_changed_at(self):
|
|
# Add a user that has no keys_changed_at set
|
|
uid = self._add_user(generation=1234, keys_changed_at=None,
|
|
client_state='aaaa')
|
|
headers = self._build_auth_headers(generation=1234,
|
|
keys_changed_at=1234,
|
|
client_state='aaaa')
|
|
# Send a request to set the keys_changed_at
|
|
self.app.get('/1.0/sync/1.5', headers=headers)
|
|
user = self._get_user(uid)
|
|
# Ensure that the user had the correct generation set
|
|
self.assertEqual(user['keys_changed_at'], 1234)
|
|
|
|
def test_x_client_state_must_have_same_client_state_as_key_id(self):
|
|
self._add_user(client_state='aaaa')
|
|
additional_headers = {'X-Client-State': 'bbbb'}
|
|
headers = self._build_auth_headers(
|
|
generation=1234,
|
|
keys_changed_at=1234,
|
|
client_state='aaaa',
|
|
**additional_headers)
|
|
# If present, the X-Client-State header must have the same client
|
|
# state as the X-KeyID header
|
|
res = self.app.get('/1.0/sync/1.5', headers=headers, status=401)
|
|
expected_error_response = {
|
|
'errors': [
|
|
{
|
|
'description': 'Unauthorized',
|
|
'location': 'body',
|
|
'name': ''
|
|
}
|
|
],
|
|
'status': 'invalid-client-state'
|
|
}
|
|
self.assertEqual(res.json, expected_error_response)
|
|
headers['X-Client-State'] = 'aaaa'
|
|
res = self.app.get('/1.0/sync/1.5', headers=headers)
|
|
|
|
def test_zero_generation_treated_as_null(self):
|
|
# Add a user that has a generation set
|
|
uid = self._add_user(generation=1234, keys_changed_at=1234,
|
|
client_state='aaaa')
|
|
headers = self._build_auth_headers(generation=0,
|
|
keys_changed_at=1234,
|
|
client_state='aaaa')
|
|
# Send a request with a generation of 0
|
|
self.app.get('/1.0/sync/1.5', headers=headers)
|
|
# Ensure that the request succeeded and that the user's generation
|
|
# was not updated
|
|
user = self._get_user(uid)
|
|
self.assertEqual(user['generation'], 1234)
|
|
|
|
def test_zero_keys_changed_at_treated_as_null(self):
|
|
# Add a user that has no keys_changed_at set
|
|
uid = self._add_user(generation=1234, keys_changed_at=None,
|
|
client_state='aaaa')
|
|
headers = self._build_auth_headers(generation=1234,
|
|
keys_changed_at=0,
|
|
client_state='aaaa')
|
|
# Send a request with a keys_changed_at of 0
|
|
self.app.get('/1.0/sync/1.5', headers=headers)
|
|
# Ensure that the request succeeded and that the user's
|
|
# keys_changed_at was not updated
|
|
user = self._get_user(uid)
|
|
self.assertEqual(user['keys_changed_at'], None)
|