def post(self, instance, requester_user): """ Create a new policy. Handles requests: POST /policies/ """ permission_type = PermissionType.POLICY_CREATE rbac_utils = get_rbac_backend().get_utils_class() rbac_utils.assert_user_has_resource_api_permission(user_db=requester_user, resource_api=instance, permission_type=permission_type) op = 'POST /policies/' db_model = self.model.to_model(instance) LOG.debug('%s verified object: %s', op, db_model) db_model = self.access.add_or_update(db_model) LOG.debug('%s created object: %s', op, db_model) LOG.audit('Policy created. Policy.id=%s' % (db_model.id), extra={'policy_db': db_model}) exec_result = self.model.from_model(db_model) return Response(json=exec_result, status=http_client.CREATED)
def delete(self, rule_ref_or_id, requester_user): """ Delete a rule. Handles requests: DELETE /rules/1 """ rule_db = self._get_by_ref_or_id(ref_or_id=rule_ref_or_id) permission_type = PermissionType.RULE_DELETE rbac_utils = get_rbac_backend().get_utils_class() rbac_utils.assert_user_has_resource_db_permission(user_db=requester_user, resource_db=rule_db, permission_type=permission_type) LOG.debug('DELETE /rules/ lookup with id=%s found object: %s', rule_ref_or_id, rule_db) try: Rule.delete(rule_db) except Exception as e: LOG.exception('Database delete encountered exception during delete of id="%s".', rule_ref_or_id) abort(http_client.INTERNAL_SERVER_ERROR, six.text_type(e)) return # use old_rule_db for cleanup. cleanup_trigger_db_for_rule(rule_db) extra = {'rule_db': rule_db} LOG.audit('Rule deleted. Rule.id=%s.' % (rule_db.id), extra=extra) return Response(status=http_client.NO_CONTENT)
def put(self, runner_type_api, name_or_id, requester_user): # Note: We only allow "enabled" attribute of the runner to be changed runner_type_db = self._get_by_name_or_id(name_or_id=name_or_id) permission_type = PermissionType.RUNNER_MODIFY rbac_utils = get_rbac_backend().get_utils_class() rbac_utils.assert_user_has_resource_db_permission(user_db=requester_user, resource_db=runner_type_db, permission_type=permission_type) old_runner_type_db = runner_type_db LOG.debug('PUT /runnertypes/ lookup with id=%s found object: %s', name_or_id, runner_type_db) try: if runner_type_api.id and runner_type_api.id != name_or_id: LOG.warning('Discarding mismatched id=%s found in payload and using uri_id=%s.', runner_type_api.id, name_or_id) runner_type_db.enabled = runner_type_api.enabled runner_type_db = RunnerType.add_or_update(runner_type_db) except (ValidationError, ValueError) as e: LOG.exception('Validation failed for runner type data=%s', runner_type_api) abort(http_client.BAD_REQUEST, six.text_type(e)) return extra = {'old_runner_type_db': old_runner_type_db, 'new_runner_type_db': runner_type_db} LOG.audit('Runner Type updated. RunnerType.id=%s.' % (runner_type_db.id), extra=extra) runner_type_api = RunnerTypeAPI.from_model(runner_type_db) return runner_type_api
def get_one(self, api_key_id_or_key, requester_user, show_secrets=None): """ List api keys. Handle: GET /apikeys/1 """ api_key_db = None try: api_key_db = ApiKey.get_by_key_or_id(api_key_id_or_key) except ApiKeyNotFoundError: msg = ('ApiKey matching %s for reference and id not found.' % (api_key_id_or_key)) LOG.exception(msg) abort(http_client.NOT_FOUND, msg) permission_type = PermissionType.API_KEY_VIEW rbac_utils = get_rbac_backend().get_utils_class() rbac_utils.assert_user_has_resource_db_permission(user_db=requester_user, resource_db=api_key_db, permission_type=permission_type) try: mask_secrets = self._get_mask_secrets(show_secrets=show_secrets, requester_user=requester_user) return ApiKeyAPI.from_model(api_key_db, mask_secrets=mask_secrets) except (ValidationError, ValueError) as e: LOG.exception('Failed to serialize API key.') abort(http_client.INTERNAL_SERVER_ERROR, six.text_type(e))
def _get_one(self, ref_or_id, requester_user, permission_type, exclude_fields=None, include_fields=None, from_model_kwargs=None): try: instance = self._get_by_ref_or_id(ref_or_id=ref_or_id, exclude_fields=exclude_fields, include_fields=include_fields) except Exception as e: LOG.exception(six.text_type(e)) abort(http_client.NOT_FOUND, six.text_type(e)) return if permission_type: rbac_utils = get_rbac_backend().get_utils_class() rbac_utils.assert_user_has_resource_db_permission(user_db=requester_user, resource_db=instance, permission_type=permission_type) # Perform resource isolation check (if supported) from_model_kwargs = from_model_kwargs or {} from_model_kwargs.update(self.from_model_kwargs) result = self.resource_model_filter(model=self.model, instance=instance, requester_user=requester_user, **from_model_kwargs) if not result: LOG.debug('Not returning the result because RBAC resource isolation is enabled and ' 'current user doesn\'t match the resource user') raise ResourceAccessDeniedPermissionIsolationError(user_db=requester_user, resource_api_or_db=instance, permission_type=permission_type) return Response(json=result)
def post(self, action_alias, requester_user): """ Create a new ActionAlias. Handles requests: POST /actionalias/ """ permission_type = PermissionType.ACTION_ALIAS_CREATE rbac_utils = get_rbac_backend().get_utils_class() rbac_utils.assert_user_has_resource_api_permission(user_db=requester_user, resource_api=action_alias, permission_type=permission_type) try: action_alias_db = ActionAliasAPI.to_model(action_alias) LOG.debug('/actionalias/ POST verified ActionAliasAPI and formulated ActionAliasDB=%s', action_alias_db) action_alias_db = ActionAlias.add_or_update(action_alias_db) except (ValidationError, ValueError, ValueValidationException) as e: LOG.exception('Validation failed for action alias data=%s.', action_alias) abort(http_client.BAD_REQUEST, six.text_type(e)) return extra = {'action_alias_db': action_alias_db} LOG.audit('Action alias created. ActionAlias.id=%s' % (action_alias_db.id), extra=extra) action_alias_api = ActionAliasAPI.from_model(action_alias_db) return Response(json=action_alias_api, status=http_client.CREATED)
def get_one(self, group_id, requester_user): rbac_utils = get_rbac_backend().get_utils_class() rbac_utils.assert_user_is_admin(user_db=requester_user) coordinator = coordination.get_coordinator() if not isinstance(group_id, six.binary_type): group_id = group_id.encode('utf-8') try: member_ids = list(coordinator.get_members(group_id).get()) except GroupNotCreated: msg = ('Group with ID "%s" not found.' % (group_id.decode('utf-8'))) raise StackStormDBObjectNotFoundError(msg) result = { 'members': [] } for member_id in member_ids: capabilities = coordinator.get_member_capabilities(group_id, member_id).get() item = { 'group_id': group_id.decode('utf-8'), 'member_id': member_id.decode('utf-8'), 'capabilities': capabilities } result['members'].append(item) return result
def _get_one_by_name_or_id(self, name_or_id, requester_user, permission_type, exclude_fields=None, include_fields=None, from_model_kwargs=None): """ :param exclude_fields: A list of object fields to exclude. :type exclude_fields: ``list`` :param include_fields: A list of object fields to include. :type include_fields: ``list`` """ instance = self._get_by_name_or_id(name_or_id=name_or_id, exclude_fields=exclude_fields, include_fields=include_fields) if permission_type: rbac_utils = get_rbac_backend().get_utils_class() rbac_utils.assert_user_has_resource_db_permission(user_db=requester_user, resource_db=instance, permission_type=permission_type) if not instance: msg = 'Unable to identify resource with name_or_id "%s".' % (name_or_id) abort(http_client.NOT_FOUND, msg) from_model_kwargs = from_model_kwargs or {} from_model_kwargs.update(self.from_model_kwargs) result = self.model.from_model(instance, **from_model_kwargs) return result
def delete(self, ref_or_id, requester_user): """ Delete an action alias. Handles requests: DELETE /actionalias/1 """ action_alias_db = self._get_by_ref_or_id(ref_or_id=ref_or_id) LOG.debug('DELETE /actionalias/ lookup with id=%s found object: %s', ref_or_id, action_alias_db) permission_type = PermissionType.ACTION_ALIAS_DELETE rbac_utils = get_rbac_backend().get_utils_class() rbac_utils.assert_user_has_resource_db_permission(user_db=requester_user, resource_db=action_alias_db, permission_type=permission_type) try: ActionAlias.delete(action_alias_db) except Exception as e: LOG.exception('Database delete encountered exception during delete of id="%s".', ref_or_id) abort(http_client.INTERNAL_SERVER_ERROR, six.text_type(e)) return extra = {'action_alias_db': action_alias_db} LOG.audit('Action alias deleted. ActionAlias.id=%s.' % (action_alias_db.id), extra=extra) return Response(status=http_client.NO_CONTENT)
def put(self, rule, rule_ref_or_id, requester_user): rule_db = self._get_by_ref_or_id(rule_ref_or_id) rbac_utils = get_rbac_backend().get_utils_class() 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 rbac_utils.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 != '' 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.) rbac_utils.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 post(self, hook, webhook_body_api, headers, requester_user): body = webhook_body_api.data permission_type = PermissionType.WEBHOOK_SEND rbac_utils = get_rbac_backend().get_utils_class() rbac_utils.assert_user_has_resource_db_permission(user_db=requester_user, resource_db=WebhookDB(name=hook), permission_type=permission_type) headers = self._get_headers_as_dict(headers) # If webhook contains a trace-tag use that else create create a unique trace-tag. trace_context = self._create_trace_context(trace_tag=headers.pop(TRACE_TAG_HEADER, None), hook=hook) if hook == 'st2' or hook == 'st2/': # When using st2 or system webhook, body needs to always be a dict if not isinstance(body, dict): type_string = get_json_type_for_python_value(body) msg = ('Webhook body needs to be an object, got: %s' % (type_string)) raise ValueError(msg) trigger = body.get('trigger', None) payload = body.get('payload', None) if not trigger: msg = 'Trigger not specified.' return abort(http_client.BAD_REQUEST, msg) self._trigger_dispatcher_service.dispatch_with_context(trigger=trigger, payload=payload, trace_context=trace_context, throw_on_validation_error=True) else: if not self._is_valid_hook(hook): self._log_request('Invalid hook.', headers, body) msg = 'Webhook %s not registered with st2' % hook return abort(http_client.NOT_FOUND, msg) triggers = self._hooks.get_triggers_for_hook(hook) payload = {} payload['headers'] = headers payload['body'] = body # Dispatch trigger instance for each of the trigger found for trigger_dict in triggers: # TODO: Instead of dispatching the whole dict we should just # dispatch TriggerDB.ref or similar self._trigger_dispatcher_service.dispatch_with_context(trigger=trigger_dict, payload=payload, trace_context=trace_context, throw_on_validation_error=True) return Response(json=body, status=http_client.ACCEPTED)
def _get_one(self, ref_or_id, requester_user): instance = self._get_by_ref_or_id(ref_or_id=ref_or_id) permission_type = PermissionType.POLICY_TYPE_VIEW rbac_utils = get_rbac_backend().get_utils_class() rbac_utils.assert_user_has_resource_db_permission(user_db=requester_user, resource_db=instance, permission_type=permission_type) result = self.model.from_model(instance) return result
def post(self, action, requester_user): """ Create a new action. Handles requests: POST /actions/ """ permission_type = PermissionType.ACTION_CREATE rbac_utils = get_rbac_backend().get_utils_class() rbac_utils.assert_user_has_resource_api_permission( user_db=requester_user, resource_api=action, permission_type=permission_type) try: # Perform validation validate_not_part_of_system_pack(action) action_validator.validate_action(action) except ( ValidationError, ValueError, ValueValidationException, InvalidActionParameterException, ) as e: LOG.exception("Unable to create action data=%s", action) abort(http_client.BAD_REQUEST, six.text_type(e)) return # Write pack data files to disk (if any are provided) data_files = getattr(action, "data_files", []) written_data_files = [] if data_files: written_data_files = self._handle_data_files(pack_ref=action.pack, data_files=data_files) action_model = ActionAPI.to_model(action) LOG.debug("/actions/ POST verified ActionAPI object=%s", action) action_db = Action.add_or_update(action_model) LOG.debug("/actions/ POST saved ActionDB object=%s", action_db) # Dispatch an internal trigger for each written data file. This way user # automate comitting this files to git using StackStorm rule if written_data_files: self._dispatch_trigger_for_written_data_files( action_db=action_db, written_data_files=written_data_files) extra = {"acion_db": action_db} LOG.audit("Action created. Action.id=%s" % (action_db.id), extra=extra) action_api = ActionAPI.from_model(action_db) return Response(json=action_api, status=http_client.CREATED)
def get_all(self, requester_user): """ List all the available permission types. Handles requests: GET /rbac/permission_types """ rbac_utils = get_rbac_backend().get_utils_class() rbac_utils.assert_user_is_admin(user_db=requester_user) result = get_resource_permission_types_with_descriptions() return result
def _validate_decrypt_query_parameter(self, decrypt, scope, requester_user): """ Validate that the provider user is either admin or requesting to decrypt value for themselves. """ rbac_utils = get_rbac_backend().get_utils_class() is_admin = rbac_utils.user_is_admin(user_db=requester_user) is_user_scope = (scope == USER_SCOPE or scope == FULL_USER_SCOPE) if decrypt and (not is_user_scope and not is_admin): msg = 'Decrypt option requires administrator access' raise AccessDeniedError(message=msg, user_db=requester_user)
def get_roles(self, include_remote=True): """ Retrieve roles assigned to that user. :param include_remote: True to also include remote role assignments. :type include_remote: ``bool`` :rtype: ``list`` of :class:`RoleDB` """ rbac_service = get_rbac_backend().get_service_class() result = rbac_service.get_roles_for_user(user_db=self, include_remote=include_remote) return result
def _schedule_execution(self, action_alias_db, params, notify, context, requester_user, show_secrets): action_ref = action_alias_db.action_ref action_db = action_utils.get_action_by_ref(action_ref) if not action_db: raise StackStormDBObjectNotFoundError( 'Action with ref "%s" not found ' % (action_ref)) rbac_utils = get_rbac_backend().get_utils_class() permission_type = PermissionType.ACTION_EXECUTE rbac_utils.assert_user_has_resource_db_permission( user_db=requester_user, resource_db=action_db, permission_type=permission_type, ) try: # prior to shipping off the params cast them to the right type. params = action_param_utils.cast_params( action_ref=action_alias_db.action_ref, params=params, cast_overrides=CAST_OVERRIDES, ) if not context: context = { "action_alias_ref": reference.get_ref_from_model(action_alias_db), "user": get_system_username(), } liveaction = LiveActionDB( action=action_alias_db.action_ref, context=context, parameters=params, notify=notify, ) _, action_execution_db = action_service.request(liveaction) mask_secrets = self._get_mask_secrets(requester_user, show_secrets=show_secrets) return ActionExecutionAPI.from_model(action_execution_db, mask_secrets=mask_secrets) 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, six.text_type(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 validate(self, validate_role_exists=False): # Parent JSON schema validation cleaned = super(BaseRoleAssigmentAPI, self).validate() # Custom validation if validate_role_exists: # Validate that the referenced roles exist in the db rbac_service = get_rbac_backend().get_service_class() # pylint: disable=no-member rbac_service.validate_roles_exists(role_names=self.roles) # pylint: enable=no-member return cleaned
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 _validate_all_scope(self, scope, requester_user): """ Validate that "all" scope can only be provided by admins on RBAC installations. """ scope = get_datastore_full_scope(scope) is_all_scope = (scope == ALL_SCOPE) rbac_utils = get_rbac_backend().get_utils_class() 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)
def get_one(self, ref_or_id, requester_user): """ Outputs the file associated with action entry_point Handles requests: GET /actions/views/entry_point/1 """ LOG.info("GET /actions/views/entry_point with ref_or_id=%s", ref_or_id) action_db = self._get_by_ref_or_id(ref_or_id=ref_or_id) permission_type = PermissionType.ACTION_VIEW rbac_utils = get_rbac_backend().get_utils_class() rbac_utils.assert_user_has_resource_db_permission( user_db=requester_user, resource_db=action_db, permission_type=permission_type, ) pack = getattr(action_db, "pack", None) entry_point = getattr(action_db, "entry_point", None) abs_path = utils.get_entry_point_abs_path(pack, entry_point) if not abs_path: raise StackStormDBObjectNotFoundError( "Action ref_or_id=%s has no entry_point to output" % ref_or_id) with codecs.open(abs_path, "r") as fp: content = fp.read() # Ensure content is utf-8 if isinstance(content, six.binary_type): content = content.decode("utf-8") try: content_type = mimetypes.guess_type(abs_path)[0] except Exception: content_type = None # Special case if /etc/mime.types doesn't contain entry for yaml, py if not content_type: _, extension = os.path.splitext(abs_path) if extension in [".yaml", ".yml"]: content_type = "application/x-yaml" elif extension in [".py"]: content_type = "application/x-python" else: content_type = "text/plain" response = Response() response.headers["Content-Type"] = content_type response.text = content return response
def _get_one(self, ref_or_id, requester_user): instance = self._get_by_ref_or_id(ref_or_id=ref_or_id) permission_type = PermissionType.POLICY_TYPE_VIEW rbac_utils = get_rbac_backend().get_utils_class() rbac_utils.assert_user_has_resource_db_permission( user_db=requester_user, resource_db=instance, permission_type=permission_type) result = self.model.from_model(instance) return result
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 get_all(self, requester_user): rbac_utils = get_rbac_backend().get_utils_class() rbac_utils.assert_user_is_admin(user_db=requester_user) coordinator = coordination.get_coordinator() group_ids = list(coordinator.get_groups().get()) group_ids = [item.decode('utf-8') for item in group_ids] result = { 'groups': group_ids } return result
def _get_one( self, ref_or_id, requester_user, permission_type, exclude_fields=None, include_fields=None, from_model_kwargs=None, ): try: instance = self._get_by_ref_or_id( ref_or_id=ref_or_id, exclude_fields=exclude_fields, include_fields=include_fields, ) except Exception as e: LOG.exception(six.text_type(e)) abort(http_client.NOT_FOUND, six.text_type(e)) return if permission_type: rbac_utils = get_rbac_backend().get_utils_class() rbac_utils.assert_user_has_resource_db_permission( user_db=requester_user, resource_db=instance, permission_type=permission_type, ) # Perform resource isolation check (if supported) from_model_kwargs = from_model_kwargs or {} from_model_kwargs.update(self.from_model_kwargs) result = self.resource_model_filter( model=self.model, instance=instance, requester_user=requester_user, **from_model_kwargs, ) if not result: LOG.debug( "Not returning the result because RBAC resource isolation is enabled and " "current user doesn't match the resource user" ) raise ResourceAccessDeniedPermissionIsolationError( user_db=requester_user, resource_api_or_db=instance, permission_type=permission_type, ) return Response(json=result)
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 permission_type = PermissionType.ACTION_EXECUTE rbac_utils = get_rbac_backend().get_utils_class() rbac_utils.assert_user_has_resource_db_permission(user_db=requester_user, resource_db=action_db, permission_type=permission_type) # Validate that the authenticated user is admin if user query param is provided user = liveaction_api.user or requester_user.name rbac_utils.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, ref_or_id, requester_user): """ Outputs the file associated with action entry_point Handles requests: GET /actions/views/entry_point/1 """ LOG.info('GET /actions/views/entry_point with ref_or_id=%s', ref_or_id) action_db = self._get_by_ref_or_id(ref_or_id=ref_or_id) permission_type = PermissionType.ACTION_VIEW rbac_utils = get_rbac_backend().get_utils_class() rbac_utils.assert_user_has_resource_db_permission(user_db=requester_user, resource_db=action_db, permission_type=permission_type) pack = getattr(action_db, 'pack', None) entry_point = getattr(action_db, 'entry_point', None) abs_path = utils.get_entry_point_abs_path(pack, entry_point) if not abs_path: raise StackStormDBObjectNotFoundError('Action ref_or_id=%s has no entry_point to output' % ref_or_id) with codecs.open(abs_path, 'r') as fp: content = fp.read() # Ensure content is utf-8 if isinstance(content, six.binary_type): content = content.decode('utf-8') try: content_type = mimetypes.guess_type(abs_path)[0] except Exception: content_type = None # Special case if /etc/mime.types doesn't contain entry for yaml, py if not content_type: _, extension = os.path.splitext(abs_path) if extension in ['.yaml', '.yml']: content_type = 'application/x-yaml' elif extension in ['.py']: content_type = 'application/x-python' else: content_type = 'text/plain' response = Response() response.headers['Content-Type'] = content_type response.text = content return response
def get(self, requester_user, auth_info): """ Meta API endpoint wich returns information about the currently authenticated user. Handle: GET /v1/user """ data = {} rbac_utils = get_rbac_backend().get_utils_class() rbac_service = get_rbac_backend().get_service_class() if cfg.CONF.rbac.enable and requester_user: role_dbs = rbac_service.get_roles_for_user(user_db=requester_user) roles = [role_db.name for role_db in role_dbs] else: roles = [] data = { 'username': requester_user.name, 'authentication': { 'method': auth_info['method'], 'location': auth_info['location'] }, 'rbac': { 'enabled': cfg.CONF.rbac.enable, 'roles': roles, 'is_admin': rbac_utils.user_is_admin(user_db=requester_user) } } if auth_info.get('token_expire', None): token_expire = auth_info['token_expire'].strftime( '%Y-%m-%dT%H:%M:%SZ') data['authentication']['token_expire'] = token_expire return data
def get_all(self, requester_user, sort=None, offset=0, limit=None, **raw_filters): rbac_utils = get_rbac_backend().get_utils_class() rbac_utils.assert_user_is_admin(user_db=requester_user) return self._get_all(sort=sort, offset=offset, limit=limit, raw_filters=raw_filters, requester_user=requester_user)
def post(self, api_key_api, requester_user): """ Create a new entry. """ permission_type = PermissionType.API_KEY_CREATE rbac_utils = get_rbac_backend().get_utils_class() rbac_utils.assert_user_has_resource_api_permission(user_db=requester_user, resource_api=api_key_api, permission_type=permission_type) api_key_db = None api_key = None try: if not getattr(api_key_api, 'user', None): if requester_user: api_key_api.user = requester_user.name else: api_key_api.user = cfg.CONF.system_user.user try: User.get_by_name(api_key_api.user) except StackStormDBObjectNotFoundError: user_db = UserDB(name=api_key_api.user) User.add_or_update(user_db) extra = {'username': api_key_api.user, 'user': user_db} LOG.audit('Registered new user "%s".' % (api_key_api.user), extra=extra) # If key_hash is provided use that and do not create a new key. The assumption # is user already has the original api-key if not getattr(api_key_api, 'key_hash', None): api_key, api_key_hash = auth_util.generate_api_key_and_hash() # store key_hash in DB api_key_api.key_hash = api_key_hash api_key_db = ApiKey.add_or_update(ApiKeyAPI.to_model(api_key_api)) except (ValidationError, ValueError) as e: LOG.exception('Validation failed for api_key data=%s.', api_key_api) abort(http_client.BAD_REQUEST, six.text_type(e)) extra = {'api_key_db': api_key_db} LOG.audit('ApiKey created. ApiKey.id=%s' % (api_key_db.id), extra=extra) api_key_create_response_api = ApiKeyCreateResponseAPI.from_model(api_key_db) # Return real api_key back to user. A one-way hash of the api_key is stored in the DB # only the real value only returned at create time. Also, no masking of key here since # the user needs to see this value atleast once. api_key_create_response_api.key = api_key return Response(json=api_key_create_response_api, status=http_client.CREATED)
def get_one(self, ref_or_id, requester_user): """ Outputs the content of all the files inside the pack. Handles requests: GET /packs/views/files/<pack_ref_or_id> """ pack_db = self._get_by_ref_or_id(ref_or_id=ref_or_id) rbac_utils = get_rbac_backend().get_utils_class() rbac_utils.assert_user_has_resource_db_permission( user_db=requester_user, resource_db=pack_db, permission_type=PermissionType.PACK_VIEW, ) if not pack_db: msg = 'Pack with ref_or_id "%s" does not exist' % (ref_or_id) raise StackStormDBObjectNotFoundError(msg) pack_ref = pack_db.ref pack_files = pack_db.files result = [] for file_path in pack_files: normalized_file_path = get_pack_file_abs_path( pack_ref=pack_ref, file_path=file_path ) if not normalized_file_path or not os.path.isfile(normalized_file_path): # Ignore references to files which don't exist on disk continue file_size = self._get_file_size(file_path=normalized_file_path) if file_size is not None and file_size > MAX_FILE_SIZE: LOG.debug( 'Skipping file "%s" which size exceeds max file size (%s bytes)' % (normalized_file_path, MAX_FILE_SIZE) ) continue content = self._get_file_content(file_path=normalized_file_path) include_file = self._include_file(file_path=file_path, content=content) if not include_file: LOG.debug('Skipping binary file "%s"' % (normalized_file_path)) continue item = {"file_path": file_path, "content": content} result.append(item) return result
def get(self, requester_user, auth_info): """ Meta API endpoint wich returns information about the currently authenticated user. Handle: GET /v1/user """ data = {} rbac_utils = get_rbac_backend().get_utils_class() rbac_service = get_rbac_backend().get_service_class() if cfg.CONF.rbac.enable and requester_user: role_dbs = rbac_service.get_roles_for_user(user_db=requester_user) roles = [role_db.name for role_db in role_dbs] else: roles = [] data = { 'username': requester_user.name, 'authentication': { 'method': auth_info['method'], 'location': auth_info['location'] }, 'rbac': { 'enabled': cfg.CONF.rbac.enable, 'roles': roles, 'is_admin': rbac_utils.user_is_admin(user_db=requester_user) } } if auth_info.get('token_expire', None): token_expire = auth_info['token_expire'].strftime('%Y-%m-%dT%H:%M:%SZ') data['authentication']['token_expire'] = token_expire return data
def get(self, requester_user, auth_info): """ Meta API endpoint wich returns information about the currently authenticated user. Handle: GET /v1/user """ data = {} rbac_utils = get_rbac_backend().get_utils_class() rbac_service = get_rbac_backend().get_service_class() if cfg.CONF.rbac.enable and requester_user: role_dbs = rbac_service.get_roles_for_user(user_db=requester_user) roles = [role_db.name for role_db in role_dbs] else: roles = [] data = { "username": requester_user.name, "authentication": { "method": auth_info["method"], "location": auth_info["location"], }, "rbac": { "enabled": cfg.CONF.rbac.enable, "roles": roles, "is_admin": rbac_utils.user_is_admin(user_db=requester_user), }, } if auth_info.get("token_expire", None): token_expire = auth_info["token_expire"].strftime("%Y-%m-%dT%H:%M:%SZ") data["authentication"]["token_expire"] = token_expire return data
def put(self, action, ref_or_id, requester_user): action_db = self._get_by_ref_or_id(ref_or_id=ref_or_id) # Assert permissions permission_type = PermissionType.ACTION_MODIFY rbac_utils = get_rbac_backend().get_utils_class() rbac_utils.assert_user_has_resource_db_permission( user_db=requester_user, resource_db=action_db, permission_type=permission_type, ) action_id = action_db.id if not getattr(action, "pack", None): action.pack = action_db.pack # Perform validation validate_not_part_of_system_pack(action) action_validator.validate_action(action) # Write pack data files to disk (if any are provided) data_files = getattr(action, "data_files", []) written_data_files = [] if data_files: written_data_files = self._handle_data_files(pack_ref=action.pack, data_files=data_files) try: action_db = ActionAPI.to_model(action) LOG.debug("/actions/ PUT incoming action: %s", action_db) action_db.id = action_id action_db = Action.add_or_update(action_db) LOG.debug("/actions/ PUT after add_or_update: %s", action_db) except (ValidationError, ValueError) as e: LOG.exception("Unable to update action data=%s", action) abort(http_client.BAD_REQUEST, six.text_type(e)) return # Dispatch an internal trigger for each written data file. This way user # automate committing this files to git using StackStorm rule if written_data_files: self._dispatch_trigger_for_written_data_files( action_db=action_db, written_data_files=written_data_files) action_api = ActionAPI.from_model(action_db) LOG.debug("PUT /actions/ client_result=%s", action_api) return action_api
def get_one(self, url, requester_user): triggers = self._hooks.get_triggers_for_hook(url) if not triggers: abort(http_client.NOT_FOUND) return permission_type = PermissionType.WEBHOOK_VIEW rbac_utils = get_rbac_backend().get_utils_class() rbac_utils.assert_user_has_resource_db_permission(user_db=requester_user, resource_db=WebhookDB(name=url), permission_type=permission_type) # For demonstration purpose return 1st return triggers[0]
def user_has_permission(user_db, permission_type): """ Check that the provided user has specified permission. """ if not cfg.CONF.rbac.enable: return True # TODO Verify permission type for the provided resource type rbac_backend = get_rbac_backend() resolver = rbac_backend.get_resolver_for_permission_type( permission_type=permission_type) result = resolver.user_has_permission(user_db=user_db, permission_type=permission_type) return result
def post(self, action, requester_user): """ Create a new action. Handles requests: POST /actions/ """ permission_type = PermissionType.ACTION_CREATE rbac_utils = get_rbac_backend().get_utils_class() rbac_utils.assert_user_has_resource_api_permission(user_db=requester_user, resource_api=action, permission_type=permission_type) try: # Perform validation validate_not_part_of_system_pack(action) action_validator.validate_action(action) except (ValidationError, ValueError, ValueValidationException, InvalidActionParameterException) as e: LOG.exception('Unable to create action data=%s', action) abort(http_client.BAD_REQUEST, six.text_type(e)) return # Write pack data files to disk (if any are provided) data_files = getattr(action, 'data_files', []) written_data_files = [] if data_files: written_data_files = self._handle_data_files(pack_ref=action.pack, data_files=data_files) action_model = ActionAPI.to_model(action) LOG.debug('/actions/ POST verified ActionAPI object=%s', action) action_db = Action.add_or_update(action_model) LOG.debug('/actions/ POST saved ActionDB object=%s', action_db) # Dispatch an internal trigger for each written data file. This way user # automate comitting this files to git using StackStorm rule if written_data_files: self._dispatch_trigger_for_written_data_files(action_db=action_db, written_data_files=written_data_files) extra = {'acion_db': action_db} LOG.audit('Action created. Action.id=%s' % (action_db.id), extra=extra) action_api = ActionAPI.from_model(action_db) return Response(json=action_api, status=http_client.CREATED)
def _get_one_by_ref_or_id(self, ref_or_id, requester_user, exclude_fields=None): instance = self._get_by_ref_or_id(ref_or_id=ref_or_id, exclude_fields=exclude_fields) rbac_utils = get_rbac_backend().get_utils_class() rbac_utils.assert_user_has_resource_db_permission(user_db=requester_user, resource_db=instance, permission_type=PermissionType.PACK_VIEW) if not instance: msg = 'Unable to identify resource with ref_or_id "%s".' % (ref_or_id) abort(http_client.NOT_FOUND, msg) return result = self.model.from_model(instance, **self.from_model_kwargs) return result
def _get_mask_secrets(self, requester_user, show_secrets=None): """ Return a value for mask_secrets which can be used in masking secret properties to be retruned by any API. The default value is as per the config however admin users have the ability to override by passing in a special query parameter ?show_secrets=True. :rtype: ``bool`` """ mask_secrets = cfg.CONF.api.mask_secrets rbac_utils = get_rbac_backend().get_utils_class() if show_secrets and rbac_utils.user_is_admin(user_db=requester_user): mask_secrets = False return mask_secrets
def get_one(self, url, requester_user): triggers = self._hooks.get_triggers_for_hook(url) if not triggers: abort(http_client.NOT_FOUND) return permission_type = PermissionType.WEBHOOK_VIEW rbac_utils = get_rbac_backend().get_utils_class() rbac_utils.assert_user_has_resource_db_permission( user_db=requester_user, resource_db=WebhookDB(name=url), permission_type=permission_type) # For demonstration purpose return 1st return triggers[0]
def get_all(self, requester_user, sort=None, offset=0, limit=None, **raw_filters): user = raw_filters.get('user', None) rbac_utils = get_rbac_backend().get_utils_class() rbac_utils.assert_user_is_admin_or_operating_on_own_resource( user_db=requester_user, user=user) return self._get_all(sort=sort, offset=offset, limit=limit, raw_filters=raw_filters, requester_user=requester_user)
def put(self, api_key_api, api_key_id_or_key, requester_user): api_key_db = ApiKey.get_by_key_or_id(api_key_id_or_key) permission_type = PermissionType.API_KEY_MODIFY rbac_utils = get_rbac_backend().get_utils_class() rbac_utils.assert_user_has_resource_db_permission( user_db=requester_user, resource_db=api_key_db, permission_type=permission_type, ) old_api_key_db = api_key_db api_key_db = ApiKeyAPI.to_model(api_key_api) try: User.get_by_name(api_key_api.user) except StackStormDBObjectNotFoundError: user_db = UserDB(name=api_key_api.user) User.add_or_update(user_db) extra = {"username": api_key_api.user, "user": user_db} LOG.audit('Registered new user "%s".' % (api_key_api.user), extra=extra) # Passing in key_hash as MASKED_ATTRIBUTE_VALUE is expected since we do not # leak it out therefore it is expected we get the same value back. Interpret # this special code and empty value as no-change if api_key_db.key_hash == MASKED_ATTRIBUTE_VALUE or not api_key_db.key_hash: api_key_db.key_hash = old_api_key_db.key_hash # Rather than silently ignore any update to key_hash it is better to explicitly # disallow and notify user. if old_api_key_db.key_hash != api_key_db.key_hash: raise ValueError("Update of key_hash is not allowed.") api_key_db.id = old_api_key_db.id api_key_db = ApiKey.add_or_update(api_key_db) extra = { "old_api_key_db": old_api_key_db, "new_api_key_db": api_key_db } LOG.audit("API Key updated. ApiKey.id=%s." % (api_key_db.id), extra=extra) api_key_api = ApiKeyAPI.from_model(api_key_db) return api_key_api
def put(self, action_alias, ref_or_id, requester_user): """ Update an action alias. Handles requests: PUT /actionalias/1 """ action_alias_db = self._get_by_ref_or_id(ref_or_id=ref_or_id) LOG.debug('PUT /actionalias/ lookup with id=%s found object: %s', ref_or_id, action_alias_db) permission_type = PermissionType.ACTION_ALIAS_MODIFY rbac_utils = get_rbac_backend().get_utils_class() rbac_utils.assert_user_has_resource_db_permission( user_db=requester_user, resource_db=action_alias_db, permission_type=permission_type) if not hasattr(action_alias, 'id'): action_alias.id = None try: if action_alias.id is not None and action_alias.id != '' and \ action_alias.id != ref_or_id: LOG.warning( 'Discarding mismatched id=%s found in payload and using uri_id=%s.', action_alias.id, ref_or_id) old_action_alias_db = action_alias_db action_alias_db = ActionAliasAPI.to_model(action_alias) action_alias_db.id = ref_or_id action_alias_db = ActionAlias.add_or_update(action_alias_db) except (ValidationError, ValueError) as e: LOG.exception('Validation failed for action alias data=%s', action_alias) abort(http_client.BAD_REQUEST, six.text_type(e)) return extra = { 'old_action_alias_db': old_action_alias_db, 'new_action_alias_db': action_alias_db } LOG.audit('Action alias updated. ActionAlias.id=%s.' % (action_alias_db.id), extra=extra) action_alias_api = ActionAliasAPI.from_model(action_alias_db) return action_alias_api
def put(self, action, ref_or_id, requester_user): action_db = self._get_by_ref_or_id(ref_or_id=ref_or_id) # Assert permissions permission_type = PermissionType.ACTION_MODIFY rbac_utils = get_rbac_backend().get_utils_class() rbac_utils.assert_user_has_resource_db_permission(user_db=requester_user, resource_db=action_db, permission_type=permission_type) action_id = action_db.id if not getattr(action, 'pack', None): action.pack = action_db.pack # Perform validation validate_not_part_of_system_pack(action) action_validator.validate_action(action) # Write pack data files to disk (if any are provided) data_files = getattr(action, 'data_files', []) written_data_files = [] if data_files: written_data_files = self._handle_data_files(pack_ref=action.pack, data_files=data_files) try: action_db = ActionAPI.to_model(action) LOG.debug('/actions/ PUT incoming action: %s', action_db) action_db.id = action_id action_db = Action.add_or_update(action_db) LOG.debug('/actions/ PUT after add_or_update: %s', action_db) except (ValidationError, ValueError) as e: LOG.exception('Unable to update action data=%s', action) abort(http_client.BAD_REQUEST, six.text_type(e)) return # Dispatch an internal trigger for each written data file. This way user # automate committing this files to git using StackStorm rule if written_data_files: self._dispatch_trigger_for_written_data_files(action_db=action_db, written_data_files=written_data_files) action_api = ActionAPI.from_model(action_db) LOG.debug('PUT /actions/ client_result=%s', action_api) return action_api
def _get_one_by_id(self, id, requester_user, permission_type, exclude_fields=None, include_fields=None, from_model_kwargs=None): """ :param exclude_fields: A list of object fields to exclude. :type exclude_fields: ``list`` :param include_fields: A list of object fields to include. :type include_fields: ``list`` """ instance = self._get_by_id(resource_id=id, exclude_fields=exclude_fields, include_fields=include_fields) if permission_type: rbac_utils = get_rbac_backend().get_utils_class() rbac_utils.assert_user_has_resource_db_permission( user_db=requester_user, resource_db=instance, permission_type=permission_type) if not instance: msg = 'Unable to identify resource with id "%s".' % id abort(http_client.NOT_FOUND, msg) from_model_kwargs = from_model_kwargs or {} from_model_kwargs.update(self.from_model_kwargs) result = self.resource_model_filter(model=self.model, instance=instance, requester_user=requester_user, **from_model_kwargs) if not result: LOG.debug( 'Not returning the result because RBAC resource isolation is enabled and ' 'current user doesn\'t match the resource user') raise ResourceAccessDeniedPermissionIsolationError( user_db=requester_user, resource_api_or_db=instance, permission_type=permission_type) return result
def delete(self, ref_or_id, requester_user): """ Delete an action. Handles requests: POST /actions/1?_method=delete DELETE /actions/1 DELETE /actions/mypack.myaction """ action_db = self._get_by_ref_or_id(ref_or_id=ref_or_id) action_id = action_db.id permission_type = PermissionType.ACTION_DELETE rbac_utils = get_rbac_backend().get_utils_class() rbac_utils.assert_user_has_resource_db_permission( user_db=requester_user, resource_db=action_db, permission_type=permission_type, ) try: validate_not_part_of_system_pack(action_db) except ValueValidationException as e: abort(http_client.BAD_REQUEST, six.text_type(e)) LOG.debug( "DELETE /actions/ lookup with ref_or_id=%s found object: %s", ref_or_id, action_db, ) try: Action.delete(action_db) except Exception as e: LOG.error( 'Database delete encountered exception during delete of id="%s". ' "Exception was %s", action_id, e, ) abort(http_client.INTERNAL_SERVER_ERROR, six.text_type(e)) return extra = {"action_db": action_db} LOG.audit("Action deleted. Action.id=%s" % (action_db.id), extra=extra) return Response(status=http_client.NO_CONTENT)
def get_one(self, resource_type, requester_user): """ List all the available permission types for a particular resource type. Handles requests: GET /rbac/permission_types/<resource type> """ rbac_utils = get_rbac_backend().get_utils_class() rbac_utils.assert_user_is_admin(user_db=requester_user) all_permission_types = get_resource_permission_types_with_descriptions() permission_types = all_permission_types.get(resource_type, None) if permission_types is None: raise exc.HTTPNotFound('Invalid resource type: %s' % (resource_type)) return permission_types
def _get_one_by_id( self, id, requester_user, permission_type, exclude_fields=None, from_model_kwargs=None, ): """Override ResourceController._get_one_by_id to contain scope of Inquiries UID hack :param exclude_fields: A list of object fields to exclude. :type exclude_fields: ``list`` """ LOG.debug('Retrieving action execution for inquiry "%s".' % id) execution_db = self._get_by_id(resource_id=id, exclude_fields=exclude_fields) if not execution_db: raise db_exceptions.StackStormDBObjectNotFoundError() # Inquiry currently does not have it's own database model and share with ActionExecution. # The object uid is in the format of "execution:<id>". To allow RBAC to resolve correctly # for inquiries, we're overriding the "get_uid" function so the object uid can be set to # "inquiry:<id>". # # TODO (mierdin): All of this should be removed once Inquiries get their own DB model. if (execution_db and getattr(execution_db, "runner", None) and execution_db.runner.get("runner_module") == INQUIRY_RUNNER): execution_db.get_uid = get_uid LOG.debug('Checking permission on inquiry "%s".' % id) if permission_type: rbac_utils = get_rbac_backend().get_utils_class() rbac_utils.assert_user_has_resource_db_permission( user_db=requester_user, resource_db=execution_db, permission_type=permission_type, ) from_model_kwargs = from_model_kwargs or {} from_model_kwargs.update(self.from_model_kwargs) result = self.model.from_model(execution_db, **from_model_kwargs) return result
def user_has_rule_trigger_permission(user_db, trigger): """ Check that the currently logged-in has necessary permissions on the trigger used / referenced inside the rule. """ if not cfg.CONF.rbac.enable: return True rbac_backend = get_rbac_backend() rules_resolver = rbac_backend.get_resolver_for_resource_type( ResourceType.RULE) has_trigger_permission = rules_resolver.user_has_trigger_permission( user_db=user_db, trigger=trigger) if has_trigger_permission: return True return False
def get_one(self, ref_or_id, requester_user): try: trigger_db = self._get_by_ref_or_id(ref_or_id=ref_or_id) except Exception as e: LOG.exception(six.text_type(e)) abort(http_client.NOT_FOUND, six.text_type(e)) return permission_type = PermissionType.TIMER_VIEW resource_db = TimerDB(pack=trigger_db.pack, name=trigger_db.name) rbac_utils = get_rbac_backend().get_utils_class() rbac_utils.assert_user_has_resource_db_permission(user_db=requester_user, resource_db=resource_db, permission_type=permission_type) result = self.model.from_model(trigger_db) return result
def put(self, sensor_type, ref_or_id, requester_user): # Note: Right now this function only supports updating of "enabled" # attribute on the SensorType model. # The reason for that is that SensorTypeAPI.to_model right now only # knows how to work with sensor type definitions from YAML files. sensor_type_db = self._get_by_ref_or_id(ref_or_id=ref_or_id) permission_type = PermissionType.SENSOR_MODIFY rbac_utils = get_rbac_backend().get_utils_class() rbac_utils.assert_user_has_resource_db_permission(user_db=requester_user, resource_db=sensor_type_db, permission_type=permission_type) sensor_type_id = sensor_type_db.id try: validate_not_part_of_system_pack(sensor_type_db) except ValueValidationException as e: abort(http_client.BAD_REQUEST, six.text_type(e)) return if not getattr(sensor_type, 'pack', None): sensor_type.pack = sensor_type_db.pack try: old_sensor_type_db = sensor_type_db sensor_type_db.id = sensor_type_id sensor_type_db.enabled = getattr(sensor_type, 'enabled', False) sensor_type_db = SensorType.add_or_update(sensor_type_db) except (ValidationError, ValueError) as e: LOG.exception('Unable to update sensor_type data=%s', sensor_type) abort(http_client.BAD_REQUEST, six.text_type(e)) return extra = { 'old_sensor_type_db': old_sensor_type_db, 'new_sensor_type_db': sensor_type_db } LOG.audit('Sensor updated. Sensor.id=%s.' % (sensor_type_db.id), extra=extra) sensor_type_api = SensorTypeAPI.from_model(sensor_type_db) return sensor_type_api
def _get_one(action_id, requester_user): """ List merged action & runner parameters by action id. Handle: GET /actions/views/parameters/1 """ action_db = LookupUtils._get_action_by_id(action_id) permission_type = PermissionType.ACTION_VIEW rbac_utils = get_rbac_backend().get_utils_class() rbac_utils.assert_user_has_resource_db_permission(user_db=requester_user, resource_db=action_db, permission_type=permission_type) runner_db = LookupUtils._get_runner_by_name(action_db.runner_type['name']) all_params = action_param_utils.get_params_view( action_db=action_db, runner_db=runner_db, merged_only=True) return {'parameters': all_params}
def delete(self, api_key_id_or_key, requester_user): """ Delete the key value pair. Handles requests: DELETE /apikeys/1 """ api_key_db = ApiKey.get_by_key_or_id(api_key_id_or_key) permission_type = PermissionType.API_KEY_DELETE rbac_utils = get_rbac_backend().get_utils_class() rbac_utils.assert_user_has_resource_db_permission(user_db=requester_user, resource_db=api_key_db, permission_type=permission_type) ApiKey.delete(api_key_db) extra = {'api_key_db': api_key_db} LOG.audit('ApiKey deleted. ApiKey.id=%s' % (api_key_db.id), extra=extra) return Response(status=http_client.NO_CONTENT)
def put(self, api_key_api, api_key_id_or_key, requester_user): api_key_db = ApiKey.get_by_key_or_id(api_key_id_or_key) permission_type = PermissionType.API_KEY_MODIFY rbac_utils = get_rbac_backend().get_utils_class() rbac_utils.assert_user_has_resource_db_permission(user_db=requester_user, resource_db=api_key_db, permission_type=permission_type) old_api_key_db = api_key_db api_key_db = ApiKeyAPI.to_model(api_key_api) try: User.get_by_name(api_key_api.user) except StackStormDBObjectNotFoundError: user_db = UserDB(name=api_key_api.user) User.add_or_update(user_db) extra = {'username': api_key_api.user, 'user': user_db} LOG.audit('Registered new user "%s".' % (api_key_api.user), extra=extra) # Passing in key_hash as MASKED_ATTRIBUTE_VALUE is expected since we do not # leak it out therefore it is expected we get the same value back. Interpret # this special code and empty value as no-change if api_key_db.key_hash == MASKED_ATTRIBUTE_VALUE or not api_key_db.key_hash: api_key_db.key_hash = old_api_key_db.key_hash # Rather than silently ignore any update to key_hash it is better to explicitly # disallow and notify user. if old_api_key_db.key_hash != api_key_db.key_hash: raise ValueError('Update of key_hash is not allowed.') api_key_db.id = old_api_key_db.id api_key_db = ApiKey.add_or_update(api_key_db) extra = {'old_api_key_db': old_api_key_db, 'new_api_key_db': api_key_db} LOG.audit('API Key updated. ApiKey.id=%s.' % (api_key_db.id), extra=extra) api_key_api = ApiKeyAPI.from_model(api_key_db) return api_key_api
def get_one(self, pack_ref, requester_user, show_secrets=False): """ Retrieve config for a particular pack. Handles requests: GET /configs/<pack_ref> """ from_model_kwargs = { 'mask_secrets': self._get_mask_secrets(requester_user, show_secrets=show_secrets) } try: instance = packs_service.get_pack_by_ref(pack_ref=pack_ref) except StackStormDBObjectNotFoundError: msg = 'Unable to identify resource with pack_ref "%s".' % (pack_ref) abort(http_client.NOT_FOUND, msg) rbac_utils = get_rbac_backend().get_utils_class() rbac_utils.assert_user_has_resource_db_permission(user_db=requester_user, resource_db=instance, permission_type=PermissionType.PACK_VIEW) return self._get_one_by_pack_ref(pack_ref=pack_ref, from_model_kwargs=from_model_kwargs)