def create(self, validated_data, **kwargs): """Create the new object.""" attributes = validated_data.get('attributes', {}) if not isinstance(attributes, dict): raise APIException( _('Attributes must be a dictionary of (string, string) pairs.') ) if any(not isinstance(key, str) or not isinstance(aval, str) for key, aval in list(attributes.items())): raise APIException(_('Attributes must be a dictionary (str, str)')) workflow_obj = None try: workflow_obj = Workflow( user=self.context['request'].user, name=validated_data['name'], description_text=validated_data.get('description_text', ''), nrows=0, ncols=0, attributes=attributes, ) workflow_obj.save() except Exception: if workflow_obj and workflow_obj.id: workflow_obj.delete() raise APIException(_('Workflow could not be created.')) return workflow_obj
def create(request): options = request.POST if 'new_item' in options: items = Item.objects.order_by('-id') top_item_id = items and items[0].id or 0 category_id = Category.objects.filter(id=options['category'])[0].id new_item = Item(id=top_item_id + 1, label=options['new_item'], details=options['details'], assigned_to_id=None, validation_id=3, category_id=category_id) new_item.save() return render_to_response('workflow/one_item.html', {'item' : new_item}) if 'new_category' in options: categories = Category.objects.order_by('-id') top_category_id = categories and categories[0].id or 0 orders = Category.objects.order_by('-order') top_order = orders and orders[0].id or 0 new_category = Category(top_category_id + 1, options['new_category'], top_order, options['workflow_id']) new_category.save() return render_to_response('workflow/one_category.html', {'category' : new_category}) if options['new_section']: sections = WorkflowSection.objects.order_by('-id') top_section_id = sections and sections[0].id or 0 new_section = WorkflowSection(top_section_id + 1, options['new_section']) new_section.save() else: new_section = WorkflowSection.objects.filter(label=options['section'])[0] workflows = Workflow.objects.order_by('-id') top_id = workflows and workflows[0].id or 0 new_workflow = Workflow(id=top_id + 1, workflow_section=new_section, label=options['new_workflow']) new_workflow.save() return HttpResponseRedirect(reverse('index'))
def _do_clone_workflow(workflow: Workflow) -> Workflow: """Clone the workflow. :param workflow: source workflow :return: Clone object """ new_workflow = Workflow( user=workflow.user, name=create_new_name( workflow.name, Workflow.objects.filter( Q(user=workflow.user) | Q(shared=workflow.user)), ), description_text=workflow.description_text, nrows=workflow.nrows, ncols=workflow.ncols, attributes=copy.deepcopy(workflow.attributes), query_builder_ops=copy.deepcopy(workflow.query_builder_ops), luser_email_column_md5=workflow.luser_email_column_md5, lusers_is_outdated=workflow.lusers_is_outdated) new_workflow.save() try: new_workflow.shared.set(list(workflow.shared.all())) new_workflow.lusers.set(list(workflow.lusers.all())) # Clone the columns for item_obj in workflow.columns.all(): do_clone_column_only(item_obj, new_workflow=new_workflow) # Update the luser_email_column if needed: if workflow.luser_email_column: new_workflow.luser_email_column = new_workflow.columns.get( name=workflow.luser_email_column.name, ) # Clone the DB table clone_table(workflow.get_data_frame_table_name(), new_workflow.get_data_frame_table_name()) # Clone actions for item_obj in workflow.actions.all(): do_clone_action(item_obj, new_workflow) for item_obj in workflow.views.all(): do_clone_view(item_obj, new_workflow) # Done! new_workflow.save() except Exception as exc: new_workflow.delete() raise exc return new_workflow
def save_attribute_form( request: HttpRequest, workflow: Workflow, template: str, form: forms.Form, attr_idx: int ) -> JsonResponse: """Process the AJAX request to create or update an attribute. :param request: Request object received :param workflow: current workflow being manipulated :param template: Template to render in the response :param form: Form used to ask for data :param attr_idx: Index of the attribute being manipulated :return: AJAX reponse """ if request.method == 'POST' and form.is_valid(): # Correct form submitted if not form.has_changed(): return JsonResponse({'html_redirect': None}) # proceed with updating the attributes. wf_attributes = workflow.attributes # If key_idx is not -1, this means we are editing an existing pair if attr_idx != -1: key = sorted(wf_attributes.keys())[attr_idx] wf_attributes.pop(key) # Rename the appearances of the variable in all actions for action_item in workflow.actions.all(): action_item.rename_variable(key, form.cleaned_data['key']) # Update value wf_attributes[form.cleaned_data['key']] = form.cleaned_data[ 'attr_value'] workflow.attributes = wf_attributes workflow.save() # Log the event Log.objects.register( request.user, Log.WORKFLOW_ATTRIBUTE_CREATE, workflow, { 'id': workflow.id, 'name': workflow.name, 'attr_key': form.cleaned_data['key'], 'attr_val': form.cleaned_data['attr_value']}) return JsonResponse({'html_redirect': ''}) return JsonResponse({ 'html_form': render_to_string( template, {'form': form, 'id': attr_idx}, request=request), })
def create(self, validated_data, **kwargs): """Create the new workflow.""" wflow_name = self.context.get('name') if not wflow_name: wflow_name = self.validated_data.get('name') if not wflow_name: raise Exception(_('Unexpected empty workflow name.')) if Workflow.objects.filter(name=wflow_name, user=self.context['user']).exists(): raise Exception( _('There is a workflow with this name. ' + 'Please provide a workflow name in the import page.')) # Initial values workflow_obj = None try: workflow_obj = Workflow( user=self.context['user'], name=wflow_name, description_text=validated_data['description_text'], nrows=0, ncols=0, attributes=validated_data['attributes'], query_builder_ops=validated_data.get('query_builder_ops', {}), ) workflow_obj.save() # Create the columns column_data = ColumnSerializer(data=validated_data.get( 'columns', []), many=True, context={'workflow': workflow_obj}) # And save its content if column_data.is_valid(): columns = column_data.save() else: raise Exception(_('Unable to save column information')) # If there is any column with position = 0, recompute (this is to # guarantee backward compatibility. if any(col.position == 0 for col in columns): for idx, col in enumerate(columns): col.position = idx + 1 col.save() # Load the data frame data_frame = validated_data.get('data_frame') if data_frame is not None: # Store the table in the DB store_table( data_frame, workflow_obj.get_data_frame_table_name(), dtype={ col.name: col.data_type for col in workflow_obj.columns.all() }, ) # Reconcile now the information in workflow and columns with # the one loaded workflow_obj.ncols = validated_data['ncols'] workflow_obj.nrows = validated_data['nrows'] workflow_obj.save() # Create the actions pointing to the workflow action_data = ActionSerializer(data=validated_data.get( 'actions', []), many=True, context={ 'workflow': workflow_obj, 'columns': columns }) if action_data.is_valid(): action_data.save() else: raise Exception(_('Unable to save column information')) # Create the views pointing to the workflow view_data = ViewSerializer(data=validated_data.get('views', []), many=True, context={ 'workflow': workflow_obj, 'columns': columns }) if view_data.is_valid(): view_data.save() else: raise Exception(_('Unable to save column information')) except Exception: # Get rid of the objects created if workflow_obj: if workflow_obj.id: workflow_obj.delete() raise return workflow_obj
def workflow_delete_column( workflow: Workflow, column: Column, cond_to_delete: Optional[List[Condition]] = None, ): """Remove column from workflow. Given a workflow and a column, removes it from the workflow (and the corresponding data frame :param workflow: Workflow object :param column: Column object to delete :param cond_to_delete: List of conditions to delete after removing the column :return: Nothing. Effect reflected in the database """ # Drop the column from the DB table storing the data frame df_drop_column(workflow.get_data_frame_table_name(), column.name) # Reposition the columns above the one being deleted workflow.reposition_columns(column.position, workflow.ncols + 1) # Delete the column column.delete() # Update the information in the workflow workflow.ncols = workflow.ncols - 1 workflow.save() if not cond_to_delete: # The conditions to delete are not given, so calculate them # Get the conditions/actions attached to this workflow cond_to_delete = [ cond for cond in Condition.objects.filter(action__workflow=workflow, ) if column in cond.columns.all() ] # If a column disappears, the conditions that contain that variable # are removed actions_without_filters = [] for condition in cond_to_delete: if condition.is_filter: actions_without_filters.append(condition.action) # Formula has the name of the deleted column. Delete it condition.delete() # Traverse the actions for which the filter has been deleted and reassess # all their conditions # TODO: Explore how to do this asynchronously (or lazy) map(lambda act: act.update_n_rows_selected(), actions_without_filters) # If a column disappears, the views that contain only that column need to # disappear as well as they are no longer relevant. for view in workflow.views.all(): if view.columns.count() == 0: view.delete()
def post(self, request, object_id=None, action=None): try: if not action: # return workflow_edit(request, None, api=True) pass enabled = request.POST.get('enabled') is not False defender_policy = False fqdn = "" public_dir = "/" try: frontend = Frontend.objects.get(pk=request.POST['frontend']) if frontend.mode == "http": try: fqdn = request.POST['fqdn'] if not validators.domain(fqdn): return JsonResponse({ 'error': _('This FQDN is not valid') }) public_dir = request.POST['public_dir'] if public_dir and len(public_dir): if public_dir[0] != '/': public_dir = '/' + public_dir if public_dir[-1] != '/': public_dir += '/' except KeyError: return JsonResponse({ 'error': _('For a HTTP Frontend, you must define a FQDN & a public_dir') }) except Frontend.DoesNotExist: return JsonResponse({ 'error': _('frontend with id {} does not exist'.format(request.POST['frontend'])) }, status=401) except KeyError: return JsonResponse({ 'error': _('You must define a frontend for a workflow') }, status=401) try: backend = Backend.objects.get(pk=request.POST['backend']) if frontend.mode != backend.mode: return JsonResponse({ 'error': _('Frontend and Backend must be in the same mode.') }) except Backend.DoesNotExist: return JsonResponse({ 'error': _('Backend with id {} does not exist'.format(request.POST['backend'])) }, status=401) except KeyError: return JsonResponse({ 'error': _('You must define a backend for a workflow') }, status=401) if request.POST.get('defender_policy'): try: defender_policy = DefenderPolicy.objects.get(pk=request.POST['defender_policy']) except DefenderPolicy.DoesNotExist: return JsonResponse({ 'error': _('Defender Policy with id {} does not exist'.format(request.POST['defender_policy'])) }, status=401) try: if object_id: workflow = Workflow.objects.get(pk=object_id) else: workflow = Workflow() workflow.enabled = enabled workflow.name = request.POST['name'] workflow.fqdn = fqdn workflow.public_dir = public_dir workflow.frontend = frontend workflow.backend = backend workflow.save() workflow_acls = [] if defender_policy: workflow.defender_policy = defender_policy workflow.workflowacl_set.all().delete() for i, tmp_acl in enumerate(json.loads(request.POST.get('acl_frontend', "[]"))): status, acl = format_acl_from_api(tmp_acl, i, before_policy=True) if not status: return acl acl.workflow = workflow workflow_acls.append(acl) acl.save() for i, tmp_acl in enumerate(json.loads(request.POST.get('acl_backend', "[]"))): status, acl = format_acl_from_api(tmp_acl, i, before_policy=False) if not status: return acl acl.workflow = workflow workflow_acls.append(acl) acl.save() workflow.workflow_json = generate_workflow(workflow) workflow.save() nodes = workflow.frontend.reload_conf() workflow.backend.reload_conf() if workflow.defender_policy: logger.info("Need to reload the Defender Policy SPOE configuration") Cluster.api_request( "darwin.defender_policy.policy.write_defender_backend_conf", workflow.defender_policy.pk ) Cluster.api_request( "darwin.defender_policy.policy.write_defender_spoe_conf", workflow.defender_policy.pk ) # Reload HAProxy on concerned nodes for node in nodes: api_res = node.api_request("services.haproxy.haproxy.restart_service") if not api_res.get('status'): logger.error("Workflow::edit: API error while trying to " "restart HAProxy service : {}".format(api_res.get('message'))) for workflow_acl in workflow.workflowacl_set.all(): workflow_acl.delete() try: workflow.delete() except Exception: pass return JsonResponse({ 'error': api_res.get('message') }, status=500) return JsonResponse({ 'message': _('Workflow saved') }) except KeyError: return JsonResponse({ 'error': _('Partial data') }, status=401) if action and not object_id: return JsonResponse({ 'error': _('You must specify an ID') }, status=401) if action not in list(COMMAND_LIST.keys()): return JsonResponse({ 'error': _('Action not allowed') }, status=403) return COMMAND_LIST[action](request, object_id, api=True) except Exception as e: logger.critical(e, exc_info=1) if settings.DEV_MODE: error = str(e) else: error = _("An error has occurred") return JsonResponse({ 'error': error }, status=500)