From b637eef5af14896a58cc33c8bf880066b3a796d4 Mon Sep 17 00:00:00 2001 From: Ian Main Date: Thu, 14 Jun 2012 17:46:12 -0700 Subject: [PATCH] Add a user creds database table and associate stacks with username. This patch takes the credentials passed in from the context and allows you to store them in the database in the 'user_creds' table for later use with HA operations. It also adds a 'username' to the stack table for direct comparison and user validation to support per-user stacks. Thanks to Angus for fixing the tests for me :) Signed-off-by: Ian Main --- heat/db/api.py | 12 ++++ heat/db/sqlalchemy/api.py | 23 +++++++ .../migrate_repo/versions/005_user_creds.py | 68 +++++++++++++++++++ heat/db/sqlalchemy/models.py | 23 +++++++ heat/engine/manager.py | 10 ++- heat/tests/test_stacks.py | 33 +++++++-- 6 files changed, 160 insertions(+), 9 deletions(-) create mode 100644 heat/db/sqlalchemy/migrate_repo/versions/005_user_creds.py diff --git a/heat/db/api.py b/heat/db/api.py index 665d1ee1..a44fa61a 100644 --- a/heat/db/api.py +++ b/heat/db/api.py @@ -103,6 +103,10 @@ def stack_get_all(context): return IMPL.stack_get_all(context) +def stack_get_by_user(context): + return IMPL.stack_get_by_user(context) + + def stack_create(context, values): return IMPL.stack_create(context, values) @@ -111,6 +115,14 @@ def stack_delete(context, stack_name): return IMPL.stack_delete(context, stack_name) +def user_creds_create(context): + return IMPL.user_creds_create(context) + + +def user_creds_get(context_id): + return IMPL.user_creds_get(context_id) + + def event_get(context, event_id): return IMPL.event_get(context, event_id) diff --git a/heat/db/sqlalchemy/api.py b/heat/db/sqlalchemy/api.py index f98431cd..f382d1d6 100644 --- a/heat/db/sqlalchemy/api.py +++ b/heat/db/sqlalchemy/api.py @@ -126,6 +126,9 @@ def resource_get_all_by_stack(context, stack_id): def stack_get(context, stack_id): result = model_query(context, models.Stack).\ filter_by(name=stack_id).first() + if result is not None and context is not None and\ + result.username != context.username: + return None return result @@ -134,6 +137,12 @@ def stack_get_all(context): return results +def stack_get_by_user(context): + results = model_query(context, models.Stack).\ + filter_by(username=context.username).all() + return results + + def stack_create(context, values): stack_ref = models.Stack() stack_ref.update(values) @@ -170,6 +179,20 @@ def stack_delete(context, stack_name): session.flush() +def user_creds_create(values): + user_creds_ref = models.UserCreds() + user_creds_ref.update(values) + user_creds_ref.save() + return user_creds_ref + + +def user_creds_get(user_creds_id): + result = model_query(None, models.UserCreds).\ + filter_by(id=user_creds_id).first() + + return result + + def event_get(context, event_id): result = model_query(context, models.Event).\ filter_by(id=event_id).first() diff --git a/heat/db/sqlalchemy/migrate_repo/versions/005_user_creds.py b/heat/db/sqlalchemy/migrate_repo/versions/005_user_creds.py new file mode 100644 index 00000000..5e0bfcfc --- /dev/null +++ b/heat/db/sqlalchemy/migrate_repo/versions/005_user_creds.py @@ -0,0 +1,68 @@ +from sqlalchemy import * +from migrate import * + + +def upgrade(migrate_engine): + meta = MetaData(bind=migrate_engine) + + user_creds = Table( + 'user_creds', meta, + Column('id', Integer, primary_key=True), + Column('created_at', DateTime(timezone=False)), + Column('updated_at', DateTime(timezone=False)), + Column('username', String(length=255, convert_unicode=False, + assert_unicode=None, + unicode_error=None, + _warn_on_bytestring=False)), + Column('password', String(length=255, convert_unicode=False, + assert_unicode=None, + unicode_error=None, + _warn_on_bytestring=False)), + Column('service_user', String(length=255, convert_unicode=False, + assert_unicode=None, + unicode_error=None, + _warn_on_bytestring=False)), + Column('service_password', String(length=255, convert_unicode=False, + assert_unicode=None, + unicode_error=None, + _warn_on_bytestring=False)), + Column('tenant', String(length=1024, convert_unicode=False, + assert_unicode=None, + unicode_error=None, + _warn_on_bytestring=False)), + Column('auth_url', Text()), + Column('aws_auth_url', Text()), + Column('tenant_id', String(length=256, convert_unicode=False, + assert_unicode=None, + unicode_error=None, + _warn_on_bytestring=False)), + Column('aws_creds', Text()) + ) + + try: + user_creds.create() + except Exception: + meta.drop_all(tables=tables) + raise + + stack = Table('stack', meta, autoload=True) + + Column('username', String(length=256, convert_unicode=False, + assert_unicode=None, + unicode_error=None, + _warn_on_bytestring=False)).create(stack) + Column('user_creds_id', Integer, ForeignKey("user_creds.id"), + nullable=False).create(stack) + + +def downgrade(migrate_engine): + meta = MetaData() + meta.bind = migrate_engine + + watch_rule = Table('user_creds', meta, autoload=True) + watch_rule.drop() + + stack = Table('stack', meta, autoload=True) + + stack.c.username.drop() + stack.c.user_creds_id.drop() diff --git a/heat/db/sqlalchemy/models.py b/heat/db/sqlalchemy/models.py index c377bbfc..eea66a37 100644 --- a/heat/db/sqlalchemy/models.py +++ b/heat/db/sqlalchemy/models.py @@ -141,6 +141,29 @@ class Stack(BASE, HeatBase): nullable=False) raw_template = relationship(RawTemplate, backref=backref('stack')) + username = Column(String) + user_creds_id = Column(Integer, ForeignKey('user_creds.id'), + nullable=False) + + +class UserCreds(BASE, HeatBase): + """ + Represents user credentials and mirrors the 'context' + handed in by wsgi. + """ + + __tablename__ = 'user_creds' + + id = Column(Integer, primary_key=True) + username = Column(String) + password = Column(String) + service_user = Column(String) + service_password = Column(String) + tenant = Column(String) + auth_url = Column(String) + aws_auth_url = Column(String) + tenant_id = Column(String) + aws_creds = Column(String) class Event(BASE, HeatBase): diff --git a/heat/engine/manager.py b/heat/engine/manager.py index 29922136..65fcb5ff 100644 --- a/heat/engine/manager.py +++ b/heat/engine/manager.py @@ -118,7 +118,7 @@ class EngineManager(manager.Manager): self._authenticate(context) res = {'stacks': []} - stacks = db_api.stack_get_all(None) + stacks = db_api.stack_get_by_user(context) if stacks is None: return res for s in stacks: @@ -146,7 +146,7 @@ class EngineManager(manager.Manager): self._authenticate(context) res = {'stacks': []} - s = db_api.stack_get(None, stack_name) + s = db_api.stack_get(context, stack_name) if s: ps = parser.Stack(context, s.name, s.raw_template.parsed_template.template, @@ -212,10 +212,14 @@ class EngineManager(manager.Manager): rt['stack_name'] = stack_name new_rt = db_api.raw_template_create(None, rt) + new_creds = db_api.user_creds_create(context.to_dict()) + s = {} s['name'] = stack_name s['raw_template_id'] = new_rt.id - new_s = db_api.stack_create(None, s) + s['user_creds_id'] = new_creds.id + s['username'] = context.username + new_s = db_api.stack_create(context, s) stack.id = new_s.id pt = {} diff --git a/heat/tests/test_stacks.py b/heat/tests/test_stacks.py index ec8a64df..c4512389 100644 --- a/heat/tests/test_stacks.py +++ b/heat/tests/test_stacks.py @@ -9,6 +9,7 @@ import sqlalchemy from nose.plugins.attrib import attr from nose import with_setup +from heat. common import context from heat.tests.v1_1 import fakes from heat.engine import instance as instances import heat.db as db_api @@ -70,9 +71,14 @@ class stacksTest(unittest.TestCase): rt['template'] = stack.t rt['stack_name'] = stack.name new_rt = db_api.raw_template_create(None, rt) + ct = {'username': 'fred', + 'password': 'mentions_fruit'} + new_creds = db_api.user_creds_create(ct) s = {} s['name'] = stack.name s['raw_template_id'] = new_rt.id + s['user_creds_id'] = new_creds.id + s['username'] = ct['username'] new_s = db_api.stack_create(None, s) stack.id = new_s.id pt = {} @@ -93,9 +99,14 @@ class stacksTest(unittest.TestCase): rt['template'] = stack.t rt['stack_name'] = stack.name new_rt = db_api.raw_template_create(None, rt) + ct = {'username': 'fred', + 'password': 'mentions_fruit'} + new_creds = db_api.user_creds_create(ct) s = {} s['name'] = stack.name s['raw_template_id'] = new_rt.id + s['user_creds_id'] = new_creds.id + s['username'] = ct['username'] new_s = db_api.stack_create(None, s) stack.id = new_s.id pt = {} @@ -127,22 +138,32 @@ class stacksTest(unittest.TestCase): assert(result['ResourceProperties']['InstanceType'] == 'm1.large') def test_stack_list(self): - self.m.StubOutWithMock(manager.EngineManager, '_authenticate') - manager.EngineManager._authenticate(None).AndReturn(True) stack = self.start_wordpress_stack('test_stack_list') rt = {} rt['template'] = stack.t rt['stack_name'] = stack.name new_rt = db_api.raw_template_create(None, rt) + ct = {'username': 'fred', + 'password': 'mentions_fruit'} + new_creds = db_api.user_creds_create(ct) + + ctx = context.get_admin_context() + self.m.StubOutWithMock(ctx, 'username') + ctx.username = 'fred' + self.m.StubOutWithMock(manager.EngineManager, '_authenticate') + manager.EngineManager._authenticate(ctx).AndReturn(True) + s = {} s['name'] = stack.name s['raw_template_id'] = new_rt.id - new_s = db_api.stack_create(None, s) + s['user_creds_id'] = new_creds.id + s['username'] = ct['username'] + new_s = db_api.stack_create(ctx, s) stack.id = new_s.id pt = {} pt['template'] = stack.t pt['raw_template_id'] = new_rt.id - new_pt = db_api.parsed_template_create(None, pt) + new_pt = db_api.parsed_template_create(ctx, pt) instances.Instance.nova().AndReturn(self.fc) self.m.ReplayAll() stack.create_blocking() @@ -152,10 +173,10 @@ class stacksTest(unittest.TestCase): params = {} parameters = {} t['Parameters']['KeyName']['Value'] = 'test' - stack = parser.Stack(None, 'test_stack_list', t, 0, params) + stack = parser.Stack(ctx, 'test_stack_list', t, 0, params) man = manager.EngineManager() - sl = man.list_stacks(None, params) + sl = man.list_stacks(ctx, params) assert(len(sl) > 0) for s in sl['stacks']: -- 2.45.2