Allow service accounts to be email-less

This removes the ability of updating service accounts through the CLI
(something we never used anyway), now that service accounts cannot be
uniquely identified by their email address.
This commit is contained in:
2017-05-04 13:02:35 +02:00
parent 095f1cda0c
commit 69d7c5c5ce
6 changed files with 228 additions and 72 deletions

View File

@@ -234,7 +234,8 @@ class UserListTests(AbstractPillarTest):
def test_list_all_users_admin(self):
# Admin access should result in all users
resp = self.client.get('/api/users', headers={'Authorization': self.make_header('admin-token')})
resp = self.client.get('/api/users',
headers={'Authorization': self.make_header('admin-token')})
users = json.loads(resp.data)
self.assertEqual(200, resp.status_code)
@@ -281,8 +282,9 @@ class UserListTests(AbstractPillarTest):
def test_own_user_subscriber_explicit_projection(self):
# With a custom projection requesting the auth list
projection = json.dumps({'auth': 1})
resp = self.client.get('/api/users/%s?projection=%s' % ('123456789abc123456789abc', projection),
headers={'Authorization': self.make_header('token')})
resp = self.client.get(
'/api/users/%s?projection=%s' % ('123456789abc123456789abc', projection),
headers={'Authorization': self.make_header('token')})
user_info = json.loads(resp.data)
self.assertEqual(200, resp.status_code)
@@ -502,7 +504,7 @@ class PermissionComputationTest(AbstractPillarTest):
self.assertEqual(
{
'groups': [{'group': ObjectId('5596e975ea893b269af85c0e'),
'methods': ['DELETE', 'GET', 'POST', 'PUT']}],
'methods': ['DELETE', 'GET', 'POST', 'PUT']}],
'world': ['GET']
},
self.sort(compute_aggr_permissions('projects', EXAMPLE_PROJECT, None)))
@@ -511,11 +513,11 @@ class PermissionComputationTest(AbstractPillarTest):
self.assertEqual(
{
'groups': [{'group': ObjectId('5596e975ea893b269af85c0e'),
'methods': ['DELETE', 'GET', 'POST', 'PUT']},
{'group': ObjectId('5596e975ea893b269af85c0f'),
'methods': ['GET']},
{'group': ObjectId('564733b56dcaf85da2faee8a'),
'methods': ['GET']}],
'methods': ['DELETE', 'GET', 'POST', 'PUT']},
{'group': ObjectId('5596e975ea893b269af85c0f'),
'methods': ['GET']},
{'group': ObjectId('564733b56dcaf85da2faee8a'),
'methods': ['GET']}],
'world': ['GET']
},
self.sort(compute_aggr_permissions('projects', EXAMPLE_PROJECT, 'texture')))
@@ -530,11 +532,11 @@ class PermissionComputationTest(AbstractPillarTest):
self.ensure_project_exists(project_overrides=EXAMPLE_PROJECT)
self.assertEqual(
{'groups': [{'group': ObjectId('5596e975ea893b269af85c0e'),
'methods': ['DELETE', 'GET', 'POST', 'PUT']},
{'group': ObjectId('5596e975ea893b269af85c0f'),
'methods': ['DELETE', 'GET']},
{'group': ObjectId('564733b56dcaf85da2faee8a'),
'methods': ['GET']}],
'methods': ['DELETE', 'GET', 'POST', 'PUT']},
{'group': ObjectId('5596e975ea893b269af85c0f'),
'methods': ['DELETE', 'GET']},
{'group': ObjectId('564733b56dcaf85da2faee8a'),
'methods': ['GET']}],
'world': ['GET']},
self.sort(compute_aggr_permissions('nodes', node, None)))
@@ -544,11 +546,11 @@ class PermissionComputationTest(AbstractPillarTest):
node['project'] = EXAMPLE_PROJECT
self.assertEqual(
{'groups': [{'group': ObjectId('5596e975ea893b269af85c0e'),
'methods': ['DELETE', 'GET', 'POST', 'PUT']},
{'group': ObjectId('5596e975ea893b269af85c0f'),
'methods': ['DELETE', 'GET']},
{'group': ObjectId('564733b56dcaf85da2faee8a'),
'methods': ['GET']}],
'methods': ['DELETE', 'GET', 'POST', 'PUT']},
{'group': ObjectId('5596e975ea893b269af85c0f'),
'methods': ['DELETE', 'GET']},
{'group': ObjectId('564733b56dcaf85da2faee8a'),
'methods': ['GET']}],
'world': ['GET']},
self.sort(compute_aggr_permissions('nodes', node, None)))
@@ -638,3 +640,67 @@ class RequireRolesTest(AbstractPillarTest):
self.assertFalse(user_has_role('admin', {'roles': []}))
self.assertFalse(user_has_role('admin', {'roles': None}))
self.assertFalse(user_has_role('admin', {}))
class UserCreationTest(AbstractPillarTest):
@responses.activate
def test_create_by_auth(self):
"""Create user by authenticating against Blender ID."""
with self.app.test_request_context():
users_coll = self.app.db().users
self.assertEqual(0, users_coll.count())
self.mock_blenderid_validate_happy()
token = 'this is my life now'
self.get('/api/users/me', auth_token=token)
with self.app.test_request_context():
users_coll = self.app.db().users
self.assertEqual(1, users_coll.count())
db_user = users_coll.find()[0]
self.assertEqual(db_user['email'], TEST_EMAIL_ADDRESS)
def test_user_without_email_address(self):
"""Regular users should always have an email address.
Regular users are created by authentication with Blender ID, so we do not
have to test that (Blender ID ensures there is an email address). We do need
to test PUT access to erase the email address, though.
"""
from pillar.api.utils import remove_private_keys
user_id = self.create_user(24 * 'd', token='user-token')
with self.app.test_request_context():
users_coll = self.app.db().users
db_user = users_coll.find_one(user_id)
puttable = remove_private_keys(db_user)
empty_email = copy.deepcopy(puttable)
empty_email['email'] = ''
without_email = copy.deepcopy(puttable)
del without_email['email']
etag = db_user['_etag']
resp = self.put(f'/api/users/{user_id}', json=puttable, etag=etag,
auth_token='user-token', expected_status=200).json()
etag = resp['_etag']
self.put(f'/api/users/{user_id}', json=empty_email, etag=etag,
auth_token='user-token', expected_status=422)
self.put(f'/api/users/{user_id}', json=without_email, etag=etag,
auth_token='user-token', expected_status=422)
# An admin should be able to edit this user, but also not clear the email address.
self.create_user(24 * 'a', roles={'admin'}, token='admin-token')
resp = self.put(f'/api/users/{user_id}', json=puttable, etag=etag,
auth_token='admin-token', expected_status=200).json()
etag = resp['_etag']
self.put(f'/api/users/{user_id}', json=empty_email, etag=etag,
auth_token='admin-token', expected_status=422)
self.put(f'/api/users/{user_id}', json=without_email, etag=etag,
auth_token='admin-token', expected_status=422)

