From 1e75d348f962686d71671bbf6427c623ed99d8dd Mon Sep 17 00:00:00 2001 From: adriant Date: Thu, 4 Feb 2016 18:19:05 +1300 Subject: [PATCH] Addressing 4.3 "Admin User Password Reset Permitted" * switching to the keystone v3 api * various fixes to tests after user_store changes * minor pep8 cleanup Change-Id: Ie40cfcae0e392aef9a3e92da4acd4f0a21a627b0 --- conf/conf.yaml | 8 +- stacktask/actions/models.py | 33 +++++-- stacktask/actions/openstack_clients.py | 13 +-- stacktask/actions/tenant_setup/tests.py | 18 ++-- stacktask/actions/tests.py | 96 ++++++++++---------- stacktask/actions/user_store.py | 65 ++++++++++--- stacktask/api/v1/openstack.py | 18 ++-- stacktask/api/v1/tests/__init__.py | 69 ++++++++++---- stacktask/api/v1/tests/test_api_admin.py | 60 +++++++++--- stacktask/api/v1/tests/test_api_openstack.py | 8 +- stacktask/api/v1/tests/test_api_taskview.py | 26 +++--- stacktask/test_settings.py | 6 +- 12 files changed, 281 insertions(+), 139 deletions(-) diff --git a/conf/conf.yaml b/conf/conf.yaml index 0c17515..8a0bcca 100644 --- a/conf/conf.yaml +++ b/conf/conf.yaml @@ -51,7 +51,8 @@ KEYSTONE: username: admin password: openstack project_name: admin - auth_url: http://localhost:5000/v2.0 + # MUST BE V3 API: + auth_url: http://localhost:5000/v3 DEFAULT_REGION: RegionOne TOKEN_SUBMISSION_URL: http://192.168.122.160:8080/token/ @@ -174,11 +175,14 @@ TASK_SETTINGS: ACTION_SETTINGS: NewUser: allowed_roles: - - Member - project_owner - project_mod - heat_stack_owner - _member_ + ResetUser: + blacklisted_roles: + - Admin + - admin DefaultProjectResources: RegionOne: network_name: somenetwork diff --git a/stacktask/actions/models.py b/stacktask/actions/models.py index d5a415a..03cc357 100644 --- a/stacktask/actions/models.py +++ b/stacktask/actions/models.py @@ -12,13 +12,16 @@ # License for the specific language governing permissions and limitations # under the License. +from logging import getLogger + +from django.conf import settings from django.db import models from django.utils import timezone -from stacktask.actions import user_store -from stacktask.actions import serializers -from django.conf import settings + from jsonfield import JSONField -from logging import getLogger + +from stacktask.actions import serializers +from stacktask.actions import user_store class Action(models.Model): @@ -475,8 +478,8 @@ class NewProject(UserNameAction): def _submit(self, token_data): """ The submit action is prformed when a token is submitted. - This is done for a user account only, and so should now only set up the user, - not the project, which was done in approve. + This is done for a user account only, and so should now only + set up the user, not the project, which was done in approve. """ id_manager = user_store.IdentityManager() @@ -552,13 +555,25 @@ class ResetUser(UserNameAction): 'email' ] + blacklist = settings.ACTION_SETTINGS.get( + 'ResetUser', {}).get("blacklisted_roles", {}) + def _validate(self): id_manager = user_store.IdentityManager() user = id_manager.find_user(self.username) if user: - if user.email == self.email: + roles = id_manager.get_all_roles(user) + + user_roles = [] + for project, roles in roles.iteritems(): + user_roles.extend(role.name for role in roles) + + if set(self.blacklist) & set(user_roles): + valid = False + self.add_note('Cannot reset users with blacklisted roles.') + elif user.email == self.email: valid = True self.action.need_token = True self.set_token_fields(["password"]) @@ -693,7 +708,6 @@ class EditUserRoles(UserIdAction): if self.action.state == "default": try: - #user = id_manager.find_user(self.username) user = self._get_target_user() roles = [] @@ -738,7 +752,8 @@ class EditUserRoles(UserIdAction): % (self.user_id, self.roles, self.project_id)) -# Update settings dict with tuples in the format: (, ) +# Update settings dict with tuples in the format: +# (, ) def register_action_class(action_class, serializer_class): data = {} data[action_class.__name__] = (action_class, serializer_class) diff --git a/stacktask/actions/openstack_clients.py b/stacktask/actions/openstack_clients.py index 644fd68..877cce7 100644 --- a/stacktask/actions/openstack_clients.py +++ b/stacktask/actions/openstack_clients.py @@ -12,19 +12,20 @@ # License for the specific language governing permissions and limitations # under the License. -from keystoneclient.v2_0 import client -from neutronclient.v2_0 import client as neutron_client from django.conf import settings +from keystoneclient.v3 import client as client_v3 + +from neutronclient.v2_0 import client as neutron_client + def get_keystoneclient(): - # TODO(Adriant): Add region support. - auth = client.Client( + auth = client_v3.Client( username=settings.KEYSTONE['username'], password=settings.KEYSTONE['password'], - tenant_name=settings.KEYSTONE['project_name'], - auth_url=settings.KEYSTONE['auth_url'], + project_name=settings.KEYSTONE['project_name'], + auth_url=settings.KEYSTONE['auth_url_v3'], region_name=settings.DEFAULT_REGION ) return auth diff --git a/stacktask/actions/tenant_setup/tests.py b/stacktask/actions/tenant_setup/tests.py index 971a575..943f128 100644 --- a/stacktask/actions/tenant_setup/tests.py +++ b/stacktask/actions/tenant_setup/tests.py @@ -13,13 +13,15 @@ # under the License. from django.test import TestCase -from stacktask.api.models import Task -from stacktask.api.v1.tests import FakeManager, setup_temp_cache -from stacktask.api.v1 import tests -from stacktask.actions.tenant_setup.models import ( - DefaultProjectResources, AddAdminToProject) + import mock +from stacktask.actions.tenant_setup.models import ( + AddAdminToProject, DefaultProjectResources) +from stacktask.api.models import Task +from stacktask.api.v1 import tests +from stacktask.api.v1.tests import FakeManager, setup_temp_cache + neutron_cache = {} @@ -263,7 +265,7 @@ class TenantSetupActionTests(TestCase): self.assertEquals(action.valid, True) project = tests.temp_cache['projects']['test_project'] - self.assertEquals(project.roles['admin'], ['admin']) + self.assertEquals(project.roles['user_id_0'], ['admin']) @mock.patch('stacktask.actions.tenant_setup.models.IdentityManager', FakeManager) @@ -292,10 +294,10 @@ class TenantSetupActionTests(TestCase): self.assertEquals(action.valid, True) project = tests.temp_cache['projects']['test_project'] - self.assertEquals(project.roles['admin'], ['admin']) + self.assertEquals(project.roles['user_id_0'], ['admin']) action.post_approve() self.assertEquals(action.valid, True) project = tests.temp_cache['projects']['test_project'] - self.assertEquals(project.roles['admin'], ['admin']) + self.assertEquals(project.roles['user_id_0'], ['admin']) diff --git a/stacktask/actions/tests.py b/stacktask/actions/tests.py index 0a95a2a..fe2f8e7 100644 --- a/stacktask/actions/tests.py +++ b/stacktask/actions/tests.py @@ -13,13 +13,15 @@ # under the License. from django.test import TestCase -from stacktask.api.models import Task -from stacktask.api.v1.tests import FakeManager, setup_temp_cache -from stacktask.api.v1 import tests -from stacktask.actions.models import ( - NewUser, NewProject, ResetUser, EditUserRoles) + import mock +from stacktask.actions.models import ( + EditUserRoles, NewProject, NewUser, ResetUser) +from stacktask.api.models import Task +from stacktask.api.v1 import tests +from stacktask.api.v1.tests import FakeManager, setup_temp_cache + class ActionTests(TestCase): @@ -60,14 +62,15 @@ class ActionTests(TestCase): action.submit(token_data) self.assertEquals(action.valid, True) self.assertEquals(len(tests.temp_cache['users']), 2) + # The new user id in this case will be "user_id_1" self.assertEquals( - tests.temp_cache['users']['test@example.com'].email, + tests.temp_cache['users']["user_id_1"].email, 'test@example.com') self.assertEquals( - tests.temp_cache['users']['test@example.com'].password, + tests.temp_cache['users']["user_id_1"].password, '123456') - self.assertEquals(project.roles['test@example.com'], ['Member']) + self.assertEquals(project.roles["user_id_1"], ['Member']) @mock.patch('stacktask.actions.models.user_store.IdentityManager', FakeManager) @@ -82,10 +85,10 @@ class ActionTests(TestCase): user = mock.Mock() user.id = 'user_id' - user.username = "test@example.com" + user.name = "test@example.com" user.email = "test@example.com" - setup_temp_cache({'test_project': project}, {user.username: user}) + setup_temp_cache({'test_project': project}, {user.id: user}) task = Task.objects.create( ip_address="0.0.0.0", keystone_user={ @@ -110,7 +113,7 @@ class ActionTests(TestCase): action.submit(token_data) self.assertEquals(action.valid, True) - self.assertEquals(project.roles[user.username], ['Member']) + self.assertEquals(project.roles[user.id], ['Member']) @mock.patch('stacktask.actions.models.user_store.IdentityManager', FakeManager) @@ -124,15 +127,15 @@ class ActionTests(TestCase): user = mock.Mock() user.id = 'user_id' - user.username = "test@example.com" + user.name = "test@example.com" user.email = "test@example.com" project = mock.Mock() project.id = 'test_project_id' project.name = 'test_project' - project.roles = {user.username: ['Member']} + project.roles = {user.id: ['Member']} - setup_temp_cache({'test_project': project}, {user.username: user}) + setup_temp_cache({'test_project': project}, {user.id: user}) task = Task.objects.create( ip_address="0.0.0.0", keystone_user={ @@ -158,7 +161,7 @@ class ActionTests(TestCase): action.submit(token_data) self.assertEquals(action.valid, True) - self.assertEquals(project.roles[user.username], ['Member']) + self.assertEquals(project.roles[user.id], ['Member']) @mock.patch('stacktask.actions.models.user_store.IdentityManager', FakeManager) @@ -192,8 +195,6 @@ class ActionTests(TestCase): action.submit(token_data) self.assertEquals(action.valid, False) - self.assertEquals('admin' in tests.temp_cache['users'], True) - @mock.patch('stacktask.actions.models.user_store.IdentityManager', FakeManager) def test_new_project(self): @@ -226,19 +227,18 @@ class ActionTests(TestCase): self.assertEquals( tests.temp_cache['projects']['test_project'].name, 'test_project') - self.assertEquals('admin' in tests.temp_cache['users'], True) - self.assertEquals(task.cache, {'project_id': 2}) + self.assertEquals(task.cache, {'project_id': "project_id_1"}) token_data = {'password': '123456'} action.submit(token_data) self.assertEquals(action.valid, True) - + print tests.temp_cache['users'] self.assertEquals( - tests.temp_cache['users']['test@example.com'].email, + tests.temp_cache['users']["user_id_1"].email, 'test@example.com') project = tests.temp_cache['projects']['test_project'] self.assertEquals( - sorted(project.roles['test@example.com']), + sorted(project.roles["user_id_1"]), sorted(['Member', '_member_', 'project_owner', 'project_mod', 'heat_stack_owner'])) @@ -272,27 +272,25 @@ class ActionTests(TestCase): self.assertEquals( tests.temp_cache['projects']['test_project'].name, 'test_project') - self.assertEquals('admin' in tests.temp_cache['users'], True) - self.assertEquals(task.cache, {'project_id': 2}) + self.assertEquals(task.cache, {'project_id': "project_id_1"}) action.post_approve() self.assertEquals(action.valid, True) self.assertEquals( tests.temp_cache['projects']['test_project'].name, 'test_project') - self.assertEquals('admin' in tests.temp_cache['users'], True) - self.assertEquals(task.cache, {'project_id': 2}) + self.assertEquals(task.cache, {'project_id': "project_id_1"}) token_data = {'password': '123456'} action.submit(token_data) self.assertEquals(action.valid, True) self.assertEquals( - tests.temp_cache['users']['test@example.com'].email, + tests.temp_cache['users']["user_id_1"].email, 'test@example.com') project = tests.temp_cache['projects']['test_project'] self.assertEquals( - sorted(project.roles['test@example.com']), + sorted(project.roles["user_id_1"]), sorted(['Member', '_member_', 'project_owner', 'project_mod', 'heat_stack_owner'])) @@ -305,10 +303,10 @@ class ActionTests(TestCase): user = mock.Mock() user.id = 'user_id' - user.username = "test@example.com" + user.name = "test@example.com" user.email = "test@example.com" - setup_temp_cache({}, {user.username: user}) + setup_temp_cache({}, {user.id: user}) task = Task.objects.create( ip_address="0.0.0.0", keystone_user={ @@ -330,18 +328,18 @@ class ActionTests(TestCase): self.assertEquals( tests.temp_cache['projects']['test_project'].name, 'test_project') - self.assertEquals(task.cache, {'project_id': 2}) + self.assertEquals(task.cache, {'project_id': "project_id_1"}) token_data = {'password': '123456'} action.submit(token_data) self.assertEquals(action.valid, True) self.assertEquals( - tests.temp_cache['users']['test@example.com'].email, + tests.temp_cache['users'][user.id].email, 'test@example.com') project = tests.temp_cache['projects']['test_project'] self.assertEquals( - sorted(project.roles['test@example.com']), + sorted(project.roles[user.id]), sorted(['Member', '_member_', 'project_owner', 'project_mod', 'heat_stack_owner'])) @@ -386,11 +384,11 @@ class ActionTests(TestCase): user = mock.Mock() user.id = 'user_id' - user.username = "test@example.com" + user.name = "test@example.com" user.email = "test@example.com" user.password = "gibberish" - setup_temp_cache({}, {user.username: user}) + setup_temp_cache({}, {user.id: user}) task = Task.objects.create( ip_address="0.0.0.0", keystone_user={ @@ -415,7 +413,7 @@ class ActionTests(TestCase): self.assertEquals(action.valid, True) self.assertEquals( - tests.temp_cache['users']['test@example.com'].password, + tests.temp_cache['users'][user.id].password, '123456') @mock.patch('stacktask.actions.models.user_store.IdentityManager', @@ -462,7 +460,7 @@ class ActionTests(TestCase): user = mock.Mock() user.id = 'user_id' - user.username = "test@example.com" + user.name = "test@example.com" user.email = "test@example.com" setup_temp_cache({'test_project': project}, {user.id: user}) @@ -491,8 +489,8 @@ class ActionTests(TestCase): action.submit(token_data) self.assertEquals(action.valid, True) - self.assertEquals(len(project.roles[user.username]), 2) - self.assertEquals(set(project.roles[user.username]), + self.assertEquals(len(project.roles[user.id]), 2) + self.assertEquals(set(project.roles[user.id]), set(['Member', 'project_mod'])) @mock.patch('stacktask.actions.models.user_store.IdentityManager', @@ -503,13 +501,13 @@ class ActionTests(TestCase): """ user = mock.Mock() user.id = 'user_id' - user.username = "test@example.com" + user.name = "test@example.com" user.email = "test@example.com" project = mock.Mock() project.id = 'test_project_id' project.name = 'test_project' - project.roles = {user.username: ['Member', 'project_mod']} + project.roles = {user.id: ['Member', 'project_mod']} setup_temp_cache({'test_project': project}, {user.id: user}) @@ -538,8 +536,8 @@ class ActionTests(TestCase): action.submit(token_data) self.assertEquals(action.valid, True) - self.assertEquals(len(project.roles[user.username]), 2) - self.assertEquals(set(project.roles[user.username]), + self.assertEquals(len(project.roles[user.id]), 2) + self.assertEquals(set(project.roles[user.id]), set(['Member', 'project_mod'])) @mock.patch('stacktask.actions.models.user_store.IdentityManager', @@ -551,13 +549,13 @@ class ActionTests(TestCase): user = mock.Mock() user.id = 'user_id' - user.username = "test@example.com" + user.name = "test@example.com" user.email = "test@example.com" project = mock.Mock() project.id = 'test_project_id' project.name = 'test_project' - project.roles = {user.username: ['Member', 'project_mod']} + project.roles = {user.id: ['Member', 'project_mod']} setup_temp_cache({'test_project': project}, {user.id: user}) @@ -585,7 +583,7 @@ class ActionTests(TestCase): action.submit(token_data) self.assertEquals(action.valid, True) - self.assertEquals(project.roles[user.username], ['Member']) + self.assertEquals(project.roles[user.id], ['Member']) @mock.patch('stacktask.actions.models.user_store.IdentityManager', FakeManager) @@ -596,13 +594,13 @@ class ActionTests(TestCase): user = mock.Mock() user.id = 'user_id' - user.username = "test@example.com" + user.name = "test@example.com" user.email = "test@example.com" project = mock.Mock() project.id = 'test_project_id' project.name = 'test_project' - project.roles = {user.username: ['Member']} + project.roles = {user.id: ['Member']} setup_temp_cache({'test_project': project}, {user.id: user}) @@ -631,4 +629,4 @@ class ActionTests(TestCase): action.submit(token_data) self.assertEquals(action.valid, True) - self.assertEquals(project.roles[user.username], ['Member']) + self.assertEquals(project.roles[user.id], ['Member']) diff --git a/stacktask/actions/user_store.py b/stacktask/actions/user_store.py index eef49a0..a83da1a 100644 --- a/stacktask/actions/user_store.py +++ b/stacktask/actions/user_store.py @@ -12,11 +12,15 @@ # License for the specific language governing permissions and limitations # under the License. -from openstack_clients import get_keystoneclient +from collections import defaultdict + +from django.conf import settings + from keystoneclient.openstack.common.apiclient import ( exceptions as ks_exceptions ) -from django.conf import settings + +from openstack_clients import get_keystoneclient def get_managable_roles(user_roles): @@ -58,14 +62,38 @@ class IdentityManager(object): user = None return user + def list_users(self, project): + try: + roles = self.ks_client.roles.list() + role_dict = {role.id: role for role in roles} + + users = {} + user_assignments = self.ks_client.role_assignments.list( + project=project) + for assignment in user_assignments: + try: + user = users.get(assignment.user['id'], None) + if user: + user.roles.append(role_dict[assignment.role['id']]) + else: + user = self.ks_client.users.get(assignment.user['id']) + user.roles = [role_dict[assignment.role['id']], ] + users[user.id] = user + except AttributeError: + # Just means the assignment is a group, so ignore it. + pass + except ks_exceptions.NotFound: + users = [] + return users.values() + def create_user(self, name, password, email, project_id): user = self.ks_client.users.create( name=name, password=password, - email=email, tenant_id=project_id) + email=email, project_id=project_id) return user def update_user_password(self, user, password): - self.ks_client.users.update_password(user, password) + self.ks_client.users.update(user, password=password) def find_role(self, name): try: @@ -75,33 +103,48 @@ class IdentityManager(object): return role def get_roles(self, user, project): - return self.ks_client.roles.roles_for_user(user, tenant=project) + return self.ks_client.roles.list(user=user, project=project) + + def get_all_roles(self, user): + """ + Returns roles for a given user across all projects. + """ + roles = self.ks_client.roles.list() + role_dict = {role.id: role for role in roles} + + user_assignments = self.ks_client.role_assignments.list(user=user) + projects = defaultdict([]) + for assignment in user_assignments: + project = assignment.scope['project']['id'] + projects[project].append(role_dict[assignment.role['id']]) + + return projects def add_user_role(self, user, role, project_id): try: - self.ks_client.roles.add_user_role(user, role, project_id) + self.ks_client.roles.grant(role, user=user, project=project_id) except ks_exceptions.Conflict: # Conflict is ok, it means the user already has this role. pass def remove_user_role(self, user, role, project_id): - self.ks_client.roles.remove_user_role(user, role, project_id) + self.ks_client.roles.revoke(role, user=user, project=project_id) def find_project(self, project_name): try: - project = self.ks_client.tenants.find(name=project_name) + project = self.ks_client.projects.find(name=project_name) except ks_exceptions.NotFound: project = None return project def get_project(self, project_id): try: - project = self.ks_client.tenants.get(project_id) + project = self.ks_client.projects.get(project_id) except ks_exceptions.NotFound: project = None return project def create_project(self, project_name, created_on): - project = self.ks_client.tenants.create(project_name, - created_on=created_on) + project = self.ks_client.projects.create(project_name, + created_on=created_on) return project diff --git a/stacktask/api/v1/openstack.py b/stacktask/api/v1/openstack.py index 6ee0c29..9052a25 100644 --- a/stacktask/api/v1/openstack.py +++ b/stacktask/api/v1/openstack.py @@ -12,14 +12,15 @@ # License for the specific language governing permissions and limitations # under the License. -from django.utils import timezone from django.conf import settings +from django.utils import timezone + from rest_framework.response import Response -from stacktask.api.v1 import tasks -from stacktask.api import utils -from stacktask.api import models from stacktask.actions import user_store +from stacktask.api import models +from stacktask.api import utils +from stacktask.api.v1 import tasks class UserList(tasks.InviteUser): @@ -35,11 +36,10 @@ class UserList(tasks.InviteUser): project = id_manager.get_project(project_id) active_emails = set() - for user in project.list_users(): + for user in id_manager.list_users(project): skip = False - self.logger.info(user) roles = [] - for role in id_manager.get_roles(user, project): + for role in user.roles: if role.name in role_blacklist: skip = True continue @@ -52,7 +52,7 @@ class UserList(tasks.InviteUser): user_status = 'Active' if enabled else 'Account Disabled' active_emails.add(email) user_list.append({'id': user.id, - 'name': user.username, + 'name': user.name, 'email': email, 'roles': roles, 'cohort': 'Member', @@ -124,7 +124,7 @@ class UserDetail(tasks.TaskView): if not roles or roles_blacklisted: return Response(no_user, status=404) return Response({'id': user.id, - "username": user.username, + "username": user.name, "email": getattr(user, 'email', ''), 'roles': roles}) diff --git a/stacktask/api/v1/tests/__init__.py b/stacktask/api/v1/tests/__init__.py index d81bc00..981e432 100644 --- a/stacktask/api/v1/tests/__init__.py +++ b/stacktask/api/v1/tests/__init__.py @@ -20,12 +20,12 @@ temp_cache = {} def setup_temp_cache(projects, users): admin_user = mock.Mock() - admin_user.id = 0 - admin_user.username = 'admin' + admin_user.id = 'user_id_0' + admin_user.name = 'admin' admin_user.password = 'password' admin_user.email = 'admin@example.com' - users.update({admin_user.username: admin_user}) + users.update({admin_user.id: admin_user}) global temp_cache @@ -59,7 +59,7 @@ class FakeProject(): usernames.append(username) users = [] for user in temp_cache['users'].values(): - if user.username in usernames: + if user.name in usernames: users.append(user) return users @@ -68,38 +68,61 @@ class FakeManager(object): def find_user(self, name): global temp_cache - return temp_cache['users'].get(name, None) + for user in temp_cache['users'].values(): + if user.name == name: + return user + return None def get_user(self, user_id): global temp_cache return temp_cache['users'].get(user_id, None) + def list_users(self, project): + global temp_cache + roles = temp_cache['projects'][project.name].roles + users = [] + + for user_id, roles in roles.iteritems(): + user = self.get_user(user_id) + user.roles = [] + + for role in roles: + r = mock.Mock() + r.name = role + user.roles.append(r) + return users + def create_user(self, name, password, email, project_id): global temp_cache user = mock.Mock() - temp_cache['i'] += 1 - user.id = temp_cache['i'] - user.username = name + user.id = "user_id_%s" % int(temp_cache['i']) + user.name = name user.password = password user.email = email user.default_project = project_id - temp_cache['users'][name] = user + temp_cache['users'][user.id] = user + + temp_cache['i'] += 0.5 return user def update_user_password(self, user, password): global temp_cache - user = temp_cache['users'][user.username] + user = temp_cache['users'][user.id] user.password = password def find_role(self, name): global temp_cache - return temp_cache['roles'].get(name, None) + if temp_cache['roles'].get(name, None): + role = mock.Mock() + role.name = name + return role + return None def get_roles(self, user, project): global temp_cache try: roles = [] - for role in project.roles[user.username]: + for role in project.roles[user.id]: r = mock.Mock() r.name = role roles.append(r) @@ -107,17 +130,29 @@ class FakeManager(object): except KeyError: return [] + def get_all_roles(self, user): + global temp_cache + projects = {} + for project in temp_cache['projects'].values(): + projects[project.id] = [] + for role in project.roles[user.id]: + r = mock.Mock() + r.name = role + projects[project.id].append(r) + + return projects + def add_user_role(self, user, role, project_id): project = self.get_project(project_id) try: - project.roles[user.username].append(role) + project.roles[user.id].append(role.name) except KeyError: - project.roles[user.username] = [role] + project.roles[user.id] = [role.name] def remove_user_role(self, user, role, project_id): project = self.get_project(project_id) try: - project.roles[user.username].remove(role) + project.roles[user.id].remove(role.name) except KeyError: pass @@ -137,8 +172,8 @@ class FakeManager(object): if p_id: project.id = p_id else: - temp_cache['i'] += 1 - project.id = temp_cache['i'] + temp_cache['i'] += 0.5 + project.id = "project_id_%s" % int(temp_cache['i']) project.name = project_name project.roles = {} temp_cache['projects'][project_name] = project diff --git a/stacktask/api/v1/tests/test_api_admin.py b/stacktask/api/v1/tests/test_api_admin.py index 190c228..8974ab8 100644 --- a/stacktask/api/v1/tests/test_api_admin.py +++ b/stacktask/api/v1/tests/test_api_admin.py @@ -12,13 +12,19 @@ # License for the specific language governing permissions and limitations # under the License. +import json + +from datetime import timedelta + +from django.utils import timezone + +import mock + from rest_framework import status from rest_framework.test import APITestCase + from stacktask.api.models import Task, Token -import mock -from django.utils import timezone -from datetime import timedelta -import json + from stacktask.api.v1.tests import FakeManager, setup_temp_cache @@ -121,11 +127,11 @@ class AdminAPITests(APITestCase): user = mock.Mock() user.id = 'user_id' - user.username = "test@example.com" + user.name = "test@example.com" user.email = "test@example.com" user.password = "test_password" - setup_temp_cache({}, {user.username: user}) + setup_temp_cache({}, {user.id: user}) url = "/v1/actions/ResetPassword" data = {'email': "test@example.com"} @@ -154,11 +160,11 @@ class AdminAPITests(APITestCase): user = mock.Mock() user.id = 'user_id' - user.username = "test@example.com" + user.name = "test@example.com" user.email = "test@example.com" user.password = "test_password" - setup_temp_cache({}, {user.username: user}) + setup_temp_cache({}, {user.id: user}) url = "/v1/actions/ResetPassword" data = {'email': "test@example.com"} @@ -373,7 +379,7 @@ class AdminAPITests(APITestCase): user = mock.Mock() user.id = 'user_id' - user.username = "test@example.com" + user.name = "test@example.com" user.email = "test@example.com" user.password = "test_password" @@ -383,7 +389,7 @@ class AdminAPITests(APITestCase): user2.email = "test2@example.com" user2.password = "test_password" - setup_temp_cache({}, {user.username: user, user2.name: user2}) + setup_temp_cache({}, {user.id: user, user2.name: user2}) url = "/v1/actions/ResetPassword" data = {'email': "test@example.com"} @@ -429,11 +435,11 @@ class AdminAPITests(APITestCase): user = mock.Mock() user.id = 'user_id' - user.username = "test@example.com" + user.name = "test@example.com" user.email = "test@example.com" user.password = "test_password" - setup_temp_cache({}, {user.username: user}) + setup_temp_cache({}, {user.id: user}) url = "/v1/actions/ResetPassword" data = {'email': "test@example.com"} @@ -982,3 +988,33 @@ class AdminAPITests(APITestCase): url, params, format='json', headers=headers ) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + + @mock.patch( + 'stacktask.actions.models.user_store.IdentityManager', FakeManager) + @mock.patch( + 'stacktask.actions.tenant_setup.models.IdentityManager', FakeManager) + def test_reset_admin(self): + """ + Ensure that you cannot issue a password reset for an + admin user. + """ + + user = mock.Mock() + user.id = 'user_id' + user.name = "test@example.com" + user.email = "test@example.com" + user.password = "test_password" + + project = mock.Mock() + project.id = 'test_project_id' + project.name = 'test_project' + project.roles = {user.id: ['admin']} + + setup_temp_cache({'test_project': project}, {user.id: user}) + + url = "/v1/actions/ResetPassword" + data = {'email': "test@example.com"} + response = self.client.post(url, data, format='json') + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data, None) + self.assertEqual(0, Token.objects.count()) diff --git a/stacktask/api/v1/tests/test_api_openstack.py b/stacktask/api/v1/tests/test_api_openstack.py index 570ecc2..e70cd86 100644 --- a/stacktask/api/v1/tests/test_api_openstack.py +++ b/stacktask/api/v1/tests/test_api_openstack.py @@ -12,10 +12,12 @@ # License for the specific language governing permissions and limitations # under the License. +import mock + from rest_framework import status from rest_framework.test import APITestCase + from stacktask.api.models import Token -import mock from stacktask.api.v1.tests import FakeManager, setup_temp_cache @@ -118,11 +120,11 @@ class OpenstackAPITests(APITestCase): user = mock.Mock() user.id = 'user_id' - user.username = "test@example.com" + user.name = "test@example.com" user.email = "test@example.com" user.password = "test_password" - setup_temp_cache({}, {user.username: user}) + setup_temp_cache({}, {user.id: user}) headers = { 'project_name': "test_project", diff --git a/stacktask/api/v1/tests/test_api_taskview.py b/stacktask/api/v1/tests/test_api_taskview.py index 7d9efcf..1b5f51b 100644 --- a/stacktask/api/v1/tests/test_api_taskview.py +++ b/stacktask/api/v1/tests/test_api_taskview.py @@ -12,10 +12,12 @@ # License for the specific language governing permissions and limitations # under the License. +import mock + from rest_framework import status from rest_framework.test import APITestCase + from stacktask.api.models import Task, Token -import mock from stacktask.api.v1.tests import FakeManager, setup_temp_cache @@ -140,10 +142,10 @@ class TaskViewTests(APITestCase): user = mock.Mock() user.id = 'user_id' - user.username = "test@example.com" + user.name = "test@example.com" user.email = "test@example.com" - setup_temp_cache({'test_project': project}, {user.username: user}) + setup_temp_cache({'test_project': project}, {user.id: user}) url = "/v1/actions/InviteUser" headers = { @@ -176,15 +178,15 @@ class TaskViewTests(APITestCase): """ user = mock.Mock() user.id = 'user_id' - user.username = "test@example.com" + user.name = "test@example.com" user.email = "test@example.com" project = mock.Mock() project.id = 'test_project_id' project.name = 'test_project' - project.roles = {user.username: ['Member']} + project.roles = {user.id: ['Member']} - setup_temp_cache({'test_project': project}, {user.username: user}) + setup_temp_cache({'test_project': project}, {user.id: user}) url = "/v1/actions/InviteUser" headers = { @@ -297,10 +299,10 @@ class TaskViewTests(APITestCase): user = mock.Mock() user.id = 'user_id' - user.username = "test@example.com" + user.name = "test@example.com" user.email = "test@example.com" - setup_temp_cache({}, {user.username: user}) + setup_temp_cache({}, {user.id: user}) url = "/v1/actions/InviteUser" headers = { @@ -344,11 +346,11 @@ class TaskViewTests(APITestCase): user = mock.Mock() user.id = 'user_id' - user.username = "test@example.com" + user.name = "test@example.com" user.email = "test@example.com" user.password = "test_password" - setup_temp_cache({}, {user.username: user}) + setup_temp_cache({}, {user.id: user}) url = "/v1/actions/ResetPassword" data = {'email': "test@example.com"} @@ -374,11 +376,11 @@ class TaskViewTests(APITestCase): user = mock.Mock() user.id = 'user_id' - user.username = "test@example.com" + user.name = "test@example.com" user.email = "test@example.com" user.password = "test_password" - setup_temp_cache({}, {user.username: user}) + setup_temp_cache({}, {user.id: user}) # Submit password reset url = "/v1/actions/ResetPassword" diff --git a/stacktask/test_settings.py b/stacktask/test_settings.py index 7151e42..85d32ed 100644 --- a/stacktask/test_settings.py +++ b/stacktask/test_settings.py @@ -63,7 +63,8 @@ KEYSTONE = { 'username': 'admin', 'password': 'openstack', 'project_name': 'admin', - 'auth_url': "http://localhost:5000/v2.0" + 'auth_url': "http://localhost:5000/v2.0", + 'auth_url_v3': "http://localhost:5000/v3" } DEFAULT_REGION = 'RegionOne' @@ -135,6 +136,9 @@ ACTION_SETTINGS = { 'NewUser': { 'allowed_roles': ['project_mod', 'project_owner', "Member"] }, + 'ResetUser': { + 'blacklisted_roles': ['admin', "Admin"] + }, 'DefaultProjectResources': { 'RegionOne': { 'DNS_NAMESERVERS': ['193.168.1.2', '193.168.1.3'],