3
0
Fork 0

feat: add tools to manage user API tokens

This commit is contained in:
Lance Edgar 2025-08-09 09:56:00 -05:00
parent fcfa47af4a
commit 72565cc49c
3 changed files with 372 additions and 0 deletions

View file

@ -6,6 +6,7 @@ from sqlalchemy import orm
import colander
from wuttaweb.grids import Grid
from wuttaweb.views import users as mod
from wuttaweb.testing import WebTestCase
@ -122,6 +123,18 @@ class TestUserView(WebTestCase):
view.configure_form(form)
self.assertNotIn('password', form)
# api tokens grid shown only if current user has perm
with patch.object(view, 'viewing', new=True):
form = view.make_form(model_instance=barney)
self.assertIn('api_tokens', form)
view.configure_form(form)
self.assertNotIn('api_tokens', form)
with patch.object(self.request, 'is_root', new=True):
form = view.make_form(model_instance=barney)
self.assertIn('api_tokens', form)
view.configure_form(form)
self.assertIn('api_tokens', form)
def test_unique_username(self):
model = self.app.model
view = self.make_view()
@ -317,3 +330,108 @@ class TestUserView(WebTestCase):
user = view.objectify(form)
self.assertIs(user, barney)
self.assertEqual(len(user.roles), 2)
def test_normalize_api_token(self):
model = self.app.model
auth = self.app.get_auth_handler()
view = self.make_view()
user = model.User(username='foo')
self.session.add(user)
token = auth.add_api_token(user, 'test token')
self.session.commit()
normal = view.normalize_api_token(token)
self.assertIn('uuid', normal)
self.assertEqual(normal['uuid'], token.uuid.hex)
self.assertIn('description', normal)
self.assertEqual(normal['description'], 'test token')
self.assertIn('created', normal)
def test_make_api_tokens_grid(self):
model = self.app.model
auth = self.app.get_auth_handler()
view = self.make_view()
user = model.User(username='foo')
self.session.add(user)
token1 = auth.add_api_token(user, 'test1')
token2 = auth.add_api_token(user, 'test2')
self.session.commit()
# grid should have 2 records but no tools/actions
grid = view.make_api_tokens_grid(user)
self.assertIsInstance(grid, Grid)
self.assertEqual(len(grid.data), 2)
self.assertEqual(len(grid.tools), 0)
self.assertEqual(len(grid.actions), 0)
# create + delete allowed
with patch.object(self.request, 'is_root', new=True):
grid = view.make_api_tokens_grid(user)
self.assertEqual(len(grid.tools), 1)
self.assertIn('create', grid.tools)
self.assertEqual(len(grid.actions), 1)
self.assertEqual(grid.actions[0].key, 'delete')
def test_add_api_token(self):
model = self.app.model
view = self.make_view()
user = model.User(username='foo')
self.session.add(user)
self.session.commit()
self.session.refresh(user)
self.assertEqual(len(user.api_tokens), 0)
with patch.object(view, 'Session', return_value=self.session):
with patch.object(self.request, 'matchdict', new={'uuid': user.uuid}):
with patch.object(self.request, 'json_body', create=True,
new={'description': 'testing'}):
result = view.add_api_token()
self.assertEqual(len(user.api_tokens), 1)
token = user.api_tokens[0]
self.assertEqual(result['token_string'], token.token_string)
self.assertEqual(result['description'], 'testing')
def test_delete_api_token(self):
model = self.app.model
auth = self.app.get_auth_handler()
view = self.make_view()
user = model.User(username='foo')
self.session.add(user)
token1 = auth.add_api_token(user, 'test1')
token2 = auth.add_api_token(user, 'test2')
self.session.commit()
self.session.refresh(user)
self.assertEqual(len(user.api_tokens), 2)
with patch.object(view, 'Session', return_value=self.session):
with patch.object(self.request, 'matchdict', new={'uuid': user.uuid}):
# normal behavior
with patch.object(self.request, 'json_body', create=True,
new={'uuid': token1.uuid.hex}):
result = view.delete_api_token()
self.assertEqual(result, {})
self.session.refresh(user)
self.assertEqual(len(user.api_tokens), 1)
token = user.api_tokens[0]
self.assertIs(token, token2)
# token for wrong user
user2 = model.User(username='bar')
self.session.add(user2)
token3 = auth.add_api_token(user2, 'test3')
self.session.commit()
with patch.object(self.request, 'json_body', create=True,
new={'uuid': token3.uuid.hex}):
result = view.delete_api_token()
self.assertEqual(result, {'error': "API token not found"})
# token not found
with patch.object(self.request, 'json_body', create=True,
new={'uuid': self.app.make_true_uuid().hex}):
result = view.delete_api_token()
self.assertEqual(result, {'error': "API token not found"})