Added 'operations hash_auth_tokens' CLI command.

This commit is contained in:
Sybren A. Stüvel 2017-09-21 14:33:24 +02:00
parent c57aefd48b
commit f17453ba10
2 changed files with 78 additions and 0 deletions

View File

@ -179,3 +179,29 @@ def index_users_update_settings():
'unordered(roles)'
]
})
@manager_operations.command
def hash_auth_tokens():
"""Hashes all unhashed authentication tokens."""
from pymongo.results import UpdateResult
from pillar.api.utils.authentication import hash_auth_token
tokens_coll = current_app.db('tokens')
query = {'token': {'$exists': True}}
cursor = tokens_coll.find(query, projection={'token': 1, '_id': 1})
log.info('Updating %d tokens', cursor.count())
for token_doc in cursor:
hashed_token = hash_auth_token(token_doc['token'])
token_id = token_doc['_id']
res: UpdateResult = tokens_coll.update_one(
{'_id': token_id},
{'$set': {'token_hashed': hashed_token},
'$unset': {'token': 1}},
)
if res.modified_count != 1:
raise ValueError(f'Unable to update token {token_id}!')
log.info('Done')

View File

@ -193,6 +193,58 @@ class AuthenticationTests(AbstractPillarTest):
self.assertEqual({tokdat_se['token_hashed'], tokdat_ne['token_hashed']},
{item['token_hashed'] for item in token_coll.find()})
def test_token_hashing_cli(self):
from dateutil.parser import parse
self.enter_app_context()
user_id1 = self.create_user(24 * 'a')
user_id2 = self.create_user(24 * 'b')
now = datetime.datetime.now(tz_util.utc)
# Force unhashed tokens into our database.
tokens_coll = self.app.db('tokens')
tokens_coll.insert_one({
'_id': ObjectId('59c0f8ef98377327e1525cd1'),
'_etag': '3b8fffa5177e87555acd95e49e6764cb81de5d70',
'_created': parse('2017-09-19T13:01:03.000+0200'),
'_updated': parse('2017-09-19T13:01:03.000+0200'),
'user': user_id1,
'token': 'unhashed-token',
'expire_time': now + datetime.timedelta(hours=1),
})
tokens_coll.insert_one({
'_id': ObjectId(24 * 'c'),
'_etag': '3b8fffa5177e87555acd95e49e6764cb81de5d70',
'_created': parse('2017-09-20T13:01:03.000+0200'),
'_updated': parse('2017-09-20T13:01:03.000+0200'),
'user': user_id2,
'token': 'some-other-token',
'expire_time': now + datetime.timedelta(hours=1),
})
# This token should work.
me1 = self.get('/api/users/me', auth_token='unhashed-token').get_json()
self.assertEqual(str(user_id1), me1['_id'])
me2 = self.get('/api/users/me', auth_token='some-other-token').get_json()
self.assertEqual(str(user_id2), me2['_id'])
from pillar.cli.operations import hash_auth_tokens
hash_auth_tokens()
# The same token should still work, but be hashed in the DB.
me1 = self.get('/api/users/me', auth_token='unhashed-token').get_json()
self.assertEqual(str(user_id1), me1['_id'])
me2 = self.get('/api/users/me', auth_token='some-other-token').get_json()
self.assertEqual(str(user_id2), me2['_id'])
db_token = tokens_coll.find_one({'_id': ObjectId('59c0f8ef98377327e1525cd1')})
self.assertNotIn('token', db_token)
self.assertIn('token_hashed', db_token)
db_token = tokens_coll.find_one({'_id': ObjectId('59c0f8ef98377327e1525cd1')})
self.assertNotIn('token', db_token)
self.assertIn('token_hashed', db_token)
class UserListTests(AbstractPillarTest):
"""Security-related tests."""