diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 0000000..dd99454 --- /dev/null +++ b/.coveragerc @@ -0,0 +1,10 @@ +[report] +exclude_lines = + pragma: no cover + def __repr__ + if self.debug: + if settings.DEBUG: + raise AssertionError + raise NotImplementedError +omit = + adjutant/wsgi.py diff --git a/adjutant/actions/v1/base.py b/adjutant/actions/v1/base.py index d8e99ce..e262e4e 100644 --- a/adjutant/actions/v1/base.py +++ b/adjutant/actions/v1/base.py @@ -367,7 +367,7 @@ class ProjectMixin(ResourceMixin): parent = id_manager.get_project(self.parent_id) if not parent: self.add_note("Parent id: '%s' does not exist." % - self.project_name) + self.parent_id) return False return True diff --git a/adjutant/actions/v1/projects.py b/adjutant/actions/v1/projects.py index 1c80b80..bf2ec60 100644 --- a/adjutant/actions/v1/projects.py +++ b/adjutant/actions/v1/projects.py @@ -21,7 +21,6 @@ from adjutant.actions.v1.base import ( BaseAction, UserNameAction, UserMixin, ProjectMixin) -# TODO(adriant): Write tests for this action. class NewProjectAction(BaseAction, ProjectMixin, UserMixin): """ Creates a new project for the current keystone_user. @@ -100,7 +99,7 @@ class NewProjectAction(BaseAction, ProjectMixin, UserMixin): self.add_note( ("Error: '%s' while adding roles %s " "to user '%s' on project '%s'") % - (e, self.username, default_roles, project_id)) + (e, default_roles, user.name, project_id)) raise # put user_id into action cache: @@ -108,7 +107,7 @@ class NewProjectAction(BaseAction, ProjectMixin, UserMixin): self.set_cache('user_id', user.id) self.add_note(("Existing user '%s' attached to project %s" + " with roles: %s") - % (self.username, project_id, + % (user.name, project_id, default_roles)) def _submit(self, token_data): @@ -208,6 +207,9 @@ class NewProjectWithUserAction(UserNameAction, ProjectMixin, UserMixin): and if the user doesn't exist, create it right away. An existing user automatically gets added to the new project. """ + if not self.valid: + return + project_id = self.get_cache('project_id') if project_id: self.action.task.cache['project_id'] = project_id diff --git a/adjutant/actions/v1/tests/test_project_actions.py b/adjutant/actions/v1/tests/test_project_actions.py index ae68636..edc2ace 100644 --- a/adjutant/actions/v1/tests/test_project_actions.py +++ b/adjutant/actions/v1/tests/test_project_actions.py @@ -18,7 +18,8 @@ from django.test.utils import override_settings import mock from adjutant.actions.v1.projects import ( - NewProjectWithUserAction, AddDefaultUsersToProjectAction) + NewProjectWithUserAction, AddDefaultUsersToProjectAction, + NewProjectAction) from adjutant.api.models import Task from adjutant.api.v1 import tests from adjutant.api.v1.tests import (FakeManager, setup_temp_cache, @@ -66,9 +67,15 @@ class ProjectActionTests(TestCase): {'project_id': 'project_id_1', 'user_id': 'user_id_1', 'user_state': 'default'}) + action.post_approve() + self.assertEquals(action.valid, True) + token_data = {'password': '123456'} action.submit(token_data) self.assertEquals(action.valid, True) + self.assertEquals( + tests.temp_cache['users']["user_id_1"].name, + 'test@example.com') self.assertEquals( tests.temp_cache['users']["user_id_1"].email, 'test@example.com') @@ -257,6 +264,129 @@ class ProjectActionTests(TestCase): sorted(['_member_', 'project_admin', 'project_mod', 'heat_stack_owner'])) + @override_settings(USERNAME_IS_EMAIL=False) + def test_new_project_user_nonmatching_email(self): + """ + Attempts to create a new project and a new user, where there is + a user with the same name but different email address + """ + + user = mock.Mock() + user.id = "test_id" + user.name = "test_user" + user.email = "different@example.com" + user.domain = 'default' + + setup_temp_cache({}, {user.id: user}) + + task = Task.objects.create( + ip_address="0.0.0.0", + keystone_user={} + ) + + data = { + 'domain_id': 'default', + 'parent_id': None, + 'username': 'test_user', + 'email': 'test@example.com', + 'project_name': 'test_project', + } + + action = NewProjectWithUserAction(data, task=task, order=1) + + action.pre_approve() + self.assertEquals(action.valid, False) + + action.post_approve() + self.assertEquals(action.valid, False) + + self.assertEquals( + tests.temp_cache['projects'].get('test_project'), None) + + token_data = {'password': '123456'} + action.submit(token_data) + self.assertEquals(action.valid, False) + + def test_new_project_project_removed(self): + """ + Tests when the project is removed after the post approve step. + """ + + setup_temp_cache({}, {}) + + task = Task.objects.create( + ip_address="0.0.0.0", + keystone_user={} + ) + + data = { + 'domain_id': 'default', + 'parent_id': None, + 'email': 'test@example.com', + 'project_name': 'test_project', + } + + action = NewProjectWithUserAction(data, task=task, order=1) + + action.pre_approve() + self.assertEquals(action.valid, True) + + action.post_approve() + self.assertEquals(action.valid, True) + self.assertEquals( + tests.temp_cache['projects']['test_project'].name, + 'test_project') + self.assertEquals( + task.cache, + {'project_id': 'project_id_1', 'user_id': 'user_id_1', + 'user_state': 'default'}) + + tests.temp_cache['projects'] = {} + + token_data = {'password': '123456'} + action.submit(token_data) + self.assertEquals(action.valid, False) + + def test_new_project_user_removed(self): + """ + Tests when the user is removed after the post approve step. + """ + + setup_temp_cache({}, {}) + + task = Task.objects.create( + ip_address="0.0.0.0", + keystone_user={} + ) + + data = { + 'domain_id': 'default', + 'parent_id': None, + 'email': 'test@example.com', + 'project_name': 'test_project', + } + + action = NewProjectWithUserAction(data, task=task, order=1) + + action.pre_approve() + self.assertEquals(action.valid, True) + + action.post_approve() + self.assertEquals(action.valid, True) + self.assertEquals( + tests.temp_cache['projects']['test_project'].name, + 'test_project') + self.assertEquals( + task.cache, + {'project_id': 'project_id_1', 'user_id': 'user_id_1', + 'user_state': 'default'}) + + tests.temp_cache['users'] = {} + + token_data = {'password': '123456'} + action.submit(token_data) + self.assertEquals(action.valid, False) + def test_new_project_disabled_user(self): """ Create a project for a user that is disabled. @@ -454,59 +584,59 @@ class ProjectActionTests(TestCase): action.post_approve() self.assertEquals(action.valid, False) - @override_settings(USERNAME_IS_EMAIL=False) - def test_new_project_email_not_username(self): - """ - Base case, no project, no user. + @override_settings(USERNAME_IS_EMAIL=False) + def test_new_project_email_not_username(self): + """ + Base case, no project, no user. - Project and user created at post_approve step, - user password at submit step. - """ + Project and user created at post_approve step, + user password at submit step. + """ - setup_temp_cache({}, {}) + setup_temp_cache({}, {}) - task = Task.objects.create( - ip_address="0.0.0.0", - keystone_user={} - ) + task = Task.objects.create( + ip_address="0.0.0.0", + keystone_user={} + ) - data = { - 'domain_id': 'default', - 'parent_id': None, - 'email': 'test@example.com', - 'username': 'test_user', - 'project_name': 'test_project', - } + data = { + 'domain_id': 'default', + 'parent_id': None, + 'email': 'test@example.com', + 'username': 'test_user', + 'project_name': 'test_project', + } - action = NewProjectWithUserAction(data, task=task, order=1) + action = NewProjectWithUserAction(data, task=task, order=1) - action.pre_approve() - self.assertEquals(action.valid, True) + action.pre_approve() + self.assertEquals(action.valid, True) - action.post_approve() - self.assertEquals(action.valid, True) - self.assertEquals( - tests.temp_cache['projects']['test_project'].name, - 'test_project') - self.assertEquals( - task.cache, - {'project_id': 'project_id_1', 'user_id': 'user_id_1', - 'user_state': 'default'}) + action.post_approve() + self.assertEquals(action.valid, True) + self.assertEquals( + tests.temp_cache['projects']['test_project'].name, + 'test_project') + self.assertEquals( + task.cache, + {'project_id': 'project_id_1', 'user_id': 'user_id_1', + 'user_state': 'default'}) - token_data = {'password': '123456'} - action.submit(token_data) - self.assertEquals(action.valid, True) - self.assertEquals( - tests.temp_cache['users']["user_id_1"].email, - 'test@example.com') - self.assertEquals( - tests.temp_cache['users']["user_id_1"].name, - 'test_user') - project = tests.temp_cache['projects']['test_project'] - self.assertEquals( - sorted(project.roles["user_id_1"]), - sorted(['_member_', 'project_admin', - 'project_mod', 'heat_stack_owner'])) + token_data = {'password': '123456'} + action.submit(token_data) + self.assertEquals(action.valid, True) + self.assertEquals( + tests.temp_cache['users']["user_id_1"].email, + 'test@example.com') + self.assertEquals( + tests.temp_cache['users']["user_id_1"].name, + 'test_user') + project = tests.temp_cache['projects']['test_project'] + self.assertEquals( + sorted(project.roles["user_id_1"]), + sorted(['_member_', 'project_admin', + 'project_mod', 'heat_stack_owner'])) @modify_dict_settings(DEFAULT_ACTION_SETTINGS={ 'key_list': ['AddDefaultUsersToProjectAction'], @@ -615,3 +745,241 @@ class ProjectActionTests(TestCase): project = tests.temp_cache['projects']['test_project'] self.assertEquals(project.roles['user_id_0'], ['admin']) + + def test_new_project_action(self): + """ + Tests the new project action for an existing user. + """ + + user = mock.Mock() + user.id = "test_user_id" + user.name = "test_user" + user.domain = 'default' + + project = mock.Mock() + project.id = "parent_project" + project.domain = "default" + + setup_temp_cache({project.id: project}, {user.id: user}) + + task = Task.objects.create( + ip_address="0.0.0.0", + keystone_user={"user_id": "test_user_id", + "project_id": "parent_project", + "project_domain_id": 'default'}) + + data = { + 'domain_id': 'default', + 'parent_id': "parent_project", + 'project_name': 'test_project', + } + + action = NewProjectAction(data, task=task, order=1) + + action.pre_approve() + self.assertEquals(action.valid, True) + + action.post_approve() + self.assertEquals(action.valid, True) + self.assertEquals( + tests.temp_cache['projects']['test_project'].name, + 'test_project') + + self.assertEquals( + tests.temp_cache['projects']['test_project'].parent, + 'parent_project') + + project = tests.temp_cache['projects']['test_project'] + self.assertEquals( + sorted(project.roles["test_user_id"]), + sorted(['_member_', 'project_admin', + 'project_mod', 'heat_stack_owner'])) + + action.submit({}) + self.assertEquals(action.valid, True) + + def test_new_project_action_rerun_post_approve(self): + """ + Tests the new project action for an existing user. + """ + + user = mock.Mock() + user.id = "test_user_id" + user.name = "test_user" + user.domain = 'default' + + project = mock.Mock() + project.id = "parent_project" + project.domain = "default" + + setup_temp_cache({project.id: project}, {user.id: user}) + + task = Task.objects.create( + ip_address="0.0.0.0", + keystone_user={"user_id": "test_user_id", + "project_id": "parent_project", + "project_domain_id": 'default'}) + + data = { + 'domain_id': 'default', + 'parent_id': "parent_project", + 'project_name': 'test_project', + } + + action = NewProjectAction(data, task=task, order=1) + + action.pre_approve() + self.assertEquals(action.valid, True) + + action.post_approve() + self.assertEquals(action.valid, True) + self.assertEquals( + tests.temp_cache['projects']['test_project'].name, + 'test_project') + + self.assertEquals( + tests.temp_cache['projects']['test_project'].parent, + 'parent_project') + + action.post_approve() + # Nothing should change + self.assertEquals(action.valid, True) + self.assertEquals( + tests.temp_cache['projects']['test_project'].name, + 'test_project') + + self.assertEquals( + tests.temp_cache['projects']['test_project'].parent, + 'parent_project') + + project = tests.temp_cache['projects']['test_project'] + self.assertEquals( + sorted(project.roles["test_user_id"]), + sorted(['_member_', 'project_admin', + 'project_mod', 'heat_stack_owner'])) + + action.submit({}) + self.assertEquals(action.valid, True) + + def test_new_project_action_wrong_parent_id(self): + """ + New project action where specifed parent id is not the same + as the current user's project id + """ + + user = mock.Mock() + user.id = "test_user_id" + user.name = "test_user" + user.domain = 'default' + + project = mock.Mock() + project.id = "parent_project" + project.domain = "default" + + setup_temp_cache({project.id: project}, {user.id: user}) + + task = Task.objects.create( + ip_address="0.0.0.0", + keystone_user={"user_id": "test_user_id", + "project_id": "not_parent_project", + "project_domain_id": 'default'}) + + data = { + 'domain_id': 'default', + 'parent_id': "parent_project", + 'project_name': 'test_project', + } + + action = NewProjectAction(data, task=task, order=1) + + action.pre_approve() + self.assertEquals(action.valid, False) + + action.post_approve() + self.assertEquals(action.valid, False) + + action.submit({}) + self.assertEquals(action.valid, False) + + def test_new_project_action_wrong_domain_id(self): + """ + New project action where specifed domain id is not the same + as the current user's domain id + """ + + user = mock.Mock() + user.id = "test_user_id" + user.name = "test_user" + user.domain = 'default' + + project = mock.Mock() + project.id = "parent_project" + project.domain = "default" + + setup_temp_cache({project.id: project}, {user.id: user}) + + task = Task.objects.create( + ip_address="0.0.0.0", + keystone_user={"user_id": "test_user_id", + "project_id": "parent_project", + "project_domain_id": 'default'}) + + data = { + 'domain_id': 'notdefault', + 'parent_id': "parent_project", + 'project_name': 'test_project', + } + + action = NewProjectAction(data, task=task, order=1) + + action.pre_approve() + self.assertEquals(action.valid, False) + + action.post_approve() + self.assertEquals(action.valid, False) + + action.submit({}) + self.assertEquals(action.valid, False) + + def test_new_project_action_no_parent_id(self): + """ + New project action where there is no specified parent id + """ + + user = mock.Mock() + user.id = "test_user_id" + user.name = "test_user" + user.domain = 'default' + + project = mock.Mock() + project.id = "parent_project" + project.domain = "default" + + setup_temp_cache({project.id: project}, {user.id: user}) + + task = Task.objects.create( + ip_address="0.0.0.0", + keystone_user={"user_id": "test_user_id", + "project_id": "parent_project", + "project_domain_id": 'default'}) + + data = { + 'domain_id': 'default', + 'parent_id': None, + 'project_name': 'test_project', + } + + action = NewProjectAction(data, task=task, order=1) + + action.pre_approve() + self.assertEquals(action.valid, True) + + action.post_approve() + self.assertEquals(action.valid, True) + + self.assertEquals( + tests.temp_cache['projects']['test_project'].parent, + 'default') + + action.submit({}) + self.assertEquals(action.valid, True) diff --git a/adjutant/api/v1/tests/__init__.py b/adjutant/api/v1/tests/__init__.py index 5caaa6d..ec906c7 100644 --- a/adjutant/api/v1/tests/__init__.py +++ b/adjutant/api/v1/tests/__init__.py @@ -228,6 +228,7 @@ class FakeManager(object): def create_project(self, project_name, created_on, parent=None, domain='default', p_id=None): parent = self._project_from_id(parent) + domain = self._domain_from_id(domain) global temp_cache project = mock.Mock() if p_id: @@ -237,10 +238,10 @@ class FakeManager(object): project.id = "project_id_%s" % int(temp_cache['i']) project.name = project_name if parent: - project.parent = parent + project.parent = parent.id else: - project.parent = domain - project.domain = domain + project.parent = domain.id + project.domain = domain.id project.roles = {} temp_cache['projects'][project_name] = project return project