def post(self, rule, requester_user): """ Create a new rule. Handles requests: POST /rules/ """ permission_type = PermissionType.RULE_CREATE rbac_utils.assert_user_has_resource_api_permission(user_db=requester_user, resource_api=rule, permission_type=permission_type) if not requester_user: requester_user = UserDB(cfg.CONF.system_user.user) # Validate that the authenticated user is admin if user query param is provided user = requester_user.name assert_user_is_admin_if_user_query_param_is_provided(user_db=requester_user, user=user) if not hasattr(rule, 'context'): rule.context = dict() rule.context['user'] = user try: rule_db = RuleAPI.to_model(rule) LOG.debug('/rules/ POST verified RuleAPI and formulated RuleDB=%s', rule_db) # Check referenced trigger and action permissions # Note: This needs to happen after "to_model" call since to_model performs some # validation (trigger exists, etc.) assert_user_has_rule_trigger_and_action_permission(user_db=requester_user, rule_api=rule) rule_db = Rule.add_or_update(rule_db) # After the rule has been added modify the ref_count. This way a failure to add # the rule due to violated constraints will have no impact on ref_count. increment_trigger_ref_count(rule_api=rule) except (ValidationError, ValueError) as e: LOG.exception('Validation failed for rule data=%s.', rule) abort(http_client.BAD_REQUEST, six.text_type(e)) return except (ValueValidationException, jsonschema.ValidationError) as e: LOG.exception('Validation failed for rule data=%s.', rule) abort(http_client.BAD_REQUEST, six.text_type(e)) return except TriggerDoesNotExistException as e: msg = ('Trigger "%s" defined in the rule does not exist in system or it\'s missing ' 'required "parameters" attribute' % (rule.trigger['type'])) LOG.exception(msg) abort(http_client.BAD_REQUEST, msg) return extra = {'rule_db': rule_db} LOG.audit('Rule created. Rule.id=%s' % (rule_db.id), extra=extra) rule_api = RuleAPI.from_model(rule_db) return Response(json=rule_api, status=exc.HTTPCreated.code)
def post(self, rule, requester_user): """ Create a new rule. Handles requests: POST /rules/ """ permission_type = PermissionType.RULE_CREATE rbac_utils.assert_user_has_resource_api_permission(user_db=requester_user, resource_api=rule, permission_type=permission_type) if not requester_user: requester_user = UserDB(cfg.CONF.system_user.user) # Validate that the authenticated user is admin if user query param is provided user = requester_user.name assert_user_is_admin_if_user_query_param_is_provided(user_db=requester_user, user=user) if not hasattr(rule, 'context'): rule.context = dict() rule.context['user'] = user try: rule_db = RuleAPI.to_model(rule) LOG.debug('/rules/ POST verified RuleAPI and formulated RuleDB=%s', rule_db) # Check referenced trigger and action permissions # Note: This needs to happen after "to_model" call since to_model performs some # validation (trigger exists, etc.) assert_user_has_rule_trigger_and_action_permission(user_db=requester_user, rule_api=rule) rule_db = Rule.add_or_update(rule_db) # After the rule has been added modify the ref_count. This way a failure to add # the rule due to violated constraints will have no impact on ref_count. increment_trigger_ref_count(rule_api=rule) except (ValidationError, ValueError) as e: LOG.exception('Validation failed for rule data=%s.', rule) abort(http_client.BAD_REQUEST, str(e)) return except (ValueValidationException, jsonschema.ValidationError) as e: LOG.exception('Validation failed for rule data=%s.', rule) abort(http_client.BAD_REQUEST, str(e)) return except TriggerDoesNotExistException as e: msg = ('Trigger "%s" defined in the rule does not exist in system or it\'s missing ' 'required "parameters" attribute' % (rule.trigger['type'])) LOG.exception(msg) abort(http_client.BAD_REQUEST, msg) return extra = {'rule_db': rule_db} LOG.audit('Rule created. Rule.id=%s' % (rule_db.id), extra=extra) rule_api = RuleAPI.from_model(rule_db) return Response(json=rule_api, status=exc.HTTPCreated.code)
def get_all(self, requester_user, prefix=None, scope=FULL_SYSTEM_SCOPE, user=None, decrypt=False, sort=None, offset=0, limit=None, **raw_filters): """ List all keys. Handles requests: GET /keys/ """ if not scope: scope = FULL_SYSTEM_SCOPE if user: # Providing a user implies a user scope scope = FULL_USER_SCOPE if not requester_user: requester_user = UserDB(cfg.CONF.system_user.user) scope = get_datastore_full_scope(scope) is_all_scope = (scope == ALL_SCOPE) is_admin = rbac_utils.user_is_admin(user_db=requester_user) if is_all_scope and not is_admin: msg = '"all" scope requires administrator access' raise AccessDeniedError(message=msg, user_db=requester_user) # User needs to be either admin or requesting items for themselves self._validate_decrypt_query_parameter(decrypt=decrypt, scope=scope, is_admin=is_admin, requester_user=requester_user) user = user or requester_user.name # Validate that the authenticated user is admin if user query param is provided assert_user_is_admin_if_user_query_param_is_provided(user_db=requester_user, user=user) from_model_kwargs = {'mask_secrets': not decrypt} if scope and scope not in ALL_SCOPE: self._validate_scope(scope=scope) raw_filters['scope'] = scope if scope == USER_SCOPE or scope == FULL_USER_SCOPE: # Make sure we only returned values scoped to current user if prefix: prefix = get_key_reference(name=prefix, scope=scope, user=user) else: prefix = get_key_reference(name='', scope=scope, user=user) raw_filters['prefix'] = prefix kvp_apis = super(KeyValuePairController, self)._get_all(from_model_kwargs=from_model_kwargs, sort=sort, offset=offset, limit=limit, raw_filters=raw_filters, requester_user=requester_user) return kvp_apis
def put(self, rule, rule_ref_or_id, requester_user): rule_db = self._get_by_ref_or_id(rule_ref_or_id) permission_type = PermissionType.RULE_MODIFY rbac_utils.assert_user_has_resource_db_permission(user_db=requester_user, resource_db=rule, permission_type=permission_type) LOG.debug('PUT /rules/ lookup with id=%s found object: %s', rule_ref_or_id, rule_db) if not requester_user: requester_user = UserDB(cfg.CONF.system_user.user) # Validate that the authenticated user is admin if user query param is provided user = requester_user.name assert_user_is_admin_if_user_query_param_is_provided(user_db=requester_user, user=user) if not hasattr(rule, 'context'): rule.context = dict() rule.context['user'] = user try: if rule.id is not None and rule.id is not '' and rule.id != rule_ref_or_id: LOG.warning('Discarding mismatched id=%s found in payload and using uri_id=%s.', rule.id, rule_ref_or_id) old_rule_db = rule_db try: rule_db = RuleAPI.to_model(rule) except TriggerDoesNotExistException as e: abort(http_client.BAD_REQUEST, str(e)) return # Check referenced trigger and action permissions # Note: This needs to happen after "to_model" call since to_model performs some # validation (trigger exists, etc.) assert_user_has_rule_trigger_and_action_permission(user_db=requester_user, rule_api=rule) rule_db.id = rule_ref_or_id rule_db = Rule.add_or_update(rule_db) # After the rule has been added modify the ref_count. This way a failure to add # the rule due to violated constraints will have no impact on ref_count. increment_trigger_ref_count(rule_api=rule) except (ValueValidationException, jsonschema.ValidationError, ValueError) as e: LOG.exception('Validation failed for rule data=%s', rule) abort(http_client.BAD_REQUEST, str(e)) return # use old_rule_db for cleanup. cleanup_trigger_db_for_rule(old_rule_db) extra = {'old_rule_db': old_rule_db, 'new_rule_db': rule_db} LOG.audit('Rule updated. Rule.id=%s.' % (rule_db.id), extra=extra) rule_api = RuleAPI.from_model(rule_db) return rule_api
def put(self, rule, rule_ref_or_id, requester_user): rule_db = self._get_by_ref_or_id(rule_ref_or_id) permission_type = PermissionType.RULE_MODIFY rbac_utils.assert_user_has_resource_db_permission(user_db=requester_user, resource_db=rule, permission_type=permission_type) LOG.debug('PUT /rules/ lookup with id=%s found object: %s', rule_ref_or_id, rule_db) if not requester_user: requester_user = UserDB(cfg.CONF.system_user.user) # Validate that the authenticated user is admin if user query param is provided user = requester_user.name assert_user_is_admin_if_user_query_param_is_provided(user_db=requester_user, user=user) if not hasattr(rule, 'context'): rule.context = dict() rule.context['user'] = user try: if rule.id is not None and rule.id is not '' and rule.id != rule_ref_or_id: LOG.warning('Discarding mismatched id=%s found in payload and using uri_id=%s.', rule.id, rule_ref_or_id) old_rule_db = rule_db try: rule_db = RuleAPI.to_model(rule) except TriggerDoesNotExistException as e: abort(http_client.BAD_REQUEST, six.text_type(e)) return # Check referenced trigger and action permissions # Note: This needs to happen after "to_model" call since to_model performs some # validation (trigger exists, etc.) assert_user_has_rule_trigger_and_action_permission(user_db=requester_user, rule_api=rule) rule_db.id = rule_ref_or_id rule_db = Rule.add_or_update(rule_db) # After the rule has been added modify the ref_count. This way a failure to add # the rule due to violated constraints will have no impact on ref_count. increment_trigger_ref_count(rule_api=rule) except (ValueValidationException, jsonschema.ValidationError, ValueError) as e: LOG.exception('Validation failed for rule data=%s', rule) abort(http_client.BAD_REQUEST, six.text_type(e)) return # use old_rule_db for cleanup. cleanup_trigger_db_for_rule(old_rule_db) extra = {'old_rule_db': old_rule_db, 'new_rule_db': rule_db} LOG.audit('Rule updated. Rule.id=%s.' % (rule_db.id), extra=extra) rule_api = RuleAPI.from_model(rule_db) return rule_api
def get_all(self, requester_user, prefix=None, scope=FULL_SYSTEM_SCOPE, user=None, decrypt=False, sort=None, offset=0, limit=None, **raw_filters): """ List all keys. Handles requests: GET /keys/ """ if not scope: scope = FULL_SYSTEM_SCOPE if user: # Providing a user implies a user scope scope = FULL_USER_SCOPE if not requester_user: requester_user = UserDB(cfg.CONF.system_user.user) scope = get_datastore_full_scope(scope) is_all_scope = (scope == ALL_SCOPE) is_admin = rbac_utils.user_is_admin(user_db=requester_user) if is_all_scope and not is_admin: msg = '"all" scope requires administrator access' raise AccessDeniedError(message=msg, user_db=requester_user) # User needs to be either admin or requesting items for themselves self._validate_decrypt_query_parameter(decrypt=decrypt, scope=scope, is_admin=is_admin, requester_user=requester_user) user = user or requester_user.name # Validate that the authenticated user is admin if user query param is provided assert_user_is_admin_if_user_query_param_is_provided(user_db=requester_user, user=user) from_model_kwargs = {'mask_secrets': not decrypt} if scope and scope not in ALL_SCOPE: self._validate_scope(scope=scope) raw_filters['scope'] = scope if scope == USER_SCOPE or scope == FULL_USER_SCOPE: # Make sure we only returned values scoped to current user if prefix: prefix = get_key_reference(name=prefix, scope=scope, user=user) else: prefix = get_key_reference(name='', scope=scope, user=user) raw_filters['prefix'] = prefix kvp_apis = super(KeyValuePairController, self)._get_all(from_model_kwargs=from_model_kwargs, sort=sort, offset=offset, limit=limit, raw_filters=raw_filters) return kvp_apis
def _handle_schedule_execution(self, liveaction_api, requester_user, context_string=None, show_secrets=False): """ :param liveaction: LiveActionAPI object. :type liveaction: :class:`LiveActionAPI` """ if not requester_user: requester_user = UserDB(cfg.CONF.system_user.user) # Assert action ref is valid action_ref = liveaction_api.action action_db = action_utils.get_action_by_ref(action_ref) if not action_db: message = 'Action "%s" cannot be found.' % action_ref LOG.warning(message) abort(http_client.BAD_REQUEST, message) # Assert the permissions assert_user_has_resource_db_permission( user_db=requester_user, resource_db=action_db, permission_type=PermissionType.ACTION_EXECUTE) # Validate that the authenticated user is admin if user query param is provided user = liveaction_api.user or requester_user.name assert_user_is_admin_if_user_query_param_is_provided( user_db=requester_user, user=user) try: return self._schedule_execution(liveaction=liveaction_api, requester_user=requester_user, user=user, context_string=context_string, show_secrets=show_secrets, pack=action_db.pack) except ValueError as e: LOG.exception('Unable to execute action.') abort(http_client.BAD_REQUEST, str(e)) except jsonschema.ValidationError as e: LOG.exception( 'Unable to execute action. Parameter validation failed.') abort(http_client.BAD_REQUEST, re.sub("u'([^']*)'", r"'\1'", e.message)) except trace_exc.TraceNotFoundException as e: abort(http_client.BAD_REQUEST, str(e)) except validation_exc.ValueValidationException as e: raise e except Exception as e: LOG.exception( 'Unable to execute action. Unexpected error encountered.') abort(http_client.INTERNAL_SERVER_ERROR, str(e))
def delete(self, name, scope=FULL_SYSTEM_SCOPE, requester_user=None, 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 assert_user_is_admin_if_user_query_param_is_provided( user_db=requester_user, 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) return Response(status=http_client.NO_CONTENT)
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 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 _handle_schedule_execution(self, liveaction_api, requester_user, context_string=None, show_secrets=False): """ :param liveaction: LiveActionAPI object. :type liveaction: :class:`LiveActionAPI` """ if not requester_user: requester_user = UserDB(cfg.CONF.system_user.user) # Assert action ref is valid action_ref = liveaction_api.action action_db = action_utils.get_action_by_ref(action_ref) if not action_db: message = 'Action "%s" cannot be found.' % (action_ref) LOG.warning(message) abort(http_client.BAD_REQUEST, message) # Assert the permissions assert_user_has_resource_db_permission(user_db=requester_user, resource_db=action_db, permission_type=PermissionType.ACTION_EXECUTE) # Validate that the authenticated user is admin if user query param is provided user = liveaction_api.user or requester_user.name assert_user_is_admin_if_user_query_param_is_provided(user_db=requester_user, user=user) try: return self._schedule_execution(liveaction=liveaction_api, requester_user=requester_user, user=user, context_string=context_string, show_secrets=show_secrets, action_db=action_db) except ValueError as e: LOG.exception('Unable to execute action.') abort(http_client.BAD_REQUEST, six.text_type(e)) except jsonschema.ValidationError as e: LOG.exception('Unable to execute action. Parameter validation failed.') abort(http_client.BAD_REQUEST, re.sub("u'([^']*)'", r"'\1'", getattr(e, 'message', six.text_type(e)))) except trace_exc.TraceNotFoundException as e: abort(http_client.BAD_REQUEST, six.text_type(e)) except validation_exc.ValueValidationException as e: raise e except Exception as e: LOG.exception('Unable to execute action. Unexpected error encountered.') abort(http_client.INTERNAL_SERVER_ERROR, six.text_type(e))
def get_one(self, name, requester_user, scope=FULL_SYSTEM_SCOPE, user=None, decrypt=False): """ List key by name. Handle: GET /keys/key1 """ if not scope: scope = FULL_SYSTEM_SCOPE if user: # Providing a user implies a user scope scope = FULL_USER_SCOPE if not requester_user: requester_user = UserDB(cfg.CONF.system_user.user) scope = get_datastore_full_scope(scope) self._validate_scope(scope=scope) is_admin = rbac_utils.user_is_admin(user_db=requester_user) # User needs to be either admin or requesting item for itself self._validate_decrypt_query_parameter(decrypt=decrypt, scope=scope, is_admin=is_admin, requester_user=requester_user) user = user or requester_user.name # Validate that the authenticated user is admin if user query param is provided assert_user_is_admin_if_user_query_param_is_provided( user_db=requester_user, user=user) key_ref = get_key_reference(scope=scope, name=name, user=user) from_model_kwargs = {'mask_secrets': not decrypt} kvp_api = self._get_one_by_scope_and_name( name=key_ref, scope=scope, from_model_kwargs=from_model_kwargs) return kvp_api
def get_one(self, name, requester_user, scope=FULL_SYSTEM_SCOPE, user=None, decrypt=False): """ List key by name. Handle: GET /keys/key1 """ if not scope: scope = FULL_SYSTEM_SCOPE if user: # Providing a user implies a user scope scope = FULL_USER_SCOPE if not requester_user: requester_user = UserDB(cfg.CONF.system_user.user) scope = get_datastore_full_scope(scope) self._validate_scope(scope=scope) is_admin = rbac_utils.user_is_admin(user_db=requester_user) # User needs to be either admin or requesting item for itself self._validate_decrypt_query_parameter(decrypt=decrypt, scope=scope, is_admin=is_admin, requester_user=requester_user) user = user or requester_user.name # Validate that the authenticated user is admin if user query param is provided assert_user_is_admin_if_user_query_param_is_provided(user_db=requester_user, user=user) key_ref = get_key_reference(scope=scope, name=name, user=user) from_model_kwargs = {'mask_secrets': not decrypt} kvp_api = self._get_one_by_scope_and_name( name=key_ref, scope=scope, from_model_kwargs=from_model_kwargs ) return kvp_api
def put(self, kvp, name, requester_user, scope=FULL_SYSTEM_SCOPE): """ Create a new entry or update an existing one. """ if not scope: scope = FULL_SYSTEM_SCOPE if not requester_user: requester_user = UserDB(cfg.CONF.system_user.user) scope = getattr(kvp, 'scope', scope) scope = get_datastore_full_scope(scope) self._validate_scope(scope=scope) user = getattr(kvp, 'user', requester_user.name) or requester_user.name # Validate that the authenticated user is admin if user query param is provided assert_user_is_admin_if_user_query_param_is_provided(user_db=requester_user, 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 get_one(self, name, requester_user, scope=FULL_SYSTEM_SCOPE, user=None, decrypt=False): """ List key by name. Handle: GET /keys/key1 """ if not scope: # Default to system scope scope = FULL_SYSTEM_SCOPE if user: # Providing a user implies a user scope scope = FULL_USER_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 needs to be either admin or requesting item for itself self._validate_decrypt_query_parameter(decrypt=decrypt, scope=scope, requester_user=requester_user) user_query_param_filter = bool(user) current_user = requester_user.name user = user or requester_user.name # Validate that the authenticated user is admin if user query param is provided assert_user_is_admin_if_user_query_param_is_provided(user_db=requester_user, user=user, require_rbac=True) # Additional guard to ensure there is no information leakage across users is_admin = rbac_utils.user_is_admin(user_db=requester_user) if is_admin and user_query_param_filter: # Retrieve values scoped to the provided user user_scope_prefix = get_key_reference(name=name, scope=USER_SCOPE, user=user) else: # RBAC not enabled or user is not an admin, retrieve user scoped values for the # current user user_scope_prefix = get_key_reference(name=name, scope=USER_SCOPE, user=current_user) if scope == FULL_USER_SCOPE: key_ref = user_scope_prefix elif scope == FULL_SYSTEM_SCOPE: key_ref = get_key_reference(scope=FULL_SYSTEM_SCOPE, name=name, user=user) else: raise ValueError('Invalid scope: %s' % (scope)) from_model_kwargs = {'mask_secrets': not decrypt} kvp_api = self._get_one_by_scope_and_name( name=key_ref, scope=scope, from_model_kwargs=from_model_kwargs ) return kvp_api
def put(self, kvp, name, requester_user, scope=FULL_SYSTEM_SCOPE): """ Create a new entry or update an existing one. """ if not scope: scope = FULL_SYSTEM_SCOPE if not requester_user: requester_user = UserDB(cfg.CONF.system_user.user) scope = getattr(kvp, 'scope', scope) scope = get_datastore_full_scope(scope) self._validate_scope(scope=scope) user = getattr(kvp, 'user', requester_user.name) or requester_user.name # Validate that the authenticated user is admin if user query param is provided assert_user_is_admin_if_user_query_param_is_provided(user_db=requester_user, user=user, require_rbac=True) # Validate that encrypted option can only be used by admins encrypted = getattr(kvp, 'encrypted', False) self._validate_encrypted_query_parameter(encrypted=encrypted, scope=scope, requester_user=requester_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 # st2client sends invalid id when initially setting a key so we ignore those id_ = kvp.__dict__.get('id', None) if not existing_kvp_api and id_ and not bson.ObjectId.is_valid(id_): del kvp.__dict__['id'] 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, six.text_type(e)) return except CryptoKeyNotSetupException as e: LOG.exception(six.text_type(e)) abort(http_client.BAD_REQUEST, six.text_type(e)) return except InvalidScopeException as e: LOG.exception(six.text_type(e)) abort(http_client.BAD_REQUEST, six.text_type(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_all(self, requester_user, prefix=None, scope=FULL_SYSTEM_SCOPE, user=None, decrypt=False, sort=None, offset=0, limit=None, **raw_filters): """ List all keys. Handles requests: GET /keys/ """ if not scope: # Default to system scope scope = FULL_SYSTEM_SCOPE if user: # Providing a user implies a user scope scope = FULL_USER_SCOPE if not requester_user: requester_user = UserDB(cfg.CONF.system_user.user) scope = get_datastore_full_scope(scope) # "all" scope can only be used by the admins (on RBAC installations) self._validate_all_scope(scope=scope, requester_user=requester_user) # User needs to be either admin or requesting items for themselves self._validate_decrypt_query_parameter(decrypt=decrypt, scope=scope, requester_user=requester_user) user_query_param_filter = bool(user) current_user = requester_user.name user = user or requester_user.name # Validate that the authenticated user is admin if user query param is provided assert_user_is_admin_if_user_query_param_is_provided(user_db=requester_user, user=user, require_rbac=True) from_model_kwargs = {'mask_secrets': not decrypt} if scope and scope not in ALL_SCOPE: self._validate_scope(scope=scope) raw_filters['scope'] = scope # Set prefix which will be used for user-scoped items. # NOTE: It's very important raw_filters['prefix'] is set when requesting user scoped items # to avoid information leakage (aka user1 retrieves items for user2) is_admin = rbac_utils.user_is_admin(user_db=requester_user) if is_admin and user_query_param_filter: # Retrieve values scoped to the provided user user_scope_prefix = get_key_reference(name=prefix or '', scope=USER_SCOPE, user=user) else: # RBAC not enabled or user is not an admin, retrieve user scoped values for the # current user user_scope_prefix = get_key_reference(name=prefix or '', scope=USER_SCOPE, user=current_user) if scope == ALL_SCOPE: # Special case for ALL_SCOPE # 1. Retrieve system scoped values raw_filters['scope'] = FULL_SYSTEM_SCOPE raw_filters['prefix'] = prefix assert 'scope' in raw_filters kvp_apis_system = super(KeyValuePairController, self)._get_all( from_model_kwargs=from_model_kwargs, sort=sort, offset=offset, limit=limit, raw_filters=raw_filters, requester_user=requester_user) # 2. Retrieve user scoped items for current user or for all the users (depending if the # authenticated user is admin and if ?user is provided) raw_filters['scope'] = FULL_USER_SCOPE if cfg.CONF.rbac.enable and is_admin and not user_query_param_filter: # Admin user retrieving user-scoped items for all the users raw_filters['prefix'] = prefix or '' else: raw_filters['prefix'] = user_scope_prefix assert 'scope' in raw_filters assert 'prefix' in raw_filters kvp_apis_user = super(KeyValuePairController, self)._get_all( from_model_kwargs=from_model_kwargs, sort=sort, offset=offset, limit=limit, raw_filters=raw_filters, requester_user=requester_user) # Combine the result kvp_apis = [] kvp_apis.extend(kvp_apis_system.json or []) kvp_apis.extend(kvp_apis_user.json or []) elif scope in [USER_SCOPE, FULL_USER_SCOPE]: # Make sure we only returned values scoped to current user prefix = get_key_reference(name=prefix or '', scope=scope, user=user) raw_filters['prefix'] = user_scope_prefix assert 'scope' in raw_filters assert 'prefix' in raw_filters kvp_apis = super(KeyValuePairController, self)._get_all( from_model_kwargs=from_model_kwargs, sort=sort, offset=offset, limit=limit, raw_filters=raw_filters, requester_user=requester_user) elif scope in [SYSTEM_SCOPE, FULL_SYSTEM_SCOPE]: raw_filters['prefix'] = prefix assert 'scope' in raw_filters kvp_apis = super(KeyValuePairController, self)._get_all( from_model_kwargs=from_model_kwargs, sort=sort, offset=offset, limit=limit, raw_filters=raw_filters, requester_user=requester_user) else: raise ValueError('Invalid scope: %s' % (scope)) return kvp_apis