diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 2fbcc48cd70fbda3297146feabcda91155621927..681a127154d604a227c4c92bd8de5b3826204011 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -120,7 +120,7 @@ html5validator: - rm -rf pages - mkdir -p pages - cp -r uffd/static pages/static - - DUMP_PAGES=pages python3 -m unittest discover tests + - DUMP_PAGES=pages python3 -m unittest discover tests/views - sed -i -e 's/href="\/static\//href=".\/static\//g' -e 's/src="\/static\//src=".\/static\//g' pages/*.html - html5validator --root pages 2>&1 | tee html5validator.log artifacts: diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/commands/__init__.py b/tests/commands/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/commands/test_role.py b/tests/commands/test_role.py new file mode 100644 index 0000000000000000000000000000000000000000..9f991eec6aedfcef684faacd3422de42fcc7f8be --- /dev/null +++ b/tests/commands/test_role.py @@ -0,0 +1,148 @@ +from uffd.database import db +from uffd.models import User, Role, RoleGroup + +from tests.utils import UffdTestCase + +class TestRoleCLI(UffdTestCase): + def setUp(self): + super().setUp() + role = Role(name='admin') + db.session.add(role) + role.groups[self.get_admin_group()] = RoleGroup(group=self.get_admin_group()) + role.members.append(self.get_admin()) + role = Role(name='base', is_default=True) + db.session.add(role) + role.groups[self.get_access_group()] = RoleGroup(group=self.get_access_group()) + db.session.add(Role(name='test')) + for user in User.query: + user.update_groups() + db.session.commit() + self.client.__exit__(None, None, None) + + def test_list(self): + result = self.app.test_cli_runner().invoke(args=['role', 'list']) + self.assertEqual(result.exit_code, 0) + + def test_show(self): + result = self.app.test_cli_runner().invoke(args=['role', 'show', 'admin']) + self.assertEqual(result.exit_code, 0) + result = self.app.test_cli_runner().invoke(args=['role', 'show', 'doesnotexist']) + self.assertEqual(result.exit_code, 1) + + def test_create(self): + result = self.app.test_cli_runner().invoke(args=['role', 'create', 'test']) # conflicting name + self.assertEqual(result.exit_code, 1) + result = self.app.test_cli_runner().invoke(args=['role', 'create', 'newrole', '--moderator-group', 'doesnotexist']) # invalid mod group + self.assertEqual(result.exit_code, 1) + result = self.app.test_cli_runner().invoke(args=['role', 'create', 'newrole', '--add-group', 'doesnotexist']) # invalid group + self.assertEqual(result.exit_code, 1) + result = self.app.test_cli_runner().invoke(args=['role', 'create', 'newrole', '--add-role', 'doesnotexist']) # invalid role + self.assertEqual(result.exit_code, 1) + result = self.app.test_cli_runner().invoke(args=['role', 'create', 'newrole', '--description', 'Role description', + '--moderator-group', 'uffd_admin', '--add-group', 'users', + '--add-role', 'admin']) + self.assertEqual(result.exit_code, 0) + with self.app.test_request_context(): + role = Role.query.filter_by(name='newrole').one() + self.assertIsNotNone(role) + self.assertEqual(role.description, 'Role description') + self.assertEqual(role.moderator_group, self.get_admin_group()) + self.assertEqual(list(role.groups), [self.get_users_group()]) + self.assertEqual(role.included_roles, Role.query.filter_by(name='admin').all()) + with self.app.test_request_context(): + for user in User.query: + self.assertNotIn(self.get_users_group(), user.groups) + result = self.app.test_cli_runner().invoke(args=['role', 'create', 'newbase', '--add-group', 'users', '--default']) + self.assertEqual(result.exit_code, 0) + with self.app.test_request_context(): + for user in User.query: + self.assertIn(self.get_users_group(), user.groups) + + def test_update(self): + result = self.app.test_cli_runner().invoke(args=['role', 'update', 'doesnotexist', '--description', 'New description']) + self.assertEqual(result.exit_code, 1) + result = self.app.test_cli_runner().invoke(args=['role', 'update', 'test', '--add-group', 'doesnotexist']) + self.assertEqual(result.exit_code, 1) + result = self.app.test_cli_runner().invoke(args=['role', 'update', 'test', '--remove-group', 'doesnotexist']) + self.assertEqual(result.exit_code, 1) + result = self.app.test_cli_runner().invoke(args=['role', 'update', 'test', '--add-role', 'doesnotexist']) + self.assertEqual(result.exit_code, 1) + result = self.app.test_cli_runner().invoke(args=['role', 'update', 'test', '--remove-role', 'doesnotexist']) + self.assertEqual(result.exit_code, 1) + result = self.app.test_cli_runner().invoke(args=['role', 'update', 'test', '--moderator-group', 'doesnotexist']) + self.assertEqual(result.exit_code, 1) + result = self.app.test_cli_runner().invoke(args=['role', 'update', 'base', '--description', 'New description', + '--moderator-group', 'uffd_admin', '--add-group', 'users', + '--remove-group', 'uffd_access', '--add-role', 'admin']) + self.assertEqual(result.exit_code, 0) + with self.app.test_request_context(): + role = Role.query.filter_by(name='base').first() + self.assertIsNotNone(role) + self.assertEqual(role.description, 'New description') + self.assertEqual(role.moderator_group, self.get_admin_group()) + self.assertEqual(list(role.groups), [self.get_users_group()]) + self.assertEqual(role.included_roles, Role.query.filter_by(name='admin').all()) + self.assertEqual(set(self.get_user().groups), {self.get_users_group(), self.get_admin_group()}) + result = self.app.test_cli_runner().invoke(args=['role', 'update', 'base', '--no-moderator-group', '--clear-groups', + '--add-group', 'uffd_access', '--remove-role', 'admin', + '--add-role', 'test']) + self.assertEqual(result.exit_code, 0) + with self.app.test_request_context(): + role = Role.query.filter_by(name='base').first() + self.assertIsNone(role.moderator_group) + self.assertEqual(list(role.groups), [self.get_access_group()]) + self.assertEqual(role.included_roles, Role.query.filter_by(name='test').all()) + self.assertEqual(set(self.get_user().groups), {self.get_access_group()}) + result = self.app.test_cli_runner().invoke(args=['role', 'update', 'base', '--clear-roles']) + self.assertEqual(result.exit_code, 0) + with self.app.test_request_context(): + role = Role.query.filter_by(name='base').first() + self.assertEqual(role.included_roles, []) + self.assertEqual(role.is_default, True) + self.assertEqual(set(self.get_user().groups), {self.get_access_group()}) + result = self.app.test_cli_runner().invoke(args=['role', 'update', 'base', '--no-default']) + self.assertEqual(result.exit_code, 0) + with self.app.test_request_context(): + role = Role.query.filter_by(name='base').first() + self.assertEqual(role.is_default, False) + self.assertEqual(set(self.get_user().groups), set()) + result = self.app.test_cli_runner().invoke(args=['role', 'update', 'base', '--default']) + self.assertEqual(result.exit_code, 0) + with self.app.test_request_context(): + role = Role.query.filter_by(name='base').first() + self.assertEqual(role.is_default, True) + self.assertEqual(set(self.get_user().groups), {self.get_access_group()}) + + # Regression test for https://git.cccv.de/uffd/uffd/-/issues/156 + def test_update_without_description(self): + with self.app.test_request_context(): + role = Role.query.filter_by(name='test').first() + role.description = 'Test description' + db.session.commit() + result = self.app.test_cli_runner().invoke(args=['role', 'update', 'test', '--clear-groups']) + self.assertEqual(result.exit_code, 0) + with self.app.test_request_context(): + role = Role.query.filter_by(name='test').first() + self.assertEqual(role.description, 'Test description') + + def test_delete(self): + with self.app.test_request_context(): + self.assertIsNotNone(Role.query.filter_by(name='test').first()) + result = self.app.test_cli_runner().invoke(args=['role', 'delete', 'test']) + self.assertEqual(result.exit_code, 0) + with self.app.test_request_context(): + self.assertIsNone(Role.query.filter_by(name='test').first()) + result = self.app.test_cli_runner().invoke(args=['role', 'delete', 'doesnotexist']) + self.assertEqual(result.exit_code, 1) + with self.app.test_request_context(): + self.assertIn(self.get_admin_group(), self.get_admin().groups) + result = self.app.test_cli_runner().invoke(args=['role', 'delete', 'admin']) + self.assertEqual(result.exit_code, 0) + with self.app.test_request_context(): + self.assertNotIn(self.get_admin_group(), self.get_admin().groups) + with self.app.test_request_context(): + self.assertIn(self.get_access_group(), self.get_user().groups) + result = self.app.test_cli_runner().invoke(args=['role', 'delete', 'base']) + self.assertEqual(result.exit_code, 0) + with self.app.test_request_context(): + self.assertNotIn(self.get_access_group(), self.get_user().groups) diff --git a/tests/commands/test_user.py b/tests/commands/test_user.py new file mode 100644 index 0000000000000000000000000000000000000000..bae05ebfaa09ffb1eeb2b59fdc3f0794fd6b2923 --- /dev/null +++ b/tests/commands/test_user.py @@ -0,0 +1,151 @@ +from uffd.database import db +from uffd.models import User, Group, Role, RoleGroup + +from tests.utils import UffdTestCase + +class TestUserCLI(UffdTestCase): + def setUp(self): + super().setUp() + role = Role(name='admin') + role.groups[self.get_admin_group()] = RoleGroup(group=self.get_admin_group()) + db.session.add(role) + db.session.add(Role(name='test')) + db.session.commit() + self.client.__exit__(None, None, None) + + def test_list(self): + result = self.app.test_cli_runner().invoke(args=['user', 'list']) + self.assertEqual(result.exit_code, 0) + + def test_show(self): + result = self.app.test_cli_runner().invoke(args=['user', 'show', 'testuser']) + self.assertEqual(result.exit_code, 0) + result = self.app.test_cli_runner().invoke(args=['user', 'show', 'doesnotexist']) + self.assertEqual(result.exit_code, 1) + + def test_create(self): + result = self.app.test_cli_runner().invoke(args=['user', 'create', 'new user', '--mail', 'foobar@example.com']) # invalid login name + self.assertEqual(result.exit_code, 1) + result = self.app.test_cli_runner().invoke(args=['user', 'create', 'newuser', '--mail', '']) # invalid mail + self.assertEqual(result.exit_code, 1) + result = self.app.test_cli_runner().invoke(args=['user', 'create', 'newuser', '--mail', 'foobar@example.com', '--password', '']) # invalid password + self.assertEqual(result.exit_code, 1) + result = self.app.test_cli_runner().invoke(args=['user', 'create', 'newuser', '--mail', 'foobar@example.com', '--displayname', '']) # invalid display name + self.assertEqual(result.exit_code, 1) + result = self.app.test_cli_runner().invoke(args=['user', 'create', 'newuser', '--mail', 'foobar@example.com', '--add-role', 'doesnotexist']) # unknown role + self.assertEqual(result.exit_code, 1) + result = self.app.test_cli_runner().invoke(args=['user', 'create', 'testuser', '--mail', 'foobar@example.com']) # conflicting name + self.assertEqual(result.exit_code, 1) + result = self.app.test_cli_runner().invoke(args=['user', 'create', 'newuser', '--mail', 'newmail@example.com', + '--displayname', 'New Display Name', '--password', 'newpassword', '--add-role', 'admin']) + self.assertEqual(result.exit_code, 0) + with self.app.test_request_context(): + user = User.query.filter_by(loginname='newuser').first() + self.assertIsNotNone(user) + self.assertEqual(user.primary_email.address, 'newmail@example.com') + self.assertEqual(user.displayname, 'New Display Name') + self.assertTrue(user.password.verify('newpassword')) + self.assertEqual(user.roles, Role.query.filter_by(name='admin').all()) + self.assertIn(self.get_admin_group(), user.groups) + + def test_update(self): + result = self.app.test_cli_runner().invoke(args=['user', 'update', 'doesnotexist', '--displayname', 'foo']) + self.assertEqual(result.exit_code, 1) + result = self.app.test_cli_runner().invoke(args=['user', 'update', 'testuser', '--mail', '']) # invalid mail + self.assertEqual(result.exit_code, 1) + result = self.app.test_cli_runner().invoke(args=['user', 'update', 'testuser', '--password', '']) # invalid password + self.assertEqual(result.exit_code, 1) + result = self.app.test_cli_runner().invoke(args=['user', 'update', 'testuser', '--displayname', '']) # invalid display name + self.assertEqual(result.exit_code, 1) + result = self.app.test_cli_runner().invoke(args=['user', 'update', 'testuser', '--remove-role', 'doesnotexist']) + self.assertEqual(result.exit_code, 1) + result = self.app.test_cli_runner().invoke(args=['user', 'update', 'testuser', '--mail', 'newmail@example.com', + '--displayname', 'New Display Name', '--password', 'newpassword']) + self.assertEqual(result.exit_code, 0) + with self.app.test_request_context(): + user = User.query.filter_by(loginname='testuser').first() + self.assertIsNotNone(user) + self.assertEqual(user.primary_email.address, 'newmail@example.com') + self.assertEqual(user.displayname, 'New Display Name') + self.assertTrue(user.password.verify('newpassword')) + result = self.app.test_cli_runner().invoke(args=['user', 'update', 'testuser', '--add-role', 'admin', '--add-role', 'test']) + self.assertEqual(result.exit_code, 0) + with self.app.test_request_context(): + user = User.query.filter_by(loginname='testuser').first() + self.assertEqual(set(user.roles), {Role.query.filter_by(name='admin').one(), Role.query.filter_by(name='test').one()}) + self.assertIn(self.get_admin_group(), user.groups) + result = self.app.test_cli_runner().invoke(args=['user', 'update', 'testuser', '--remove-role', 'admin']) + self.assertEqual(result.exit_code, 0) + with self.app.test_request_context(): + user = User.query.filter_by(loginname='testuser').first() + self.assertEqual(user.roles, Role.query.filter_by(name='test').all()) + self.assertNotIn(self.get_admin_group(), user.groups) + result = self.app.test_cli_runner().invoke(args=['user', 'update', 'testuser', '--clear-roles', '--add-role', 'admin']) + self.assertEqual(result.exit_code, 0) + with self.app.test_request_context(): + user = User.query.filter_by(loginname='testuser').first() + self.assertEqual(user.roles, Role.query.filter_by(name='admin').all()) + self.assertIn(self.get_admin_group(), user.groups) + + def test_delete(self): + with self.app.test_request_context(): + self.assertIsNotNone(User.query.filter_by(loginname='testuser').first()) + result = self.app.test_cli_runner().invoke(args=['user', 'delete', 'testuser']) + self.assertEqual(result.exit_code, 0) + with self.app.test_request_context(): + self.assertIsNone(User.query.filter_by(loginname='testuser').first()) + result = self.app.test_cli_runner().invoke(args=['user', 'delete', 'doesnotexist']) + self.assertEqual(result.exit_code, 1) + +class TestGroupCLI(UffdTestCase): + def setUp(self): + super().setUp() + self.client.__exit__(None, None, None) + + def test_list(self): + result = self.app.test_cli_runner().invoke(args=['group', 'list']) + self.assertEqual(result.exit_code, 0) + + def test_show(self): + result = self.app.test_cli_runner().invoke(args=['group', 'show', 'users']) + self.assertEqual(result.exit_code, 0) + result = self.app.test_cli_runner().invoke(args=['group', 'show', 'doesnotexist']) + self.assertEqual(result.exit_code, 1) + + def test_create(self): + result = self.app.test_cli_runner().invoke(args=['group', 'create', 'users']) # Duplicate name + self.assertEqual(result.exit_code, 1) + result = self.app.test_cli_runner().invoke(args=['group', 'create', 'new group']) + self.assertEqual(result.exit_code, 1) + result = self.app.test_cli_runner().invoke(args=['group', 'create', 'newgroup', '--description', 'A new group']) + self.assertEqual(result.exit_code, 0) + with self.app.test_request_context(): + group = Group.query.filter_by(name='newgroup').first() + self.assertIsNotNone(group) + self.assertEqual(group.description, 'A new group') + + def test_update(self): + result = self.app.test_cli_runner().invoke(args=['group', 'update', 'doesnotexist', '--description', 'foo']) + self.assertEqual(result.exit_code, 1) + result = self.app.test_cli_runner().invoke(args=['group', 'update', 'users', '--description', 'New description']) + self.assertEqual(result.exit_code, 0) + with self.app.test_request_context(): + group = Group.query.filter_by(name='users').first() + self.assertEqual(group.description, 'New description') + + def test_update_without_description(self): + result = self.app.test_cli_runner().invoke(args=['group', 'update', 'users']) # Should not change anything + self.assertEqual(result.exit_code, 0) + with self.app.test_request_context(): + group = Group.query.filter_by(name='users').first() + self.assertEqual(group.description, 'Base group for all users') + + def test_delete(self): + with self.app.test_request_context(): + self.assertIsNotNone(Group.query.filter_by(name='users').first()) + result = self.app.test_cli_runner().invoke(args=['group', 'delete', 'users']) + self.assertEqual(result.exit_code, 0) + with self.app.test_request_context(): + self.assertIsNone(Group.query.filter_by(name='users').first()) + result = self.app.test_cli_runner().invoke(args=['group', 'delete', 'doesnotexist']) + self.assertEqual(result.exit_code, 1) diff --git a/tests/models/__init__.py b/tests/models/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/models/test_invite.py b/tests/models/test_invite.py new file mode 100644 index 0000000000000000000000000000000000000000..fbbf8ab7896fc41e0b54f7825346a3657053a9fe --- /dev/null +++ b/tests/models/test_invite.py @@ -0,0 +1,266 @@ +import datetime + +from flask import current_app + +from uffd.database import db +from uffd.models import Invite, InviteGrant, InviteSignup, User, Role, RoleGroup + +from tests.utils import UffdTestCase, db_flush + +class TestInviteModel(UffdTestCase): + def test_expire(self): + invite = Invite(valid_until=datetime.datetime.utcnow() + datetime.timedelta(seconds=60), creator=self.get_admin()) + self.assertFalse(invite.expired) + self.assertTrue(invite.active) + invite.valid_until = datetime.datetime.utcnow() - datetime.timedelta(seconds=60) + self.assertTrue(invite.expired) + self.assertFalse(invite.active) + + def test_void(self): + invite = Invite(valid_until=datetime.datetime.utcnow() + datetime.timedelta(seconds=60), single_use=False, creator=self.get_admin()) + self.assertFalse(invite.voided) + self.assertTrue(invite.active) + invite.used = True + self.assertFalse(invite.voided) + self.assertTrue(invite.active) + invite = Invite(valid_until=datetime.datetime.utcnow() + datetime.timedelta(seconds=60), single_use=True, creator=self.get_admin()) + self.assertFalse(invite.voided) + self.assertTrue(invite.active) + invite.used = True + self.assertTrue(invite.voided) + self.assertFalse(invite.active) + + def test_permitted(self): + role = Role(name='testrole') + invite = Invite(valid_until=datetime.datetime.utcnow() + datetime.timedelta(seconds=60), allow_signup=True, roles=[role]) + self.assertFalse(invite.permitted) + self.assertFalse(invite.active) + invite.creator = self.get_admin() + self.assertTrue(invite.permitted) + self.assertTrue(invite.active) + invite.creator = self.get_user() + self.assertFalse(invite.permitted) + self.assertFalse(invite.active) + role.moderator_group = self.get_access_group() + current_app.config['ACL_SIGNUP_GROUP'] = 'uffd_access' + self.assertTrue(invite.permitted) + self.assertTrue(invite.active) + role.moderator_group = None + self.assertFalse(invite.permitted) + self.assertFalse(invite.active) + role.moderator_group = self.get_access_group() + current_app.config['ACL_SIGNUP_GROUP'] = 'uffd_admin' + self.assertFalse(invite.permitted) + self.assertFalse(invite.active) + + def test_disable(self): + invite = Invite(valid_until=datetime.datetime.utcnow() + datetime.timedelta(seconds=60), creator=self.get_admin()) + self.assertTrue(invite.active) + invite.disable() + self.assertFalse(invite.active) + + def test_reset_disabled(self): + invite = Invite(valid_until=datetime.datetime.utcnow() + datetime.timedelta(seconds=60), creator=self.get_admin()) + invite.disable() + self.assertFalse(invite.active) + invite.reset() + self.assertTrue(invite.active) + + def test_reset_expired(self): + invite = Invite(valid_until=datetime.datetime.utcnow() - datetime.timedelta(seconds=60), creator=self.get_admin()) + self.assertFalse(invite.active) + invite.reset() + self.assertFalse(invite.active) + + def test_reset_single_use(self): + invite = Invite(valid_until=datetime.datetime.utcnow() + datetime.timedelta(seconds=60), single_use=False, creator=self.get_admin()) + invite.used = True + invite.disable() + self.assertFalse(invite.active) + invite.reset() + self.assertTrue(invite.active) + + def test_short_token(self): + invite = Invite(valid_until=datetime.datetime.utcnow() + datetime.timedelta(seconds=60), creator=self.get_admin()) + db.session.add(invite) + db.session.commit() + self.assertTrue(len(invite.short_token) <= len(invite.token)/3) + +class TestInviteGrantModel(UffdTestCase): + def test_success(self): + user = self.get_user() + group0 = self.get_access_group() + role0 = Role(name='baserole', groups={group0: RoleGroup(group=group0)}) + db.session.add(role0) + user.roles.append(role0) + user.update_groups() + group1 = self.get_admin_group() + role1 = Role(name='testrole1', groups={group1: RoleGroup(group=group1)}) + db.session.add(role1) + role2 = Role(name='testrole2') + db.session.add(role2) + invite = Invite(valid_until=datetime.datetime.utcnow() + datetime.timedelta(seconds=60), roles=[role1, role2], creator=self.get_admin()) + self.assertIn(role0, user.roles) + self.assertNotIn(role1, user.roles) + self.assertNotIn(role2, user.roles) + self.assertIn(group0, user.groups) + self.assertNotIn(group1, user.groups) + self.assertFalse(invite.used) + grant = InviteGrant(invite=invite, user=user) + success, msg = grant.apply() + self.assertTrue(success) + self.assertIn(role0, user.roles) + self.assertIn(role1, user.roles) + self.assertIn(role2, user.roles) + self.assertIn(group0, user.groups) + self.assertIn(group1, user.groups) + self.assertTrue(invite.used) + db.session.commit() + db_flush() + user = self.get_user() + self.assertIn('baserole', [role.name for role in user.roles_effective]) + self.assertIn('testrole1', [role.name for role in user.roles]) + self.assertIn('testrole2', [role.name for role in user.roles]) + self.assertIn(self.get_access_group(), user.groups) + self.assertIn(self.get_admin_group(), user.groups) + + def test_inactive(self): + user = self.get_user() + group = self.get_admin_group() + role = Role(name='testrole1', groups={group: RoleGroup(group=group)}) + db.session.add(role) + invite = Invite(valid_until=datetime.datetime.utcnow() + datetime.timedelta(seconds=60), roles=[role], single_use=True, used=True, creator=self.get_admin()) + self.assertFalse(invite.active) + grant = InviteGrant(invite=invite, user=user) + success, msg = grant.apply() + self.assertFalse(success) + self.assertIsInstance(msg, str) + self.assertNotIn(role, user.roles) + self.assertNotIn(group, user.groups) + + def test_no_roles(self): + user = self.get_user() + invite = Invite(valid_until=datetime.datetime.utcnow() + datetime.timedelta(seconds=60), creator=self.get_admin()) + self.assertTrue(invite.active) + grant = InviteGrant(invite=invite, user=user) + success, msg = grant.apply() + self.assertFalse(success) + self.assertIsInstance(msg, str) + + def test_no_new_roles(self): + user = self.get_user() + role = Role(name='testrole1') + db.session.add(role) + user.roles.append(role) + invite = Invite(valid_until=datetime.datetime.utcnow() + datetime.timedelta(seconds=60), roles=[role], creator=self.get_admin()) + self.assertTrue(invite.active) + grant = InviteGrant(invite=invite, user=user) + success, msg = grant.apply() + self.assertFalse(success) + self.assertIsInstance(msg, str) + +class TestInviteSignupModel(UffdTestCase): + def create_base_roles(self): + baserole = Role(name='base', is_default=True) + baserole.groups[self.get_access_group()] = RoleGroup() + baserole.groups[self.get_users_group()] = RoleGroup() + db.session.add(baserole) + db.session.commit() + + def test_success(self): + self.create_base_roles() + base_role = Role.query.filter_by(name='base').one() + base_group1 = self.get_access_group() + base_group2 = self.get_users_group() + group = self.get_admin_group() + role1 = Role(name='testrole1', groups={group: RoleGroup(group=group)}) + db.session.add(role1) + role2 = Role(name='testrole2') + db.session.add(role2) + invite = Invite(valid_until=datetime.datetime.utcnow() + datetime.timedelta(seconds=60), roles=[role1, role2], allow_signup=True, creator=self.get_admin()) + signup = InviteSignup(invite=invite, loginname='newuser', displayname='New User', mail='test@example.com', password='notsecret') + self.assertFalse(invite.used) + valid, msg = signup.validate() + self.assertTrue(valid) + self.assertFalse(invite.used) + user, msg = signup.finish('notsecret') + self.assertIsInstance(user, User) + self.assertTrue(invite.used) + self.assertEqual(user.loginname, 'newuser') + self.assertEqual(user.displayname, 'New User') + self.assertEqual(user.primary_email.address, 'test@example.com') + self.assertEqual(signup.user, user) + self.assertIn(base_role, user.roles_effective) + self.assertIn(role1, user.roles) + self.assertIn(role2, user.roles) + self.assertIn(base_group1, user.groups) + self.assertIn(base_group2, user.groups) + self.assertIn(group, user.groups) + db.session.commit() + db_flush() + self.assertEqual(len(User.query.filter_by(loginname='newuser').all()), 1) + + def test_success_no_roles(self): + self.create_base_roles() + base_role = Role.query.filter_by(name='base').one() + base_group1 = self.get_access_group() + base_group2 = self.get_users_group() + invite = Invite(valid_until=datetime.datetime.utcnow() + datetime.timedelta(seconds=60), allow_signup=True, creator=self.get_admin()) + signup = InviteSignup(invite=invite, loginname='newuser', displayname='New User', mail='test@example.com', password='notsecret') + self.assertFalse(invite.used) + valid, msg = signup.validate() + self.assertTrue(valid) + self.assertFalse(invite.used) + user, msg = signup.finish('notsecret') + self.assertIsInstance(user, User) + self.assertTrue(invite.used) + self.assertEqual(user.loginname, 'newuser') + self.assertEqual(user.displayname, 'New User') + self.assertEqual(user.primary_email.address, 'test@example.com') + self.assertEqual(signup.user, user) + self.assertIn(base_role, user.roles_effective) + self.assertEqual(len(user.roles_effective), 1) + self.assertIn(base_group1, user.groups) + self.assertIn(base_group2, user.groups) + self.assertEqual(len(user.groups), 2) + db.session.commit() + db_flush() + self.assertEqual(len(User.query.filter_by(loginname='newuser').all()), 1) + + def test_inactive(self): + invite = Invite(valid_until=datetime.datetime.utcnow() + datetime.timedelta(seconds=60), allow_signup=True, single_use=True, used=True, creator=self.get_admin()) + self.assertFalse(invite.active) + signup = InviteSignup(invite=invite, loginname='newuser', displayname='New User', mail='test@example.com', password='notsecret') + valid, msg = signup.validate() + self.assertFalse(valid) + self.assertIsInstance(msg, str) + user, msg = signup.finish('notsecret') + self.assertIsNone(user) + self.assertIsInstance(msg, str) + + def test_invalid(self): + invite = Invite(valid_until=datetime.datetime.utcnow() + datetime.timedelta(seconds=60), allow_signup=True, creator=self.get_admin()) + self.assertTrue(invite.active) + signup = InviteSignup(invite=invite, loginname='', displayname='New User', mail='test@example.com', password='notsecret') + valid, msg = signup.validate() + self.assertFalse(valid) + self.assertIsInstance(msg, str) + + def test_invalid2(self): + invite = Invite(valid_until=datetime.datetime.utcnow() + datetime.timedelta(seconds=60), allow_signup=True, creator=self.get_admin()) + self.assertTrue(invite.active) + signup = InviteSignup(invite=invite, loginname='newuser', displayname='New User', mail='test@example.com', password='notsecret') + user, msg = signup.finish('wrongpassword') + self.assertIsNone(user) + self.assertIsInstance(msg, str) + + def test_no_signup(self): + invite = Invite(valid_until=datetime.datetime.utcnow() + datetime.timedelta(seconds=60), allow_signup=False, creator=self.get_admin()) + self.assertTrue(invite.active) + signup = InviteSignup(invite=invite, loginname='newuser', displayname='New User', mail='test@example.com', password='notsecret') + valid, msg = signup.validate() + self.assertFalse(valid) + self.assertIsInstance(msg, str) + user, msg = signup.finish('notsecret') + self.assertIsNone(user) + self.assertIsInstance(msg, str) diff --git a/tests/models/test_mfa.py b/tests/models/test_mfa.py new file mode 100644 index 0000000000000000000000000000000000000000..f2494fafc124119bd1d73b733f707036ca8144d7 --- /dev/null +++ b/tests/models/test_mfa.py @@ -0,0 +1,99 @@ +import unittest +import datetime +import time + +from uffd.database import db +from uffd.models import RecoveryCodeMethod, TOTPMethod, WebauthnMethod +from uffd.models.mfa import _hotp + +from tests.utils import UffdTestCase + +class TestMfaPrimitives(unittest.TestCase): + def test_hotp(self): + self.assertEqual(_hotp(5555555, b'\xae\xa3T\x05\x89\xd6\xb76\xf61r\x92\xcc\xb5WZ\xe6)\x05q'), '458290') + self.assertEqual(_hotp(5555555, b'\xae\xa3T\x05\x89\xd6\xb76\xf61r\x92\xcc\xb5WZ\xe6)\x05q', digits=8), '20458290') + for digits in range(1, 10): + self.assertEqual(len(_hotp(1, b'abcd', digits=digits)), digits) + self.assertEqual(_hotp(1234, b''), '161024') + self.assertEqual(_hotp(0, b'\x04\x8fM\xcc\x7f\x82\x9c$a\x1b\xb3'), '279354') + self.assertEqual(_hotp(2**64-1, b'abcde'), '899292') + +def get_fido2_test_cred(self): + try: + from uffd.fido2_compat import AttestedCredentialData + except ImportError: + self.skipTest('fido2 could not be imported') + # Example public key from webauthn spec 6.5.1.1 + return AttestedCredentialData(bytes.fromhex('00000000000000000000000000000000'+'0040'+'053cbcc9d37a61d3bac87cdcc77ee326256def08ab15775d3a720332e4101d14fae95aeee3bc9698781812e143c0597dc6e180595683d501891e9dd030454c0a'+'A501020326200121582065eda5a12577c2bae829437fe338701a10aaa375e1bb5b5de108de439c08551d2258201e52ed75701163f7f9e40ddf9f341b3dc9ba860af7e0ca7ca7e9eecd0084d19c')) + +class TestMfaMethodModels(UffdTestCase): + def test_common_attributes(self): + method = TOTPMethod(user=self.get_user(), name='testname') + self.assertTrue(method.created <= datetime.datetime.utcnow()) + self.assertEqual(method.name, 'testname') + self.assertEqual(method.user.loginname, 'testuser') + method.user = self.get_admin() + self.assertEqual(method.user.loginname, 'testadmin') + + def test_recovery_code_method(self): + method = RecoveryCodeMethod(user=self.get_user()) + db.session.add(method) + db.session.commit() + method_id = method.id + method_code = method.code + db.session.expunge(method) + method = RecoveryCodeMethod.query.get(method_id) + self.assertFalse(hasattr(method, 'code')) + self.assertFalse(method.verify('')) + self.assertFalse(method.verify('A'*8)) + self.assertTrue(method.verify(method_code)) + + def test_totp_method_attributes(self): + method = TOTPMethod(user=self.get_user(), name='testname') + raw_key = method.raw_key + issuer = method.issuer + accountname = method.accountname + key_uri = method.key_uri + self.assertEqual(method.name, 'testname') + # Restore method with key parameter + _method = TOTPMethod(user=self.get_user(), key=method.key, name='testname') + self.assertEqual(_method.name, 'testname') + self.assertEqual(_method.raw_key, raw_key) + self.assertEqual(_method.issuer, issuer) + self.assertEqual(_method.accountname, accountname) + self.assertEqual(_method.key_uri, key_uri) + db.session.add(method) + db.session.commit() + _method_id = _method.id + db.session.expunge(_method) + # Restore method from db + _method = TOTPMethod.query.get(_method_id) + self.assertEqual(_method.name, 'testname') + self.assertEqual(_method.raw_key, raw_key) + self.assertEqual(_method.issuer, issuer) + self.assertEqual(_method.accountname, accountname) + self.assertEqual(_method.key_uri, key_uri) + + def test_totp_method_verify(self): + method = TOTPMethod(user=self.get_user()) + counter = int(time.time()/30) + self.assertFalse(method.verify('')) + self.assertFalse(method.verify(_hotp(counter-2, method.raw_key))) + self.assertTrue(method.verify(_hotp(counter, method.raw_key))) + self.assertFalse(method.verify(_hotp(counter+2, method.raw_key))) + + def test_webauthn_method(self): + data = get_fido2_test_cred(self) + method = WebauthnMethod(user=self.get_user(), cred=data, name='testname') + self.assertEqual(method.name, 'testname') + db.session.add(method) + db.session.commit() + method_id = method.id + method_cred = method.cred + db.session.expunge(method) + _method = WebauthnMethod.query.get(method_id) + self.assertEqual(_method.name, 'testname') + self.assertEqual(bytes(method_cred), bytes(_method.cred)) + self.assertEqual(data.credential_id, _method.cred.credential_id) + self.assertEqual(data.public_key, _method.cred.public_key) + # We only test (de-)serialization here, as everything else is currently implemented in the views diff --git a/tests/models/test_role.py b/tests/models/test_role.py new file mode 100644 index 0000000000000000000000000000000000000000..8c786a75c1f818aaa2036dba7bba412ead3527f4 --- /dev/null +++ b/tests/models/test_role.py @@ -0,0 +1,134 @@ +import unittest + +from uffd.database import db +from uffd.models import User, Role, RoleGroup, TOTPMethod +from uffd.models.role import flatten_recursive + +from tests.utils import UffdTestCase + +class TestPrimitives(unittest.TestCase): + def test_flatten_recursive(self): + class Node: + def __init__(self, *neighbors): + self.neighbors = set(neighbors or set()) + + cycle = Node() + cycle.neighbors.add(cycle) + common = Node(cycle) + intermediate1 = Node(common) + intermediate2 = Node(common, intermediate1) + stub = Node() + backref = Node() + start1 = Node(intermediate1, intermediate2, stub, backref) + backref.neighbors.add(start1) + start2 = Node() + self.assertSetEqual(flatten_recursive({start1, start2}, 'neighbors'), + {start1, start2, backref, stub, intermediate1, intermediate2, common, cycle}) + self.assertSetEqual(flatten_recursive(set(), 'neighbors'), set()) + +class TestUserRoleAttributes(UffdTestCase): + def test_roles_effective(self): + db.session.add(User(loginname='service', is_service_user=True, primary_email_address='service@example.com', displayname='Service')) + db.session.commit() + user = self.get_user() + service_user = User.query.filter_by(loginname='service').one_or_none() + included_by_default_role = Role(name='included_by_default') + default_role = Role(name='default', is_default=True, included_roles=[included_by_default_role]) + included_role = Role(name='included') + cycle_role = Role(name='cycle') + direct_role1 = Role(name='role1', members=[user, service_user], included_roles=[included_role, cycle_role]) + direct_role2 = Role(name='role2', members=[user, service_user], included_roles=[included_role]) + cycle_role.included_roles.append(direct_role1) + db.session.add_all([included_by_default_role, default_role, included_role, cycle_role, direct_role1, direct_role2]) + self.assertSetEqual(user.roles_effective, {direct_role1, direct_role2, cycle_role, included_role, default_role, included_by_default_role}) + self.assertSetEqual(service_user.roles_effective, {direct_role1, direct_role2, cycle_role, included_role}) + + def test_compute_groups(self): + user = self.get_user() + group1 = self.get_users_group() + group2 = self.get_access_group() + role1 = Role(name='role1', groups={group1: RoleGroup(group=group1)}) + role2 = Role(name='role2', groups={group1: RoleGroup(group=group1), group2: RoleGroup(group=group2)}) + db.session.add_all([role1, role2]) + self.assertSetEqual(user.compute_groups(), set()) + role1.members.append(user) + role2.members.append(user) + self.assertSetEqual(user.compute_groups(), {group1, group2}) + role2.groups[group2].requires_mfa = True + self.assertSetEqual(user.compute_groups(), {group1}) + db.session.add(TOTPMethod(user=user)) + self.assertSetEqual(user.compute_groups(), {group1, group2}) + + def test_update_groups(self): + user = self.get_user() + group1 = self.get_users_group() + group2 = self.get_access_group() + role1 = Role(name='role1', members=[user], groups={group1: RoleGroup(group=group1)}) + role2 = Role(name='role2', groups={group2: RoleGroup(group=group2)}) + db.session.add_all([role1, role2]) + user.groups = [group2] + groups_added, groups_removed = user.update_groups() + self.assertSetEqual(groups_added, {group1}) + self.assertSetEqual(groups_removed, {group2}) + self.assertSetEqual(set(user.groups), {group1}) + groups_added, groups_removed = user.update_groups() + self.assertSetEqual(groups_added, set()) + self.assertSetEqual(groups_removed, set()) + self.assertSetEqual(set(user.groups), {group1}) + +class TestRoleModel(UffdTestCase): + def test_members_effective(self): + db.session.add(User(loginname='service', is_service_user=True, primary_email_address='service@example.com', displayname='Service')) + db.session.commit() + user1 = self.get_user() + user2 = self.get_admin() + service = User.query.filter_by(loginname='service').one_or_none() + included_by_default_role = Role(name='included_by_default') + default_role = Role(name='default', is_default=True, included_roles=[included_by_default_role]) + included_role = Role(name='included') + direct_role = Role(name='direct', members=[user1, user2, service], included_roles=[included_role]) + empty_role = Role(name='empty', included_roles=[included_role]) + self.assertSetEqual(included_by_default_role.members_effective, {user1, user2}) + self.assertSetEqual(default_role.members_effective, {user1, user2}) + self.assertSetEqual(included_role.members_effective, {user1, user2, service}) + self.assertSetEqual(direct_role.members_effective, {user1, user2, service}) + self.assertSetEqual(empty_role.members_effective, set()) + + def test_included_roles_recursive(self): + baserole = Role(name='base') + role1 = Role(name='role1', included_roles=[baserole]) + role2 = Role(name='role2', included_roles=[baserole]) + role3 = Role(name='role3', included_roles=[role1, role2]) + self.assertSetEqual(role1.included_roles_recursive, {baserole}) + self.assertSetEqual(role2.included_roles_recursive, {baserole}) + self.assertSetEqual(role3.included_roles_recursive, {baserole, role1, role2}) + baserole.included_roles.append(role1) + self.assertSetEqual(role3.included_roles_recursive, {baserole, role1, role2}) + + def test_groups_effective(self): + group1 = self.get_users_group() + group2 = self.get_access_group() + baserole = Role(name='base', groups={group1: RoleGroup(group=group1)}) + role1 = Role(name='role1', groups={group2: RoleGroup(group=group2)}, included_roles=[baserole]) + self.assertSetEqual(baserole.groups_effective, {group1}) + self.assertSetEqual(role1.groups_effective, {group1, group2}) + + def test_update_member_groups(self): + user1 = self.get_user() + user1.update_groups() + user2 = self.get_admin() + user2.update_groups() + group1 = self.get_users_group() + group2 = self.get_access_group() + group3 = self.get_admin_group() + baserole = Role(name='base', members=[user1], groups={group1: RoleGroup(group=group1)}) + role1 = Role(name='role1', members=[user2], groups={group2: RoleGroup(group=group2)}, included_roles=[baserole]) + db.session.add_all([baserole, role1]) + baserole.update_member_groups() + role1.update_member_groups() + self.assertSetEqual(set(user1.groups), {group1}) + self.assertSetEqual(set(user2.groups), {group1, group2}) + baserole.groups[group3] = RoleGroup() + baserole.update_member_groups() + self.assertSetEqual(set(user1.groups), {group1, group3}) + self.assertSetEqual(set(user2.groups), {group1, group2, group3}) diff --git a/tests/test_services.py b/tests/models/test_services.py similarity index 73% rename from tests/test_services.py rename to tests/models/test_services.py index 5a1848bcc24181c799b08060f4b3cc7f1fc2ac35..82808db46dfd0bd71ebeb7f5462093de5cdf0208 100644 --- a/tests/test_services.py +++ b/tests/models/test_services.py @@ -1,14 +1,10 @@ -import datetime -import unittest - -from flask import url_for - -from utils import dump, UffdTestCase from uffd.remailer import remailer from uffd.tasks import cleanup_task from uffd.database import db from uffd.models import Service, ServiceUser, User, UserEmail, RemailerMode +from tests.utils import UffdTestCase + class TestServiceUser(UffdTestCase): def setUp(self): super().setUp() @@ -219,98 +215,3 @@ class TestServiceUser(UffdTestCase): self.assertEqual(run_query(remailer_email2_1), { (service2.id, user1.id), }) - -class TestServices(UffdTestCase): - def setUpApp(self): - self.app.config['SERVICES'] = [ - { - 'title': 'Service Title', - 'subtitle': 'Service Subtitle', - 'description': 'Short description of the service as plain text', - 'url': 'https://example.com/', - 'logo_url': '/static/fairy-dust-color.png', - 'required_group': 'users', - 'permission_levels': [ - {'name': 'Moderator', 'required_group': 'moderators'}, - {'name': 'Admin', 'required_group': 'uffd_admin'}, - ], - 'confidential': True, - 'groups': [ - {'name': 'Group "crew_crew"', 'required_group': 'users'}, - {'name': 'Group "crew_logistik"', 'required_group': 'uffd_admin'}, - ], - 'infos': [ - {'title': 'Documentation', 'html': '<p>Some information about the service as html</p>', 'required_group': 'users'}, - ], - 'links': [ - {'title': 'Link to an external site', 'url': '#', 'required_group': 'users'}, - ], - }, - { - 'title': 'Minimal Service Title', - } - ] - self.app.config['SERVICES_PUBLIC'] = True - - def test_overview(self): - r = self.client.get(path=url_for('service.overview')) - dump('service_overview_guest', r) - self.assertEqual(r.status_code, 200) - self.assertNotIn(b'https://example.com/', r.data) - self.login_as('user') - r = self.client.get(path=url_for('service.overview')) - dump('service_overview_user', r) - self.assertEqual(r.status_code, 200) - self.assertIn(b'https://example.com/', r.data) - - def test_overview_disabled(self): - self.app.config['SERVICES'] = [] - # Should return login page - r = self.client.get(path=url_for('service.overview'), follow_redirects=True) - dump('service_overview_disabled_guest', r) - self.assertEqual(r.status_code, 200) - self.assertIn(b'name="password"', r.data) - self.login_as('user') - # Should return access denied page - r = self.client.get(path=url_for('service.overview'), follow_redirects=True) - dump('service_overview_disabled_user', r) - self.assertEqual(r.status_code, 403) - self.login_as('admin') - # Should return (empty) overview page - r = self.client.get(path=url_for('service.overview'), follow_redirects=True) - dump('service_overview_disabled_admin', r) - self.assertEqual(r.status_code, 200) - - def test_overview_nonpublic(self): - self.app.config['SERVICES_PUBLIC'] = False - # Should return login page - r = self.client.get(path=url_for('service.overview'), follow_redirects=True) - dump('service_overview_nonpublic_guest', r) - self.assertEqual(r.status_code, 200) - self.assertIn(b'name="password"', r.data) - self.login_as('user') - # Should return overview page - r = self.client.get(path=url_for('service.overview'), follow_redirects=True) - dump('service_overview_nonpublic_user', r) - self.assertEqual(r.status_code, 200) - self.login_as('admin') - # Should return overview page - r = self.client.get(path=url_for('service.overview'), follow_redirects=True) - dump('service_overview_nonpublic_admin', r) - self.assertEqual(r.status_code, 200) - - def test_overview_public(self): - # Should return overview page - r = self.client.get(path=url_for('service.overview'), follow_redirects=True) - dump('service_overview_public_guest', r) - self.assertEqual(r.status_code, 200) - self.login_as('user') - # Should return overview page - r = self.client.get(path=url_for('service.overview'), follow_redirects=True) - dump('service_overview_public_user', r) - self.assertEqual(r.status_code, 200) - self.login_as('admin') - # Should return overview page - r = self.client.get(path=url_for('service.overview'), follow_redirects=True) - dump('service_overview_public_admin', r) - self.assertEqual(r.status_code, 200) diff --git a/tests/models/test_signup.py b/tests/models/test_signup.py new file mode 100644 index 0000000000000000000000000000000000000000..cfed4fb23324c8071e91ffd5b459dba6bb5eadc7 --- /dev/null +++ b/tests/models/test_signup.py @@ -0,0 +1,169 @@ +import datetime + +from uffd.database import db +from uffd.models import Signup, User + +from tests.utils import UffdTestCase, db_flush + +def refetch_signup(signup): + db.session.add(signup) + db.session.commit() + id = signup.id + db.session.expunge(signup) + return Signup.query.get(id) + +# We assume in all tests that Signup.validate and Signup.password.verify do +# not alter any state + +class TestSignupModel(UffdTestCase): + def assert_validate_valid(self, signup): + valid, msg = signup.validate() + self.assertTrue(valid) + self.assertIsInstance(msg, str) + + def assert_validate_invalid(self, signup): + valid, msg = signup.validate() + self.assertFalse(valid) + self.assertIsInstance(msg, str) + self.assertNotEqual(msg, '') + + def assert_finish_success(self, signup, password): + self.assertIsNone(signup.user) + user, msg = signup.finish(password) + db.session.commit() + self.assertIsNotNone(user) + self.assertIsInstance(msg, str) + self.assertIsNotNone(signup.user) + + def assert_finish_failure(self, signup, password): + prev_id = signup.user_id + user, msg = signup.finish(password) + self.assertIsNone(user) + self.assertIsInstance(msg, str) + self.assertNotEqual(msg, '') + self.assertEqual(signup.user_id, prev_id) + + def test_password(self): + signup = Signup(loginname='newuser', displayname='New User', mail='test@example.com') + self.assertFalse(signup.password.verify('notsecret')) + self.assertFalse(signup.password.verify('')) + self.assertFalse(signup.password.verify('wrongpassword')) + self.assertTrue(signup.set_password('notsecret')) + self.assertTrue(signup.password.verify('notsecret')) + self.assertFalse(signup.password.verify('wrongpassword')) + + def test_expired(self): + # TODO: Find a better way to test this! + signup = Signup(loginname='newuser', displayname='New User', mail='test@example.com', password='notsecret') + self.assertFalse(signup.expired) + signup.created = created=datetime.datetime.utcnow() - datetime.timedelta(hours=49) + self.assertTrue(signup.expired) + + def test_completed(self): + signup = Signup(loginname='newuser', displayname='New User', mail='test@example.com', password='notsecret') + self.assertFalse(signup.completed) + signup.finish('notsecret') + db.session.commit() + self.assertTrue(signup.completed) + signup = refetch_signup(signup) + self.assertTrue(signup.completed) + + def test_validate(self): + signup = Signup(loginname='newuser', displayname='New User', mail='test@example.com', password='notsecret') + self.assert_validate_valid(signup) + self.assert_validate_valid(refetch_signup(signup)) + + def test_validate_completed(self): + signup = Signup(loginname='newuser', displayname='New User', mail='test@example.com', password='notsecret') + self.assert_finish_success(signup, 'notsecret') + self.assert_validate_invalid(signup) + self.assert_validate_invalid(refetch_signup(signup)) + + def test_validate_expired(self): + signup = Signup(loginname='newuser', displayname='New User', mail='test@example.com', + password='notsecret', created=datetime.datetime.utcnow()-datetime.timedelta(hours=49)) + self.assert_validate_invalid(signup) + self.assert_validate_invalid(refetch_signup(signup)) + + def test_validate_loginname(self): + signup = Signup(loginname='', displayname='New User', mail='test@example.com', password='notsecret') + self.assert_validate_invalid(signup) + self.assert_validate_invalid(refetch_signup(signup)) + + def test_validate_displayname(self): + signup = Signup(loginname='newuser', displayname='', mail='test@example.com', password='notsecret') + self.assert_validate_invalid(signup) + self.assert_validate_invalid(refetch_signup(signup)) + + def test_validate_mail(self): + signup = Signup(loginname='newuser', displayname='New User', mail='', password='notsecret') + self.assert_validate_invalid(signup) + self.assert_validate_invalid(refetch_signup(signup)) + + def test_validate_password(self): + signup = Signup(loginname='newuser', displayname='New User', mail='test@example.com') + self.assertFalse(signup.set_password('')) + self.assert_validate_invalid(signup) + self.assert_validate_invalid(refetch_signup(signup)) + + def test_validate_exists(self): + signup = Signup(loginname='testuser', displayname='New User', mail='test@example.com', password='notsecret') + self.assert_validate_invalid(signup) + self.assert_validate_invalid(refetch_signup(signup)) + + def test_finish(self): + signup = Signup(loginname='newuser', displayname='New User', mail='test@example.com', password='notsecret') + self.assert_finish_success(signup, 'notsecret') + user = User.query.filter_by(loginname='newuser').one_or_none() + self.assertEqual(user.loginname, 'newuser') + self.assertEqual(user.displayname, 'New User') + self.assertEqual(user.primary_email.address, 'test@example.com') + + def test_finish_completed(self): + signup = Signup(loginname='newuser', displayname='New User', mail='test@example.com', password='notsecret') + self.assert_finish_success(signup, 'notsecret') + self.assert_finish_failure(refetch_signup(signup), 'notsecret') + + def test_finish_expired(self): + # TODO: Find a better way to test this! + signup = Signup(loginname='newuser', displayname='New User', mail='test@example.com', + password='notsecret', created=datetime.datetime.utcnow()-datetime.timedelta(hours=49)) + self.assert_finish_failure(signup, 'notsecret') + self.assert_finish_failure(refetch_signup(signup), 'notsecret') + + def test_finish_wrongpassword(self): + signup = Signup(loginname='newuser', displayname='New User', mail='test@example.com') + self.assert_finish_failure(signup, '') + self.assert_finish_failure(signup, 'wrongpassword') + signup = refetch_signup(signup) + self.assert_finish_failure(signup, '') + self.assert_finish_failure(signup, 'wrongpassword') + signup = Signup(loginname='newuser', displayname='New User', mail='test@example.com', password='notsecret') + self.assert_finish_failure(signup, 'wrongpassword') + self.assert_finish_failure(refetch_signup(signup), 'wrongpassword') + + def test_finish_duplicate(self): + signup = Signup(loginname='testuser', displayname='New User', mail='test@example.com', password='notsecret') + self.assert_finish_failure(signup, 'notsecret') + self.assert_finish_failure(refetch_signup(signup), 'notsecret') + + def test_duplicate(self): + signup = Signup(loginname='newuser', displayname='New User', mail='test1@example.com', password='notsecret') + self.assert_validate_valid(signup) + db.session.add(signup) + db.session.commit() + signup1_id = signup.id + signup = Signup(loginname='newuser', displayname='New User', mail='test2@example.com', password='notsecret') + self.assert_validate_valid(signup) + db.session.add(signup) + db.session.commit() + signup2_id = signup.id + db_flush() + signup = Signup.query.get(signup2_id) + self.assert_finish_success(signup, 'notsecret') + db.session.commit() + db_flush() + signup = Signup.query.get(signup1_id) + self.assert_finish_failure(signup, 'notsecret') + user = User.query.filter_by(loginname='newuser').one_or_none() + self.assertEqual(user.primary_email.address, 'test2@example.com') diff --git a/tests/models/test_user.py b/tests/models/test_user.py new file mode 100644 index 0000000000000000000000000000000000000000..42fabfcf296b14422ff51a31f997a7daacc23a87 --- /dev/null +++ b/tests/models/test_user.py @@ -0,0 +1,189 @@ +import datetime + +import sqlalchemy + +from uffd.database import db +from uffd.models import User, UserEmail, Group + +from tests.utils import UffdTestCase + +class TestUserModel(UffdTestCase): + def test_has_permission(self): + user_ = self.get_user() # has 'users' and 'uffd_access' group + admin = self.get_admin() # has 'users', 'uffd_access' and 'uffd_admin' group + self.assertTrue(user_.has_permission(None)) + self.assertTrue(admin.has_permission(None)) + self.assertTrue(user_.has_permission('users')) + self.assertTrue(admin.has_permission('users')) + self.assertFalse(user_.has_permission('notagroup')) + self.assertFalse(admin.has_permission('notagroup')) + self.assertFalse(user_.has_permission('uffd_admin')) + self.assertTrue(admin.has_permission('uffd_admin')) + self.assertFalse(user_.has_permission(['uffd_admin'])) + self.assertTrue(admin.has_permission(['uffd_admin'])) + self.assertFalse(user_.has_permission(['uffd_admin', 'notagroup'])) + self.assertTrue(admin.has_permission(['uffd_admin', 'notagroup'])) + self.assertFalse(user_.has_permission(['notagroup', 'uffd_admin'])) + self.assertTrue(admin.has_permission(['notagroup', 'uffd_admin'])) + self.assertTrue(user_.has_permission(['uffd_admin', 'users'])) + self.assertTrue(admin.has_permission(['uffd_admin', 'users'])) + self.assertTrue(user_.has_permission([['uffd_admin', 'users'], ['users', 'uffd_access']])) + self.assertTrue(admin.has_permission([['uffd_admin', 'users'], ['users', 'uffd_access']])) + self.assertFalse(user_.has_permission(['uffd_admin', ['users', 'notagroup']])) + self.assertTrue(admin.has_permission(['uffd_admin', ['users', 'notagroup']])) + + def test_unix_uid_generation(self): + self.app.config['USER_MIN_UID'] = 10000 + self.app.config['USER_MAX_UID'] = 18999 + self.app.config['USER_SERVICE_MIN_UID'] = 19000 + self.app.config['USER_SERVICE_MAX_UID'] =19999 + User.query.delete() + db.session.commit() + user0 = User(loginname='user0', displayname='user0', primary_email_address='user0@example.com') + user1 = User(loginname='user1', displayname='user1', primary_email_address='user1@example.com') + user2 = User(loginname='user2', displayname='user2', primary_email_address='user2@example.com') + db.session.add_all([user0, user1, user2]) + db.session.commit() + self.assertEqual(user0.unix_uid, 10000) + self.assertEqual(user1.unix_uid, 10001) + self.assertEqual(user2.unix_uid, 10002) + db.session.delete(user1) + db.session.commit() + user3 = User(loginname='user3', displayname='user3', primary_email_address='user3@example.com') + db.session.add(user3) + db.session.commit() + self.assertEqual(user3.unix_uid, 10003) + service0 = User(loginname='service0', displayname='service0', primary_email_address='service0@example.com', is_service_user=True) + service1 = User(loginname='service1', displayname='service1', primary_email_address='service1@example.com', is_service_user=True) + db.session.add_all([service0, service1]) + db.session.commit() + self.assertEqual(service0.unix_uid, 19000) + self.assertEqual(service1.unix_uid, 19001) + + def test_unix_uid_generation_overlapping(self): + self.app.config['USER_MIN_UID'] = 10000 + self.app.config['USER_MAX_UID'] = 19999 + self.app.config['USER_SERVICE_MIN_UID'] = 10000 + self.app.config['USER_SERVICE_MAX_UID'] = 19999 + User.query.delete() + db.session.commit() + user0 = User(loginname='user0', displayname='user0', primary_email_address='user0@example.com') + service0 = User(loginname='service0', displayname='service0', primary_email_address='service0@example.com', is_service_user=True) + user1 = User(loginname='user1', displayname='user1', primary_email_address='user1@example.com') + db.session.add_all([user0, service0, user1]) + db.session.commit() + self.assertEqual(user0.unix_uid, 10000) + self.assertEqual(service0.unix_uid, 10001) + self.assertEqual(user1.unix_uid, 10002) + + def test_unix_uid_generation_overflow(self): + self.app.config['USER_MIN_UID'] = 10000 + self.app.config['USER_MAX_UID'] = 10001 + User.query.delete() + db.session.commit() + user0 = User(loginname='user0', displayname='user0', primary_email_address='user0@example.com') + user1 = User(loginname='user1', displayname='user1', primary_email_address='user1@example.com') + db.session.add_all([user0, user1]) + db.session.commit() + self.assertEqual(user0.unix_uid, 10000) + self.assertEqual(user1.unix_uid, 10001) + with self.assertRaises(sqlalchemy.exc.IntegrityError): + user2 = User(loginname='user2', displayname='user2', primary_email_address='user2@example.com') + db.session.add(user2) + db.session.commit() + + def test_init_primary_email_address(self): + user = User(primary_email_address='foobar@example.com') + self.assertEqual(user.primary_email.address, 'foobar@example.com') + self.assertEqual(user.primary_email.verified, True) + self.assertEqual(user.primary_email.user, user) + user = User(primary_email_address='invalid') + self.assertEqual(user.primary_email.address, 'invalid') + self.assertEqual(user.primary_email.verified, True) + self.assertEqual(user.primary_email.user, user) + + def test_set_primary_email_address(self): + user = User() + self.assertFalse(user.set_primary_email_address('invalid')) + self.assertIsNone(user.primary_email) + self.assertEqual(len(user.all_emails), 0) + self.assertTrue(user.set_primary_email_address('foobar@example.com')) + self.assertEqual(user.primary_email.address, 'foobar@example.com') + self.assertEqual(len(user.all_emails), 1) + self.assertFalse(user.set_primary_email_address('invalid')) + self.assertEqual(user.primary_email.address, 'foobar@example.com') + self.assertEqual(len(user.all_emails), 1) + self.assertTrue(user.set_primary_email_address('other@example.com')) + self.assertEqual(user.primary_email.address, 'other@example.com') + self.assertEqual(len(user.all_emails), 2) + self.assertEqual({user.all_emails[0].address, user.all_emails[1].address}, {'foobar@example.com', 'other@example.com'}) + +class TestUserEmailModel(UffdTestCase): + def test_set_address(self): + email = UserEmail() + self.assertFalse(email.set_address('invalid')) + self.assertIsNone(email.address) + self.assertFalse(email.set_address('')) + self.assertFalse(email.set_address('@')) + self.app.config['REMAILER_DOMAIN'] = 'remailer.example.com' + self.assertFalse(email.set_address('foobar@remailer.example.com')) + self.assertFalse(email.set_address('v1-1-testuser@remailer.example.com')) + self.assertFalse(email.set_address('v1-1-testuser @ remailer.example.com')) + self.assertFalse(email.set_address('v1-1-testuser@REMAILER.example.com')) + self.assertFalse(email.set_address('v1-1-testuser@foobar@remailer.example.com')) + self.assertTrue(email.set_address('foobar@example.com')) + self.assertEqual(email.address, 'foobar@example.com') + + def test_verification(self): + email = UserEmail(address='foo@example.com') + self.assertFalse(email.finish_verification('test')) + secret = email.start_verification() + self.assertTrue(email.verification_secret) + self.assertTrue(email.verification_secret.verify(secret)) + self.assertFalse(email.verification_expired) + self.assertFalse(email.finish_verification('test')) + orig_expires = email.verification_expires + email.verification_expires = datetime.datetime.utcnow() - datetime.timedelta(days=1) + self.assertFalse(email.finish_verification(secret)) + email.verification_expires = orig_expires + self.assertTrue(email.finish_verification(secret)) + self.assertFalse(email.verification_secret) + self.assertTrue(email.verification_expired) + +class TestGroupModel(UffdTestCase): + def test_unix_gid_generation(self): + self.app.config['GROUP_MIN_GID'] = 20000 + self.app.config['GROUP_MAX_GID'] = 49999 + Group.query.delete() + db.session.commit() + group0 = Group(name='group0', description='group0') + group1 = Group(name='group1', description='group1') + group2 = Group(name='group2', description='group2') + db.session.add_all([group0, group1, group2]) + db.session.commit() + self.assertEqual(group0.unix_gid, 20000) + self.assertEqual(group1.unix_gid, 20001) + self.assertEqual(group2.unix_gid, 20002) + db.session.delete(group1) + db.session.commit() + group3 = Group(name='group3', description='group3') + db.session.add(group3) + db.session.commit() + self.assertEqual(group3.unix_gid, 20003) + + def test_unix_gid_generation(self): + self.app.config['GROUP_MIN_GID'] = 20000 + self.app.config['GROUP_MAX_GID'] = 20001 + Group.query.delete() + db.session.commit() + group0 = Group(name='group0', description='group0') + group1 = Group(name='group1', description='group1') + db.session.add_all([group0, group1]) + db.session.commit() + self.assertEqual(group0.unix_gid, 20000) + self.assertEqual(group1.unix_gid, 20001) + db.session.commit() + with self.assertRaises(sqlalchemy.exc.IntegrityError): + group2 = Group(name='group2', description='group2') + db.session.add(group2) + db.session.commit() diff --git a/tests/test_ratelimit.py b/tests/test_ratelimit.py index a10903f4055ecaa93c472d6c12fdb0333cd4e6e7..377e1b4c9c792dc3f92f92179258c13d74bdb86f 100644 --- a/tests/test_ratelimit.py +++ b/tests/test_ratelimit.py @@ -1,10 +1,6 @@ -import time +from uffd.models.ratelimit import get_addrkey, format_delay, Ratelimit -from flask import Flask, Blueprint, session, url_for - -from uffd.models.ratelimit import get_addrkey, format_delay, Ratelimit, RatelimitEvent - -from utils import UffdTestCase +from tests.utils import UffdTestCase class TestRatelimit(UffdTestCase): def test_limiting(self): diff --git a/tests/test_remailer.py b/tests/test_remailer.py index 239f3743d1669a543213f31a650ca06cc7f023f0..3600dbedfd17371dfce07d940db573ec27f9e9d5 100644 --- a/tests/test_remailer.py +++ b/tests/test_remailer.py @@ -1,6 +1,6 @@ from uffd.remailer import remailer -from utils import UffdTestCase +from tests.utils import UffdTestCase USER_ID = 1234 SERVICE1_ID = 4223 diff --git a/tests/test_role.py b/tests/test_role.py deleted file mode 100644 index 3283d2734ba025f6db168a29363217ed5cb5c985..0000000000000000000000000000000000000000 --- a/tests/test_role.py +++ /dev/null @@ -1,429 +0,0 @@ -import datetime -import time -import unittest - -from flask import url_for, session - -from uffd import create_app, db -from uffd.models import User, Group, Role, RoleGroup, TOTPMethod -from uffd.models.role import flatten_recursive - -from utils import dump, UffdTestCase - -class TestPrimitives(unittest.TestCase): - def test_flatten_recursive(self): - class Node: - def __init__(self, *neighbors): - self.neighbors = set(neighbors or set()) - - cycle = Node() - cycle.neighbors.add(cycle) - common = Node(cycle) - intermediate1 = Node(common) - intermediate2 = Node(common, intermediate1) - stub = Node() - backref = Node() - start1 = Node(intermediate1, intermediate2, stub, backref) - backref.neighbors.add(start1) - start2 = Node() - self.assertSetEqual(flatten_recursive({start1, start2}, 'neighbors'), - {start1, start2, backref, stub, intermediate1, intermediate2, common, cycle}) - self.assertSetEqual(flatten_recursive(set(), 'neighbors'), set()) - -class TestUserRoleAttributes(UffdTestCase): - def test_roles_effective(self): - db.session.add(User(loginname='service', is_service_user=True, primary_email_address='service@example.com', displayname='Service')) - db.session.commit() - user = self.get_user() - service_user = User.query.filter_by(loginname='service').one_or_none() - included_by_default_role = Role(name='included_by_default') - default_role = Role(name='default', is_default=True, included_roles=[included_by_default_role]) - included_role = Role(name='included') - cycle_role = Role(name='cycle') - direct_role1 = Role(name='role1', members=[user, service_user], included_roles=[included_role, cycle_role]) - direct_role2 = Role(name='role2', members=[user, service_user], included_roles=[included_role]) - cycle_role.included_roles.append(direct_role1) - db.session.add_all([included_by_default_role, default_role, included_role, cycle_role, direct_role1, direct_role2]) - self.assertSetEqual(user.roles_effective, {direct_role1, direct_role2, cycle_role, included_role, default_role, included_by_default_role}) - self.assertSetEqual(service_user.roles_effective, {direct_role1, direct_role2, cycle_role, included_role}) - - def test_compute_groups(self): - user = self.get_user() - group1 = self.get_users_group() - group2 = self.get_access_group() - role1 = Role(name='role1', groups={group1: RoleGroup(group=group1)}) - role2 = Role(name='role2', groups={group1: RoleGroup(group=group1), group2: RoleGroup(group=group2)}) - db.session.add_all([role1, role2]) - self.assertSetEqual(user.compute_groups(), set()) - role1.members.append(user) - role2.members.append(user) - self.assertSetEqual(user.compute_groups(), {group1, group2}) - role2.groups[group2].requires_mfa = True - self.assertSetEqual(user.compute_groups(), {group1}) - db.session.add(TOTPMethod(user=user)) - self.assertSetEqual(user.compute_groups(), {group1, group2}) - - def test_update_groups(self): - user = self.get_user() - group1 = self.get_users_group() - group2 = self.get_access_group() - role1 = Role(name='role1', members=[user], groups={group1: RoleGroup(group=group1)}) - role2 = Role(name='role2', groups={group2: RoleGroup(group=group2)}) - db.session.add_all([role1, role2]) - user.groups = [group2] - groups_added, groups_removed = user.update_groups() - self.assertSetEqual(groups_added, {group1}) - self.assertSetEqual(groups_removed, {group2}) - self.assertSetEqual(set(user.groups), {group1}) - groups_added, groups_removed = user.update_groups() - self.assertSetEqual(groups_added, set()) - self.assertSetEqual(groups_removed, set()) - self.assertSetEqual(set(user.groups), {group1}) - -class TestRoleModel(UffdTestCase): - def test_members_effective(self): - db.session.add(User(loginname='service', is_service_user=True, primary_email_address='service@example.com', displayname='Service')) - db.session.commit() - user1 = self.get_user() - user2 = self.get_admin() - service = User.query.filter_by(loginname='service').one_or_none() - included_by_default_role = Role(name='included_by_default') - default_role = Role(name='default', is_default=True, included_roles=[included_by_default_role]) - included_role = Role(name='included') - direct_role = Role(name='direct', members=[user1, user2, service], included_roles=[included_role]) - empty_role = Role(name='empty', included_roles=[included_role]) - self.assertSetEqual(included_by_default_role.members_effective, {user1, user2}) - self.assertSetEqual(default_role.members_effective, {user1, user2}) - self.assertSetEqual(included_role.members_effective, {user1, user2, service}) - self.assertSetEqual(direct_role.members_effective, {user1, user2, service}) - self.assertSetEqual(empty_role.members_effective, set()) - - def test_included_roles_recursive(self): - baserole = Role(name='base') - role1 = Role(name='role1', included_roles=[baserole]) - role2 = Role(name='role2', included_roles=[baserole]) - role3 = Role(name='role3', included_roles=[role1, role2]) - self.assertSetEqual(role1.included_roles_recursive, {baserole}) - self.assertSetEqual(role2.included_roles_recursive, {baserole}) - self.assertSetEqual(role3.included_roles_recursive, {baserole, role1, role2}) - baserole.included_roles.append(role1) - self.assertSetEqual(role3.included_roles_recursive, {baserole, role1, role2}) - - def test_groups_effective(self): - group1 = self.get_users_group() - group2 = self.get_access_group() - baserole = Role(name='base', groups={group1: RoleGroup(group=group1)}) - role1 = Role(name='role1', groups={group2: RoleGroup(group=group2)}, included_roles=[baserole]) - self.assertSetEqual(baserole.groups_effective, {group1}) - self.assertSetEqual(role1.groups_effective, {group1, group2}) - - def test_update_member_groups(self): - user1 = self.get_user() - user1.update_groups() - user2 = self.get_admin() - user2.update_groups() - group1 = self.get_users_group() - group2 = self.get_access_group() - group3 = self.get_admin_group() - baserole = Role(name='base', members=[user1], groups={group1: RoleGroup(group=group1)}) - role1 = Role(name='role1', members=[user2], groups={group2: RoleGroup(group=group2)}, included_roles=[baserole]) - db.session.add_all([baserole, role1]) - baserole.update_member_groups() - role1.update_member_groups() - self.assertSetEqual(set(user1.groups), {group1}) - self.assertSetEqual(set(user2.groups), {group1, group2}) - baserole.groups[group3] = RoleGroup() - baserole.update_member_groups() - self.assertSetEqual(set(user1.groups), {group1, group3}) - self.assertSetEqual(set(user2.groups), {group1, group2, group3}) - -class TestRoleViews(UffdTestCase): - def setUp(self): - super().setUp() - self.login_as('admin') - - def test_index(self): - db.session.add(Role(name='base', description='Base role description')) - db.session.add(Role(name='test1', description='Test1 role description')) - db.session.commit() - r = self.client.get(path=url_for('role.index'), follow_redirects=True) - dump('role_index', r) - self.assertEqual(r.status_code, 200) - - def test_index_empty(self): - r = self.client.get(path=url_for('role.index'), follow_redirects=True) - dump('role_index_empty', r) - self.assertEqual(r.status_code, 200) - - def test_show(self): - role = Role(name='base', description='Base role description') - db.session.add(role) - db.session.commit() - r = self.client.get(path=url_for('role.show', roleid=role.id), follow_redirects=True) - dump('role_show', r) - self.assertEqual(r.status_code, 200) - - def test_new(self): - r = self.client.get(path=url_for('role.new'), follow_redirects=True) - dump('role_new', r) - self.assertEqual(r.status_code, 200) - - def test_update(self): - role = Role(name='base', description='Base role description') - db.session.add(role) - db.session.commit() - role.groups[self.get_admin_group()] = RoleGroup() - db.session.commit() - self.assertEqual(role.name, 'base') - self.assertEqual(role.description, 'Base role description') - self.assertSetEqual(set(role.groups), {self.get_admin_group()}) - r = self.client.post(path=url_for('role.update', roleid=role.id), - data={'name': 'base1', 'description': 'Base role description1', 'moderator-group': '', 'group-%d'%self.get_users_group().id: '1', 'group-%d'%self.get_access_group().id: '1'}, - follow_redirects=True) - dump('role_update', r) - self.assertEqual(r.status_code, 200) - role = Role.query.get(role.id) - self.assertEqual(role.name, 'base1') - self.assertEqual(role.description, 'Base role description1') - self.assertSetEqual(set(role.groups), {self.get_access_group(), self.get_users_group()}) - # TODO: verify that group memberships are updated - - def test_create(self): - self.assertIsNone(Role.query.filter_by(name='base').first()) - r = self.client.post(path=url_for('role.update'), - data={'name': 'base', 'description': 'Base role description', 'moderator-group': '', 'group-%d'%self.get_users_group().id: '1', 'group-%d'%self.get_access_group().id: '1'}, - follow_redirects=True) - dump('role_create', r) - self.assertEqual(r.status_code, 200) - role = Role.query.filter_by(name='base').first() - self.assertIsNotNone(role) - self.assertEqual(role.name, 'base') - self.assertEqual(role.description, 'Base role description') - self.assertSetEqual(set(role.groups), {self.get_access_group(), self.get_users_group()}) - # TODO: verify that group memberships are updated (currently not possible with ldap mock!) - - def test_create_with_moderator_group(self): - self.assertIsNone(Role.query.filter_by(name='base').first()) - r = self.client.post(path=url_for('role.update'), - data={'name': 'base', 'description': 'Base role description', 'moderator-group': self.get_admin_group().id, 'group-%d'%self.get_users_group().id: '1', 'group-%d'%self.get_access_group().id: '1'}, - follow_redirects=True) - self.assertEqual(r.status_code, 200) - role = Role.query.filter_by(name='base').first() - self.assertIsNotNone(role) - self.assertEqual(role.name, 'base') - self.assertEqual(role.description, 'Base role description') - self.assertEqual(role.moderator_group.name, 'uffd_admin') - self.assertSetEqual(set(role.groups), {self.get_access_group(), self.get_users_group()}) - # TODO: verify that group memberships are updated (currently not possible with ldap mock!) - - def test_delete(self): - role = Role(name='base', description='Base role description') - db.session.add(role) - db.session.commit() - role_id = role.id - self.assertIsNotNone(Role.query.get(role_id)) - r = self.client.get(path=url_for('role.delete', roleid=role.id), follow_redirects=True) - dump('role_delete', r) - self.assertEqual(r.status_code, 200) - self.assertIsNone(Role.query.get(role_id)) - # TODO: verify that group memberships are updated (currently not possible with ldap mock!) - - def test_set_default(self): - db.session.add(User(loginname='service', is_service_user=True, primary_email_address='service@example.com', displayname='Service')) - db.session.commit() - role = Role(name='test') - db.session.add(role) - role.groups[self.get_admin_group()] = RoleGroup() - user1 = self.get_user() - user2 = self.get_admin() - service_user = User.query.filter_by(loginname='service').one_or_none() - self.assertSetEqual(set(self.get_user().roles_effective), set()) - self.assertSetEqual(set(self.get_admin().roles_effective), set()) - self.assertSetEqual(set(service_user.roles_effective), set()) - role.members.append(self.get_user()) - role.members.append(service_user) - self.assertSetEqual(set(self.get_user().roles_effective), {role}) - self.assertSetEqual(set(self.get_admin().roles_effective), set()) - self.assertSetEqual(set(service_user.roles_effective), {role}) - db.session.commit() - role_id = role.id - self.assertSetEqual(set(role.members), {self.get_user(), service_user}) - r = self.client.get(path=url_for('role.set_default', roleid=role.id), follow_redirects=True) - dump('role_set_default', r) - self.assertEqual(r.status_code, 200) - role = Role.query.get(role_id) - service_user = User.query.filter_by(loginname='service').one_or_none() - self.assertSetEqual(set(role.members), {service_user}) - self.assertSetEqual(set(self.get_user().roles_effective), {role}) - self.assertSetEqual(set(self.get_admin().roles_effective), {role}) - - def test_unset_default(self): - admin_role = Role(name='admin', is_default=True) - db.session.add(admin_role) - admin_role.groups[self.get_admin_group()] = RoleGroup() - db.session.add(User(loginname='service', is_service_user=True, primary_email_address='service@example.com', displayname='Service')) - db.session.commit() - role = Role(name='test', is_default=True) - db.session.add(role) - service_user = User.query.filter_by(loginname='service').one_or_none() - role.members.append(service_user) - self.assertSetEqual(set(self.get_user().roles_effective), {role, admin_role}) - self.assertSetEqual(set(self.get_admin().roles_effective), {role, admin_role}) - self.assertSetEqual(set(service_user.roles_effective), {role}) - db.session.commit() - role_id = role.id - admin_role_id = admin_role.id - self.assertSetEqual(set(role.members), {service_user}) - r = self.client.get(path=url_for('role.unset_default', roleid=role.id), follow_redirects=True) - dump('role_unset_default', r) - self.assertEqual(r.status_code, 200) - role = Role.query.get(role_id) - admin_role = Role.query.get(admin_role_id) - service_user = User.query.filter_by(loginname='service').one_or_none() - self.assertSetEqual(set(role.members), {service_user}) - self.assertSetEqual(set(self.get_user().roles_effective), {admin_role}) - self.assertSetEqual(set(self.get_admin().roles_effective), {admin_role}) - -class TestRoleCLI(UffdTestCase): - def setUp(self): - super().setUp() - role = Role(name='admin') - db.session.add(role) - role.groups[self.get_admin_group()] = RoleGroup(group=self.get_admin_group()) - role.members.append(self.get_admin()) - role = Role(name='base', is_default=True) - db.session.add(role) - role.groups[self.get_access_group()] = RoleGroup(group=self.get_access_group()) - db.session.add(Role(name='test')) - for user in User.query: - user.update_groups() - db.session.commit() - self.client.__exit__(None, None, None) - - def test_list(self): - result = self.app.test_cli_runner().invoke(args=['role', 'list']) - self.assertEqual(result.exit_code, 0) - - def test_show(self): - result = self.app.test_cli_runner().invoke(args=['role', 'show', 'admin']) - self.assertEqual(result.exit_code, 0) - result = self.app.test_cli_runner().invoke(args=['role', 'show', 'doesnotexist']) - self.assertEqual(result.exit_code, 1) - - def test_create(self): - result = self.app.test_cli_runner().invoke(args=['role', 'create', 'test']) # conflicting name - self.assertEqual(result.exit_code, 1) - result = self.app.test_cli_runner().invoke(args=['role', 'create', 'newrole', '--moderator-group', 'doesnotexist']) # invalid mod group - self.assertEqual(result.exit_code, 1) - result = self.app.test_cli_runner().invoke(args=['role', 'create', 'newrole', '--add-group', 'doesnotexist']) # invalid group - self.assertEqual(result.exit_code, 1) - result = self.app.test_cli_runner().invoke(args=['role', 'create', 'newrole', '--add-role', 'doesnotexist']) # invalid role - self.assertEqual(result.exit_code, 1) - result = self.app.test_cli_runner().invoke(args=['role', 'create', 'newrole', '--description', 'Role description', - '--moderator-group', 'uffd_admin', '--add-group', 'users', - '--add-role', 'admin']) - self.assertEqual(result.exit_code, 0) - with self.app.test_request_context(): - role = Role.query.filter_by(name='newrole').one() - self.assertIsNotNone(role) - self.assertEqual(role.description, 'Role description') - self.assertEqual(role.moderator_group, self.get_admin_group()) - self.assertEqual(list(role.groups), [self.get_users_group()]) - self.assertEqual(role.included_roles, Role.query.filter_by(name='admin').all()) - with self.app.test_request_context(): - for user in User.query: - self.assertNotIn(self.get_users_group(), user.groups) - result = self.app.test_cli_runner().invoke(args=['role', 'create', 'newbase', '--add-group', 'users', '--default']) - self.assertEqual(result.exit_code, 0) - with self.app.test_request_context(): - for user in User.query: - self.assertIn(self.get_users_group(), user.groups) - - def test_update(self): - result = self.app.test_cli_runner().invoke(args=['role', 'update', 'doesnotexist', '--description', 'New description']) - self.assertEqual(result.exit_code, 1) - result = self.app.test_cli_runner().invoke(args=['role', 'update', 'test', '--add-group', 'doesnotexist']) - self.assertEqual(result.exit_code, 1) - result = self.app.test_cli_runner().invoke(args=['role', 'update', 'test', '--remove-group', 'doesnotexist']) - self.assertEqual(result.exit_code, 1) - result = self.app.test_cli_runner().invoke(args=['role', 'update', 'test', '--add-role', 'doesnotexist']) - self.assertEqual(result.exit_code, 1) - result = self.app.test_cli_runner().invoke(args=['role', 'update', 'test', '--remove-role', 'doesnotexist']) - self.assertEqual(result.exit_code, 1) - result = self.app.test_cli_runner().invoke(args=['role', 'update', 'test', '--moderator-group', 'doesnotexist']) - self.assertEqual(result.exit_code, 1) - result = self.app.test_cli_runner().invoke(args=['role', 'update', 'base', '--description', 'New description', - '--moderator-group', 'uffd_admin', '--add-group', 'users', - '--remove-group', 'uffd_access', '--add-role', 'admin']) - self.assertEqual(result.exit_code, 0) - with self.app.test_request_context(): - role = Role.query.filter_by(name='base').first() - self.assertIsNotNone(role) - self.assertEqual(role.description, 'New description') - self.assertEqual(role.moderator_group, self.get_admin_group()) - self.assertEqual(list(role.groups), [self.get_users_group()]) - self.assertEqual(role.included_roles, Role.query.filter_by(name='admin').all()) - self.assertEqual(set(self.get_user().groups), {self.get_users_group(), self.get_admin_group()}) - result = self.app.test_cli_runner().invoke(args=['role', 'update', 'base', '--no-moderator-group', '--clear-groups', - '--add-group', 'uffd_access', '--remove-role', 'admin', - '--add-role', 'test']) - self.assertEqual(result.exit_code, 0) - with self.app.test_request_context(): - role = Role.query.filter_by(name='base').first() - self.assertIsNone(role.moderator_group) - self.assertEqual(list(role.groups), [self.get_access_group()]) - self.assertEqual(role.included_roles, Role.query.filter_by(name='test').all()) - self.assertEqual(set(self.get_user().groups), {self.get_access_group()}) - result = self.app.test_cli_runner().invoke(args=['role', 'update', 'base', '--clear-roles']) - self.assertEqual(result.exit_code, 0) - with self.app.test_request_context(): - role = Role.query.filter_by(name='base').first() - self.assertEqual(role.included_roles, []) - self.assertEqual(role.is_default, True) - self.assertEqual(set(self.get_user().groups), {self.get_access_group()}) - result = self.app.test_cli_runner().invoke(args=['role', 'update', 'base', '--no-default']) - self.assertEqual(result.exit_code, 0) - with self.app.test_request_context(): - role = Role.query.filter_by(name='base').first() - self.assertEqual(role.is_default, False) - self.assertEqual(set(self.get_user().groups), set()) - result = self.app.test_cli_runner().invoke(args=['role', 'update', 'base', '--default']) - self.assertEqual(result.exit_code, 0) - with self.app.test_request_context(): - role = Role.query.filter_by(name='base').first() - self.assertEqual(role.is_default, True) - self.assertEqual(set(self.get_user().groups), {self.get_access_group()}) - - # Regression test for https://git.cccv.de/uffd/uffd/-/issues/156 - def test_update_without_description(self): - with self.app.test_request_context(): - role = Role.query.filter_by(name='test').first() - role.description = 'Test description' - db.session.commit() - result = self.app.test_cli_runner().invoke(args=['role', 'update', 'test', '--clear-groups']) - self.assertEqual(result.exit_code, 0) - with self.app.test_request_context(): - role = Role.query.filter_by(name='test').first() - self.assertEqual(role.description, 'Test description') - - def test_delete(self): - with self.app.test_request_context(): - self.assertIsNotNone(Role.query.filter_by(name='test').first()) - result = self.app.test_cli_runner().invoke(args=['role', 'delete', 'test']) - self.assertEqual(result.exit_code, 0) - with self.app.test_request_context(): - self.assertIsNone(Role.query.filter_by(name='test').first()) - result = self.app.test_cli_runner().invoke(args=['role', 'delete', 'doesnotexist']) - self.assertEqual(result.exit_code, 1) - with self.app.test_request_context(): - self.assertIn(self.get_admin_group(), self.get_admin().groups) - result = self.app.test_cli_runner().invoke(args=['role', 'delete', 'admin']) - self.assertEqual(result.exit_code, 0) - with self.app.test_request_context(): - self.assertNotIn(self.get_admin_group(), self.get_admin().groups) - with self.app.test_request_context(): - self.assertIn(self.get_access_group(), self.get_user().groups) - result = self.app.test_cli_runner().invoke(args=['role', 'delete', 'base']) - self.assertEqual(result.exit_code, 0) - with self.app.test_request_context(): - self.assertNotIn(self.get_access_group(), self.get_user().groups) diff --git a/tests/test_utils.py b/tests/test_utils.py index 3d42f4437c4307d8f7c38829fe6c515513418ae5..61dca8630e4c99d884bdadc1401eb4f55a6a1fb0 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,6 +1,6 @@ from uffd.utils import nopad_b32decode, nopad_b32encode, nopad_urlsafe_b64decode, nopad_urlsafe_b64encode -from utils import UffdTestCase +from tests.utils import UffdTestCase class TestUtils(UffdTestCase): def test_nopad_b32(self): diff --git a/tests/utils.py b/tests/utils.py index 3b98e64b9d8ac3dba8968338ccdd450ab3cc2ee2..afea2e0ff8841b16ac278a8caa7f8c3cb5201c16 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -1,9 +1,7 @@ import os -import tempfile -import shutil import unittest -from flask import request, url_for +from flask import url_for from uffd import create_app, db from uffd.models import User, Group, Mail diff --git a/tests/views/__init__.py b/tests/views/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/test_api.py b/tests/views/test_api.py similarity index 99% rename from tests/test_api.py rename to tests/views/test_api.py index 678aa467e8d3b68c7327384ba3a10de04f906c72..fce8554b4095cbe8ab1779bb59b04cf956fa7d9f 100644 --- a/tests/test_api.py +++ b/tests/views/test_api.py @@ -7,7 +7,7 @@ from uffd.remailer import remailer from uffd.database import db from uffd.models import APIClient, Service, User, RemailerMode from uffd.views.api import apikey_required -from utils import UffdTestCase, db_flush +from tests.utils import UffdTestCase, db_flush def basic_auth(username, password): return ('Authorization', 'Basic ' + base64.b64encode(f'{username}:{password}'.encode()).decode()) diff --git a/tests/test_invite.py b/tests/views/test_invite.py similarity index 68% rename from tests/test_invite.py rename to tests/views/test_invite.py index d9bc44dbac1263096aea8036a6a22e428eb45fa2..13d270877dd15b37e2e6c0f9ce721c57a554d845 100644 --- a/tests/test_invite.py +++ b/tests/views/test_invite.py @@ -1,272 +1,11 @@ -import unittest import datetime -import time -from flask import url_for, session, current_app +from flask import url_for, current_app -from uffd import create_app, db -from uffd.models import Invite, InviteGrant, InviteSignup, User, Group, Role, RoleGroup -from uffd.views.session import login_get_user +from uffd.database import db +from uffd.models import Invite, InviteGrant, InviteSignup, Role, RoleGroup -from utils import dump, UffdTestCase, db_flush - -class TestInviteModel(UffdTestCase): - def test_expire(self): - invite = Invite(valid_until=datetime.datetime.utcnow() + datetime.timedelta(seconds=60), creator=self.get_admin()) - self.assertFalse(invite.expired) - self.assertTrue(invite.active) - invite.valid_until = datetime.datetime.utcnow() - datetime.timedelta(seconds=60) - self.assertTrue(invite.expired) - self.assertFalse(invite.active) - - def test_void(self): - invite = Invite(valid_until=datetime.datetime.utcnow() + datetime.timedelta(seconds=60), single_use=False, creator=self.get_admin()) - self.assertFalse(invite.voided) - self.assertTrue(invite.active) - invite.used = True - self.assertFalse(invite.voided) - self.assertTrue(invite.active) - invite = Invite(valid_until=datetime.datetime.utcnow() + datetime.timedelta(seconds=60), single_use=True, creator=self.get_admin()) - self.assertFalse(invite.voided) - self.assertTrue(invite.active) - invite.used = True - self.assertTrue(invite.voided) - self.assertFalse(invite.active) - - def test_permitted(self): - role = Role(name='testrole') - invite = Invite(valid_until=datetime.datetime.utcnow() + datetime.timedelta(seconds=60), allow_signup=True, roles=[role]) - self.assertFalse(invite.permitted) - self.assertFalse(invite.active) - invite.creator = self.get_admin() - self.assertTrue(invite.permitted) - self.assertTrue(invite.active) - invite.creator = self.get_user() - self.assertFalse(invite.permitted) - self.assertFalse(invite.active) - role.moderator_group = self.get_access_group() - current_app.config['ACL_SIGNUP_GROUP'] = 'uffd_access' - self.assertTrue(invite.permitted) - self.assertTrue(invite.active) - role.moderator_group = None - self.assertFalse(invite.permitted) - self.assertFalse(invite.active) - role.moderator_group = self.get_access_group() - current_app.config['ACL_SIGNUP_GROUP'] = 'uffd_admin' - self.assertFalse(invite.permitted) - self.assertFalse(invite.active) - - def test_disable(self): - invite = Invite(valid_until=datetime.datetime.utcnow() + datetime.timedelta(seconds=60), creator=self.get_admin()) - self.assertTrue(invite.active) - invite.disable() - self.assertFalse(invite.active) - - def test_reset_disabled(self): - invite = Invite(valid_until=datetime.datetime.utcnow() + datetime.timedelta(seconds=60), creator=self.get_admin()) - invite.disable() - self.assertFalse(invite.active) - invite.reset() - self.assertTrue(invite.active) - - def test_reset_expired(self): - invite = Invite(valid_until=datetime.datetime.utcnow() - datetime.timedelta(seconds=60), creator=self.get_admin()) - self.assertFalse(invite.active) - invite.reset() - self.assertFalse(invite.active) - - def test_reset_single_use(self): - invite = Invite(valid_until=datetime.datetime.utcnow() + datetime.timedelta(seconds=60), single_use=False, creator=self.get_admin()) - invite.used = True - invite.disable() - self.assertFalse(invite.active) - invite.reset() - self.assertTrue(invite.active) - - def test_short_token(self): - invite = Invite(valid_until=datetime.datetime.utcnow() + datetime.timedelta(seconds=60), creator=self.get_admin()) - db.session.add(invite) - db.session.commit() - self.assertTrue(len(invite.short_token) <= len(invite.token)/3) - -class TestInviteGrantModel(UffdTestCase): - def test_success(self): - user = self.get_user() - group0 = self.get_access_group() - role0 = Role(name='baserole', groups={group0: RoleGroup(group=group0)}) - db.session.add(role0) - user.roles.append(role0) - user.update_groups() - group1 = self.get_admin_group() - role1 = Role(name='testrole1', groups={group1: RoleGroup(group=group1)}) - db.session.add(role1) - role2 = Role(name='testrole2') - db.session.add(role2) - invite = Invite(valid_until=datetime.datetime.utcnow() + datetime.timedelta(seconds=60), roles=[role1, role2], creator=self.get_admin()) - self.assertIn(role0, user.roles) - self.assertNotIn(role1, user.roles) - self.assertNotIn(role2, user.roles) - self.assertIn(group0, user.groups) - self.assertNotIn(group1, user.groups) - self.assertFalse(invite.used) - grant = InviteGrant(invite=invite, user=user) - success, msg = grant.apply() - self.assertTrue(success) - self.assertIn(role0, user.roles) - self.assertIn(role1, user.roles) - self.assertIn(role2, user.roles) - self.assertIn(group0, user.groups) - self.assertIn(group1, user.groups) - self.assertTrue(invite.used) - db.session.commit() - db_flush() - user = self.get_user() - self.assertIn('baserole', [role.name for role in user.roles_effective]) - self.assertIn('testrole1', [role.name for role in user.roles]) - self.assertIn('testrole2', [role.name for role in user.roles]) - self.assertIn(self.get_access_group(), user.groups) - self.assertIn(self.get_admin_group(), user.groups) - - def test_inactive(self): - user = self.get_user() - group = self.get_admin_group() - role = Role(name='testrole1', groups={group: RoleGroup(group=group)}) - db.session.add(role) - invite = Invite(valid_until=datetime.datetime.utcnow() + datetime.timedelta(seconds=60), roles=[role], single_use=True, used=True, creator=self.get_admin()) - self.assertFalse(invite.active) - grant = InviteGrant(invite=invite, user=user) - success, msg = grant.apply() - self.assertFalse(success) - self.assertIsInstance(msg, str) - self.assertNotIn(role, user.roles) - self.assertNotIn(group, user.groups) - - def test_no_roles(self): - user = self.get_user() - invite = Invite(valid_until=datetime.datetime.utcnow() + datetime.timedelta(seconds=60), creator=self.get_admin()) - self.assertTrue(invite.active) - grant = InviteGrant(invite=invite, user=user) - success, msg = grant.apply() - self.assertFalse(success) - self.assertIsInstance(msg, str) - - def test_no_new_roles(self): - user = self.get_user() - role = Role(name='testrole1') - db.session.add(role) - user.roles.append(role) - invite = Invite(valid_until=datetime.datetime.utcnow() + datetime.timedelta(seconds=60), roles=[role], creator=self.get_admin()) - self.assertTrue(invite.active) - grant = InviteGrant(invite=invite, user=user) - success, msg = grant.apply() - self.assertFalse(success) - self.assertIsInstance(msg, str) - -class TestInviteSignupModel(UffdTestCase): - def create_base_roles(self): - baserole = Role(name='base', is_default=True) - baserole.groups[self.get_access_group()] = RoleGroup() - baserole.groups[self.get_users_group()] = RoleGroup() - db.session.add(baserole) - db.session.commit() - - def test_success(self): - self.create_base_roles() - base_role = Role.query.filter_by(name='base').one() - base_group1 = self.get_access_group() - base_group2 = self.get_users_group() - group = self.get_admin_group() - role1 = Role(name='testrole1', groups={group: RoleGroup(group=group)}) - db.session.add(role1) - role2 = Role(name='testrole2') - db.session.add(role2) - invite = Invite(valid_until=datetime.datetime.utcnow() + datetime.timedelta(seconds=60), roles=[role1, role2], allow_signup=True, creator=self.get_admin()) - signup = InviteSignup(invite=invite, loginname='newuser', displayname='New User', mail='test@example.com', password='notsecret') - self.assertFalse(invite.used) - valid, msg = signup.validate() - self.assertTrue(valid) - self.assertFalse(invite.used) - user, msg = signup.finish('notsecret') - self.assertIsInstance(user, User) - self.assertTrue(invite.used) - self.assertEqual(user.loginname, 'newuser') - self.assertEqual(user.displayname, 'New User') - self.assertEqual(user.primary_email.address, 'test@example.com') - self.assertEqual(signup.user, user) - self.assertIn(base_role, user.roles_effective) - self.assertIn(role1, user.roles) - self.assertIn(role2, user.roles) - self.assertIn(base_group1, user.groups) - self.assertIn(base_group2, user.groups) - self.assertIn(group, user.groups) - db.session.commit() - db_flush() - self.assertEqual(len(User.query.filter_by(loginname='newuser').all()), 1) - - def test_success_no_roles(self): - self.create_base_roles() - base_role = Role.query.filter_by(name='base').one() - base_group1 = self.get_access_group() - base_group2 = self.get_users_group() - invite = Invite(valid_until=datetime.datetime.utcnow() + datetime.timedelta(seconds=60), allow_signup=True, creator=self.get_admin()) - signup = InviteSignup(invite=invite, loginname='newuser', displayname='New User', mail='test@example.com', password='notsecret') - self.assertFalse(invite.used) - valid, msg = signup.validate() - self.assertTrue(valid) - self.assertFalse(invite.used) - user, msg = signup.finish('notsecret') - self.assertIsInstance(user, User) - self.assertTrue(invite.used) - self.assertEqual(user.loginname, 'newuser') - self.assertEqual(user.displayname, 'New User') - self.assertEqual(user.primary_email.address, 'test@example.com') - self.assertEqual(signup.user, user) - self.assertIn(base_role, user.roles_effective) - self.assertEqual(len(user.roles_effective), 1) - self.assertIn(base_group1, user.groups) - self.assertIn(base_group2, user.groups) - self.assertEqual(len(user.groups), 2) - db.session.commit() - db_flush() - self.assertEqual(len(User.query.filter_by(loginname='newuser').all()), 1) - - def test_inactive(self): - invite = Invite(valid_until=datetime.datetime.utcnow() + datetime.timedelta(seconds=60), allow_signup=True, single_use=True, used=True, creator=self.get_admin()) - self.assertFalse(invite.active) - signup = InviteSignup(invite=invite, loginname='newuser', displayname='New User', mail='test@example.com', password='notsecret') - valid, msg = signup.validate() - self.assertFalse(valid) - self.assertIsInstance(msg, str) - user, msg = signup.finish('notsecret') - self.assertIsNone(user) - self.assertIsInstance(msg, str) - - def test_invalid(self): - invite = Invite(valid_until=datetime.datetime.utcnow() + datetime.timedelta(seconds=60), allow_signup=True, creator=self.get_admin()) - self.assertTrue(invite.active) - signup = InviteSignup(invite=invite, loginname='', displayname='New User', mail='test@example.com', password='notsecret') - valid, msg = signup.validate() - self.assertFalse(valid) - self.assertIsInstance(msg, str) - - def test_invalid2(self): - invite = Invite(valid_until=datetime.datetime.utcnow() + datetime.timedelta(seconds=60), allow_signup=True, creator=self.get_admin()) - self.assertTrue(invite.active) - signup = InviteSignup(invite=invite, loginname='newuser', displayname='New User', mail='test@example.com', password='notsecret') - user, msg = signup.finish('wrongpassword') - self.assertIsNone(user) - self.assertIsInstance(msg, str) - - def test_no_signup(self): - invite = Invite(valid_until=datetime.datetime.utcnow() + datetime.timedelta(seconds=60), allow_signup=False, creator=self.get_admin()) - self.assertTrue(invite.active) - signup = InviteSignup(invite=invite, loginname='newuser', displayname='New User', mail='test@example.com', password='notsecret') - valid, msg = signup.validate() - self.assertFalse(valid) - self.assertIsInstance(msg, str) - user, msg = signup.finish('notsecret') - self.assertIsNone(user) - self.assertIsInstance(msg, str) +from tests.utils import dump, UffdTestCase, db_flush class TestInviteAdminViews(UffdTestCase): def setUpApp(self): diff --git a/tests/test_mail.py b/tests/views/test_mail.py similarity index 95% rename from tests/test_mail.py rename to tests/views/test_mail.py index 6660b3be3b43e52c48aa48d7ca5ae7e5c1d7a423..638bb949ca943c8da576d5b1b443f1def03eef58 100644 --- a/tests/test_mail.py +++ b/tests/views/test_mail.py @@ -1,13 +1,11 @@ -import datetime -import time import unittest -from flask import url_for, session +from flask import url_for -from uffd import create_app, db +from uffd.database import db from uffd.models import Mail -from utils import dump, UffdTestCase, db_flush +from tests.utils import dump, UffdTestCase class TestMailViews(UffdTestCase): def setUp(self): diff --git a/tests/test_mfa.py b/tests/views/test_mfa.py similarity index 79% rename from tests/test_mfa.py rename to tests/views/test_mfa.py index f1e254a3bcebe483c7a163edc4d187f4c006db94..589bf3707bc782505bdb324822c7e504b1da7d4f 100644 --- a/tests/test_mfa.py +++ b/tests/views/test_mfa.py @@ -1,24 +1,12 @@ -import unittest -import datetime import time from flask import url_for, session, request -from uffd import create_app, db -from uffd.models import User, Role, RoleGroup, MFAMethod, MFAType, RecoveryCodeMethod, TOTPMethod, WebauthnMethod +from uffd.database import db +from uffd.models import Role, RoleGroup, MFAMethod, RecoveryCodeMethod, TOTPMethod, WebauthnMethod from uffd.models.mfa import _hotp -from utils import dump, UffdTestCase, db_flush - -class TestMfaPrimitives(unittest.TestCase): - def test_hotp(self): - self.assertEqual(_hotp(5555555, b'\xae\xa3T\x05\x89\xd6\xb76\xf61r\x92\xcc\xb5WZ\xe6)\x05q'), '458290') - self.assertEqual(_hotp(5555555, b'\xae\xa3T\x05\x89\xd6\xb76\xf61r\x92\xcc\xb5WZ\xe6)\x05q', digits=8), '20458290') - for digits in range(1, 10): - self.assertEqual(len(_hotp(1, b'abcd', digits=digits)), digits) - self.assertEqual(_hotp(1234, b''), '161024') - self.assertEqual(_hotp(0, b'\x04\x8fM\xcc\x7f\x82\x9c$a\x1b\xb3'), '279354') - self.assertEqual(_hotp(2**64-1, b'abcde'), '899292') +from tests.utils import dump, UffdTestCase, db_flush def get_fido2_test_cred(self): try: @@ -28,78 +16,6 @@ def get_fido2_test_cred(self): # Example public key from webauthn spec 6.5.1.1 return AttestedCredentialData(bytes.fromhex('00000000000000000000000000000000'+'0040'+'053cbcc9d37a61d3bac87cdcc77ee326256def08ab15775d3a720332e4101d14fae95aeee3bc9698781812e143c0597dc6e180595683d501891e9dd030454c0a'+'A501020326200121582065eda5a12577c2bae829437fe338701a10aaa375e1bb5b5de108de439c08551d2258201e52ed75701163f7f9e40ddf9f341b3dc9ba860af7e0ca7ca7e9eecd0084d19c')) -class TestMfaMethodModels(UffdTestCase): - def test_common_attributes(self): - method = TOTPMethod(user=self.get_user(), name='testname') - self.assertTrue(method.created <= datetime.datetime.utcnow()) - self.assertEqual(method.name, 'testname') - self.assertEqual(method.user.loginname, 'testuser') - method.user = self.get_admin() - self.assertEqual(method.user.loginname, 'testadmin') - - def test_recovery_code_method(self): - method = RecoveryCodeMethod(user=self.get_user()) - db.session.add(method) - db.session.commit() - method_id = method.id - method_code = method.code - db.session.expunge(method) - method = RecoveryCodeMethod.query.get(method_id) - self.assertFalse(hasattr(method, 'code')) - self.assertFalse(method.verify('')) - self.assertFalse(method.verify('A'*8)) - self.assertTrue(method.verify(method_code)) - - def test_totp_method_attributes(self): - method = TOTPMethod(user=self.get_user(), name='testname') - raw_key = method.raw_key - issuer = method.issuer - accountname = method.accountname - key_uri = method.key_uri - self.assertEqual(method.name, 'testname') - # Restore method with key parameter - _method = TOTPMethod(user=self.get_user(), key=method.key, name='testname') - self.assertEqual(_method.name, 'testname') - self.assertEqual(_method.raw_key, raw_key) - self.assertEqual(_method.issuer, issuer) - self.assertEqual(_method.accountname, accountname) - self.assertEqual(_method.key_uri, key_uri) - db.session.add(method) - db.session.commit() - _method_id = _method.id - db.session.expunge(_method) - # Restore method from db - _method = TOTPMethod.query.get(_method_id) - self.assertEqual(_method.name, 'testname') - self.assertEqual(_method.raw_key, raw_key) - self.assertEqual(_method.issuer, issuer) - self.assertEqual(_method.accountname, accountname) - self.assertEqual(_method.key_uri, key_uri) - - def test_totp_method_verify(self): - method = TOTPMethod(user=self.get_user()) - counter = int(time.time()/30) - self.assertFalse(method.verify('')) - self.assertFalse(method.verify(_hotp(counter-2, method.raw_key))) - self.assertTrue(method.verify(_hotp(counter, method.raw_key))) - self.assertFalse(method.verify(_hotp(counter+2, method.raw_key))) - - def test_webauthn_method(self): - data = get_fido2_test_cred(self) - method = WebauthnMethod(user=self.get_user(), cred=data, name='testname') - self.assertEqual(method.name, 'testname') - db.session.add(method) - db.session.commit() - method_id = method.id - method_cred = method.cred - db.session.expunge(method) - _method = WebauthnMethod.query.get(method_id) - self.assertEqual(_method.name, 'testname') - self.assertEqual(bytes(method_cred), bytes(_method.cred)) - self.assertEqual(data.credential_id, _method.cred.credential_id) - self.assertEqual(data.public_key, _method.cred.public_key) - # We only test (de-)serialization here, as everything else is currently implemented in the views - class TestMfaViews(UffdTestCase): def setUp(self): super().setUp() diff --git a/tests/test_oauth2.py b/tests/views/test_oauth2.py similarity index 98% rename from tests/test_oauth2.py rename to tests/views/test_oauth2.py index e47ba99593a7fea1e89d55897730af28410192ac..72c9ff7f6d2b7a0cc0e898109dc46fe988fd3a3e 100644 --- a/tests/test_oauth2.py +++ b/tests/views/test_oauth2.py @@ -1,14 +1,13 @@ -import datetime from urllib.parse import urlparse, parse_qs from flask import url_for, session -from uffd import create_app, db +from uffd.database import db from uffd.password_hash import PlaintextPasswordHash from uffd.remailer import remailer -from uffd.models import User, DeviceLoginConfirmation, Service, OAuth2Client, OAuth2DeviceLoginInitiation, RemailerMode +from uffd.models import DeviceLoginConfirmation, Service, OAuth2Client, OAuth2DeviceLoginInitiation, RemailerMode -from utils import dump, UffdTestCase +from tests.utils import dump, UffdTestCase class TestViews(UffdTestCase): def setUpDB(self): diff --git a/tests/views/test_role.py b/tests/views/test_role.py new file mode 100644 index 0000000000000000000000000000000000000000..befe12af068e0d2254c1c2c2548f09e31bd7231b --- /dev/null +++ b/tests/views/test_role.py @@ -0,0 +1,153 @@ +from flask import url_for + +from uffd.database import db +from uffd.models import User, Role, RoleGroup + +from tests.utils import dump, UffdTestCase + +class TestRoleViews(UffdTestCase): + def setUp(self): + super().setUp() + self.login_as('admin') + + def test_index(self): + db.session.add(Role(name='base', description='Base role description')) + db.session.add(Role(name='test1', description='Test1 role description')) + db.session.commit() + r = self.client.get(path=url_for('role.index'), follow_redirects=True) + dump('role_index', r) + self.assertEqual(r.status_code, 200) + + def test_index_empty(self): + r = self.client.get(path=url_for('role.index'), follow_redirects=True) + dump('role_index_empty', r) + self.assertEqual(r.status_code, 200) + + def test_show(self): + role = Role(name='base', description='Base role description') + db.session.add(role) + db.session.commit() + r = self.client.get(path=url_for('role.show', roleid=role.id), follow_redirects=True) + dump('role_show', r) + self.assertEqual(r.status_code, 200) + + def test_new(self): + r = self.client.get(path=url_for('role.new'), follow_redirects=True) + dump('role_new', r) + self.assertEqual(r.status_code, 200) + + def test_update(self): + role = Role(name='base', description='Base role description') + db.session.add(role) + db.session.commit() + role.groups[self.get_admin_group()] = RoleGroup() + db.session.commit() + self.assertEqual(role.name, 'base') + self.assertEqual(role.description, 'Base role description') + self.assertSetEqual(set(role.groups), {self.get_admin_group()}) + r = self.client.post(path=url_for('role.update', roleid=role.id), + data={'name': 'base1', 'description': 'Base role description1', 'moderator-group': '', 'group-%d'%self.get_users_group().id: '1', 'group-%d'%self.get_access_group().id: '1'}, + follow_redirects=True) + dump('role_update', r) + self.assertEqual(r.status_code, 200) + role = Role.query.get(role.id) + self.assertEqual(role.name, 'base1') + self.assertEqual(role.description, 'Base role description1') + self.assertSetEqual(set(role.groups), {self.get_access_group(), self.get_users_group()}) + # TODO: verify that group memberships are updated + + def test_create(self): + self.assertIsNone(Role.query.filter_by(name='base').first()) + r = self.client.post(path=url_for('role.update'), + data={'name': 'base', 'description': 'Base role description', 'moderator-group': '', 'group-%d'%self.get_users_group().id: '1', 'group-%d'%self.get_access_group().id: '1'}, + follow_redirects=True) + dump('role_create', r) + self.assertEqual(r.status_code, 200) + role = Role.query.filter_by(name='base').first() + self.assertIsNotNone(role) + self.assertEqual(role.name, 'base') + self.assertEqual(role.description, 'Base role description') + self.assertSetEqual(set(role.groups), {self.get_access_group(), self.get_users_group()}) + # TODO: verify that group memberships are updated (currently not possible with ldap mock!) + + def test_create_with_moderator_group(self): + self.assertIsNone(Role.query.filter_by(name='base').first()) + r = self.client.post(path=url_for('role.update'), + data={'name': 'base', 'description': 'Base role description', 'moderator-group': self.get_admin_group().id, 'group-%d'%self.get_users_group().id: '1', 'group-%d'%self.get_access_group().id: '1'}, + follow_redirects=True) + self.assertEqual(r.status_code, 200) + role = Role.query.filter_by(name='base').first() + self.assertIsNotNone(role) + self.assertEqual(role.name, 'base') + self.assertEqual(role.description, 'Base role description') + self.assertEqual(role.moderator_group.name, 'uffd_admin') + self.assertSetEqual(set(role.groups), {self.get_access_group(), self.get_users_group()}) + # TODO: verify that group memberships are updated (currently not possible with ldap mock!) + + def test_delete(self): + role = Role(name='base', description='Base role description') + db.session.add(role) + db.session.commit() + role_id = role.id + self.assertIsNotNone(Role.query.get(role_id)) + r = self.client.get(path=url_for('role.delete', roleid=role.id), follow_redirects=True) + dump('role_delete', r) + self.assertEqual(r.status_code, 200) + self.assertIsNone(Role.query.get(role_id)) + # TODO: verify that group memberships are updated (currently not possible with ldap mock!) + + def test_set_default(self): + db.session.add(User(loginname='service', is_service_user=True, primary_email_address='service@example.com', displayname='Service')) + db.session.commit() + role = Role(name='test') + db.session.add(role) + role.groups[self.get_admin_group()] = RoleGroup() + user1 = self.get_user() + user2 = self.get_admin() + service_user = User.query.filter_by(loginname='service').one_or_none() + self.assertSetEqual(set(self.get_user().roles_effective), set()) + self.assertSetEqual(set(self.get_admin().roles_effective), set()) + self.assertSetEqual(set(service_user.roles_effective), set()) + role.members.append(self.get_user()) + role.members.append(service_user) + self.assertSetEqual(set(self.get_user().roles_effective), {role}) + self.assertSetEqual(set(self.get_admin().roles_effective), set()) + self.assertSetEqual(set(service_user.roles_effective), {role}) + db.session.commit() + role_id = role.id + self.assertSetEqual(set(role.members), {self.get_user(), service_user}) + r = self.client.get(path=url_for('role.set_default', roleid=role.id), follow_redirects=True) + dump('role_set_default', r) + self.assertEqual(r.status_code, 200) + role = Role.query.get(role_id) + service_user = User.query.filter_by(loginname='service').one_or_none() + self.assertSetEqual(set(role.members), {service_user}) + self.assertSetEqual(set(self.get_user().roles_effective), {role}) + self.assertSetEqual(set(self.get_admin().roles_effective), {role}) + + def test_unset_default(self): + admin_role = Role(name='admin', is_default=True) + db.session.add(admin_role) + admin_role.groups[self.get_admin_group()] = RoleGroup() + db.session.add(User(loginname='service', is_service_user=True, primary_email_address='service@example.com', displayname='Service')) + db.session.commit() + role = Role(name='test', is_default=True) + db.session.add(role) + service_user = User.query.filter_by(loginname='service').one_or_none() + role.members.append(service_user) + self.assertSetEqual(set(self.get_user().roles_effective), {role, admin_role}) + self.assertSetEqual(set(self.get_admin().roles_effective), {role, admin_role}) + self.assertSetEqual(set(service_user.roles_effective), {role}) + db.session.commit() + role_id = role.id + admin_role_id = admin_role.id + self.assertSetEqual(set(role.members), {service_user}) + r = self.client.get(path=url_for('role.unset_default', roleid=role.id), follow_redirects=True) + dump('role_unset_default', r) + self.assertEqual(r.status_code, 200) + role = Role.query.get(role_id) + admin_role = Role.query.get(admin_role_id) + service_user = User.query.filter_by(loginname='service').one_or_none() + self.assertSetEqual(set(role.members), {service_user}) + self.assertSetEqual(set(self.get_user().roles_effective), {admin_role}) + self.assertSetEqual(set(self.get_admin().roles_effective), {admin_role}) diff --git a/tests/test_rolemod.py b/tests/views/test_rolemod.py similarity index 98% rename from tests/test_rolemod.py rename to tests/views/test_rolemod.py index 2950c83c43dba8801a524969e725397dff551d76..1f74fe9b2890768438ed97f1c3f0aa76380383bd 100644 --- a/tests/test_rolemod.py +++ b/tests/views/test_rolemod.py @@ -1,9 +1,9 @@ from flask import url_for from uffd.database import db -from uffd.models import User, Group, Role, RoleGroup +from uffd.models import Role, RoleGroup -from utils import dump, UffdTestCase +from tests.utils import dump, UffdTestCase class TestRolemodViewsLoggedOut(UffdTestCase): def test_acl_nologin(self): diff --git a/tests/test_selfservice.py b/tests/views/test_selfservice.py similarity index 99% rename from tests/test_selfservice.py rename to tests/views/test_selfservice.py index 6f686f4bb80643b908fe615eb07fe4f7f717eb33..8260c04c8db1bb5227543678db3de63f321a15e1 100644 --- a/tests/test_selfservice.py +++ b/tests/views/test_selfservice.py @@ -1,13 +1,12 @@ import datetime import re -import unittest from flask import url_for, request -from uffd import create_app, db -from uffd.models import PasswordToken, User, UserEmail, Role, RoleGroup, Service, ServiceUser +from uffd.database import db +from uffd.models import PasswordToken, UserEmail, Role, RoleGroup, Service, ServiceUser -from utils import dump, UffdTestCase +from tests.utils import dump, UffdTestCase class TestSelfservice(UffdTestCase): def test_index(self): diff --git a/tests/views/test_services.py b/tests/views/test_services.py new file mode 100644 index 0000000000000000000000000000000000000000..1e57d8be565b242f1a96ba1474e0d2ee3888381e --- /dev/null +++ b/tests/views/test_services.py @@ -0,0 +1,98 @@ +from flask import url_for + +from tests.utils import dump, UffdTestCase + +class TestServices(UffdTestCase): + def setUpApp(self): + self.app.config['SERVICES'] = [ + { + 'title': 'Service Title', + 'subtitle': 'Service Subtitle', + 'description': 'Short description of the service as plain text', + 'url': 'https://example.com/', + 'logo_url': '/static/fairy-dust-color.png', + 'required_group': 'users', + 'permission_levels': [ + {'name': 'Moderator', 'required_group': 'moderators'}, + {'name': 'Admin', 'required_group': 'uffd_admin'}, + ], + 'confidential': True, + 'groups': [ + {'name': 'Group "crew_crew"', 'required_group': 'users'}, + {'name': 'Group "crew_logistik"', 'required_group': 'uffd_admin'}, + ], + 'infos': [ + {'title': 'Documentation', 'html': '<p>Some information about the service as html</p>', 'required_group': 'users'}, + ], + 'links': [ + {'title': 'Link to an external site', 'url': '#', 'required_group': 'users'}, + ], + }, + { + 'title': 'Minimal Service Title', + } + ] + self.app.config['SERVICES_PUBLIC'] = True + + def test_overview(self): + r = self.client.get(path=url_for('service.overview')) + dump('service_overview_guest', r) + self.assertEqual(r.status_code, 200) + self.assertNotIn(b'https://example.com/', r.data) + self.login_as('user') + r = self.client.get(path=url_for('service.overview')) + dump('service_overview_user', r) + self.assertEqual(r.status_code, 200) + self.assertIn(b'https://example.com/', r.data) + + def test_overview_disabled(self): + self.app.config['SERVICES'] = [] + # Should return login page + r = self.client.get(path=url_for('service.overview'), follow_redirects=True) + dump('service_overview_disabled_guest', r) + self.assertEqual(r.status_code, 200) + self.assertIn(b'name="password"', r.data) + self.login_as('user') + # Should return access denied page + r = self.client.get(path=url_for('service.overview'), follow_redirects=True) + dump('service_overview_disabled_user', r) + self.assertEqual(r.status_code, 403) + self.login_as('admin') + # Should return (empty) overview page + r = self.client.get(path=url_for('service.overview'), follow_redirects=True) + dump('service_overview_disabled_admin', r) + self.assertEqual(r.status_code, 200) + + def test_overview_nonpublic(self): + self.app.config['SERVICES_PUBLIC'] = False + # Should return login page + r = self.client.get(path=url_for('service.overview'), follow_redirects=True) + dump('service_overview_nonpublic_guest', r) + self.assertEqual(r.status_code, 200) + self.assertIn(b'name="password"', r.data) + self.login_as('user') + # Should return overview page + r = self.client.get(path=url_for('service.overview'), follow_redirects=True) + dump('service_overview_nonpublic_user', r) + self.assertEqual(r.status_code, 200) + self.login_as('admin') + # Should return overview page + r = self.client.get(path=url_for('service.overview'), follow_redirects=True) + dump('service_overview_nonpublic_admin', r) + self.assertEqual(r.status_code, 200) + + def test_overview_public(self): + # Should return overview page + r = self.client.get(path=url_for('service.overview'), follow_redirects=True) + dump('service_overview_public_guest', r) + self.assertEqual(r.status_code, 200) + self.login_as('user') + # Should return overview page + r = self.client.get(path=url_for('service.overview'), follow_redirects=True) + dump('service_overview_public_user', r) + self.assertEqual(r.status_code, 200) + self.login_as('admin') + # Should return overview page + r = self.client.get(path=url_for('service.overview'), follow_redirects=True) + dump('service_overview_public_admin', r) + self.assertEqual(r.status_code, 200) diff --git a/tests/test_session.py b/tests/views/test_session.py similarity index 98% rename from tests/test_session.py rename to tests/views/test_session.py index bad3b887499b6c9169809a39bcc049a921320629..0cb5c340688eed0a6d2eab8b2f67453f6e2268eb 100644 --- a/tests/test_session.py +++ b/tests/views/test_session.py @@ -3,12 +3,12 @@ import unittest from flask import url_for, request -from uffd import create_app, db +from uffd.database import db from uffd.password_hash import PlaintextPasswordHash from uffd.models import DeviceLoginConfirmation, Service, OAuth2Client, OAuth2DeviceLoginInitiation, User from uffd.views.session import login_required -from utils import dump, UffdTestCase, db_flush +from tests.utils import dump, UffdTestCase, db_flush class TestSession(UffdTestCase): def setUpApp(self): diff --git a/tests/test_signup.py b/tests/views/test_signup.py similarity index 66% rename from tests/test_signup.py rename to tests/views/test_signup.py index 9affe7f90dab378f1f4aa8c062730d4fe8e3c7b4..8242481e284be17deae83bcc6ac21a19fe75dc26 100644 --- a/tests/test_signup.py +++ b/tests/views/test_signup.py @@ -1,14 +1,12 @@ -import unittest import datetime -import time -from flask import url_for, session, request +from flask import url_for, request -from uffd import create_app, db -from uffd.models import Signup, User, Role, RoleGroup +from uffd.database import db +from uffd.models import Signup, Role, RoleGroup from uffd.views.session import login_get_user -from utils import dump, UffdTestCase, db_flush +from tests.utils import dump, UffdTestCase, db_flush def refetch_signup(signup): db.session.add(signup) @@ -17,162 +15,6 @@ def refetch_signup(signup): db.session.expunge(signup) return Signup.query.get(id) -# We assume in all tests that Signup.validate and Signup.password.verify do -# not alter any state - -class TestSignupModel(UffdTestCase): - def assert_validate_valid(self, signup): - valid, msg = signup.validate() - self.assertTrue(valid) - self.assertIsInstance(msg, str) - - def assert_validate_invalid(self, signup): - valid, msg = signup.validate() - self.assertFalse(valid) - self.assertIsInstance(msg, str) - self.assertNotEqual(msg, '') - - def assert_finish_success(self, signup, password): - self.assertIsNone(signup.user) - user, msg = signup.finish(password) - db.session.commit() - self.assertIsNotNone(user) - self.assertIsInstance(msg, str) - self.assertIsNotNone(signup.user) - - def assert_finish_failure(self, signup, password): - prev_id = signup.user_id - user, msg = signup.finish(password) - self.assertIsNone(user) - self.assertIsInstance(msg, str) - self.assertNotEqual(msg, '') - self.assertEqual(signup.user_id, prev_id) - - def test_password(self): - signup = Signup(loginname='newuser', displayname='New User', mail='test@example.com') - self.assertFalse(signup.password.verify('notsecret')) - self.assertFalse(signup.password.verify('')) - self.assertFalse(signup.password.verify('wrongpassword')) - self.assertTrue(signup.set_password('notsecret')) - self.assertTrue(signup.password.verify('notsecret')) - self.assertFalse(signup.password.verify('wrongpassword')) - - def test_expired(self): - # TODO: Find a better way to test this! - signup = Signup(loginname='newuser', displayname='New User', mail='test@example.com', password='notsecret') - self.assertFalse(signup.expired) - signup.created = created=datetime.datetime.utcnow() - datetime.timedelta(hours=49) - self.assertTrue(signup.expired) - - def test_completed(self): - signup = Signup(loginname='newuser', displayname='New User', mail='test@example.com', password='notsecret') - self.assertFalse(signup.completed) - signup.finish('notsecret') - db.session.commit() - self.assertTrue(signup.completed) - signup = refetch_signup(signup) - self.assertTrue(signup.completed) - - def test_validate(self): - signup = Signup(loginname='newuser', displayname='New User', mail='test@example.com', password='notsecret') - self.assert_validate_valid(signup) - self.assert_validate_valid(refetch_signup(signup)) - - def test_validate_completed(self): - signup = Signup(loginname='newuser', displayname='New User', mail='test@example.com', password='notsecret') - self.assert_finish_success(signup, 'notsecret') - self.assert_validate_invalid(signup) - self.assert_validate_invalid(refetch_signup(signup)) - - def test_validate_expired(self): - signup = Signup(loginname='newuser', displayname='New User', mail='test@example.com', - password='notsecret', created=datetime.datetime.utcnow()-datetime.timedelta(hours=49)) - self.assert_validate_invalid(signup) - self.assert_validate_invalid(refetch_signup(signup)) - - def test_validate_loginname(self): - signup = Signup(loginname='', displayname='New User', mail='test@example.com', password='notsecret') - self.assert_validate_invalid(signup) - self.assert_validate_invalid(refetch_signup(signup)) - - def test_validate_displayname(self): - signup = Signup(loginname='newuser', displayname='', mail='test@example.com', password='notsecret') - self.assert_validate_invalid(signup) - self.assert_validate_invalid(refetch_signup(signup)) - - def test_validate_mail(self): - signup = Signup(loginname='newuser', displayname='New User', mail='', password='notsecret') - self.assert_validate_invalid(signup) - self.assert_validate_invalid(refetch_signup(signup)) - - def test_validate_password(self): - signup = Signup(loginname='newuser', displayname='New User', mail='test@example.com') - self.assertFalse(signup.set_password('')) - self.assert_validate_invalid(signup) - self.assert_validate_invalid(refetch_signup(signup)) - - def test_validate_exists(self): - signup = Signup(loginname='testuser', displayname='New User', mail='test@example.com', password='notsecret') - self.assert_validate_invalid(signup) - self.assert_validate_invalid(refetch_signup(signup)) - - def test_finish(self): - signup = Signup(loginname='newuser', displayname='New User', mail='test@example.com', password='notsecret') - self.assert_finish_success(signup, 'notsecret') - user = User.query.filter_by(loginname='newuser').one_or_none() - self.assertEqual(user.loginname, 'newuser') - self.assertEqual(user.displayname, 'New User') - self.assertEqual(user.primary_email.address, 'test@example.com') - - def test_finish_completed(self): - signup = Signup(loginname='newuser', displayname='New User', mail='test@example.com', password='notsecret') - self.assert_finish_success(signup, 'notsecret') - self.assert_finish_failure(refetch_signup(signup), 'notsecret') - - def test_finish_expired(self): - # TODO: Find a better way to test this! - signup = Signup(loginname='newuser', displayname='New User', mail='test@example.com', - password='notsecret', created=datetime.datetime.utcnow()-datetime.timedelta(hours=49)) - self.assert_finish_failure(signup, 'notsecret') - self.assert_finish_failure(refetch_signup(signup), 'notsecret') - - def test_finish_wrongpassword(self): - signup = Signup(loginname='newuser', displayname='New User', mail='test@example.com') - self.assert_finish_failure(signup, '') - self.assert_finish_failure(signup, 'wrongpassword') - signup = refetch_signup(signup) - self.assert_finish_failure(signup, '') - self.assert_finish_failure(signup, 'wrongpassword') - signup = Signup(loginname='newuser', displayname='New User', mail='test@example.com', password='notsecret') - self.assert_finish_failure(signup, 'wrongpassword') - self.assert_finish_failure(refetch_signup(signup), 'wrongpassword') - - def test_finish_duplicate(self): - signup = Signup(loginname='testuser', displayname='New User', mail='test@example.com', password='notsecret') - self.assert_finish_failure(signup, 'notsecret') - self.assert_finish_failure(refetch_signup(signup), 'notsecret') - - def test_duplicate(self): - signup = Signup(loginname='newuser', displayname='New User', mail='test1@example.com', password='notsecret') - self.assert_validate_valid(signup) - db.session.add(signup) - db.session.commit() - signup1_id = signup.id - signup = Signup(loginname='newuser', displayname='New User', mail='test2@example.com', password='notsecret') - self.assert_validate_valid(signup) - db.session.add(signup) - db.session.commit() - signup2_id = signup.id - db_flush() - signup = Signup.query.get(signup2_id) - self.assert_finish_success(signup, 'notsecret') - db.session.commit() - db_flush() - signup = Signup.query.get(signup1_id) - self.assert_finish_failure(signup, 'notsecret') - user = User.query.filter_by(loginname='newuser').one_or_none() - self.assertEqual(user.primary_email.address, 'test2@example.com') - class TestSignupViews(UffdTestCase): def setUpApp(self): self.app.config['SELF_SIGNUP'] = True diff --git a/tests/test_user.py b/tests/views/test_user.py similarity index 56% rename from tests/test_user.py rename to tests/views/test_user.py index 81f3a292eb0c4fabd116fe3b092690481fb14128..308167bfce88e01737d211aba18f311a58e10870 100644 --- a/tests/test_user.py +++ b/tests/views/test_user.py @@ -1,157 +1,9 @@ -import datetime -import unittest - -from flask import url_for, session -import sqlalchemy - -from uffd import create_app, db -from uffd.remailer import remailer -from uffd.models import User, UserEmail, Group, Role, RoleGroup, Service, ServiceUser - -from utils import dump, UffdTestCase - -class TestUserModel(UffdTestCase): - def test_has_permission(self): - user_ = self.get_user() # has 'users' and 'uffd_access' group - admin = self.get_admin() # has 'users', 'uffd_access' and 'uffd_admin' group - self.assertTrue(user_.has_permission(None)) - self.assertTrue(admin.has_permission(None)) - self.assertTrue(user_.has_permission('users')) - self.assertTrue(admin.has_permission('users')) - self.assertFalse(user_.has_permission('notagroup')) - self.assertFalse(admin.has_permission('notagroup')) - self.assertFalse(user_.has_permission('uffd_admin')) - self.assertTrue(admin.has_permission('uffd_admin')) - self.assertFalse(user_.has_permission(['uffd_admin'])) - self.assertTrue(admin.has_permission(['uffd_admin'])) - self.assertFalse(user_.has_permission(['uffd_admin', 'notagroup'])) - self.assertTrue(admin.has_permission(['uffd_admin', 'notagroup'])) - self.assertFalse(user_.has_permission(['notagroup', 'uffd_admin'])) - self.assertTrue(admin.has_permission(['notagroup', 'uffd_admin'])) - self.assertTrue(user_.has_permission(['uffd_admin', 'users'])) - self.assertTrue(admin.has_permission(['uffd_admin', 'users'])) - self.assertTrue(user_.has_permission([['uffd_admin', 'users'], ['users', 'uffd_access']])) - self.assertTrue(admin.has_permission([['uffd_admin', 'users'], ['users', 'uffd_access']])) - self.assertFalse(user_.has_permission(['uffd_admin', ['users', 'notagroup']])) - self.assertTrue(admin.has_permission(['uffd_admin', ['users', 'notagroup']])) - - def test_unix_uid_generation(self): - self.app.config['USER_MIN_UID'] = 10000 - self.app.config['USER_MAX_UID'] = 18999 - self.app.config['USER_SERVICE_MIN_UID'] = 19000 - self.app.config['USER_SERVICE_MAX_UID'] =19999 - User.query.delete() - db.session.commit() - user0 = User(loginname='user0', displayname='user0', primary_email_address='user0@example.com') - user1 = User(loginname='user1', displayname='user1', primary_email_address='user1@example.com') - user2 = User(loginname='user2', displayname='user2', primary_email_address='user2@example.com') - db.session.add_all([user0, user1, user2]) - db.session.commit() - self.assertEqual(user0.unix_uid, 10000) - self.assertEqual(user1.unix_uid, 10001) - self.assertEqual(user2.unix_uid, 10002) - db.session.delete(user1) - db.session.commit() - user3 = User(loginname='user3', displayname='user3', primary_email_address='user3@example.com') - db.session.add(user3) - db.session.commit() - self.assertEqual(user3.unix_uid, 10003) - service0 = User(loginname='service0', displayname='service0', primary_email_address='service0@example.com', is_service_user=True) - service1 = User(loginname='service1', displayname='service1', primary_email_address='service1@example.com', is_service_user=True) - db.session.add_all([service0, service1]) - db.session.commit() - self.assertEqual(service0.unix_uid, 19000) - self.assertEqual(service1.unix_uid, 19001) - - def test_unix_uid_generation_overlapping(self): - self.app.config['USER_MIN_UID'] = 10000 - self.app.config['USER_MAX_UID'] = 19999 - self.app.config['USER_SERVICE_MIN_UID'] = 10000 - self.app.config['USER_SERVICE_MAX_UID'] = 19999 - User.query.delete() - db.session.commit() - user0 = User(loginname='user0', displayname='user0', primary_email_address='user0@example.com') - service0 = User(loginname='service0', displayname='service0', primary_email_address='service0@example.com', is_service_user=True) - user1 = User(loginname='user1', displayname='user1', primary_email_address='user1@example.com') - db.session.add_all([user0, service0, user1]) - db.session.commit() - self.assertEqual(user0.unix_uid, 10000) - self.assertEqual(service0.unix_uid, 10001) - self.assertEqual(user1.unix_uid, 10002) - - def test_unix_uid_generation_overflow(self): - self.app.config['USER_MIN_UID'] = 10000 - self.app.config['USER_MAX_UID'] = 10001 - User.query.delete() - db.session.commit() - user0 = User(loginname='user0', displayname='user0', primary_email_address='user0@example.com') - user1 = User(loginname='user1', displayname='user1', primary_email_address='user1@example.com') - db.session.add_all([user0, user1]) - db.session.commit() - self.assertEqual(user0.unix_uid, 10000) - self.assertEqual(user1.unix_uid, 10001) - with self.assertRaises(sqlalchemy.exc.IntegrityError): - user2 = User(loginname='user2', displayname='user2', primary_email_address='user2@example.com') - db.session.add(user2) - db.session.commit() - - def test_init_primary_email_address(self): - user = User(primary_email_address='foobar@example.com') - self.assertEqual(user.primary_email.address, 'foobar@example.com') - self.assertEqual(user.primary_email.verified, True) - self.assertEqual(user.primary_email.user, user) - user = User(primary_email_address='invalid') - self.assertEqual(user.primary_email.address, 'invalid') - self.assertEqual(user.primary_email.verified, True) - self.assertEqual(user.primary_email.user, user) - - def test_set_primary_email_address(self): - user = User() - self.assertFalse(user.set_primary_email_address('invalid')) - self.assertIsNone(user.primary_email) - self.assertEqual(len(user.all_emails), 0) - self.assertTrue(user.set_primary_email_address('foobar@example.com')) - self.assertEqual(user.primary_email.address, 'foobar@example.com') - self.assertEqual(len(user.all_emails), 1) - self.assertFalse(user.set_primary_email_address('invalid')) - self.assertEqual(user.primary_email.address, 'foobar@example.com') - self.assertEqual(len(user.all_emails), 1) - self.assertTrue(user.set_primary_email_address('other@example.com')) - self.assertEqual(user.primary_email.address, 'other@example.com') - self.assertEqual(len(user.all_emails), 2) - self.assertEqual({user.all_emails[0].address, user.all_emails[1].address}, {'foobar@example.com', 'other@example.com'}) - -class TestUserEmailModel(UffdTestCase): - def test_set_address(self): - email = UserEmail() - self.assertFalse(email.set_address('invalid')) - self.assertIsNone(email.address) - self.assertFalse(email.set_address('')) - self.assertFalse(email.set_address('@')) - self.app.config['REMAILER_DOMAIN'] = 'remailer.example.com' - self.assertFalse(email.set_address('foobar@remailer.example.com')) - self.assertFalse(email.set_address('v1-1-testuser@remailer.example.com')) - self.assertFalse(email.set_address('v1-1-testuser @ remailer.example.com')) - self.assertFalse(email.set_address('v1-1-testuser@REMAILER.example.com')) - self.assertFalse(email.set_address('v1-1-testuser@foobar@remailer.example.com')) - self.assertTrue(email.set_address('foobar@example.com')) - self.assertEqual(email.address, 'foobar@example.com') - - def test_verification(self): - email = UserEmail(address='foo@example.com') - self.assertFalse(email.finish_verification('test')) - secret = email.start_verification() - self.assertTrue(email.verification_secret) - self.assertTrue(email.verification_secret.verify(secret)) - self.assertFalse(email.verification_expired) - self.assertFalse(email.finish_verification('test')) - orig_expires = email.verification_expires - email.verification_expires = datetime.datetime.utcnow() - datetime.timedelta(days=1) - self.assertFalse(email.finish_verification(secret)) - email.verification_expires = orig_expires - self.assertTrue(email.finish_verification(secret)) - self.assertFalse(email.verification_secret) - self.assertTrue(email.verification_expired) +from flask import url_for + +from uffd.database import db +from uffd.models import User, UserEmail, Group, Role, Service, ServiceUser + +from tests.utils import dump, UffdTestCase class TestUserViews(UffdTestCase): def setUp(self): @@ -507,138 +359,6 @@ newuser12,newuser12@example.com,{role1.id};{role1.id} roles = sorted([r.name for r in user.roles]) self.assertEqual(roles, ['role1']) -class TestUserCLI(UffdTestCase): - def setUp(self): - super().setUp() - role = Role(name='admin') - role.groups[self.get_admin_group()] = RoleGroup(group=self.get_admin_group()) - db.session.add(role) - db.session.add(Role(name='test')) - db.session.commit() - self.client.__exit__(None, None, None) - - def test_list(self): - result = self.app.test_cli_runner().invoke(args=['user', 'list']) - self.assertEqual(result.exit_code, 0) - - def test_show(self): - result = self.app.test_cli_runner().invoke(args=['user', 'show', 'testuser']) - self.assertEqual(result.exit_code, 0) - result = self.app.test_cli_runner().invoke(args=['user', 'show', 'doesnotexist']) - self.assertEqual(result.exit_code, 1) - - def test_create(self): - result = self.app.test_cli_runner().invoke(args=['user', 'create', 'new user', '--mail', 'foobar@example.com']) # invalid login name - self.assertEqual(result.exit_code, 1) - result = self.app.test_cli_runner().invoke(args=['user', 'create', 'newuser', '--mail', '']) # invalid mail - self.assertEqual(result.exit_code, 1) - result = self.app.test_cli_runner().invoke(args=['user', 'create', 'newuser', '--mail', 'foobar@example.com', '--password', '']) # invalid password - self.assertEqual(result.exit_code, 1) - result = self.app.test_cli_runner().invoke(args=['user', 'create', 'newuser', '--mail', 'foobar@example.com', '--displayname', '']) # invalid display name - self.assertEqual(result.exit_code, 1) - result = self.app.test_cli_runner().invoke(args=['user', 'create', 'newuser', '--mail', 'foobar@example.com', '--add-role', 'doesnotexist']) # unknown role - self.assertEqual(result.exit_code, 1) - result = self.app.test_cli_runner().invoke(args=['user', 'create', 'testuser', '--mail', 'foobar@example.com']) # conflicting name - self.assertEqual(result.exit_code, 1) - result = self.app.test_cli_runner().invoke(args=['user', 'create', 'newuser', '--mail', 'newmail@example.com', - '--displayname', 'New Display Name', '--password', 'newpassword', '--add-role', 'admin']) - self.assertEqual(result.exit_code, 0) - with self.app.test_request_context(): - user = User.query.filter_by(loginname='newuser').first() - self.assertIsNotNone(user) - self.assertEqual(user.primary_email.address, 'newmail@example.com') - self.assertEqual(user.displayname, 'New Display Name') - self.assertTrue(user.password.verify('newpassword')) - self.assertEqual(user.roles, Role.query.filter_by(name='admin').all()) - self.assertIn(self.get_admin_group(), user.groups) - - def test_update(self): - result = self.app.test_cli_runner().invoke(args=['user', 'update', 'doesnotexist', '--displayname', 'foo']) - self.assertEqual(result.exit_code, 1) - result = self.app.test_cli_runner().invoke(args=['user', 'update', 'testuser', '--mail', '']) # invalid mail - self.assertEqual(result.exit_code, 1) - result = self.app.test_cli_runner().invoke(args=['user', 'update', 'testuser', '--password', '']) # invalid password - self.assertEqual(result.exit_code, 1) - result = self.app.test_cli_runner().invoke(args=['user', 'update', 'testuser', '--displayname', '']) # invalid display name - self.assertEqual(result.exit_code, 1) - result = self.app.test_cli_runner().invoke(args=['user', 'update', 'testuser', '--remove-role', 'doesnotexist']) - self.assertEqual(result.exit_code, 1) - result = self.app.test_cli_runner().invoke(args=['user', 'update', 'testuser', '--mail', 'newmail@example.com', - '--displayname', 'New Display Name', '--password', 'newpassword']) - self.assertEqual(result.exit_code, 0) - with self.app.test_request_context(): - user = User.query.filter_by(loginname='testuser').first() - self.assertIsNotNone(user) - self.assertEqual(user.primary_email.address, 'newmail@example.com') - self.assertEqual(user.displayname, 'New Display Name') - self.assertTrue(user.password.verify('newpassword')) - result = self.app.test_cli_runner().invoke(args=['user', 'update', 'testuser', '--add-role', 'admin', '--add-role', 'test']) - self.assertEqual(result.exit_code, 0) - with self.app.test_request_context(): - user = User.query.filter_by(loginname='testuser').first() - self.assertEqual(set(user.roles), {Role.query.filter_by(name='admin').one(), Role.query.filter_by(name='test').one()}) - self.assertIn(self.get_admin_group(), user.groups) - result = self.app.test_cli_runner().invoke(args=['user', 'update', 'testuser', '--remove-role', 'admin']) - self.assertEqual(result.exit_code, 0) - with self.app.test_request_context(): - user = User.query.filter_by(loginname='testuser').first() - self.assertEqual(user.roles, Role.query.filter_by(name='test').all()) - self.assertNotIn(self.get_admin_group(), user.groups) - result = self.app.test_cli_runner().invoke(args=['user', 'update', 'testuser', '--clear-roles', '--add-role', 'admin']) - self.assertEqual(result.exit_code, 0) - with self.app.test_request_context(): - user = User.query.filter_by(loginname='testuser').first() - self.assertEqual(user.roles, Role.query.filter_by(name='admin').all()) - self.assertIn(self.get_admin_group(), user.groups) - - def test_delete(self): - with self.app.test_request_context(): - self.assertIsNotNone(User.query.filter_by(loginname='testuser').first()) - result = self.app.test_cli_runner().invoke(args=['user', 'delete', 'testuser']) - self.assertEqual(result.exit_code, 0) - with self.app.test_request_context(): - self.assertIsNone(User.query.filter_by(loginname='testuser').first()) - result = self.app.test_cli_runner().invoke(args=['user', 'delete', 'doesnotexist']) - self.assertEqual(result.exit_code, 1) - -class TestGroupModel(UffdTestCase): - def test_unix_gid_generation(self): - self.app.config['GROUP_MIN_GID'] = 20000 - self.app.config['GROUP_MAX_GID'] = 49999 - Group.query.delete() - db.session.commit() - group0 = Group(name='group0', description='group0') - group1 = Group(name='group1', description='group1') - group2 = Group(name='group2', description='group2') - db.session.add_all([group0, group1, group2]) - db.session.commit() - self.assertEqual(group0.unix_gid, 20000) - self.assertEqual(group1.unix_gid, 20001) - self.assertEqual(group2.unix_gid, 20002) - db.session.delete(group1) - db.session.commit() - group3 = Group(name='group3', description='group3') - db.session.add(group3) - db.session.commit() - self.assertEqual(group3.unix_gid, 20003) - - def test_unix_gid_generation(self): - self.app.config['GROUP_MIN_GID'] = 20000 - self.app.config['GROUP_MAX_GID'] = 20001 - Group.query.delete() - db.session.commit() - group0 = Group(name='group0', description='group0') - group1 = Group(name='group1', description='group1') - db.session.add_all([group0, group1]) - db.session.commit() - self.assertEqual(group0.unix_gid, 20000) - self.assertEqual(group1.unix_gid, 20001) - db.session.commit() - with self.assertRaises(sqlalchemy.exc.IntegrityError): - group2 = Group(name='group2', description='group2') - db.session.add(group2) - db.session.commit() - class TestGroupViews(UffdTestCase): def setUp(self): super().setUp() @@ -772,56 +492,3 @@ class TestGroupViews(UffdTestCase): self.assertEqual(r.status_code, 200) self.assertIsNone(Group.query.get(group1_id)) self.assertIsNotNone(Group.query.get(group2_id)) - -class TestGroupCLI(UffdTestCase): - def setUp(self): - super().setUp() - self.client.__exit__(None, None, None) - - def test_list(self): - result = self.app.test_cli_runner().invoke(args=['group', 'list']) - self.assertEqual(result.exit_code, 0) - - def test_show(self): - result = self.app.test_cli_runner().invoke(args=['group', 'show', 'users']) - self.assertEqual(result.exit_code, 0) - result = self.app.test_cli_runner().invoke(args=['group', 'show', 'doesnotexist']) - self.assertEqual(result.exit_code, 1) - - def test_create(self): - result = self.app.test_cli_runner().invoke(args=['group', 'create', 'users']) # Duplicate name - self.assertEqual(result.exit_code, 1) - result = self.app.test_cli_runner().invoke(args=['group', 'create', 'new group']) - self.assertEqual(result.exit_code, 1) - result = self.app.test_cli_runner().invoke(args=['group', 'create', 'newgroup', '--description', 'A new group']) - self.assertEqual(result.exit_code, 0) - with self.app.test_request_context(): - group = Group.query.filter_by(name='newgroup').first() - self.assertIsNotNone(group) - self.assertEqual(group.description, 'A new group') - - def test_update(self): - result = self.app.test_cli_runner().invoke(args=['group', 'update', 'doesnotexist', '--description', 'foo']) - self.assertEqual(result.exit_code, 1) - result = self.app.test_cli_runner().invoke(args=['group', 'update', 'users', '--description', 'New description']) - self.assertEqual(result.exit_code, 0) - with self.app.test_request_context(): - group = Group.query.filter_by(name='users').first() - self.assertEqual(group.description, 'New description') - - def test_update_without_description(self): - result = self.app.test_cli_runner().invoke(args=['group', 'update', 'users']) # Should not change anything - self.assertEqual(result.exit_code, 0) - with self.app.test_request_context(): - group = Group.query.filter_by(name='users').first() - self.assertEqual(group.description, 'Base group for all users') - - def test_delete(self): - with self.app.test_request_context(): - self.assertIsNotNone(Group.query.filter_by(name='users').first()) - result = self.app.test_cli_runner().invoke(args=['group', 'delete', 'users']) - self.assertEqual(result.exit_code, 0) - with self.app.test_request_context(): - self.assertIsNone(Group.query.filter_by(name='users').first()) - result = self.app.test_cli_runner().invoke(args=['group', 'delete', 'doesnotexist']) - self.assertEqual(result.exit_code, 1)