View File

@@ -0,0 +1,87 @@
import json
from pillar.tests import AbstractPillarTest
class ServiceAccountCreationTest(AbstractPillarTest):
def test_create_service_account(self):
from pillar.api.utils.authentication import force_cli_user
from pillar.api import service
with self.app.test_request_context():
force_cli_user()
account, token = service.create_service_account(
'jemoeder@jevader.nl', ['flamenco_manager'], {'flamenco_manager': {}})
self.assertEqual(f'SRV-{account["_id"]}', account['full_name'])
self.assertEqual(f'SRV-{account["_id"]}', account['username'])
self.assertEqual(['flamenco_manager', 'service'], account['roles'])
self.assertEqual([], account['auth'])
self.assertEqual({'flamenco_manager': {}}, account['service'])
self.assertAllowsAccess(token, account['_id'])
def test_without_email_address(self):
from pillar.api.utils.authentication import force_cli_user
from pillar.api.service import create_service_account as create_sa
with self.app.test_request_context():
force_cli_user()
account, token = create_sa('', ['flamenco_manager'], {'flamenco_manager': {}})
self.assertNotIn('email', account)
self.assertAllowsAccess(token, account['_id'])
def test_two_without_email_address(self):
from pillar.api.utils.authentication import force_cli_user
from pillar.api.service import create_service_account as create_sa
with self.app.test_request_context():
force_cli_user()
account1, token1 = create_sa('', ['flamenco_manager'], {'flamenco_manager': {}})
account2, token2 = create_sa('', ['flamenco_manager'], {'flamenco_manager': {}})
self.assertAllowsAccess(token1, account1['_id'])
self.assertAllowsAccess(token2, account2['_id'])
def test_put_without_email_address(self):
from pillar.api.utils import remove_private_keys
from pillar.api.utils.authentication import force_cli_user
from pillar.api.service import create_service_account as create_sa
with self.app.test_request_context():
force_cli_user()
account, token = create_sa('', ['flamenco_manager'], {'flamenco_manager': {}})
puttable = remove_private_keys(account)
user_id = account['_id']
# The user should be able to edit themselves, even without email address.
etag = account['_etag']
puttable['full_name'] = 'þor'
resp = self.put(f'/api/users/{user_id}',
json=puttable,
auth_token=token['token'],
etag=etag).json()
etag = resp['_etag']
with self.app.test_request_context():
users_coll = self.app.db().users
db_user = users_coll.find_one(user_id)
self.assertNotIn('email', db_user)
self.assertEqual('þor', db_user['full_name'])
# An admin should be able to edit this email-less user.
self.create_user(24 * 'a', roles={'admin'}, token='admin-token')
puttable['username'] = 'bigdüde'
self.put(f'/api/users/{user_id}',
json=puttable,
auth_token='admin-token',
etag=etag)
with self.app.test_request_context():
users_coll = self.app.db().users
db_user = users_coll.find_one(user_id)
self.assertNotIn('email', db_user)
self.assertEqual('bigdüde', db_user['username'])