Merge "Role based resource access control - update executions"

This commit is contained in:
Jenkins 2017-04-06 08:08:19 +00:00 committed by Gerrit Code Review
commit ab53901453
3 changed files with 104 additions and 50 deletions

View File

@ -28,6 +28,7 @@ import sqlalchemy as sa
from sqlalchemy.ext.compiler import compiles from sqlalchemy.ext.compiler import compiles
from sqlalchemy.sql.expression import Insert from sqlalchemy.sql.expression import Insert
from mistral import context
from mistral.db.sqlalchemy import base as b from mistral.db.sqlalchemy import base as b
from mistral.db.sqlalchemy import model_base as mb from mistral.db.sqlalchemy import model_base as mb
from mistral.db.sqlalchemy import sqlite_lock from mistral.db.sqlalchemy import sqlite_lock
@ -255,8 +256,10 @@ def _get_db_object_by_name(model, name):
return _secure_query(model).filter_by(name=name).first() return _secure_query(model).filter_by(name=name).first()
def _get_db_object_by_id(model, id): def _get_db_object_by_id(model, id, insecure=False):
return _secure_query(model).filter_by(id=id).first() query = b.model_query(model) if insecure else _secure_query(model)
return query.filter_by(id=id).first()
def _get_db_object_by_name_or_id(model, identifier, insecure=False): def _get_db_object_by_name_or_id(model, identifier, insecure=False):
@ -735,7 +738,13 @@ def _get_action_executions(**kwargs):
@b.session_aware() @b.session_aware()
def get_workflow_execution(id, session=None): def get_workflow_execution(id, session=None):
wf_ex = _get_db_object_by_id(models.WorkflowExecution, id) ctx = context.ctx()
wf_ex = _get_db_object_by_id(
models.WorkflowExecution,
id,
insecure=ctx.is_admin
)
if not wf_ex: if not wf_ex:
raise exc.DBEntityNotFoundError( raise exc.DBEntityNotFoundError(
@ -783,6 +792,8 @@ def create_workflow_execution(values, session=None):
def update_workflow_execution(id, values, session=None): def update_workflow_execution(id, values, session=None):
wf_ex = get_workflow_execution(id) wf_ex = get_workflow_execution(id)
m_dbutils.check_db_obj_access(wf_ex)
wf_ex.update(values.copy()) wf_ex.update(values.copy())
return wf_ex return wf_ex

View File

@ -19,6 +19,7 @@ from oslo_config import cfg
import random import random
import testtools import testtools
from mistral import context as auth_context
from mistral.db.sqlalchemy import sqlite_lock from mistral.db.sqlalchemy import sqlite_lock
from mistral.db.v2.sqlalchemy import api as db_api from mistral.db.v2.sqlalchemy import api as db_api
from mistral.db.v2.sqlalchemy import models as db_models from mistral.db.v2.sqlalchemy import models as db_models
@ -89,6 +90,9 @@ class SQLiteLocksTest(test_base.DbTestCase):
self.assertEqual(0, len(sqlite_lock.get_locks())) self.assertEqual(0, len(sqlite_lock.get_locks()))
def _run_correct_locking(self, wf_ex): def _run_correct_locking(self, wf_ex):
# Set context info for the thread.
auth_context.set_ctx(test_base.get_context())
self._random_sleep() self._random_sleep()
with db_api.transaction(): with db_api.transaction():

View File

@ -29,7 +29,9 @@ from mistral.tests.unit import base as test_base
from mistral.utils import filter_utils from mistral.utils import filter_utils
user_context = test_base.get_context(default=False) DEFAULT_CTX = test_base.get_context()
USER_CTX = test_base.get_context(default=False)
ADM_CTX = test_base.get_context(default=False, admin=True)
WORKBOOKS = [ WORKBOOKS = [
{ {
@ -305,7 +307,7 @@ class WorkbookTest(SQLAlchemyTest):
self.assertEqual(created, fetched[0]) self.assertEqual(created, fetched[0])
# Create a new user. # Create a new user.
auth_context.set_ctx(test_base.get_context(default=False)) auth_context.set_ctx(USER_CTX)
created = db_api.create_workbook(WORKBOOKS[1]) created = db_api.create_workbook(WORKBOOKS[1])
fetched = db_api.get_workbooks() fetched = db_api.get_workbooks()
@ -324,7 +326,7 @@ class WorkbookTest(SQLAlchemyTest):
self.assertEqual(created1, fetched[0]) self.assertEqual(created1, fetched[0])
# Create a new user. # Create a new user.
auth_context.set_ctx(test_base.get_context(default=False)) auth_context.set_ctx(USER_CTX)
fetched = db_api.get_workbooks() fetched = db_api.get_workbooks()
@ -347,7 +349,7 @@ class WorkbookTest(SQLAlchemyTest):
auth_context.ctx().project_id) auth_context.ctx().project_id)
# Create a new user. # Create a new user.
auth_context.set_ctx(test_base.get_context(default=False)) auth_context.set_ctx(USER_CTX)
fetched = db_api.get_workbooks() fetched = db_api.get_workbooks()
@ -606,7 +608,7 @@ class WorkflowDefinitionTest(SQLAlchemyTest):
created = db_api.create_workflow_definition(WF_DEFINITIONS[0]) created = db_api.create_workflow_definition(WF_DEFINITIONS[0])
# Switch to another project. # Switch to another project.
auth_context.set_ctx(test_base.get_context(default=False)) auth_context.set_ctx(USER_CTX)
self.assertRaises( self.assertRaises(
exc.NotAllowedException, exc.NotAllowedException,
@ -619,7 +621,7 @@ class WorkflowDefinitionTest(SQLAlchemyTest):
created = db_api.create_workflow_definition(WF_DEFINITIONS[1]) created = db_api.create_workflow_definition(WF_DEFINITIONS[1])
# Switch to admin. # Switch to admin.
auth_context.set_ctx(test_base.get_context(default=False, admin=True)) auth_context.set_ctx(ADM_CTX)
updated = db_api.update_workflow_definition( updated = db_api.update_workflow_definition(
created['id'], created['id'],
@ -632,7 +634,7 @@ class WorkflowDefinitionTest(SQLAlchemyTest):
self.assertEqual('my new definition', updated.definition) self.assertEqual('my new definition', updated.definition)
# Switch back. # Switch back.
auth_context.set_ctx(test_base.get_context()) auth_context.set_ctx(DEFAULT_CTX)
fetched = db_api.get_workflow_definition(created['id']) fetched = db_api.get_workflow_definition(created['id'])
@ -645,7 +647,7 @@ class WorkflowDefinitionTest(SQLAlchemyTest):
created = db_api.create_workflow_definition(system_workflow) created = db_api.create_workflow_definition(system_workflow)
# Switch to admin. # Switch to admin.
auth_context.set_ctx(test_base.get_context(default=False, admin=True)) auth_context.set_ctx(ADM_CTX)
updated = db_api.update_workflow_definition( updated = db_api.update_workflow_definition(
created['id'], created['id'],
@ -689,13 +691,13 @@ class WorkflowDefinitionTest(SQLAlchemyTest):
created = db_api.create_workflow_definition(WF_DEFINITIONS[0]) created = db_api.create_workflow_definition(WF_DEFINITIONS[0])
# Create a new user. # Create a new user.
auth_context.set_ctx(test_base.get_context(default=False)) auth_context.set_ctx(USER_CTX)
cron_trigger = copy.copy(CRON_TRIGGER) cron_trigger = copy.copy(CRON_TRIGGER)
cron_trigger['workflow_id'] = created.id cron_trigger['workflow_id'] = created.id
db_api.create_cron_trigger(cron_trigger) db_api.create_cron_trigger(cron_trigger)
auth_context.set_ctx(test_base.get_context(default=True)) auth_context.set_ctx(DEFAULT_CTX)
self.assertRaises( self.assertRaises(
exc.NotAllowedException, exc.NotAllowedException,
@ -708,7 +710,7 @@ class WorkflowDefinitionTest(SQLAlchemyTest):
created = db_api.create_workflow_definition(WF_DEFINITIONS[0]) created = db_api.create_workflow_definition(WF_DEFINITIONS[0])
# Switch to another user. # Switch to another user.
auth_context.set_ctx(test_base.get_context(default=False)) auth_context.set_ctx(USER_CTX)
event_trigger = copy.copy(EVENT_TRIGGERS[0]) event_trigger = copy.copy(EVENT_TRIGGERS[0])
event_trigger.update({'workflow_id': created.id}) event_trigger.update({'workflow_id': created.id})
@ -716,7 +718,7 @@ class WorkflowDefinitionTest(SQLAlchemyTest):
db_api.create_event_trigger(event_trigger) db_api.create_event_trigger(event_trigger)
# Switch back. # Switch back.
auth_context.set_ctx(test_base.get_context(default=True)) auth_context.set_ctx(DEFAULT_CTX)
self.assertRaises( self.assertRaises(
exc.NotAllowedException, exc.NotAllowedException,
@ -810,7 +812,7 @@ class WorkflowDefinitionTest(SQLAlchemyTest):
created = db_api.create_workflow_definition(WF_DEFINITIONS[0]) created = db_api.create_workflow_definition(WF_DEFINITIONS[0])
# Switch to another project. # Switch to another project.
auth_context.set_ctx(test_base.get_context(default=False)) auth_context.set_ctx(USER_CTX)
self.assertRaises( self.assertRaises(
exc.NotAllowedException, exc.NotAllowedException,
@ -822,12 +824,12 @@ class WorkflowDefinitionTest(SQLAlchemyTest):
created = db_api.create_workflow_definition(WF_DEFINITIONS[0]) created = db_api.create_workflow_definition(WF_DEFINITIONS[0])
# Switch to admin. # Switch to admin.
auth_context.set_ctx(test_base.get_context(default=False, admin=True)) auth_context.set_ctx(ADM_CTX)
db_api.delete_workflow_definition(created['id']) db_api.delete_workflow_definition(created['id'])
# Switch back. # Switch back.
auth_context.set_ctx(test_base.get_context()) auth_context.set_ctx(DEFAULT_CTX)
self.assertRaises( self.assertRaises(
exc.DBEntityNotFoundError, exc.DBEntityNotFoundError,
@ -846,7 +848,7 @@ class WorkflowDefinitionTest(SQLAlchemyTest):
self.assertEqual(created1, fetched[0]) self.assertEqual(created1, fetched[0])
# Create a new user. # Create a new user.
auth_context.set_ctx(test_base.get_context(default=False)) auth_context.set_ctx(USER_CTX)
fetched = db_api.get_workflow_definitions() fetched = db_api.get_workflow_definitions()
@ -871,7 +873,7 @@ class WorkflowDefinitionTest(SQLAlchemyTest):
) )
# Create a new user. # Create a new user.
auth_context.set_ctx(test_base.get_context(default=False)) auth_context.set_ctx(USER_CTX)
fetched = db_api.get_workflow_definitions() fetched = db_api.get_workflow_definitions()
@ -1352,7 +1354,7 @@ class ActionExecutionTest(SQLAlchemyTest):
created = db_api.create_action_execution(ACTION_EXECS[0]) created = db_api.create_action_execution(ACTION_EXECS[0])
# Create a new user. # Create a new user.
auth_context.set_ctx(test_base.get_context(default=False)) auth_context.set_ctx(USER_CTX)
self.assertRaises( self.assertRaises(
exc.DBEntityNotFoundError, exc.DBEntityNotFoundError,
@ -1455,6 +1457,43 @@ class WorkflowExecutionTest(SQLAlchemyTest):
self.assertEqual(updated, fetched) self.assertEqual(updated, fetched)
self.assertIsNotNone(fetched.updated_at) self.assertIsNotNone(fetched.updated_at)
def test_update_workflow_execution_by_admin(self):
with db_api.transaction():
created = db_api.create_workflow_execution(WF_EXECS[0])
auth_context.set_ctx(ADM_CTX)
updated = db_api.update_workflow_execution(
created.id,
{'state': 'RUNNING', 'state_info': "Running..."}
)
auth_context.set_ctx(DEFAULT_CTX)
self.assertEqual('RUNNING', updated.state)
self.assertEqual(
'RUNNING',
db_api.load_workflow_execution(updated.id).state
)
fetched = db_api.get_workflow_execution(created.id)
self.assertEqual(updated, fetched)
self.assertIsNotNone(fetched.updated_at)
def test_update_workflow_execution_by_others_fail(self):
with db_api.transaction():
created = db_api.create_workflow_execution(WF_EXECS[0])
auth_context.set_ctx(USER_CTX)
self.assertRaises(
exc.DBEntityNotFoundError,
db_api.update_workflow_execution,
created.id,
{'state': 'RUNNING', 'state_info': "Running..."}
)
def test_create_or_update_workflow_execution(self): def test_create_or_update_workflow_execution(self):
id = 'not-existing-id' id = 'not-existing-id'
@ -2243,7 +2282,7 @@ class CronTriggerTest(SQLAlchemyTest):
created0 = db_api.create_cron_trigger(CRON_TRIGGERS[0]) created0 = db_api.create_cron_trigger(CRON_TRIGGERS[0])
# Switch to another tenant. # Switch to another tenant.
auth_context.set_ctx(user_context) auth_context.set_ctx(USER_CTX)
fetched = db_api.get_cron_triggers( fetched = db_api.get_cron_triggers(
insecure=True, insecure=True,
@ -2559,7 +2598,7 @@ RESOURCE_MEMBERS = [
'resource_id': '123e4567-e89b-12d3-a456-426655440000', 'resource_id': '123e4567-e89b-12d3-a456-426655440000',
'resource_type': 'workflow', 'resource_type': 'workflow',
'project_id': security.get_project_id(), 'project_id': security.get_project_id(),
'member_id': user_context.project_id, 'member_id': USER_CTX.project_id,
'status': 'pending', 'status': 'pending',
}, },
{ {
@ -2580,18 +2619,18 @@ class ResourceMemberTest(SQLAlchemyTest):
fetched = db_api.get_resource_member( fetched = db_api.get_resource_member(
'123e4567-e89b-12d3-a456-426655440000', '123e4567-e89b-12d3-a456-426655440000',
'workflow', 'workflow',
user_context.project_id USER_CTX.project_id
) )
self.assertEqual(created_1, fetched) self.assertEqual(created_1, fetched)
# Switch to another tenant. # Switch to another tenant.
auth_context.set_ctx(user_context) auth_context.set_ctx(USER_CTX)
fetched = db_api.get_resource_member( fetched = db_api.get_resource_member(
'123e4567-e89b-12d3-a456-426655440000', '123e4567-e89b-12d3-a456-426655440000',
'workflow', 'workflow',
user_context.project_id USER_CTX.project_id
) )
self.assertEqual(created_1, fetched) self.assertEqual(created_1, fetched)
@ -2630,7 +2669,7 @@ class ResourceMemberTest(SQLAlchemyTest):
db_api.create_resource_member(RESOURCE_MEMBERS[1]) db_api.create_resource_member(RESOURCE_MEMBERS[1])
# Switch to another tenant. # Switch to another tenant.
auth_context.set_ctx(user_context) auth_context.set_ctx(USER_CTX)
fetched = db_api.get_resource_members( fetched = db_api.get_resource_members(
created.resource_id, created.resource_id,
@ -2644,12 +2683,12 @@ class ResourceMemberTest(SQLAlchemyTest):
created = db_api.create_resource_member(RESOURCE_MEMBERS[0]) created = db_api.create_resource_member(RESOURCE_MEMBERS[0])
# Switch to another tenant. # Switch to another tenant.
auth_context.set_ctx(user_context) auth_context.set_ctx(USER_CTX)
updated = db_api.update_resource_member( updated = db_api.update_resource_member(
created.resource_id, created.resource_id,
'workflow', 'workflow',
user_context.project_id, USER_CTX.project_id,
{'status': 'accepted'} {'status': 'accepted'}
) )
@ -2664,7 +2703,7 @@ class ResourceMemberTest(SQLAlchemyTest):
db_api.update_resource_member, db_api.update_resource_member,
created.resource_id, created.resource_id,
'workflow', 'workflow',
user_context.project_id, USER_CTX.project_id,
{'status': 'accepted'} {'status': 'accepted'}
) )
@ -2674,7 +2713,7 @@ class ResourceMemberTest(SQLAlchemyTest):
db_api.delete_resource_member( db_api.delete_resource_member(
created.resource_id, created.resource_id,
'workflow', 'workflow',
user_context.project_id, USER_CTX.project_id,
) )
fetched = db_api.get_resource_members( fetched = db_api.get_resource_members(
@ -2688,14 +2727,14 @@ class ResourceMemberTest(SQLAlchemyTest):
created = db_api.create_resource_member(RESOURCE_MEMBERS[0]) created = db_api.create_resource_member(RESOURCE_MEMBERS[0])
# Switch to another tenant. # Switch to another tenant.
auth_context.set_ctx(user_context) auth_context.set_ctx(USER_CTX)
self.assertRaises( self.assertRaises(
exc.DBEntityNotFoundError, exc.DBEntityNotFoundError,
db_api.delete_resource_member, db_api.delete_resource_member,
created.resource_id, created.resource_id,
'workflow', 'workflow',
user_context.project_id, USER_CTX.project_id,
) )
def test_delete_resource_member_already_deleted(self): def test_delete_resource_member_already_deleted(self):
@ -2704,7 +2743,7 @@ class ResourceMemberTest(SQLAlchemyTest):
db_api.delete_resource_member( db_api.delete_resource_member(
created.resource_id, created.resource_id,
'workflow', 'workflow',
user_context.project_id, USER_CTX.project_id,
) )
self.assertRaises( self.assertRaises(
@ -2712,7 +2751,7 @@ class ResourceMemberTest(SQLAlchemyTest):
db_api.delete_resource_member, db_api.delete_resource_member,
created.resource_id, created.resource_id,
'workflow', 'workflow',
user_context.project_id, USER_CTX.project_id,
) )
def test_delete_nonexistent_resource_member(self): def test_delete_nonexistent_resource_member(self):
@ -2730,7 +2769,7 @@ class WorkflowSharingTest(SQLAlchemyTest):
wf = db_api.create_workflow_definition(WF_DEFINITIONS[1]) wf = db_api.create_workflow_definition(WF_DEFINITIONS[1])
# Switch to another tenant. # Switch to another tenant.
auth_context.set_ctx(user_context) auth_context.set_ctx(USER_CTX)
self.assertRaises( self.assertRaises(
exc.DBEntityNotFoundError, exc.DBEntityNotFoundError,
@ -2739,25 +2778,25 @@ class WorkflowSharingTest(SQLAlchemyTest):
) )
# Switch to original tenant, share workflow to another tenant. # Switch to original tenant, share workflow to another tenant.
auth_context.set_ctx(test_base.get_context()) auth_context.set_ctx(DEFAULT_CTX)
workflow_sharing = { workflow_sharing = {
'resource_id': wf.id, 'resource_id': wf.id,
'resource_type': 'workflow', 'resource_type': 'workflow',
'project_id': security.get_project_id(), 'project_id': security.get_project_id(),
'member_id': user_context.project_id, 'member_id': USER_CTX.project_id,
'status': 'pending', 'status': 'pending',
} }
db_api.create_resource_member(workflow_sharing) db_api.create_resource_member(workflow_sharing)
# Switch to another tenant, accept the sharing, get workflows. # Switch to another tenant, accept the sharing, get workflows.
auth_context.set_ctx(user_context) auth_context.set_ctx(USER_CTX)
db_api.update_resource_member( db_api.update_resource_member(
wf.id, wf.id,
'workflow', 'workflow',
user_context.project_id, USER_CTX.project_id,
{'status': 'accepted'} {'status': 'accepted'}
) )
@ -2772,19 +2811,19 @@ class WorkflowSharingTest(SQLAlchemyTest):
'resource_id': wf.id, 'resource_id': wf.id,
'resource_type': 'workflow', 'resource_type': 'workflow',
'project_id': security.get_project_id(), 'project_id': security.get_project_id(),
'member_id': user_context.project_id, 'member_id': USER_CTX.project_id,
'status': 'pending', 'status': 'pending',
} }
db_api.create_resource_member(workflow_sharing) db_api.create_resource_member(workflow_sharing)
# Switch to another tenant, accept the sharing. # Switch to another tenant, accept the sharing.
auth_context.set_ctx(user_context) auth_context.set_ctx(USER_CTX)
db_api.update_resource_member( db_api.update_resource_member(
wf.id, wf.id,
'workflow', 'workflow',
user_context.project_id, USER_CTX.project_id,
{'status': 'accepted'} {'status': 'accepted'}
) )
@ -2793,12 +2832,12 @@ class WorkflowSharingTest(SQLAlchemyTest):
self.assertEqual(wf, fetched) self.assertEqual(wf, fetched)
# Switch to original tenant, delete the workflow. # Switch to original tenant, delete the workflow.
auth_context.set_ctx(test_base.get_context()) auth_context.set_ctx(DEFAULT_CTX)
db_api.delete_workflow_definition(wf.id) db_api.delete_workflow_definition(wf.id)
# Switch to another tenant, can not see that workflow. # Switch to another tenant, can not see that workflow.
auth_context.set_ctx(user_context) auth_context.set_ctx(USER_CTX)
self.assertRaises( self.assertRaises(
exc.DBEntityNotFoundError, exc.DBEntityNotFoundError,
@ -2813,19 +2852,19 @@ class WorkflowSharingTest(SQLAlchemyTest):
'resource_id': wf.id, 'resource_id': wf.id,
'resource_type': 'workflow', 'resource_type': 'workflow',
'project_id': security.get_project_id(), 'project_id': security.get_project_id(),
'member_id': user_context.project_id, 'member_id': USER_CTX.project_id,
'status': 'pending', 'status': 'pending',
} }
db_api.create_resource_member(workflow_sharing) db_api.create_resource_member(workflow_sharing)
# Switch to another tenant, accept the sharing. # Switch to another tenant, accept the sharing.
auth_context.set_ctx(user_context) auth_context.set_ctx(USER_CTX)
db_api.update_resource_member( db_api.update_resource_member(
wf.id, wf.id,
'workflow', 'workflow',
user_context.project_id, USER_CTX.project_id,
{'status': 'accepted'} {'status': 'accepted'}
) )
@ -2834,7 +2873,7 @@ class WorkflowSharingTest(SQLAlchemyTest):
db_api.create_cron_trigger(CRON_TRIGGERS[0]) db_api.create_cron_trigger(CRON_TRIGGERS[0])
# Switch to original tenant, try to delete the workflow. # Switch to original tenant, try to delete the workflow.
auth_context.set_ctx(test_base.get_context()) auth_context.set_ctx(DEFAULT_CTX)
self.assertRaises( self.assertRaises(
exc.DBError, exc.DBError,
@ -2893,7 +2932,7 @@ class EventTriggerTest(SQLAlchemyTest):
db_api.create_event_trigger(EVENT_TRIGGERS[0]) db_api.create_event_trigger(EVENT_TRIGGERS[0])
# Switch to another tenant. # Switch to another tenant.
auth_context.set_ctx(user_context) auth_context.set_ctx(USER_CTX)
db_api.create_event_trigger(EVENT_TRIGGERS[1]) db_api.create_event_trigger(EVENT_TRIGGERS[1])
fetched = db_api.get_event_triggers() fetched = db_api.get_event_triggers()