def test_lookup_cast(self): KeyValuePair.add_or_update(KeyValuePairDB(name='count', value='5.5')) lookup = KeyValueLookup(scope=FULL_SYSTEM_SCOPE) self.assertEqual(str(lookup.count), '5.5') self.assertEqual(float(lookup.count), 5.5) self.assertEqual(int(lookup.count), 5)
def test_get_finalized_params_system_values(self): KeyValuePair.add_or_update(KeyValuePairDB(name='actionstr', value='foo')) KeyValuePair.add_or_update(KeyValuePairDB(name='actionnumber', value='1.0')) params = { 'runnerint': 555 } liveaction_db = self._get_liveaction_model(params) runner_params, action_params = param_utils.get_finalized_params( ParamsUtilsTest.runnertype_db.runner_parameters, ParamsUtilsTest.action_system_default_db.parameters, liveaction_db.parameters, liveaction_db.context) # Asserts for runner params. # Assert that default values for runner params are resolved. self.assertEqual(runner_params.get('runnerstr'), 'defaultfoo') # Assert that a runner param from action exec is picked up. self.assertEqual(runner_params.get('runnerint'), 555) # Assert that an immutable param cannot be overridden by action param or execution param. self.assertEqual(runner_params.get('runnerimmutable'), 'runnerimmutable') # Asserts for action params. self.assertEqual(action_params.get('actionstr'), 'foo') self.assertEqual(action_params.get('actionnumber'), 1.0)
def delete(self, name): """ Delete the key value pair. Handles requests: DELETE /keys/1 """ lock_name = self._get_lock_name_for_key(name=name) # Note: We use lock to avoid a race with self._coordinator.get_lock(lock_name): kvp_db = self._get_by_name(resource_name=name) if not kvp_db: abort(http_client.NOT_FOUND) return LOG.debug('DELETE /keys/ lookup with name=%s found object: %s', name, kvp_db) try: KeyValuePair.delete(kvp_db) except Exception as e: LOG.exception('Database delete encountered exception during ' 'delete of name="%s". ', name) abort(http_client.INTERNAL_SERVER_ERROR, str(e)) return extra = {'kvp_db': kvp_db} LOG.audit('KeyValuePair deleted. KeyValuePair.id=%s' % (kvp_db.id), extra=extra)
def test_get_config_dynamic_config_item_list(self): pack_name = 'dummy_pack_schema_with_nested_object_7' loader = ContentPackConfigLoader(pack_name=pack_name) KeyValuePair.add_or_update(KeyValuePairDB(name='k0', value='v0')) KeyValuePair.add_or_update(KeyValuePairDB(name='k1', value='v1')) #################### # values in list values = { 'level0_key': [ 'a', '{{st2kv.system.k0}}', 'b', '{{st2kv.system.k1}}', ] } config_db = ConfigDB(pack=pack_name, values=values) config_db = Config.add_or_update(config_db) config_rendered = loader.get_config() self.assertEquals(config_rendered, { 'level0_key': [ 'a', 'v0', 'b', 'v1' ] }) config_db.delete()
def test_user_scope_lookups_user_sep_in_name(self): KeyValuePair.add_or_update(KeyValuePairDB(name='stanley:r:i:p', value='v4', scope=FULL_USER_SCOPE)) lookup = UserKeyValueLookup(scope=FULL_USER_SCOPE, user='******') # This is the only way to lookup because USER_SEPARATOR (':') cannot be a part of # variable name in Python. self.assertEquals(str(lookup['r:i:p']), 'v4')
def test_get_value_from_datastore_through_render_live_params(self): # Register datastore value to be refered by this test-case register_kwargs = [ { 'name': 'test_key', 'value': 'foo' }, { 'name': 'user1:test_key', 'value': 'bar', 'scope': FULL_USER_SCOPE }, { 'name': '%s:test_key' % cfg.CONF.system_user.user, 'value': 'baz', 'scope': FULL_USER_SCOPE }, ] for kwargs in register_kwargs: KeyValuePair.add_or_update(KeyValuePairDB(**kwargs)) # Assert that datastore value can be got via the Jinja expression from individual scopes. context = {'user': '******'} param = { 'system_value': { 'default': '{{ st2kv.system.test_key }}' }, 'user_value': { 'default': '{{ st2kv.user.test_key }}' }, } live_params = param_utils.render_live_params(runner_parameters={}, action_parameters=param, params={}, action_context=context) self.assertEqual(live_params['system_value'], 'foo') self.assertEqual(live_params['user_value'], 'bar') # Assert that datastore value in the user-scope that is registered by user1 # cannot be got by the operation of user2. context = {'user': '******'} param = {'user_value': {'default': '{{ st2kv.user.test_key }}'}} live_params = param_utils.render_live_params(runner_parameters={}, action_parameters=param, params={}, action_context=context) self.assertEqual(live_params['user_value'], '') # Assert that system-user's scope is selected when user and api_user parameter specified context = {} param = {'user_value': {'default': '{{ st2kv.user.test_key }}'}} live_params = param_utils.render_live_params(runner_parameters={}, action_parameters=param, params={}, action_context=context) self.assertEqual(live_params['user_value'], 'baz')
def test_user_scope_lookups_dot_in_user(self): KeyValuePair.add_or_update( KeyValuePairDB(name='first.last:r.i.p', value='v4', scope=FULL_USER_SCOPE)) lookup = UserKeyValueLookup(scope=FULL_USER_SCOPE, user='******') self.assertEqual(str(lookup.r.i.p), 'v4') self.assertEqual(str(lookup['r']['i']['p']), 'v4')
def test_user_scope_lookups_dot_in_user(self): KeyValuePair.add_or_update( KeyValuePairDB(name="first.last:r.i.p", value="v4", scope=FULL_USER_SCOPE)) lookup = UserKeyValueLookup(scope=FULL_USER_SCOPE, user="******") self.assertEqual(str(lookup.r.i.p), "v4") self.assertEqual(str(lookup["r"]["i"]["p"]), "v4")
def delete(self, name, requester_user, scope=FULL_SYSTEM_SCOPE, user=None): """ Delete the key value pair. Handles requests: DELETE /keys/1 """ if not scope: scope = FULL_SYSTEM_SCOPE if not requester_user: requester_user = UserDB(cfg.CONF.system_user.user) scope = get_datastore_full_scope(scope) self._validate_scope(scope=scope) user = user or requester_user.name # Validate that the authenticated user is admin if user query param is provided rbac_utils = get_rbac_backend().get_utils_class() rbac_utils.assert_user_is_admin_if_user_query_param_is_provided( user_db=requester_user, user=user, require_rbac=True) key_ref = get_key_reference(scope=scope, name=name, user=user) lock_name = self._get_lock_name_for_key(name=key_ref, scope=scope) # Note: We use lock to avoid a race with self._coordinator.get_lock(lock_name): from_model_kwargs = {"mask_secrets": True} kvp_api = self._get_one_by_scope_and_name( name=key_ref, scope=scope, from_model_kwargs=from_model_kwargs) kvp_db = KeyValuePairAPI.to_model(kvp_api) LOG.debug( "DELETE /keys/ lookup with scope=%s name=%s found object: %s", scope, name, kvp_db, ) try: KeyValuePair.delete(kvp_db) except Exception as e: LOG.exception( "Database delete encountered exception during " 'delete of name="%s". ', name, ) abort(http_client.INTERNAL_SERVER_ERROR, six.text_type(e)) return extra = {"kvp_db": kvp_db} LOG.audit("KeyValuePair deleted. KeyValuePair.id=%s" % (kvp_db.id), extra=extra) return Response(status=http_client.NO_CONTENT)
def test_non_hierarchical_lookup(self): k1 = KeyValuePair.add_or_update(KeyValuePairDB(name='k1', value='v1')) k2 = KeyValuePair.add_or_update(KeyValuePairDB(name='k2', value='v2')) k3 = KeyValuePair.add_or_update(KeyValuePairDB(name='k3', value='v3')) lookup = KeyValueLookup() self.assertEquals(str(lookup.k1), k1.value) self.assertEquals(str(lookup.k2), k2.value) self.assertEquals(str(lookup.k3), k3.value)
def test_user_scope_lookups_user_sep_in_name(self): KeyValuePair.add_or_update( KeyValuePairDB(name='stanley:r:i:p', value='v4', scope=FULL_USER_SCOPE)) lookup = UserKeyValueLookup(scope=FULL_USER_SCOPE, user='******') # This is the only way to lookup because USER_SEPARATOR (':') cannot be a part of # variable name in Python. self.assertEqual(str(lookup['r:i:p']), 'v4')
def test_lookups_older_scope_names_backward_compatibility(self): k1 = KeyValuePair.add_or_update(KeyValuePairDB(name='a.b', value='v1', scope=FULL_SYSTEM_SCOPE)) lookup = KeyValueLookup(scope=SYSTEM_SCOPE) self.assertEquals(str(lookup['a']['b']), k1.value) k2 = KeyValuePair.add_or_update(KeyValuePairDB(name='stanley:r.i.p', value='v4', scope=FULL_USER_SCOPE)) user_lookup = UserKeyValueLookup(scope=USER_SCOPE, user='******') self.assertEquals(str(user_lookup['r']['i']['p']), k2.value)
def test_hierarchical_lookup_dict(self): k1 = KeyValuePair.add_or_update(KeyValuePairDB(name='a.b', value='v1')) k2 = KeyValuePair.add_or_update(KeyValuePairDB(name='a.b.c', value='v2')) k3 = KeyValuePair.add_or_update(KeyValuePairDB(name='b.c', value='v3')) lookup = KeyValueLookup() self.assertEquals(str(lookup['a']['b']), k1.value) self.assertEquals(str(lookup['a']['b']['c']), k2.value) self.assertEquals(str(lookup['b']['c']), k3.value) self.assertEquals(str(lookup['a']), '')
def setUp(self): super(KeyValuesControllerRBACTestCase, self).setUp() self.kvps = {} # Insert mock users user_1_db = UserDB(name='user1') user_1_db = User.add_or_update(user_1_db) self.users['user_1'] = user_1_db user_2_db = UserDB(name='user2') user_2_db = User.add_or_update(user_2_db) self.users['user_2'] = user_2_db # Insert mock kvp objects kvp_api = KeyValuePairSetAPI(name='test_system_scope', value='value1', scope=FULL_SYSTEM_SCOPE) kvp_db = KeyValuePairSetAPI.to_model(kvp_api) kvp_db = KeyValuePair.add_or_update(kvp_db) kvp_db = KeyValuePairAPI.from_model(kvp_db) self.kvps['kvp_1'] = kvp_db kvp_api = KeyValuePairSetAPI(name='test_system_scope_secret', value='value_secret', scope=FULL_SYSTEM_SCOPE, secret=True) kvp_db = KeyValuePairSetAPI.to_model(kvp_api) kvp_db = KeyValuePair.add_or_update(kvp_db) kvp_db = KeyValuePairAPI.from_model(kvp_db) self.kvps['kvp_2'] = kvp_db name = get_key_reference(scope=FULL_USER_SCOPE, name='test_user_scope_1', user='******') kvp_db = KeyValuePairDB(name=name, value='valueu12', scope=FULL_USER_SCOPE) kvp_db = KeyValuePair.add_or_update(kvp_db) kvp_db = KeyValuePairAPI.from_model(kvp_db) self.kvps['kvp_3'] = kvp_db name = get_key_reference(scope=FULL_USER_SCOPE, name='test_user_scope_2', user='******') kvp_api = KeyValuePairSetAPI(name=name, value='user_secret', scope=FULL_USER_SCOPE, secret=True) kvp_db = KeyValuePairSetAPI.to_model(kvp_api) kvp_db = KeyValuePair.add_or_update(kvp_db) kvp_db = KeyValuePairAPI.from_model(kvp_db) self.kvps['kvp_4'] = kvp_db name = get_key_reference(scope=FULL_USER_SCOPE, name='test_user_scope_3', user='******') kvp_db = KeyValuePairDB(name=name, value='valueu21', scope=FULL_USER_SCOPE) kvp_db = KeyValuePair.add_or_update(kvp_db) kvp_db = KeyValuePairAPI.from_model(kvp_db) self.kvps['kvp_5'] = kvp_db self.system_scoped_items_count = 2 self.user_scoped_items_count = 3 self.user_scoped_items_per_user_count = { 'user1': 2, 'user2': 1 }
def test_lookup_with_key_prefix(self): KeyValuePair.add_or_update(KeyValuePairDB(name='some:prefix:stanley:k5', value='v5', scope=FULL_USER_SCOPE)) # No prefix provided, should return None lookup = UserKeyValueLookup(user='******', scope=FULL_USER_SCOPE) self.assertEqual(str(lookup.k5), '') # Prefix provided lookup = UserKeyValueLookup(prefix='some:prefix', user='******', scope=FULL_USER_SCOPE) self.assertEqual(str(lookup.k5), 'v5')
def test_get_value_from_datastore_through_render_live_params(self): # Register datastore value to be refered by this test-case register_kwargs = [ {"name": "test_key", "value": "foo"}, {"name": "user1:test_key", "value": "bar", "scope": FULL_USER_SCOPE}, { "name": "%s:test_key" % cfg.CONF.system_user.user, "value": "baz", "scope": FULL_USER_SCOPE, }, ] for kwargs in register_kwargs: KeyValuePair.add_or_update(KeyValuePairDB(**kwargs)) # Assert that datastore value can be got via the Jinja expression from individual scopes. context = {"user": "******"} param = { "system_value": {"default": "{{ st2kv.system.test_key }}"}, "user_value": {"default": "{{ st2kv.user.test_key }}"}, } live_params = param_utils.render_live_params( runner_parameters={}, action_parameters=param, params={}, action_context=context, ) self.assertEqual(live_params["system_value"], "foo") self.assertEqual(live_params["user_value"], "bar") # Assert that datastore value in the user-scope that is registered by user1 # cannot be got by the operation of user2. context = {"user": "******"} param = {"user_value": {"default": "{{ st2kv.user.test_key }}"}} live_params = param_utils.render_live_params( runner_parameters={}, action_parameters=param, params={}, action_context=context, ) self.assertEqual(live_params["user_value"], "") # Assert that system-user's scope is selected when user and api_user parameter specified context = {} param = {"user_value": {"default": "{{ st2kv.user.test_key }}"}} live_params = param_utils.render_live_params( runner_parameters={}, action_parameters=param, params={}, action_context=context, ) self.assertEqual(live_params["user_value"], "baz")
def test_get_config_dynamic_config_item_under_additional_properties(self): pack_name = "dummy_pack_schema_with_additional_properties_1" loader = ContentPackConfigLoader(pack_name=pack_name) encrypted_value = crypto.symmetric_encrypt(KeyValuePairAPI.crypto_key, "v1_encrypted") KeyValuePair.add_or_update( KeyValuePairDB(name="k1_encrypted", value=encrypted_value, secret=True)) #################### # values in objects under an object with additionalProperties values = { "profiles": { "dev": { # no host or port to test default value "token": "hard-coded-secret", }, "prod": { "host": "127.1.2.7", "port": 8282, # encrypted in datastore "token": "{{st2kv.system.k1_encrypted}}", # schema declares `secret: true` which triggers auto-decryption. # If this were not encrypted, it would try to decrypt it and fail. }, } } config_db = ConfigDB(pack=pack_name, values=values) config_db = Config.add_or_update(config_db) config_rendered = loader.get_config() self.assertEqual( config_rendered, { "region": "us-east-1", "profiles": { "dev": { "host": "127.0.0.3", "port": 8080, "token": "hard-coded-secret", }, "prod": { "host": "127.1.2.7", "port": 8282, "token": "v1_encrypted", }, }, }, ) config_db.delete()
def test_lookups_older_scope_names_backward_compatibility(self): k1 = KeyValuePair.add_or_update( KeyValuePairDB(name="a.b", value="v1", scope=FULL_SYSTEM_SCOPE)) lookup = KeyValueLookup(scope=SYSTEM_SCOPE) self.assertEqual(str(lookup["a"]["b"]), k1.value) k2 = KeyValuePair.add_or_update( KeyValuePairDB(name="stanley:r.i.p", value="v4", scope=FULL_USER_SCOPE)) user_lookup = UserKeyValueLookup(scope=USER_SCOPE, user="******") self.assertEqual(str(user_lookup["r"]["i"]["p"]), k2.value)
def delete(self, name, requester_user, scope=FULL_SYSTEM_SCOPE, user=None): """ Delete the key value pair. Handles requests: DELETE /keys/1 """ if not scope: scope = FULL_SYSTEM_SCOPE if not requester_user: requester_user = UserDB(cfg.CONF.system_user.user) scope = get_datastore_full_scope(scope) self._validate_scope(scope=scope) user = user or requester_user.name # Validate that the authenticated user is admin if user query param is provided rbac_utils = get_rbac_backend().get_utils_class() rbac_utils.assert_user_is_admin_if_user_query_param_is_provided(user_db=requester_user, user=user, require_rbac=True) key_ref = get_key_reference(scope=scope, name=name, user=user) lock_name = self._get_lock_name_for_key(name=key_ref, scope=scope) # Note: We use lock to avoid a race with self._coordinator.get_lock(lock_name): from_model_kwargs = {'mask_secrets': True} kvp_api = self._get_one_by_scope_and_name( name=key_ref, scope=scope, from_model_kwargs=from_model_kwargs ) kvp_db = KeyValuePairAPI.to_model(kvp_api) LOG.debug('DELETE /keys/ lookup with scope=%s name=%s found object: %s', scope, name, kvp_db) try: KeyValuePair.delete(kvp_db) except Exception as e: LOG.exception('Database delete encountered exception during ' 'delete of name="%s". ', name) abort(http_client.INTERNAL_SERVER_ERROR, six.text_type(e)) return extra = {'kvp_db': kvp_db} LOG.audit('KeyValuePair deleted. KeyValuePair.id=%s' % (kvp_db.id), extra=extra) return Response(status=http_client.NO_CONTENT)
def test_secret_lookup(self): secret_value = '0055A2D9A09E1071931925933744965EEA7E23DCF59A8D1D7A3' + \ '64338294916D37E83C4796283C584751750E39844E2FD97A3727DB5D553F638' k1 = KeyValuePair.add_or_update(KeyValuePairDB( name='k1', value=secret_value, secret=True, encrypted=True) ) k2 = KeyValuePair.add_or_update(KeyValuePairDB(name='k2', value='v2')) lookup = KeyValueLookup() self.assertEquals(str(lookup.k1), k1.value) self.assertEquals(str(lookup.k2), k2.value)
def set_datastore_value_for_config_key(pack_name, key_name, value, secret=False, user=None): """ Set config value in the datastore. This function takes care of correctly encoding the key name, serializing the value, etc. :param pack_name: Pack name. :type pack_name: ``str`` :param key_name: Config key name. :type key_name: ``str`` :param secret: True if this value is a secret. :type secret: ``bool`` :param user: Optional username if working on a user-scoped config item. :type user: ``str`` :rtype: :class:`KeyValuePairDB` """ if user: scope = FULL_USER_SCOPE else: scope = FULL_SYSTEM_SCOPE name = get_key_reference(scope=scope, name=key_name, user=user) kvp_api = KeyValuePairAPI(name=name, value=value, scope=scope, secret=secret) kvp_db = KeyValuePairAPI.to_model(kvp_api) # TODO: Obtain a lock try: existing_kvp_db = KeyValuePair.get_by_scope_and_name(scope=scope, name=name) except StackStormDBObjectNotFoundError: existing_kvp_db = None if existing_kvp_db: kvp_db.id = existing_kvp_db.id kvp_db = KeyValuePair.add_or_update(kvp_db) return kvp_db
def setUp(self): super(KeyValuesControllerRBACTestCase, self).setUp() self.kvps = {} # Insert mock users user_1_db = UserDB(name="user1") user_1_db = User.add_or_update(user_1_db) self.users["user_1"] = user_1_db user_2_db = UserDB(name="user2") user_2_db = User.add_or_update(user_2_db) self.users["user_2"] = user_2_db # Insert mock kvp objects kvp_api = KeyValuePairSetAPI(name="test_system_scope", value="value1", scope=FULL_SYSTEM_SCOPE) kvp_db = KeyValuePairSetAPI.to_model(kvp_api) kvp_db = KeyValuePair.add_or_update(kvp_db) kvp_db = KeyValuePairAPI.from_model(kvp_db) self.kvps["kvp_1"] = kvp_db kvp_api = KeyValuePairSetAPI( name="test_system_scope_secret", value="value_secret", scope=FULL_SYSTEM_SCOPE, secret=True ) kvp_db = KeyValuePairSetAPI.to_model(kvp_api) kvp_db = KeyValuePair.add_or_update(kvp_db) kvp_db = KeyValuePairAPI.from_model(kvp_db) self.kvps["kvp_2"] = kvp_db name = get_key_reference(scope=FULL_USER_SCOPE, name="test_user_scope_1", user="******") kvp_db = KeyValuePairDB(name=name, value="valueu12", scope=FULL_USER_SCOPE) kvp_db = KeyValuePair.add_or_update(kvp_db) kvp_db = KeyValuePairAPI.from_model(kvp_db) self.kvps["kvp_3"] = kvp_db name = get_key_reference(scope=FULL_USER_SCOPE, name="test_user_scope_2", user="******") kvp_api = KeyValuePairSetAPI(name=name, value="user_secret", scope=FULL_USER_SCOPE, secret=True) kvp_db = KeyValuePairSetAPI.to_model(kvp_api) kvp_db = KeyValuePair.add_or_update(kvp_db) kvp_db = KeyValuePairAPI.from_model(kvp_db) self.kvps["kvp_4"] = kvp_db name = get_key_reference(scope=FULL_USER_SCOPE, name="test_user_scope_3", user="******") kvp_db = KeyValuePairDB(name=name, value="valueu21", scope=FULL_USER_SCOPE) kvp_db = KeyValuePair.add_or_update(kvp_db) kvp_db = KeyValuePairAPI.from_model(kvp_db) self.kvps["kvp_5"] = kvp_db self.system_scoped_items_count = 2 self.user_scoped_items_count = 3 self.user_scoped_items_per_user_count = {"user1": 2, "user2": 1}
def setUp(self): super(TemplatingUtilsTestCase, self).setUp() # Insert mock DB objects kvp_1_db = KeyValuePairDB(name='key1', value='valuea') kvp_1_db = KeyValuePair.add_or_update(kvp_1_db) kvp_2_db = KeyValuePairDB(name='key2', value='valueb') kvp_2_db = KeyValuePair.add_or_update(kvp_2_db) kvp_3_db = KeyValuePairDB(name='stanley:key1', value='valuestanley1', scope=USER_SCOPE) kvp_3_db = KeyValuePair.add_or_update(kvp_3_db) kvp_4_db = KeyValuePairDB(name='joe:key1', value='valuejoe1', scope=USER_SCOPE) kvp_4_db = KeyValuePair.add_or_update(kvp_4_db)
def setUp(self): super(TemplatingUtilsTestCase, self).setUp() # Insert mock DB objects kvp_1_db = KeyValuePairDB(name='key1', value='valuea') kvp_1_db = KeyValuePair.add_or_update(kvp_1_db) kvp_2_db = KeyValuePairDB(name='key2', value='valueb') kvp_2_db = KeyValuePair.add_or_update(kvp_2_db) kvp_3_db = KeyValuePairDB(name='stanley:key1', value='valuestanley1', scope=FULL_USER_SCOPE) kvp_3_db = KeyValuePair.add_or_update(kvp_3_db) kvp_4_db = KeyValuePairDB(name='joe:key1', value='valuejoe1', scope=FULL_USER_SCOPE) kvp_4_db = KeyValuePair.add_or_update(kvp_4_db)
def put(self, name, kvp): """ Create a new entry or update an existing one. """ # TODO: There is a race, add custom add_or_update which updates by non # id field existing_kvp = self.__get_by_name(name=name) kvp.name = name try: kvp_db = KeyValuePairAPI.to_model(kvp) if existing_kvp: kvp_db.id = existing_kvp.id kvp_db = KeyValuePair.add_or_update(kvp_db) except (ValidationError, ValueError) as e: LOG.exception('Validation failed for key value data=%s', kvp) abort(http_client.BAD_REQUEST, str(e)) return extra = {'kvp_db': kvp_db} LOG.audit('KeyValuePair updated. KeyValuePair.id=%s' % (kvp_db.id), extra=extra) kvp_api = KeyValuePairAPI.from_model(kvp_db) return kvp_api
def put(self, name, kvp): """ Create a new entry or update an existing one. """ lock_name = self._get_lock_name_for_key(name=name) # TODO: Custom permission check since the key doesn't need to exist here # Note: We use lock to avoid a race with self._coordinator.get_lock(lock_name): existing_kvp = self._get_by_name(resource_name=name) kvp.name = name try: kvp_db = KeyValuePairAPI.to_model(kvp) if existing_kvp: kvp_db.id = existing_kvp.id kvp_db = KeyValuePair.add_or_update(kvp_db) except (ValidationError, ValueError) as e: LOG.exception('Validation failed for key value data=%s', kvp) abort(http_client.BAD_REQUEST, str(e)) return except CryptoKeyNotSetupException as e: LOG.exception(str(e)) abort(http_client.BAD_REQUEST, str(e)) return extra = {'kvp_db': kvp_db} LOG.audit('KeyValuePair updated. KeyValuePair.id=%s' % (kvp_db.id), extra=extra) kvp_api = KeyValuePairAPI.from_model(kvp_db) return kvp_api
def get_key(key=None, user=None, scope=None, decrypt=False): """Retrieve key from KVP store """ if not isinstance(key, six.string_types): raise TypeError('Given key is not typeof string.') if not isinstance(decrypt, bool): raise TypeError('Decrypt parameter is not typeof bool.') if not user: user = UserDB(cfg.CONF.system_user.user) scope, key_id = _derive_scope_and_key(key, user, scope) scope = get_datastore_full_scope(scope) LOG.debug('get_key scope: %s', scope) _validate_scope(scope=scope) is_admin = rbac_utils.user_is_admin(user_db=user) # User needs to be either admin or requesting item for itself _validate_decrypt_query_parameter(decrypt=decrypt, scope=scope, is_admin=is_admin, user=user) value = KeyValuePair.get_by_scope_and_name(scope, key_id) if value: return deserialize_key_value(value.value, decrypt) return None
def test_filter_decrypt_kv(self): KeyValuePair.add_or_update(KeyValuePairDB(name='k8', value=self.secret_value, scope=FULL_SYSTEM_SCOPE, secret=True)) context = {} context.update({SYSTEM_SCOPE: KeyValueLookup(scope=SYSTEM_SCOPE)}) context.update({ DATASTORE_PARENT_SCOPE: { SYSTEM_SCOPE: KeyValueLookup(scope=FULL_SYSTEM_SCOPE) } }) template = '{{st2kv.system.k8 | decrypt_kv}}' actual = self.env.from_string(template).render(context) self.assertEqual(actual, self.secret)
def test_get_config_dynamic_config_item(self): pack_name = "dummy_pack_schema_with_nested_object_6" loader = ContentPackConfigLoader(pack_name=pack_name) #################### # value in top level item KeyValuePair.add_or_update(KeyValuePairDB(name="k1", value="v1")) values = {"level0_key": "{{st2kv.system.k1}}"} config_db = ConfigDB(pack=pack_name, values=values) config_db = Config.add_or_update(config_db) config_rendered = loader.get_config() self.assertEqual(config_rendered, {"level0_key": "v1"}) config_db.delete()
def _get_kv(self, key): scope = self._scope LOG.debug('Lookup system kv: scope: %s and key: %s', scope, key) kvp = KeyValuePair.get_by_scope_and_name(scope=scope, name=key) if kvp: LOG.debug('Got value %s from datastore.', kvp.value) return kvp.value if kvp else ''
def test_filter_decrypt_kv_with_user_scope_value(self): KeyValuePair.add_or_update(KeyValuePairDB(name='stanley:k8', value=self.secret_value, scope=FULL_USER_SCOPE, secret=True)) context = {} context.update({USER_SCOPE: UserKeyValueLookup(user='******', scope=USER_SCOPE)}) context.update({ DATASTORE_PARENT_SCOPE: { USER_SCOPE: UserKeyValueLookup(user='******', scope=FULL_USER_SCOPE) } }) template = '{{st2kv.user.k8 | decrypt_kv}}' actual = self.env.from_string(template).render(context) self.assertEqual(actual, self.secret)
def get_kvp_for_name(name): try: kvp_db = KeyValuePair.get_by_name(name) except ValueError: kvp_db = None return kvp_db
def put(self, kvp, name, scope=FULL_SYSTEM_SCOPE): """ Create a new entry or update an existing one. """ if not scope: scope = FULL_SYSTEM_SCOPE requester_user = get_requester() scope = getattr(kvp, 'scope', scope) scope = get_datastore_full_scope(scope) self._validate_scope(scope=scope) user = getattr(kvp, 'user', requester_user) or requester_user # Validate that the authenticated user is admin if user query param is provided assert_request_user_is_admin_if_user_query_param_is_provider(request=pecan.request, user=user) key_ref = get_key_reference(scope=scope, name=name, user=user) lock_name = self._get_lock_name_for_key(name=key_ref, scope=scope) LOG.debug('PUT scope: %s, name: %s', scope, name) # TODO: Custom permission check since the key doesn't need to exist here # Note: We use lock to avoid a race with self._coordinator.get_lock(lock_name): try: existing_kvp_api = self._get_one_by_scope_and_name( scope=scope, name=key_ref ) except StackStormDBObjectNotFoundError: existing_kvp_api = None kvp.name = key_ref kvp.scope = scope try: kvp_db = KeyValuePairAPI.to_model(kvp) if existing_kvp_api: kvp_db.id = existing_kvp_api.id kvp_db = KeyValuePair.add_or_update(kvp_db) except (ValidationError, ValueError) as e: LOG.exception('Validation failed for key value data=%s', kvp) abort(http_client.BAD_REQUEST, str(e)) return except CryptoKeyNotSetupException as e: LOG.exception(str(e)) abort(http_client.BAD_REQUEST, str(e)) return except InvalidScopeException as e: LOG.exception(str(e)) abort(http_client.BAD_REQUEST, str(e)) return extra = {'kvp_db': kvp_db} LOG.audit('KeyValuePair updated. KeyValuePair.id=%s' % (kvp_db.id), extra=extra) kvp_api = KeyValuePairAPI.from_model(kvp_db) return kvp_api
def test_get_config_dynamic_config_item(self): pack_name = 'dummy_pack_schema_with_nested_object_6' loader = ContentPackConfigLoader(pack_name=pack_name) #################### # value in top level item KeyValuePair.add_or_update(KeyValuePairDB(name='k1', value='v1')) values = {'level0_key': '{{st2kv.system.k1}}'} config_db = ConfigDB(pack=pack_name, values=values) config_db = Config.add_or_update(config_db) config_rendered = loader.get_config() self.assertEqual(config_rendered, {'level0_key': 'v1'}) config_db.delete()
def put(self, name, kvp): """ Create a new entry or update an existing one. """ lock_name = self._get_lock_name_for_key(name=name) # TODO: Custom permission check since the key doesn't need to exist here # Note: We use lock to avoid a race with self._coordinator.get_lock(lock_name): existing_kvp = self.__get_by_name(name=name) kvp.name = name try: kvp_db = KeyValuePairAPI.to_model(kvp) if existing_kvp: kvp_db.id = existing_kvp.id kvp_db = KeyValuePair.add_or_update(kvp_db) except (ValidationError, ValueError) as e: LOG.exception('Validation failed for key value data=%s', kvp) abort(http_client.BAD_REQUEST, str(e)) return extra = {'kvp_db': kvp_db} LOG.audit('KeyValuePair updated. KeyValuePair.id=%s' % (kvp_db.id), extra=extra) kvp_api = KeyValuePairAPI.from_model(kvp_db) return kvp_api
def delete(self, name, scope=FULL_SYSTEM_SCOPE, user=None): """ Delete the key value pair. Handles requests: DELETE /keys/1 """ if not scope: scope = FULL_SYSTEM_SCOPE scope = get_datastore_full_scope(scope) self._validate_scope(scope=scope) requester_user = get_requester() user = user or requester_user # Validate that the authenticated user is admin if user query param is provided assert_request_user_is_admin_if_user_query_param_is_provided( request=pecan.request, user=user) key_ref = get_key_reference(scope=scope, name=name, user=user) lock_name = self._get_lock_name_for_key(name=key_ref, scope=scope) # Note: We use lock to avoid a race with self._coordinator.get_lock(lock_name): from_model_kwargs = {'mask_secrets': True} kvp_api = self._get_one_by_scope_and_name( name=key_ref, scope=scope, from_model_kwargs=from_model_kwargs) kvp_db = KeyValuePairAPI.to_model(kvp_api) LOG.debug( 'DELETE /keys/ lookup with scope=%s name=%s found object: %s', scope, name, kvp_db) try: KeyValuePair.delete(kvp_db) except Exception as e: LOG.exception( 'Database delete encountered exception during ' 'delete of name="%s". ', name) abort(http_client.INTERNAL_SERVER_ERROR, str(e)) return extra = {'kvp_db': kvp_db} LOG.audit('KeyValuePair deleted. KeyValuePair.id=%s' % (kvp_db.id), extra=extra)
def delete(self, name, scope=FULL_SYSTEM_SCOPE, user=None): """ Delete the key value pair. Handles requests: DELETE /keys/1 """ if not scope: scope = FULL_SYSTEM_SCOPE scope = get_datastore_full_scope(scope) self._validate_scope(scope=scope) requester_user = get_requester() user = user or requester_user # Validate that the authenticated user is admin if user query param is provided assert_request_user_is_admin_if_user_query_param_is_provider(request=pecan.request, user=user) key_ref = get_key_reference(scope=scope, name=name, user=user) lock_name = self._get_lock_name_for_key(name=key_ref, scope=scope) # Note: We use lock to avoid a race with self._coordinator.get_lock(lock_name): from_model_kwargs = {'mask_secrets': True} kvp_api = self._get_one_by_scope_and_name( name=key_ref, scope=scope, from_model_kwargs=from_model_kwargs ) kvp_db = KeyValuePairAPI.to_model(kvp_api) LOG.debug('DELETE /keys/ lookup with scope=%s name=%s found object: %s', scope, name, kvp_db) try: KeyValuePair.delete(kvp_db) except Exception as e: LOG.exception('Database delete encountered exception during ' 'delete of name="%s". ', name) abort(http_client.INTERNAL_SERVER_ERROR, str(e)) return extra = {'kvp_db': kvp_db} LOG.audit('KeyValuePair deleted. KeyValuePair.id=%s' % (kvp_db.id), extra=extra)
def put(self, kvp, name, scope=FULL_SYSTEM_SCOPE): """ Create a new entry or update an existing one. """ if not scope: scope = FULL_SYSTEM_SCOPE requester_user = get_requester() scope = getattr(kvp, 'scope', scope) scope = get_datastore_full_scope(scope) self._validate_scope(scope=scope) user = getattr(kvp, 'user', requester_user) or requester_user # Validate that the authenticated user is admin if user query param is provided assert_request_user_is_admin_if_user_query_param_is_provided( request=pecan.request, user=user) key_ref = get_key_reference(scope=scope, name=name, user=user) lock_name = self._get_lock_name_for_key(name=key_ref, scope=scope) LOG.debug('PUT scope: %s, name: %s', scope, name) # TODO: Custom permission check since the key doesn't need to exist here # Note: We use lock to avoid a race with self._coordinator.get_lock(lock_name): try: existing_kvp_api = self._get_one_by_scope_and_name( scope=scope, name=key_ref) except StackStormDBObjectNotFoundError: existing_kvp_api = None kvp.name = key_ref kvp.scope = scope try: kvp_db = KeyValuePairAPI.to_model(kvp) if existing_kvp_api: kvp_db.id = existing_kvp_api.id kvp_db = KeyValuePair.add_or_update(kvp_db) except (ValidationError, ValueError) as e: LOG.exception('Validation failed for key value data=%s', kvp) abort(http_client.BAD_REQUEST, str(e)) return except CryptoKeyNotSetupException as e: LOG.exception(str(e)) abort(http_client.BAD_REQUEST, str(e)) return except InvalidScopeException as e: LOG.exception(str(e)) abort(http_client.BAD_REQUEST, str(e)) return extra = {'kvp_db': kvp_db} LOG.audit('KeyValuePair updated. KeyValuePair.id=%s' % (kvp_db.id), extra=extra) kvp_api = KeyValuePairAPI.from_model(kvp_db) return kvp_api
def test_non_hierarchical_lookup(self): k1 = KeyValuePair.add_or_update(KeyValuePairDB(name='k1', value='v1')) k2 = KeyValuePair.add_or_update(KeyValuePairDB(name='k2', value='v2')) k3 = KeyValuePair.add_or_update(KeyValuePairDB(name='k3', value='v3')) k4 = KeyValuePair.add_or_update(KeyValuePairDB(name='stanley:k4', value='v4', scope=FULL_USER_SCOPE)) lookup = KeyValueLookup() self.assertEquals(str(lookup.k1), k1.value) self.assertEquals(str(lookup.k2), k2.value) self.assertEquals(str(lookup.k3), k3.value) # Scoped lookup lookup = KeyValueLookup(scope=FULL_SYSTEM_SCOPE) self.assertEquals(str(lookup.k4), '') user_lookup = UserKeyValueLookup(scope=FULL_USER_SCOPE, user='******') self.assertEquals(str(user_lookup.k4), k4.value)
def migrate_datastore(): key_value_items = KeyValuePair.get_all() try: for kvp in key_value_items: kvp_id = getattr(kvp, 'id', None) secret = getattr(kvp, 'secret', False) scope = getattr(kvp, 'scope', SYSTEM_SCOPE) new_kvp_db = KeyValuePairDB(id=kvp_id, name=kvp.name, expire_timestamp=kvp.expire_timestamp, value=kvp.value, secret=secret, scope=scope) KeyValuePair.add_or_update(new_kvp_db) except: print('ERROR: Failed migrating datastore item with name: %s' % kvp.name) tb.print_exc() raise
def __get_by_name(name): try: return KeyValuePair.get_by_name(name) except ValueError as e: LOG.debug( 'Database lookup for name="%s" resulted in exception : %s.', name, e) return None
def get_required_sensor_refs(self): partition_lookup_key = self._get_partition_lookup_key(self.sensor_node_name) kvp = KeyValuePair.get_by_name(partition_lookup_key) sensor_refs_str = kvp.value if kvp.value else '' self._supported_sensor_refs = set([ sensor_ref.strip() for sensor_ref in sensor_refs_str.split(',')]) return self._supported_sensor_refs
def test_get_value_from_datastore_through_render_live_params(self): # Register datastore value to be refered by this test-case register_kwargs = [ {'name': 'test_key', 'value': 'foo'}, {'name': 'user1:test_key', 'value': 'bar', 'scope': FULL_USER_SCOPE}, {'name': '%s:test_key' % cfg.CONF.system_user.user, 'value': 'baz', 'scope': FULL_USER_SCOPE}, ] for kwargs in register_kwargs: KeyValuePair.add_or_update(KeyValuePairDB(**kwargs)) # Assert that datastore value can be got via the Jinja expression from individual scopes. context = {'user': '******'} param = { 'system_value': {'default': '{{ st2kv.system.test_key }}'}, 'user_value': {'default': '{{ st2kv.user.test_key }}'}, } live_params = param_utils.render_live_params(runner_parameters={}, action_parameters=param, params={}, action_context=context) self.assertEqual(live_params['system_value'], 'foo') self.assertEqual(live_params['user_value'], 'bar') # Assert that datastore value in the user-scope that is registered by user1 # cannot be got by the operation of user2. context = {'user': '******'} param = {'user_value': {'default': '{{ st2kv.user.test_key }}'}} live_params = param_utils.render_live_params(runner_parameters={}, action_parameters=param, params={}, action_context=context) self.assertEqual(live_params['user_value'], '') # Assert that system-user's scope is selected when user and api_user parameter specified context = {} param = {'user_value': {'default': '{{ st2kv.user.test_key }}'}} live_params = param_utils.render_live_params(runner_parameters={}, action_parameters=param, params={}, action_context=context) self.assertEqual(live_params['user_value'], 'baz')
def test_system_transform(self): k5 = KeyValuePair.add_or_update(KeyValuePairDB(name="k5", value="v5")) k6 = KeyValuePair.add_or_update(KeyValuePairDB(name="k6", value="v6")) k7 = KeyValuePair.add_or_update(KeyValuePairDB(name="k7", value="v7")) try: transformer = datatransform.get_transformer(PAYLOAD) mapping = {"ip5": "{{trigger.k2}}-static", "ip6": "{{system.k6}}-static", "ip7": "{{system.k7}}-static"} result = transformer(mapping) expected = {"ip5": "v2-static", "ip6": "v6-static", "ip7": "v7-static"} self.assertEqual(result, expected) finally: KeyValuePair.delete(k5) KeyValuePair.delete(k6) KeyValuePair.delete(k7)
def test_hierarchical_lookup_dict(self): k1 = KeyValuePair.add_or_update(KeyValuePairDB(name='a.b', value='v1')) k2 = KeyValuePair.add_or_update(KeyValuePairDB(name='a.b.c', value='v2')) k3 = KeyValuePair.add_or_update(KeyValuePairDB(name='b.c', value='v3')) k4 = KeyValuePair.add_or_update(KeyValuePairDB(name='stanley:r.i.p', value='v4', scope=FULL_USER_SCOPE)) lookup = KeyValueLookup() self.assertEquals(str(lookup['a']['b']), k1.value) self.assertEquals(str(lookup['a']['b']['c']), k2.value) self.assertEquals(str(lookup['b']['c']), k3.value) self.assertEquals(str(lookup['a']), '') # Scoped lookup lookup = KeyValueLookup(scope=FULL_SYSTEM_SCOPE) self.assertEquals(str(lookup['r']['i']['p']), '') user_lookup = UserKeyValueLookup(scope=FULL_USER_SCOPE, user='******') self.assertEquals(str(user_lookup['r']['i']['p']), k4.value)
def test_hierarchical_lookup_dotted(self): k1 = KeyValuePair.add_or_update(KeyValuePairDB(name='a.b', value='v1')) k2 = KeyValuePair.add_or_update(KeyValuePairDB(name='a.b.c', value='v2')) k3 = KeyValuePair.add_or_update(KeyValuePairDB(name='b.c', value='v3')) k4 = KeyValuePair.add_or_update(KeyValuePairDB(name='stanley:r.i.p', value='v4', scope=USER_SCOPE)) lookup = KeyValueLookup() self.assertEquals(str(lookup.a.b), k1.value) self.assertEquals(str(lookup.a.b.c), k2.value) self.assertEquals(str(lookup.b.c), k3.value) self.assertEquals(str(lookup.a), '') # Scoped lookup lookup = KeyValueLookup(scope=SYSTEM_SCOPE) self.assertEquals(str(lookup.r.i.p), '') user_lookup = UserKeyValueLookup(scope=USER_SCOPE, user='******') self.assertEquals(str(user_lookup.r.i.p), k4.value)
def test_get_finalized_params_older_kv_scopes_backwards_compatibility(self): KeyValuePair.add_or_update(KeyValuePairDB(name="cmd_to_run", value="echo MELANIA", scope=FULL_SYSTEM_SCOPE)) # k2 = KeyValuePair.add_or_update(KeyValuePairDB(name='ivanka:cmd_to_run', # value='echo MA DAD IS GREAT', # scope=USER_SCOPE)) params = { "sys_cmd": "{{system.cmd_to_run}}", # 'user_cmd': '{{user.ivanka:cmd_to_run}}' Not supported yet. } runner_param_info = {"r1": {}} action_param_info = {"sys_cmd": {}} action_context = {} r_runner_params, r_action_params = param_utils.get_finalized_params( runner_param_info, action_param_info, params, action_context ) self.assertEqual(r_action_params["sys_cmd"], "echo MELANIA")
def _get_kv(self, key): scope = self._scope try: kvp = KeyValuePair.get_by_scope_and_name(scope=scope, name=key) except StackStormDBObjectNotFoundError: kvp = None return kvp.value if kvp else ""