Policy Check Finish
This commit is contained in:
@@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 3.0.6 on 2020-06-04 17:13
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('agents', '0002_auto_20200531_2058'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='agent',
|
||||||
|
name='checks_last_generated',
|
||||||
|
field=models.DateTimeField(blank=True, null=True),
|
||||||
|
),
|
||||||
|
]
|
||||||
22
api/tacticalrmm/agents/migrations/0004_auto_20200604_1721.py
Normal file
22
api/tacticalrmm/agents/migrations/0004_auto_20200604_1721.py
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
# Generated by Django 3.0.6 on 2020-06-04 17:21
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('agents', '0003_agent_checks_last_generated'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='agent',
|
||||||
|
name='checks_last_generated',
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='agent',
|
||||||
|
name='policies_pending',
|
||||||
|
field=models.BooleanField(default=False),
|
||||||
|
),
|
||||||
|
]
|
||||||
20
api/tacticalrmm/agents/migrations/0005_agent_policy.py
Normal file
20
api/tacticalrmm/agents/migrations/0005_agent_policy.py
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
# Generated by Django 3.0.7 on 2020-06-09 16:07
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('automation', '0003_auto_20200609_1607'),
|
||||||
|
('agents', '0004_auto_20200604_1721'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='agent',
|
||||||
|
name='policy',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='agents', to='automation.Policy'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -16,6 +16,8 @@ from django.contrib.postgres.fields import JSONField
|
|||||||
|
|
||||||
from core.models import TZ_CHOICES
|
from core.models import TZ_CHOICES
|
||||||
|
|
||||||
|
import automation
|
||||||
|
|
||||||
|
|
||||||
class Agent(models.Model):
|
class Agent(models.Model):
|
||||||
version = models.CharField(default="0.1.0", max_length=255)
|
version = models.CharField(default="0.1.0", max_length=255)
|
||||||
@@ -50,9 +52,17 @@ class Agent(models.Model):
|
|||||||
is_updating = models.BooleanField(default=False)
|
is_updating = models.BooleanField(default=False)
|
||||||
choco_installed = models.BooleanField(default=False)
|
choco_installed = models.BooleanField(default=False)
|
||||||
wmi_detail = JSONField(null=True)
|
wmi_detail = JSONField(null=True)
|
||||||
|
policies_pending = models.BooleanField(default=False)
|
||||||
time_zone = models.CharField(
|
time_zone = models.CharField(
|
||||||
max_length=255, choices=TZ_CHOICES, null=True, blank=True
|
max_length=255, choices=TZ_CHOICES, null=True, blank=True
|
||||||
)
|
)
|
||||||
|
policy = models.ForeignKey(
|
||||||
|
"automation.Policy",
|
||||||
|
related_name="agents",
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
on_delete=models.SET_NULL,
|
||||||
|
)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.hostname
|
return self.hostname
|
||||||
@@ -185,6 +195,21 @@ class Agent(models.Model):
|
|||||||
except:
|
except:
|
||||||
return [{"model": "unknown", "size": "unknown", "interfaceType": "unknown"}]
|
return [{"model": "unknown", "size": "unknown", "interfaceType": "unknown"}]
|
||||||
|
|
||||||
|
|
||||||
|
def generate_checks_from_policies(self):
|
||||||
|
# Clear agent checks managed by policy
|
||||||
|
self.agentchecks.filter(managed_by_policy=True).delete()
|
||||||
|
|
||||||
|
# Clear agent checks that have overriden_by_policy set
|
||||||
|
self.agentchecks.update(overriden_by_policy=False)
|
||||||
|
|
||||||
|
# Generate checks based on policies
|
||||||
|
automation.models.Policy.generate_policy_checks(self)
|
||||||
|
|
||||||
|
# Set policies_pending to false to disable policy generation on next checkin
|
||||||
|
self.policies_pending = False
|
||||||
|
self.save()
|
||||||
|
|
||||||
# https://github.com/Ylianst/MeshCentral/issues/59#issuecomment-521965347
|
# https://github.com/Ylianst/MeshCentral/issues/59#issuecomment-521965347
|
||||||
def get_login_token(self, key, user, action=3):
|
def get_login_token(self, key, user, action=3):
|
||||||
key = bytes.fromhex(key)
|
key = bytes.fromhex(key)
|
||||||
|
|||||||
@@ -311,7 +311,11 @@ class CheckRunner(APIView):
|
|||||||
|
|
||||||
def get(self, request, pk):
|
def get(self, request, pk):
|
||||||
agent = get_object_or_404(Agent, pk=pk)
|
agent = get_object_or_404(Agent, pk=pk)
|
||||||
checks = Check.objects.filter(agent__pk=pk)
|
|
||||||
|
if agent.policies_pending:
|
||||||
|
agent.generate_checks_from_policies()
|
||||||
|
|
||||||
|
checks = Check.objects.filter(agent__pk=pk, overriden_by_policy=False)
|
||||||
|
|
||||||
ret = {
|
ret = {
|
||||||
"agent": agent.pk,
|
"agent": agent.pk,
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
default_app_config = 'automation.apps.AutomationConfig'
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
from .models import Policy
|
from .models import Policy, PolicyExclusions
|
||||||
|
|
||||||
admin.site.register(Policy)
|
admin.site.register(Policy)
|
||||||
|
admin.site.register(PolicyExclusions)
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
from django.apps import AppConfig
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
class AutomationConfig(AppConfig):
|
class AutomationConfig(AppConfig):
|
||||||
name = "automation"
|
name = "automation"
|
||||||
|
|
||||||
|
def ready(self):
|
||||||
|
|
||||||
|
# registering signals defined in signals.py
|
||||||
|
import automation.signals
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
# Generated by Django 3.0.6 on 2020-06-04 17:13
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('clients', '0002_auto_20200531_2058'),
|
||||||
|
('agents', '0003_agent_checks_last_generated'),
|
||||||
|
('automation', '0001_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='policy',
|
||||||
|
name='enforced',
|
||||||
|
field=models.BooleanField(default=False),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='PolicyExclusions',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('agents', models.ManyToManyField(related_name='policy_exclusions', to='agents.Agent')),
|
||||||
|
('clients', models.ManyToManyField(related_name='policy_exclusions', to='clients.Client')),
|
||||||
|
('policy', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='exclusions', to='automation.Policy')),
|
||||||
|
('sites', models.ManyToManyField(related_name='policy_exclusions', to='clients.Site')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
# Generated by Django 3.0.7 on 2020-06-09 16:07
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('automation', '0002_auto_20200604_1713'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='policy',
|
||||||
|
name='agents',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='policy',
|
||||||
|
name='clients',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='policy',
|
||||||
|
name='sites',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='policyexclusions',
|
||||||
|
name='clients',
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -6,9 +6,7 @@ class Policy(models.Model):
|
|||||||
name = models.CharField(max_length=255, unique=True)
|
name = models.CharField(max_length=255, unique=True)
|
||||||
desc = models.CharField(max_length=255)
|
desc = models.CharField(max_length=255)
|
||||||
active = models.BooleanField(default=False)
|
active = models.BooleanField(default=False)
|
||||||
agents = models.ManyToManyField(Agent, related_name="policies")
|
enforced = models.BooleanField(default=False)
|
||||||
sites = models.ManyToManyField(Site, related_name="policies")
|
|
||||||
clients = models.ManyToManyField(Client, related_name="policies")
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
@@ -30,8 +28,160 @@ class Policy(models.Model):
|
|||||||
for site in client.sites.all():
|
for site in client.sites.all():
|
||||||
filtered_sites_ids.append(site.site)
|
filtered_sites_ids.append(site.site)
|
||||||
|
|
||||||
site_agents = Agent.objects.filter(site__in=filtered_sites_ids)
|
return Agent.objects.filter(models.Q(pk__in=explicit_agents.only("pk")) | models.Q(site__in=filtered_sites_ids) | models.Q(client__in=client_ids)).distinct()
|
||||||
client_agents = Agent.objects.filter(client__in=client_ids)
|
|
||||||
|
|
||||||
# Combine querysets and remove duplicates
|
@staticmethod
|
||||||
return explicit_agents.union(site_agents, client_agents)
|
def cascade_policy_checks(agent):
|
||||||
|
# Get checks added to agent directly
|
||||||
|
agent_checks = list(agent.agentchecks.filter(managed_by_policy=False))
|
||||||
|
|
||||||
|
# Get policies applied to agent and agent site and client
|
||||||
|
client_policy = Client.objects.get(client=agent.client).policy
|
||||||
|
site_policy = Site.objects.get(site=agent.site).policy
|
||||||
|
agent_policy = agent.policy
|
||||||
|
|
||||||
|
# Used to hold the policies that will be applied and the order in which they are applied
|
||||||
|
# Enforced policies are applied first
|
||||||
|
enforced_checks = list()
|
||||||
|
policy_checks = list()
|
||||||
|
|
||||||
|
if agent_policy != None:
|
||||||
|
if agent_policy.active:
|
||||||
|
if agent_policy.enforced:
|
||||||
|
for check in agent_policy.policychecks.all():
|
||||||
|
enforced_checks.append(check)
|
||||||
|
else:
|
||||||
|
for check in agent_policy.policychecks.all():
|
||||||
|
policy_checks.append(check)
|
||||||
|
|
||||||
|
if site_policy != None:
|
||||||
|
if site_policy.active:
|
||||||
|
if site_policy.enforced:
|
||||||
|
for check in site_policy.policychecks.all():
|
||||||
|
enforced_checks.append(check)
|
||||||
|
else:
|
||||||
|
for check in site_policy.policychecks.all():
|
||||||
|
policy_checks.append(check)
|
||||||
|
|
||||||
|
if client_policy != None:
|
||||||
|
if client_policy.active:
|
||||||
|
if client_policy.enforced:
|
||||||
|
for check in client_policy.policychecks.all():
|
||||||
|
enforced_checks.append(check)
|
||||||
|
else:
|
||||||
|
for check in client_policy.policychecks.all():
|
||||||
|
policy_checks.append(check)
|
||||||
|
|
||||||
|
# Sorted Checks already added
|
||||||
|
added_diskspace_checks = list()
|
||||||
|
added_ping_checks = list()
|
||||||
|
added_winsvc_checks = list()
|
||||||
|
added_script_checks = list()
|
||||||
|
added_eventlog_checks = list()
|
||||||
|
added_cpuload_checks = list()
|
||||||
|
added_memory_checks = list()
|
||||||
|
|
||||||
|
# Lists all agent and policy checks that will be created
|
||||||
|
diskspace_checks = list()
|
||||||
|
ping_checks = list()
|
||||||
|
winsvc_checks = list()
|
||||||
|
script_checks = list()
|
||||||
|
eventlog_checks = list()
|
||||||
|
cpuload_checks = list()
|
||||||
|
memory_checks = list()
|
||||||
|
|
||||||
|
# Loop over checks in with enforced policies first, then non-enforced policies
|
||||||
|
for check in enforced_checks + agent_checks + policy_checks:
|
||||||
|
if check.check_type == "diskspace":
|
||||||
|
# Check if drive letter was already added
|
||||||
|
if check.disk not in added_diskspace_checks:
|
||||||
|
added_diskspace_checks.append(check.disk)
|
||||||
|
# Dont create the check if it is an agent check
|
||||||
|
if check.agent == None:
|
||||||
|
diskspace_checks.append(check)
|
||||||
|
elif check.agent != None:
|
||||||
|
check.overriden_by_policy = True
|
||||||
|
check.save()
|
||||||
|
|
||||||
|
if check.check_type == "ping":
|
||||||
|
# Check if IP/host was already added
|
||||||
|
if check.ip not in added_ping_checks:
|
||||||
|
added_ping_checks.append(check.ip)
|
||||||
|
# Dont create the check if it is an agent check
|
||||||
|
if check.agent == None:
|
||||||
|
ping_checks.append(check)
|
||||||
|
added_ping_checks.append(check.ip)
|
||||||
|
elif check.agent != None:
|
||||||
|
check.overriden_by_policy = True
|
||||||
|
check.save()
|
||||||
|
|
||||||
|
if check.check_type == "cpuload":
|
||||||
|
# Check if cpuload check exists
|
||||||
|
if len(added_cpuload_checks) == 0:
|
||||||
|
added_cpuload_checks.append(check)
|
||||||
|
# Dont create the check if it is an agent check
|
||||||
|
if check.agent == None:
|
||||||
|
cpuload_checks.append(check)
|
||||||
|
elif check.agent != None:
|
||||||
|
check.overriden_by_policy = True
|
||||||
|
check.save()
|
||||||
|
|
||||||
|
if check.check_type == "memory":
|
||||||
|
# Check if memory check exists
|
||||||
|
if len(added_memory_checks) == 0:
|
||||||
|
added_memory_checks.append(check)
|
||||||
|
# Dont create the check if it is an agent check
|
||||||
|
if check.agent == None:
|
||||||
|
memory_checks.append(check)
|
||||||
|
elif check.agent != None:
|
||||||
|
check.overriden_by_policy = True
|
||||||
|
check.save()
|
||||||
|
|
||||||
|
if check.check_type == "winsvc":
|
||||||
|
# Check if service name was already added
|
||||||
|
if check.svc_name not in added_winsvc_checks:
|
||||||
|
added_winsvc_checks.append(check.svc_name)
|
||||||
|
# Dont create the check if it is an agent check
|
||||||
|
if check.agent == None:
|
||||||
|
winsvc_checks.append(check)
|
||||||
|
elif check.agent != None:
|
||||||
|
check.overriden_by_policy = True
|
||||||
|
check.save()
|
||||||
|
|
||||||
|
if check.check_type == "script":
|
||||||
|
# Check if script id was already added
|
||||||
|
if check.script not in added_script_checks:
|
||||||
|
added_script_checks.append(check.script)
|
||||||
|
# Dont create the check if it is an agent check
|
||||||
|
if check.agent == None:
|
||||||
|
script_checks.append(check)
|
||||||
|
elif check.agent != None:
|
||||||
|
check.overriden_by_policy = True
|
||||||
|
check.save()
|
||||||
|
|
||||||
|
if check.check_type == "eventlog":
|
||||||
|
# Check if events were already added
|
||||||
|
if [check.log_name, check.event_id] not in added_eventlog_checks:
|
||||||
|
added_eventlog_checks.append([check.log_name, check.event_id])
|
||||||
|
if check.agent == None:
|
||||||
|
eventlog_checks.append(check)
|
||||||
|
elif check.agent != None:
|
||||||
|
check.overriden_by_policy = True
|
||||||
|
check.save()
|
||||||
|
|
||||||
|
return diskspace_checks + ping_checks + cpuload_checks + memory_checks + winsvc_checks + script_checks + eventlog_checks
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def generate_policy_checks(agent):
|
||||||
|
checks = Policy.cascade_policy_checks(agent)
|
||||||
|
|
||||||
|
if checks != None:
|
||||||
|
if len(checks) > 0:
|
||||||
|
for check in checks:
|
||||||
|
check.create_policy_check(agent)
|
||||||
|
|
||||||
|
|
||||||
|
class PolicyExclusions(models.Model):
|
||||||
|
policy = models.ForeignKey(Policy, related_name="exclusions", on_delete=models.CASCADE)
|
||||||
|
agents = models.ManyToManyField(Agent, related_name="policy_exclusions")
|
||||||
|
sites = models.ManyToManyField(Site, related_name="policy_exclusions")
|
||||||
|
|||||||
@@ -1,25 +1,73 @@
|
|||||||
from rest_framework import serializers
|
from rest_framework.serializers import (
|
||||||
|
ModelSerializer,
|
||||||
|
SerializerMethodField,
|
||||||
|
StringRelatedField,
|
||||||
|
ReadOnlyField,
|
||||||
|
ValidationError
|
||||||
|
)
|
||||||
|
|
||||||
from .models import Policy
|
from .models import Policy
|
||||||
from autotasks.models import AutomatedTask
|
from autotasks.models import AutomatedTask
|
||||||
|
from checks.models import Check
|
||||||
|
from clients.models import Client
|
||||||
from autotasks.serializers import TaskSerializer
|
from autotasks.serializers import TaskSerializer
|
||||||
from checks.serializers import CheckSerializer
|
from checks.serializers import CheckSerializer
|
||||||
|
|
||||||
|
|
||||||
class PolicySerializer(serializers.ModelSerializer):
|
class PolicySerializer(ModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Policy
|
model = Policy
|
||||||
fields = "__all__"
|
fields = "__all__"
|
||||||
|
|
||||||
|
|
||||||
class PolicyRelationSerializer(serializers.ModelSerializer):
|
class PolicyTableSerializer(ModelSerializer):
|
||||||
|
|
||||||
|
clients = StringRelatedField(many=True, read_only=True)
|
||||||
|
sites = StringRelatedField(many=True, read_only=True)
|
||||||
|
agents = StringRelatedField(many=True, read_only=True)
|
||||||
|
|
||||||
|
clients_count = SerializerMethodField(read_only=True)
|
||||||
|
sites_count = SerializerMethodField(read_only=True)
|
||||||
|
agents_count = SerializerMethodField(read_only=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Policy
|
model = Policy
|
||||||
fields = "__all__"
|
fields = "__all__"
|
||||||
|
depth = 1
|
||||||
|
|
||||||
|
def get_clients_count(self, policy):
|
||||||
|
return policy.clients.count()
|
||||||
|
|
||||||
|
def get_sites_count(self, policy):
|
||||||
|
return policy.sites.count()
|
||||||
|
|
||||||
|
def get_agents_count(self, policy):
|
||||||
|
return policy.agents.count()
|
||||||
|
|
||||||
|
|
||||||
|
class PolicyOverviewSerializer(ModelSerializer):
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Client
|
||||||
|
fields = (
|
||||||
|
"pk",
|
||||||
|
"client",
|
||||||
|
"sites",
|
||||||
|
"policy"
|
||||||
|
)
|
||||||
depth = 2
|
depth = 2
|
||||||
|
|
||||||
|
|
||||||
class AutoTaskPolicySerializer(serializers.ModelSerializer):
|
class PolicyCheckStatusSerializer(ModelSerializer):
|
||||||
|
|
||||||
|
hostname = ReadOnlyField(source="agent.hostname")
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Check
|
||||||
|
fields = "__all__"
|
||||||
|
|
||||||
|
|
||||||
|
class AutoTaskPolicySerializer(ModelSerializer):
|
||||||
|
|
||||||
autotasks = TaskSerializer(many=True, read_only=True)
|
autotasks = TaskSerializer(many=True, read_only=True)
|
||||||
|
|
||||||
|
|||||||
51
api/tacticalrmm/automation/signals.py
Normal file
51
api/tacticalrmm/automation/signals.py
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
from django.db.models.signals import post_save, post_delete, m2m_changed
|
||||||
|
from django.dispatch import receiver
|
||||||
|
|
||||||
|
from automation.models import Policy
|
||||||
|
from agents.models import Agent
|
||||||
|
|
||||||
|
def set_agent_policy_update_field(policy_list, many=False):
|
||||||
|
|
||||||
|
if many:
|
||||||
|
for policy in policy_list:
|
||||||
|
policy.related_agents().update(policies_pending=True)
|
||||||
|
else:
|
||||||
|
policy_list.related_agents().update(policies_pending=True)
|
||||||
|
|
||||||
|
@receiver(post_save, sender="checks.Check")
|
||||||
|
def post_save_check_handler(sender, instance, created, **kwargs):
|
||||||
|
|
||||||
|
# don't run when policy managed check is saved
|
||||||
|
if instance.managed_by_policy == True:
|
||||||
|
return
|
||||||
|
|
||||||
|
# For created checks
|
||||||
|
if created:
|
||||||
|
if instance.policy != None:
|
||||||
|
set_agent_policy_update_field(instance.policy)
|
||||||
|
elif instance.agent != None:
|
||||||
|
instance.agent.policies_pending=True
|
||||||
|
instance.agent.save()
|
||||||
|
|
||||||
|
# Checks that are updated except for agent
|
||||||
|
else:
|
||||||
|
if instance.policy != None:
|
||||||
|
set_agent_policy_update_field(instance.policy)
|
||||||
|
|
||||||
|
@receiver(post_delete, sender="checks.Check")
|
||||||
|
def post_delete_check_handler(sender, instance, **kwargs):
|
||||||
|
|
||||||
|
# don't run when policy managed check is saved
|
||||||
|
if instance.managed_by_policy == True:
|
||||||
|
return
|
||||||
|
|
||||||
|
if instance.policy != None:
|
||||||
|
set_agent_policy_update_field(instance.policy)
|
||||||
|
elif instance.agent != None:
|
||||||
|
instance.agent.policies_pending=True
|
||||||
|
instance.agent.save()
|
||||||
|
|
||||||
|
@receiver([post_save, post_delete], sender="automation.Policy")
|
||||||
|
def post_save_policy_handler(sender, instance, **kwargs):
|
||||||
|
|
||||||
|
set_agent_policy_update_field(instance)
|
||||||
@@ -9,7 +9,7 @@ urlpatterns = [
|
|||||||
path("policies/<int:pk>/", views.GetUpdateDeletePolicy.as_view()),
|
path("policies/<int:pk>/", views.GetUpdateDeletePolicy.as_view()),
|
||||||
path("<int:pk>/policychecks/", views.PolicyCheck.as_view()),
|
path("<int:pk>/policychecks/", views.PolicyCheck.as_view()),
|
||||||
path("<int:pk>/policyautomatedtasks/", views.PolicyAutoTask.as_view()),
|
path("<int:pk>/policyautomatedtasks/", views.PolicyAutoTask.as_view()),
|
||||||
path("<int:policy>/policycheckstatus/<int:check>/check/", views.PolicyCheck.as_view()),
|
path("policycheckstatus/<int:check>/check/", views.PolicyCheck.as_view()),
|
||||||
path("<int:policy>/policyautomatedtaskstatus/<int:task>/task/", views.PolicyAutoTask.as_view()),
|
path("policyautomatedtaskstatus/<int:task>/task/", views.PolicyAutoTask.as_view()),
|
||||||
path("runwintask/<int:pk>/", views.RunPolicyTask.as_view()),
|
path("runwintask/<int:pk>/", views.PolicyAutoTask.as_view()),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -20,8 +20,10 @@ from agents.serializers import AgentHostnameSerializer
|
|||||||
|
|
||||||
from .serializers import (
|
from .serializers import (
|
||||||
PolicySerializer,
|
PolicySerializer,
|
||||||
PolicyRelationSerializer,
|
PolicyTableSerializer,
|
||||||
AutoTaskPolicySerializer,
|
PolicyOverviewSerializer,
|
||||||
|
PolicyCheckStatusSerializer,
|
||||||
|
AutoTaskPolicySerializer
|
||||||
)
|
)
|
||||||
|
|
||||||
from checks.serializers import CheckSerializer
|
from checks.serializers import CheckSerializer
|
||||||
@@ -31,30 +33,24 @@ class GetAddPolicies(APIView):
|
|||||||
|
|
||||||
policies = Policy.objects.all()
|
policies = Policy.objects.all()
|
||||||
|
|
||||||
return Response(PolicyRelationSerializer(policies, many=True).data)
|
return Response(PolicyTableSerializer(policies, many=True).data)
|
||||||
|
|
||||||
def post(self, request):
|
def post(self, request):
|
||||||
name = request.data["name"].strip()
|
name = request.data["name"].strip()
|
||||||
desc = request.data["desc"].strip()
|
desc = request.data["desc"].strip()
|
||||||
active = request.data["active"]
|
active = request.data["active"]
|
||||||
|
enforced = active = request.data["enforced"]
|
||||||
|
|
||||||
if Policy.objects.filter(name=name):
|
if Policy.objects.filter(name=name):
|
||||||
content = {"error": f"Policy {name} already exists"}
|
content = {"error": f"Policy {name} already exists"}
|
||||||
return Response(content, status=status.HTTP_400_BAD_REQUEST)
|
return Response(content, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
policy = Policy.objects.create(name=name, desc=desc, active=active)
|
policy = Policy.objects.create(name=name, desc=desc, active=active, enforced=enforced)
|
||||||
except DataError:
|
except DataError:
|
||||||
content = {"error": "Policy name too long (max 255 chars)"}
|
content = {"error": "Policy name too long (max 255 chars)"}
|
||||||
return Response(content, status=status.HTTP_400_BAD_REQUEST)
|
return Response(content, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
# Add Clients, Sites to Policy
|
|
||||||
if len(request.data["clients"]) > 0:
|
|
||||||
policy.clients.set(request.data["clients"])
|
|
||||||
|
|
||||||
if len(request.data["sites"]) > 0:
|
|
||||||
policy.sites.set(request.data["sites"])
|
|
||||||
|
|
||||||
return Response("ok")
|
return Response("ok")
|
||||||
|
|
||||||
|
|
||||||
@@ -63,7 +59,7 @@ class GetUpdateDeletePolicy(APIView):
|
|||||||
|
|
||||||
policy = get_object_or_404(Policy, pk=pk)
|
policy = get_object_or_404(Policy, pk=pk)
|
||||||
|
|
||||||
return Response(PolicyRelationSerializer(policy).data)
|
return Response(PolicySerializer(policy).data)
|
||||||
|
|
||||||
def put(self, request, pk):
|
def put(self, request, pk):
|
||||||
|
|
||||||
@@ -72,24 +68,14 @@ class GetUpdateDeletePolicy(APIView):
|
|||||||
policy.name = request.data["name"]
|
policy.name = request.data["name"]
|
||||||
policy.desc = request.data["desc"]
|
policy.desc = request.data["desc"]
|
||||||
policy.active = request.data["active"]
|
policy.active = request.data["active"]
|
||||||
|
policy.enforced = request.data["enforced"]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
policy.save(update_fields=["name", "desc", "active"])
|
policy.save(update_fields=["name", "desc", "active", "enforced"])
|
||||||
except DataError:
|
except DataError:
|
||||||
content = {"error": "Policy name too long (max 255 chars)"}
|
content = {"error": "Policy name too long (max 255 chars)"}
|
||||||
return Response(content, status=status.HTTP_400_BAD_REQUEST)
|
return Response(content, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
# Update Clients, Sites to Policy
|
|
||||||
if len(request.data["clients"]) > 0:
|
|
||||||
policy.clients.set(request.data["clients"])
|
|
||||||
else:
|
|
||||||
policy.clients.clear()
|
|
||||||
|
|
||||||
if len(request.data["sites"]) > 0:
|
|
||||||
policy.sites.set(request.data["sites"])
|
|
||||||
else:
|
|
||||||
policy.sites.clear()
|
|
||||||
|
|
||||||
return Response("ok")
|
return Response("ok")
|
||||||
|
|
||||||
def delete(self, request, pk):
|
def delete(self, request, pk):
|
||||||
@@ -110,25 +96,29 @@ class PolicyAutoTask(APIView):
|
|||||||
# TODO pull agents and status for policy task
|
# TODO pull agents and status for policy task
|
||||||
return Response(list())
|
return Response(list())
|
||||||
|
|
||||||
class RunPolicyTask(APIView):
|
def put(self, request, pk):
|
||||||
def get(self, request, pk):
|
|
||||||
|
|
||||||
# TODO: Run task for all Agents under policy
|
|
||||||
return Response("ok")
|
return Response("ok")
|
||||||
|
|
||||||
|
|
||||||
class PolicyCheck(APIView):
|
class PolicyCheck(APIView):
|
||||||
def get(self, request, pk):
|
def get(self, request, pk):
|
||||||
checks = Check.objects.filter(policy__pk=pk)
|
checks = Check.objects.filter(policy__pk=pk, agent=None)
|
||||||
return Response(CheckSerializer(checks, many=True).data)
|
return Response(CheckSerializer(checks, many=True).data)
|
||||||
|
|
||||||
def patch(self, request, policy, check):
|
def patch(self, request, check):
|
||||||
|
|
||||||
# TODO pull agents and status for policy check
|
checks = Check.objects.filter(parent_check=check)
|
||||||
|
return Response(PolicyCheckStatusSerializer(checks, many=True).data)
|
||||||
|
|
||||||
|
def put(self, request):
|
||||||
|
|
||||||
|
# TODO run policy check manually for all agents
|
||||||
return Response(list())
|
return Response(list())
|
||||||
|
|
||||||
|
|
||||||
class OverviewPolicy(APIView):
|
class OverviewPolicy(APIView):
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
|
"""
|
||||||
clients = Client.objects.all()
|
clients = Client.objects.all()
|
||||||
response = {}
|
response = {}
|
||||||
|
|
||||||
@@ -154,8 +144,11 @@ class OverviewPolicy(APIView):
|
|||||||
|
|
||||||
response[client.client] = client_sites
|
response[client.client] = client_sites
|
||||||
|
|
||||||
|
|
||||||
return Response(response)
|
return Response(response)
|
||||||
|
"""
|
||||||
|
clients = Client.objects.all()
|
||||||
|
return Response(PolicyOverviewSerializer(clients, many=True).data)
|
||||||
|
|
||||||
|
|
||||||
class GetRelated(APIView):
|
class GetRelated(APIView):
|
||||||
def get(self, request, pk):
|
def get(self, request, pk):
|
||||||
@@ -182,35 +175,29 @@ class GetRelated(APIView):
|
|||||||
def post(self, request):
|
def post(self, request):
|
||||||
# Update Agents, Clients, Sites to Policy
|
# Update Agents, Clients, Sites to Policy
|
||||||
|
|
||||||
policies = request.data["policies"]
|
|
||||||
related_type = request.data["type"]
|
related_type = request.data["type"]
|
||||||
pk = request.data["pk"]
|
pk = request.data["pk"]
|
||||||
|
|
||||||
if len(policies) > 0:
|
if request.data["policy"] != 0:
|
||||||
|
policy = Policy.objects.get(pk=request.data["policy"])
|
||||||
if related_type == "client":
|
if related_type == "client":
|
||||||
client = get_object_or_404(Client, pk=pk)
|
Client.objects.filter(pk=pk).update(policy=policy)
|
||||||
client.policies.set(policies)
|
|
||||||
|
|
||||||
if related_type == "site":
|
if related_type == "site":
|
||||||
site = get_object_or_404(Site, pk=pk)
|
Site.objects.filter(pk=pk).update(policy=policy)
|
||||||
site.policies.set(policies)
|
|
||||||
|
|
||||||
if related_type == "agent":
|
if related_type == "agent":
|
||||||
agent = get_object_or_404(Agent, pk=pk)
|
Agent.objects.filter(pk=pk).update(policy=policy, policies_pending=True)
|
||||||
agent.policies.set(policies)
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
if related_type == "client":
|
if related_type == "client":
|
||||||
client = get_object_or_404(Client, pk=pk)
|
Client.objects.filter(pk=pk).update(policy=None)
|
||||||
client.policies.clear()
|
|
||||||
|
|
||||||
if related_type == "site":
|
if related_type == "site":
|
||||||
site = get_object_or_404(Site, pk=pk)
|
Site.objects.filter(pk=pk).update(policy=None)
|
||||||
site.policies.clear()
|
|
||||||
|
|
||||||
if related_type == "agent":
|
if related_type == "agent":
|
||||||
agent = get_object_or_404(Agent, pk=pk)
|
Agent.objects.filter(pk=pk).update(policy=None, policies_pending=True)
|
||||||
agent.policies.clear()
|
|
||||||
|
|
||||||
return Response("ok")
|
return Response("ok")
|
||||||
|
|
||||||
@@ -219,16 +206,16 @@ class GetRelated(APIView):
|
|||||||
pk = request.data["pk"]
|
pk = request.data["pk"]
|
||||||
|
|
||||||
if related_type == "agent":
|
if related_type == "agent":
|
||||||
agent = get_object_or_404(Agent, pk=pk)
|
policy = Policy.objects.filter(agents__pk=pk).first()
|
||||||
return Response(PolicySerializer(agent.policies.all(), many=True).data)
|
return Response(PolicySerializer(policy).data)
|
||||||
|
|
||||||
if related_type == "site":
|
if related_type == "site":
|
||||||
site = get_object_or_404(Site, pk=pk)
|
policy = Policy.objects.filter(sites__pk=pk).first()
|
||||||
return Response(PolicySerializer(site.policies.all(), many=True).data)
|
return Response(PolicySerializer(policy).data)
|
||||||
|
|
||||||
if related_type == "client":
|
if related_type == "client":
|
||||||
client = get_object_or_404(Client, pk=pk)
|
policy = Policy.objects.filter(clients__pk=pk).first()
|
||||||
return Response(PolicySerializer(client.policies.all(), many=True).data)
|
return Response(PolicySerializer(policy).data)
|
||||||
|
|
||||||
content = {"error": "Data was submitted incorrectly"}
|
content = {"error": "Data was submitted incorrectly"}
|
||||||
return Response(content, status=status.HTTP_400_BAD_REQUEST)
|
return Response(content, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 3.0.6 on 2020-06-04 17:13
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('checks', '0001_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='check',
|
||||||
|
name='managed_by_policy',
|
||||||
|
field=models.BooleanField(default=False),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 3.0.6 on 2020-06-04 17:59
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('checks', '0002_check_managed_by_policy'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='check',
|
||||||
|
name='overriden_by_policy',
|
||||||
|
field=models.BooleanField(default=False),
|
||||||
|
),
|
||||||
|
]
|
||||||
18
api/tacticalrmm/checks/migrations/0004_check_parent_check.py
Normal file
18
api/tacticalrmm/checks/migrations/0004_check_parent_check.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 3.0.7 on 2020-06-07 00:18
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('checks', '0003_check_overriden_by_policy'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='check',
|
||||||
|
name='parent_check',
|
||||||
|
field=models.PositiveIntegerField(blank=True, null=True),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -10,6 +10,8 @@ from django.core.validators import MinValueValidator, MaxValueValidator
|
|||||||
|
|
||||||
from core.models import CoreSettings
|
from core.models import CoreSettings
|
||||||
|
|
||||||
|
import agents
|
||||||
|
|
||||||
from .tasks import handle_check_email_alert_task
|
from .tasks import handle_check_email_alert_task
|
||||||
|
|
||||||
CHECK_TYPE_CHOICES = [
|
CHECK_TYPE_CHOICES = [
|
||||||
@@ -66,6 +68,9 @@ class Check(models.Model):
|
|||||||
blank=True,
|
blank=True,
|
||||||
on_delete=models.CASCADE,
|
on_delete=models.CASCADE,
|
||||||
)
|
)
|
||||||
|
managed_by_policy = models.BooleanField(default=False)
|
||||||
|
overriden_by_policy = models.BooleanField(default=False)
|
||||||
|
parent_check = models.PositiveIntegerField(null=True, blank=True)
|
||||||
name = models.CharField(max_length=255, null=True, blank=True)
|
name = models.CharField(max_length=255, null=True, blank=True)
|
||||||
check_type = models.CharField(
|
check_type = models.CharField(
|
||||||
max_length=50, choices=CHECK_TYPE_CHOICES, default="diskspace"
|
max_length=50, choices=CHECK_TYPE_CHOICES, default="diskspace"
|
||||||
@@ -212,6 +217,35 @@ class Check(models.Model):
|
|||||||
|
|
||||||
return default_services
|
return default_services
|
||||||
|
|
||||||
|
def create_policy_check(self, agent):
|
||||||
|
Check.objects.create(
|
||||||
|
agent=agent,
|
||||||
|
policy=self.policy,
|
||||||
|
managed_by_policy=True,
|
||||||
|
parent_check=self.pk,
|
||||||
|
name=self.name,
|
||||||
|
check_type=self.check_type,
|
||||||
|
email_alert=self.email_alert,
|
||||||
|
text_alert=self.text_alert,
|
||||||
|
fails_b4_alert=self.fails_b4_alert,
|
||||||
|
extra_details=self.extra_details,
|
||||||
|
threshold=self.threshold,
|
||||||
|
disk=self.disk,
|
||||||
|
ip=self.ip,
|
||||||
|
script=self.script,
|
||||||
|
timeout=self.timeout,
|
||||||
|
svc_name=self.svc_name,
|
||||||
|
svc_display_name=self.svc_display_name,
|
||||||
|
pass_if_start_pending=self.pass_if_start_pending,
|
||||||
|
restart_if_stopped=self.restart_if_stopped,
|
||||||
|
svc_policy_mode=self.svc_policy_mode,
|
||||||
|
log_name=self.log_name,
|
||||||
|
event_id=self.event_id,
|
||||||
|
event_type=self.event_type,
|
||||||
|
fail_when=self.fail_when,
|
||||||
|
search_last_days=self.search_last_days,
|
||||||
|
)
|
||||||
|
|
||||||
def send_email(self):
|
def send_email(self):
|
||||||
|
|
||||||
CORE = CoreSettings.objects.first()
|
CORE = CoreSettings.objects.first()
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ class CheckSerializer(serializers.ModelSerializer):
|
|||||||
# disk checks
|
# disk checks
|
||||||
# make sure no duplicate diskchecks exist for an agent/policy
|
# make sure no duplicate diskchecks exist for an agent/policy
|
||||||
if check_type == "diskspace" and not self.instance: # only on create
|
if check_type == "diskspace" and not self.instance: # only on create
|
||||||
checks = Check.objects.filter(**self.context).filter(check_type="diskspace")
|
checks = Check.objects.filter(**self.context).filter(check_type="diskspace").exclude(managed_by_policy=True)
|
||||||
if checks:
|
if checks:
|
||||||
for check in checks:
|
for check in checks:
|
||||||
if val["disk"] in check.disk:
|
if val["disk"] in check.disk:
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ class GetUpdateDeleteCheck(APIView):
|
|||||||
def delete(self, request, pk):
|
def delete(self, request, pk):
|
||||||
check = get_object_or_404(Check, pk=pk)
|
check = get_object_or_404(Check, pk=pk)
|
||||||
check.delete()
|
check.delete()
|
||||||
|
|
||||||
return Response(f"{check.readable_desc} was deleted!")
|
return Response(f"{check.readable_desc} was deleted!")
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
# Generated by Django 3.0.7 on 2020-06-09 16:07
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('automation', '0003_auto_20200609_1607'),
|
||||||
|
('clients', '0002_auto_20200531_2058'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='client',
|
||||||
|
name='policy',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='clients', to='automation.Policy'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='site',
|
||||||
|
name='policy',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='sites', to='automation.Policy'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -4,6 +4,13 @@ from agents.models import Agent
|
|||||||
|
|
||||||
class Client(models.Model):
|
class Client(models.Model):
|
||||||
client = models.CharField(max_length=255, unique=True)
|
client = models.CharField(max_length=255, unique=True)
|
||||||
|
policy = models.ForeignKey(
|
||||||
|
"automation.Policy",
|
||||||
|
related_name="clients",
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
on_delete=models.SET_NULL,
|
||||||
|
)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.client
|
return self.client
|
||||||
@@ -22,6 +29,13 @@ class Client(models.Model):
|
|||||||
class Site(models.Model):
|
class Site(models.Model):
|
||||||
client = models.ForeignKey(Client, related_name="sites", on_delete=models.CASCADE)
|
client = models.ForeignKey(Client, related_name="sites", on_delete=models.CASCADE)
|
||||||
site = models.CharField(max_length=255)
|
site = models.CharField(max_length=255)
|
||||||
|
policy = models.ForeignKey(
|
||||||
|
"automation.Policy",
|
||||||
|
related_name="sites",
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
on_delete=models.SET_NULL,
|
||||||
|
)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.site
|
return self.site
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ services:
|
|||||||
# Container that hosts Vue frontend
|
# Container that hosts Vue frontend
|
||||||
app:
|
app:
|
||||||
image: node:12
|
image: node:12
|
||||||
command: /bin/bash -c "npm install --force && npm run serve -- --host 0.0.0.0 --port 80 --public ${APP_HOST}"
|
command: /bin/bash -c "npm install && npm run serve -- --host 0.0.0.0 --port 80 --public ${APP_HOST}"
|
||||||
working_dir: /home/node
|
working_dir: /home/node
|
||||||
volumes:
|
volumes:
|
||||||
- ../web:/home/node
|
- ../web:/home/node
|
||||||
|
|||||||
@@ -91,29 +91,9 @@ This allows you to edit the files locally and those changes will be presented to
|
|||||||
|
|
||||||
Files that need to be manually created are:
|
Files that need to be manually created are:
|
||||||
- api/tacticalrmm/tacticalrmm/local_settings.py
|
- api/tacticalrmm/tacticalrmm/local_settings.py
|
||||||
- web/.env.local
|
- web/.env
|
||||||
|
|
||||||
For HMR to work with vue you may need to alter the web/vue.config.js file to with these changes
|
For HMR to work with vue you can copy .env.example and modify the setting to fit your dev environment.
|
||||||
|
|
||||||
```
|
|
||||||
devServer: {
|
|
||||||
//host: "192.168.99.150",
|
|
||||||
disableHostCheck: true,
|
|
||||||
public: "YOUR_APP_URL"
|
|
||||||
},
|
|
||||||
```
|
|
||||||
|
|
||||||
Since this file is checked into git you can configure git to ignore it and the changes will stay intact
|
|
||||||
|
|
||||||
```
|
|
||||||
git update-index --assume-unchanged ./web/vue.config.js
|
|
||||||
```
|
|
||||||
|
|
||||||
To revert this run
|
|
||||||
|
|
||||||
```
|
|
||||||
git update-index --no-assume-unchanged ./web/vue.config.js
|
|
||||||
```
|
|
||||||
|
|
||||||
### Create Python Virtual Env
|
### Create Python Virtual Env
|
||||||
|
|
||||||
|
|||||||
@@ -83,6 +83,9 @@
|
|||||||
<template v-slot:header-cell-statusicon="props">
|
<template v-slot:header-cell-statusicon="props">
|
||||||
<q-th auto-width :props="props"></q-th>
|
<q-th auto-width :props="props"></q-th>
|
||||||
</template>
|
</template>
|
||||||
|
<template v-slot:header-cell-policystatus="props">
|
||||||
|
<q-th auto-width :props="props"></q-th>
|
||||||
|
</template>
|
||||||
<!-- body slots -->
|
<!-- body slots -->
|
||||||
<template slot="body" slot-scope="props" :props="props">
|
<template slot="body" slot-scope="props" :props="props">
|
||||||
<q-tr @contextmenu="checkpk = props.row.id">
|
<q-tr @contextmenu="checkpk = props.row.id">
|
||||||
@@ -128,6 +131,18 @@
|
|||||||
v-model="props.row.email_alert"
|
v-model="props.row.email_alert"
|
||||||
/>
|
/>
|
||||||
</q-td>
|
</q-td>
|
||||||
|
<!-- policy check icon -->
|
||||||
|
<q-td v-if="props.row.managed_by_policy">
|
||||||
|
<q-icon style="font-size: 1.3rem;" name="policy">
|
||||||
|
<q-tooltip>This check is managed by a policy</q-tooltip>
|
||||||
|
</q-icon>
|
||||||
|
</q-td>
|
||||||
|
<q-td v-else-if="props.row.overriden_by_policy">
|
||||||
|
<q-icon style="font-size: 1.3rem;" name="remove_circle_outline">
|
||||||
|
<q-tooltip>This check is overriden by a policy</q-tooltip>
|
||||||
|
</q-icon>
|
||||||
|
</q-td>
|
||||||
|
<q-td v-else></q-td>
|
||||||
<!-- status icon -->
|
<!-- status icon -->
|
||||||
<q-td v-if="props.row.status === 'pending'"></q-td>
|
<q-td v-if="props.row.status === 'pending'"></q-td>
|
||||||
<q-td v-else-if="props.row.status === 'passing'">
|
<q-td v-else-if="props.row.status === 'passing'">
|
||||||
@@ -292,6 +307,7 @@ export default {
|
|||||||
columns: [
|
columns: [
|
||||||
{ name: "smsalert", field: "text_alert", align: "left" },
|
{ name: "smsalert", field: "text_alert", align: "left" },
|
||||||
{ name: "emailalert", field: "email_alert", align: "left" },
|
{ name: "emailalert", field: "email_alert", align: "left" },
|
||||||
|
{ name: "policystatus", align: "left" },
|
||||||
{ name: "statusicon", align: "left" },
|
{ name: "statusicon", align: "left" },
|
||||||
{ name: "desc", label: "Description", align: "left" },
|
{ name: "desc", label: "Description", align: "left" },
|
||||||
{ name: "status", label: "Status", field: "status", align: "left" },
|
{ name: "status", label: "Status", field: "status", align: "left" },
|
||||||
|
|||||||
@@ -62,19 +62,36 @@
|
|||||||
class="settings-tbl-sticky"
|
class="settings-tbl-sticky"
|
||||||
:data="policies"
|
:data="policies"
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
:visible-columns="visibleColumns"
|
|
||||||
:pagination.sync="pagination"
|
:pagination.sync="pagination"
|
||||||
:selected.sync="selected"
|
:selected.sync="selected"
|
||||||
@selection="policyRowSelected"
|
|
||||||
selection="single"
|
selection="single"
|
||||||
|
@selection="policyRowSelected"
|
||||||
row-key="id"
|
row-key="id"
|
||||||
binary-state-sort
|
binary-state-sort
|
||||||
hide-bottom
|
hide-bottom
|
||||||
flat
|
|
||||||
>
|
>
|
||||||
|
<!-- header slots -->
|
||||||
<template v-slot:header="props">
|
<template v-slot:header="props">
|
||||||
<q-tr :props="props">
|
<q-tr :props="props">
|
||||||
<q-th v-for="col in props.cols" :key="col.name" :props="props">{{ col.label }}</q-th>
|
<template v-for="col in props.cols">
|
||||||
|
|
||||||
|
<q-th v-if="col.name === 'active'" auto-width :key="col.name">
|
||||||
|
<q-icon name="power_settings_new" size="1.5em">
|
||||||
|
<q-tooltip>Enable Policy</q-tooltip>
|
||||||
|
</q-icon>
|
||||||
|
</q-th>
|
||||||
|
|
||||||
|
<q-th v-else-if="col.name === 'enforced'" auto-width :key="col.name">
|
||||||
|
<q-icon name="security" size="1.5em">
|
||||||
|
<q-tooltip>Enforce Policy (Will override Agent checks)</q-tooltip>
|
||||||
|
</q-icon>
|
||||||
|
</q-th>
|
||||||
|
|
||||||
|
<q-th v-else :key="col.name" :props="props">
|
||||||
|
{{ col.label }}
|
||||||
|
</q-th>
|
||||||
|
|
||||||
|
</template>
|
||||||
</q-tr>
|
</q-tr>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:body="props">
|
<template v-slot:body="props">
|
||||||
@@ -126,15 +143,30 @@
|
|||||||
</q-item>
|
</q-item>
|
||||||
</q-list>
|
</q-list>
|
||||||
</q-menu>
|
</q-menu>
|
||||||
|
<!-- enabled checkbox -->
|
||||||
|
<q-td>
|
||||||
|
<q-checkbox
|
||||||
|
dense
|
||||||
|
@input="toggleCheckbox(props.row, 'Active')"
|
||||||
|
v-model="props.row.active"
|
||||||
|
/>
|
||||||
|
</q-td>
|
||||||
|
<!-- enforced checkbox -->
|
||||||
|
<q-td>
|
||||||
|
<q-checkbox
|
||||||
|
dense
|
||||||
|
@input="toggleCheckbox(props.row, 'Enforced')"
|
||||||
|
v-model="props.row.enforced"
|
||||||
|
/>
|
||||||
|
</q-td>
|
||||||
<q-td>{{ props.row.name }}</q-td>
|
<q-td>{{ props.row.name }}</q-td>
|
||||||
<q-td>{{ props.row.desc }}</q-td>
|
<q-td>{{ props.row.desc }}</q-td>
|
||||||
<q-td>{{ props.row.active }}</q-td>
|
|
||||||
<q-td>
|
<q-td>
|
||||||
<span
|
<span
|
||||||
style="cursor:pointer;color:blue;text-decoration:underline"
|
style="cursor:pointer;color:blue;text-decoration:underline"
|
||||||
@click="showRelationsModal(props.row)"
|
@click="showRelationsModal(props.row)"
|
||||||
>
|
>
|
||||||
{{ `Show Relations (${props.row.clients.length + props.row.sites.length + props.row.agents.length}+)` }}
|
{{ `Show Relations (${props.row.clients_count + props.row.sites_count + props.row.agents_count}+)` }}
|
||||||
</span>
|
</span>
|
||||||
</q-td>
|
</q-td>
|
||||||
</q-tr>
|
</q-tr>
|
||||||
@@ -196,13 +228,9 @@ export default {
|
|||||||
policy: null,
|
policy: null,
|
||||||
editPolicyId: null,
|
editPolicyId: null,
|
||||||
selected: [],
|
selected: [],
|
||||||
pagination: {
|
|
||||||
rowsPerPage: 0,
|
|
||||||
sortBy: "id",
|
|
||||||
descending: false
|
|
||||||
},
|
|
||||||
columns: [
|
columns: [
|
||||||
{ name: "id", label: "ID", field: "id" },
|
{ name: "active", label: "Active", field: "active", align: "left" },
|
||||||
|
{ name: "enforced", label: "Enforced", field: "enforced", align: "left" },
|
||||||
{
|
{
|
||||||
name: "name",
|
name: "name",
|
||||||
label: "Name",
|
label: "Name",
|
||||||
@@ -215,24 +243,17 @@ export default {
|
|||||||
label: "Description",
|
label: "Description",
|
||||||
field: "desc",
|
field: "desc",
|
||||||
align: "left",
|
align: "left",
|
||||||
sortable: false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "active",
|
|
||||||
label: "Active",
|
|
||||||
field: "active",
|
|
||||||
align: "left",
|
|
||||||
sortable: true
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "actions",
|
name: "actions",
|
||||||
label: "Actions",
|
label: "Actions",
|
||||||
field: "actions",
|
field: "actions",
|
||||||
align: "left",
|
align: "left",
|
||||||
sortable: false
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
visibleColumns: ["name", "desc", "active", "actions"]
|
pagination: {
|
||||||
|
rowsPerPage: 9999
|
||||||
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@@ -295,6 +316,32 @@ export default {
|
|||||||
showPolicyOverview() {
|
showPolicyOverview() {
|
||||||
this.showPolicyOverviewModal = true
|
this.showPolicyOverviewModal = true
|
||||||
this.clearRow();
|
this.clearRow();
|
||||||
|
},
|
||||||
|
toggleCheckbox(policy, type) {
|
||||||
|
let text = "";
|
||||||
|
|
||||||
|
if (type === "Active") {
|
||||||
|
text = policy.active ? "Policy enabled successfully" : "Policy disabled successfully";
|
||||||
|
} else if (type === "Enforced") {
|
||||||
|
text = policy.enforced ? "Policy enforced successfully" : "Policy enforcement disabled";
|
||||||
|
}
|
||||||
|
|
||||||
|
const data ={
|
||||||
|
id: policy.id,
|
||||||
|
name: policy.name,
|
||||||
|
desc: policy.desc,
|
||||||
|
active: policy.active,
|
||||||
|
enforced: policy.enforced
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$store
|
||||||
|
.dispatch("automation/editPolicy", data)
|
||||||
|
.then(response => {
|
||||||
|
this.$q.notify(notifySuccessConfig(text));
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
this.$q.notify(notifyErrorConfig("An Error occured while editing policy"));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
|||||||
@@ -105,19 +105,18 @@ export default {
|
|||||||
processTreeDataFromApi(data) {
|
processTreeDataFromApi(data) {
|
||||||
/* Structure
|
/* Structure
|
||||||
* [{
|
* [{
|
||||||
* "client_name_1": {
|
* client: Client Name 1,
|
||||||
* "policies": [
|
* policy: {
|
||||||
* {
|
|
||||||
* id: 1,
|
* id: 1,
|
||||||
* name: "Policy Name 1"
|
* name: "Policy Name 1"
|
||||||
|
* },
|
||||||
|
* sites: [{
|
||||||
|
* name: "Site Name 1",
|
||||||
|
* policy: {
|
||||||
|
* id: 2,
|
||||||
|
* name: "Policy Name 2"
|
||||||
* }
|
* }
|
||||||
* ],
|
* }]
|
||||||
* sites: {
|
|
||||||
* "site_name_1": {
|
|
||||||
* "policies": []
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* }]
|
* }]
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -129,7 +128,7 @@ export default {
|
|||||||
for (let client in data) {
|
for (let client in data) {
|
||||||
var client_temp = {};
|
var client_temp = {};
|
||||||
|
|
||||||
client_temp["label"] = client;
|
client_temp["label"] = data[client].client;
|
||||||
client_temp["id"] = unique_id;
|
client_temp["id"] = unique_id;
|
||||||
client_temp["icon"] = "business";
|
client_temp["icon"] = "business";
|
||||||
client_temp["selectable"] = false;
|
client_temp["selectable"] = false;
|
||||||
@@ -139,27 +138,25 @@ export default {
|
|||||||
|
|
||||||
// Add any policies assigned to client
|
// Add any policies assigned to client
|
||||||
|
|
||||||
if (data[client].policies.length > 0) {
|
if (data[client].policy !== null) {
|
||||||
for (let policy in data[client].policies) {
|
|
||||||
let disabled = "";
|
let disabled = "";
|
||||||
|
|
||||||
// Indicate if the policy is active or not
|
// Indicate if the policy is active or not
|
||||||
if (!data[client].policies[policy].active) {
|
if (!data[client].policy.active) {
|
||||||
disabled = " (disabled)";
|
disabled = " (disabled)";
|
||||||
}
|
}
|
||||||
|
|
||||||
client_temp["children"].push({
|
client_temp["children"].push({
|
||||||
label: data[client].policies[policy].name + disabled,
|
label: data[client].policy.name + disabled,
|
||||||
icon: "policy",
|
icon: "policy",
|
||||||
id: data[client].policies[policy].id
|
id: data[client].policy.id
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Iterate through Sites
|
// Iterate through Sites
|
||||||
for (let site in data[client].sites) {
|
for (let site in data[client].sites) {
|
||||||
var site_temp = {};
|
var site_temp = {};
|
||||||
site_temp["label"] = site;
|
site_temp["label"] = data[client].sites[site].site;
|
||||||
site_temp["id"] = unique_id;
|
site_temp["id"] = unique_id;
|
||||||
site_temp["icon"] = "apartment";
|
site_temp["icon"] = "apartment";
|
||||||
site_temp["selectable"] = false;
|
site_temp["selectable"] = false;
|
||||||
@@ -167,23 +164,21 @@ export default {
|
|||||||
unique_id--;
|
unique_id--;
|
||||||
|
|
||||||
// Add any policies assigned to site
|
// Add any policies assigned to site
|
||||||
if (data[client].sites[site].policies.length > 0) {
|
if (data[client].sites[site].policy !== null) {
|
||||||
site_temp["children"] = [];
|
site_temp["children"] = [];
|
||||||
|
|
||||||
for (let policy in data[client].sites[site].policies) {
|
|
||||||
|
|
||||||
// Indicate if the policy is active or not
|
// Indicate if the policy is active or not
|
||||||
let disabled = "";
|
let disabled = "";
|
||||||
if (!data[client].sites[site].policies[policy].active) {
|
if (!data[client].sites[site].policy.active) {
|
||||||
disabled = " (disabled)";
|
disabled = " (disabled)";
|
||||||
}
|
}
|
||||||
|
|
||||||
site_temp["children"].push({
|
site_temp["children"].push({
|
||||||
label: data[client].sites[site].policies[policy].name + disabled,
|
label: data[client].sites[site].policy.name + disabled,
|
||||||
icon: "policy",
|
icon: "policy",
|
||||||
id: data[client].sites[site].policies[policy].id
|
id: data[client].sites[site].policy.id
|
||||||
});
|
});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add Site to Client children array
|
// Add Site to Client children array
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<q-card style="width: 60vw" >
|
<q-card style="width: 60vw" >
|
||||||
<q-card-section class="row items-center">
|
<q-card-section class="row items-center">
|
||||||
<div class="text-h6">Edit policies assigned to {{ type }}</div>
|
<div class="text-h6">Edit policy assigned to {{ type }}</div>
|
||||||
<q-space />
|
<q-space />
|
||||||
<q-btn icon="close" flat round dense v-close-popup />
|
<q-btn icon="close" flat round dense v-close-popup />
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
@@ -11,10 +11,9 @@
|
|||||||
v-model="selected"
|
v-model="selected"
|
||||||
:options="options"
|
:options="options"
|
||||||
filled
|
filled
|
||||||
multiple
|
|
||||||
use-chips
|
|
||||||
options-selected-class="text-green"
|
options-selected-class="text-green"
|
||||||
dense
|
dense
|
||||||
|
clearable
|
||||||
>
|
>
|
||||||
<template v-slot:option="props">
|
<template v-slot:option="props">
|
||||||
<q-item
|
<q-item
|
||||||
@@ -32,7 +31,7 @@
|
|||||||
</q-select>
|
</q-select>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
<q-card-section class="row items-center">
|
<q-card-section class="row items-center">
|
||||||
<q-btn label="Add Polices" color="primary" type="submit" />
|
<q-btn label="Add Policy" color="primary" type="submit" />
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
</q-form>
|
</q-form>
|
||||||
</q-card>
|
</q-card>
|
||||||
@@ -57,7 +56,7 @@ export default {
|
|||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
selected: [],
|
selected: null,
|
||||||
options: []
|
options: []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -73,7 +72,7 @@ export default {
|
|||||||
let data = {};
|
let data = {};
|
||||||
data.pk = this.pk,
|
data.pk = this.pk,
|
||||||
data.type = this.type;
|
data.type = this.type;
|
||||||
data.policies = this.selected.map(policy => policy.value);
|
data.policy = this.selected === null ? 0 : this.selected.value;
|
||||||
|
|
||||||
this.$store
|
this.$store
|
||||||
.dispatch("automation/updateRelatedPolicies", data)
|
.dispatch("automation/updateRelatedPolicies", data)
|
||||||
@@ -102,14 +101,16 @@ export default {
|
|||||||
});;
|
});;
|
||||||
|
|
||||||
},
|
},
|
||||||
getRelations(pk, type) {
|
getRelation(pk, type) {
|
||||||
this.$store
|
this.$store
|
||||||
.dispatch("automation/getRelatedPolicies", {pk, type})
|
.dispatch("automation/getRelatedPolicies", {pk, type})
|
||||||
.then(r => {
|
.then(r => {
|
||||||
this.selected = r.data.map(item => ({
|
if (r.data.id !== undefined) {
|
||||||
label: item.name,
|
this.selected = {
|
||||||
value: item.id
|
label: r.data.name,
|
||||||
}))
|
value: r.data.id
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.catch(e => {
|
.catch(e => {
|
||||||
this.$q.notify(notifyErrorConfig("Add error occured while loading"));
|
this.$q.notify(notifyErrorConfig("Add error occured while loading"));
|
||||||
@@ -118,7 +119,7 @@ export default {
|
|||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.getPolicies();
|
this.getPolicies();
|
||||||
this.getRelations(this.pk, this.type);
|
this.getRelation(this.pk, this.type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@@ -25,69 +25,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
<q-card-section class="row">
|
<q-card-section class="row">
|
||||||
<div class="col-2">Clients:</div>
|
<div class="col-2">Enforced:</div>
|
||||||
<div class="col-10">
|
<div class="col-10">
|
||||||
<q-select
|
<q-toggle v-model="enforced" color="green" />
|
||||||
v-model="selectedClients"
|
|
||||||
:options="clientOptions"
|
|
||||||
filled
|
|
||||||
multiple
|
|
||||||
use-chips
|
|
||||||
options-selected-class="text-green"
|
|
||||||
>
|
|
||||||
<template v-slot:no-option>
|
|
||||||
<q-item>
|
|
||||||
<q-item-section class="text-grey">No Results</q-item-section>
|
|
||||||
</q-item>
|
|
||||||
</template>
|
|
||||||
<template v-slot:option="props">
|
|
||||||
<q-item
|
|
||||||
v-bind="props.itemProps"
|
|
||||||
v-on="props.itemEvents"
|
|
||||||
>
|
|
||||||
<q-item-section avatar>
|
|
||||||
<q-icon v-if="props.selected" name="check" />
|
|
||||||
</q-item-section>
|
|
||||||
<q-item-section>
|
|
||||||
<q-item-label v-html="props.opt.label" />
|
|
||||||
</q-item-section>
|
|
||||||
</q-item>
|
|
||||||
</template>
|
|
||||||
</q-select>
|
|
||||||
</div>
|
|
||||||
</q-card-section>
|
|
||||||
<q-card-section class="row">
|
|
||||||
<div class="col-2">Sites:</div>
|
|
||||||
<div class="col-10">
|
|
||||||
<q-select
|
|
||||||
v-model="selectedSites"
|
|
||||||
:options="siteOptions"
|
|
||||||
filled
|
|
||||||
multiple
|
|
||||||
use-chips
|
|
||||||
options-selected-class="text-green"
|
|
||||||
>
|
|
||||||
<template v-slot:no-option>
|
|
||||||
<q-item>
|
|
||||||
<q-item-section class="text-grey">No Results</q-item-section>
|
|
||||||
</q-item>
|
|
||||||
</template>
|
|
||||||
<template v-slot:option="props">
|
|
||||||
<q-item
|
|
||||||
v-bind="props.itemProps"
|
|
||||||
v-on="props.itemEvents"
|
|
||||||
>
|
|
||||||
<q-item-section avatar>
|
|
||||||
<q-icon v-if="props.selected" name="check" />
|
|
||||||
</q-item-section>
|
|
||||||
<q-item-section>
|
|
||||||
<!-- <q-item-label overline>{{ props.opt.client }}</q-item-label> -->
|
|
||||||
<q-item-label v-html="props.opt.label" />
|
|
||||||
<q-item-label caption>{{ props.opt.client }}</q-item-label>
|
|
||||||
</q-item-section>
|
|
||||||
</q-item>
|
|
||||||
</template>
|
|
||||||
</q-select>
|
|
||||||
</div>
|
</div>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
<q-card-section class="row items-center">
|
<q-card-section class="row items-center">
|
||||||
@@ -99,21 +39,17 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import mixins, { notifySuccessConfig, notifyErrorConfig } from "@/mixins/mixins";
|
import mixins, { notifySuccessConfig, notifyErrorConfig } from "@/mixins/mixins";
|
||||||
import dropdown_formatter from "@/mixins/dropdown_formatter";
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "PolicyForm",
|
name: "PolicyForm",
|
||||||
mixins: [mixins, dropdown_formatter],
|
mixins: [mixins],
|
||||||
props: { pk: Number },
|
props: { pk: Number },
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
name: "",
|
name: "",
|
||||||
desc: "",
|
desc: "",
|
||||||
active: false,
|
enforced: false,
|
||||||
selectedSites: [],
|
active: false
|
||||||
selectedClients: [],
|
|
||||||
clientOptions: [],
|
|
||||||
siteOptions: []
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -134,14 +70,7 @@ export default {
|
|||||||
this.name = r.data.name;
|
this.name = r.data.name;
|
||||||
this.desc = r.data.desc;
|
this.desc = r.data.desc;
|
||||||
this.active = r.data.active;
|
this.active = r.data.active;
|
||||||
this.selectedSites = r.data.sites.map(site => ({
|
this.enforced = r.data.enforced;
|
||||||
label: site.site,
|
|
||||||
value: site.id
|
|
||||||
}) );
|
|
||||||
this.selectedClients = r.data.clients.map(client => ({
|
|
||||||
label: client.client,
|
|
||||||
value: client.id
|
|
||||||
}) );
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
submit() {
|
submit() {
|
||||||
@@ -157,8 +86,7 @@ export default {
|
|||||||
name: this.name,
|
name: this.name,
|
||||||
desc: this.desc,
|
desc: this.desc,
|
||||||
active: this.active,
|
active: this.active,
|
||||||
sites: this.selectedSites.map(site => site.value),
|
enforced: this.enforced
|
||||||
clients: this.selectedClients.map(client => client.value)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (this.pk) {
|
if (this.pk) {
|
||||||
@@ -186,20 +114,6 @@ export default {
|
|||||||
this.$q.notify(notifyErrorConfig(e.response.data));
|
this.$q.notify(notifyErrorConfig(e.response.data));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
|
||||||
getClients() {
|
|
||||||
this.$store
|
|
||||||
.dispatch("loadClients")
|
|
||||||
.then(r => {
|
|
||||||
this.clientOptions = this.formatClients(r.data);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
getSites() {
|
|
||||||
this.$store
|
|
||||||
.dispatch("loadSites")
|
|
||||||
.then(r => {
|
|
||||||
this.siteOptions = this.formatSites(r.data);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
@@ -207,9 +121,6 @@ export default {
|
|||||||
if (this.pk) {
|
if (this.pk) {
|
||||||
this.getPolicy();
|
this.getPolicy();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.getClients();
|
|
||||||
this.getSites();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
@@ -144,7 +144,7 @@ export default {
|
|||||||
getCheckData() {
|
getCheckData() {
|
||||||
this.$q.loading.show();
|
this.$q.loading.show();
|
||||||
this.$store
|
this.$store
|
||||||
.dispatch("automation/loadCheckStatus", { policypk: this.item.policy, checkpk: this.item.id })
|
.dispatch("automation/loadCheckStatus", { checkpk: this.item.id })
|
||||||
.then(r => {
|
.then(r => {
|
||||||
this.$q.loading.hide();
|
this.$q.loading.hide();
|
||||||
this.tableData = r.data
|
this.tableData = r.data
|
||||||
@@ -157,7 +157,7 @@ export default {
|
|||||||
getTaskData() {
|
getTaskData() {
|
||||||
this.$q.loading.show();
|
this.$q.loading.show();
|
||||||
this.$store
|
this.$store
|
||||||
.dispatch("automation/loadAutomatedTaskStatus", { policypk: this.item.policy, taskpk: this.item.id })
|
.dispatch("automation/loadAutomatedTaskStatus", { taskpk: this.item.id })
|
||||||
.then(r => {
|
.then(r => {
|
||||||
this.$q.loading.hide();
|
this.$q.loading.hide();
|
||||||
this.tableData = r.data
|
this.tableData = r.data
|
||||||
@@ -174,7 +174,23 @@ export default {
|
|||||||
closeScriptOutput() {
|
closeScriptOutput() {
|
||||||
this.showScriptOutput = false;
|
this.showScriptOutput = false;
|
||||||
this.scriptInfo = {}
|
this.scriptInfo = {}
|
||||||
}
|
},
|
||||||
|
pingInfo(desc, output) {
|
||||||
|
this.$q.dialog({
|
||||||
|
title: desc,
|
||||||
|
style: "width: 50vw; max-width: 60vw",
|
||||||
|
message: `<pre>${output}</pre>`,
|
||||||
|
html: true
|
||||||
|
});
|
||||||
|
},
|
||||||
|
scriptMoreInfo(props) {
|
||||||
|
this.scriptInfo = props;
|
||||||
|
this.showScriptOutput = true;
|
||||||
|
},
|
||||||
|
eventLogMoreInfo(props) {
|
||||||
|
this.evtLogData = props;
|
||||||
|
this.showEventLogOutput = true;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
if (this.type === "task") {
|
if (this.type === "task") {
|
||||||
|
|||||||
@@ -55,11 +55,11 @@ export default {
|
|||||||
context.commit("setPolicyChecks", r.data);
|
context.commit("setPolicyChecks", r.data);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
loadCheckStatus(context, { policypk, checkpk }) {
|
loadCheckStatus(context, { checkpk }) {
|
||||||
return axios.patch(`/automation/${policypk}/policycheckstatus/${checkpk}/check/`);
|
return axios.patch(`/automation/policycheckstatus/${checkpk}/check/`);
|
||||||
},
|
},
|
||||||
loadAutomatedTaskStatus(context, { policypk, taskpk }) {
|
loadAutomatedTaskStatus(context, { taskpk }) {
|
||||||
return axios.patch(`/automation/${policypk}/policyautomatedtaskstatus/${taskpk}/task/`);
|
return axios.patch(`/automation/policyautomatedtaskstatus/${taskpk}/task/`);
|
||||||
},
|
},
|
||||||
loadPolicy(context, pk) {
|
loadPolicy(context, pk) {
|
||||||
return axios.get(`/automation/policies/${pk}/`);
|
return axios.get(`/automation/policies/${pk}/`);
|
||||||
@@ -76,7 +76,10 @@ export default {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
runPolicyTask(context, pk) {
|
runPolicyTask(context, pk) {
|
||||||
return axios.get(`/automation/runwintask/${pk}/`);
|
return axios.put(`/automation/runwintask/${pk}/`);
|
||||||
|
},
|
||||||
|
runPolicyCheck(context, pk) {
|
||||||
|
return axios.put(`/automation/runpolicycheck/${pk}/`);
|
||||||
},
|
},
|
||||||
getRelated(context, pk) {
|
getRelated(context, pk) {
|
||||||
return axios.get(`/automation/policies/${pk}/related/`);
|
return axios.get(`/automation/policies/${pk}/related/`);
|
||||||
|
|||||||
@@ -241,6 +241,8 @@ describe("AutomationManager.vue", () => {
|
|||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// TODO Test Checkboxes on table
|
||||||
|
|
||||||
// TODO: Test @close and @hide events
|
// TODO: Test @close and @hide events
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -8,48 +8,16 @@ const localVue = createLocalVue();
|
|||||||
localVue.use(Vuex);
|
localVue.use(Vuex);
|
||||||
|
|
||||||
/*** TEST DATA ***/
|
/*** TEST DATA ***/
|
||||||
const clients = [
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
client: "Test Client"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
client: "Test Client2"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 3,
|
|
||||||
client: "Test Client3"
|
|
||||||
}
|
|
||||||
];
|
|
||||||
const sites = [
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
site: "Site Name",
|
|
||||||
client_name: "Test Client"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
site: "Site Name2",
|
|
||||||
client_name: "Test Client2"
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
const policy = {
|
const policy = {
|
||||||
id: 1,
|
id: 1,
|
||||||
name: "Test Policy",
|
name: "Test Policy",
|
||||||
desc: "Test Desc",
|
desc: "Test Desc",
|
||||||
active: true,
|
enforced: false,
|
||||||
clients: [],
|
active: true
|
||||||
sites: []
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let actions, rootActions, store;
|
let actions, rootActions, store;
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
rootActions = {
|
|
||||||
loadClients: jest.fn(() => new Promise(res => res({ data: clients }))),
|
|
||||||
loadSites: jest.fn(() => new Promise(res => res({ data: sites }))),
|
|
||||||
};
|
|
||||||
|
|
||||||
actions = {
|
actions = {
|
||||||
loadPolicy: jest.fn(() => new Promise(res => res({ data: policy }))),
|
loadPolicy: jest.fn(() => new Promise(res => res({ data: policy }))),
|
||||||
@@ -87,8 +55,6 @@ describe("PolicyForm.vue when editting", () => {
|
|||||||
/*** TESTS ***/
|
/*** TESTS ***/
|
||||||
it("calls vuex actions on mount with pk prop set", () => {
|
it("calls vuex actions on mount with pk prop set", () => {
|
||||||
|
|
||||||
expect(rootActions.loadClients).toHaveBeenCalled();
|
|
||||||
expect(rootActions.loadSites).toHaveBeenCalled();
|
|
||||||
expect(actions.loadPolicy).toHaveBeenCalledWith(expect.anything(), 1);
|
expect(actions.loadPolicy).toHaveBeenCalledWith(expect.anything(), 1);
|
||||||
|
|
||||||
});
|
});
|
||||||
@@ -127,24 +93,11 @@ describe("PolicyForm.vue when adding", () => {
|
|||||||
/*** TESTS ***/
|
/*** TESTS ***/
|
||||||
it("calls vuex actions on mount", () => {
|
it("calls vuex actions on mount", () => {
|
||||||
|
|
||||||
expect(rootActions.loadClients).toHaveBeenCalled();
|
|
||||||
expect(rootActions.loadSites).toHaveBeenCalled();
|
|
||||||
|
|
||||||
// Not called unless pk prop is set
|
// Not called unless pk prop is set
|
||||||
expect(actions.loadPolicy).not.toHaveBeenCalled();
|
expect(actions.loadPolicy).not.toHaveBeenCalled();
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Sets client and site options correctly", async () => {
|
|
||||||
|
|
||||||
// Make sure the promises are resolved
|
|
||||||
await flushPromises();
|
|
||||||
|
|
||||||
expect(wrapper.vm.clientOptions).toHaveLength(3);
|
|
||||||
expect(wrapper.vm.siteOptions).toHaveLength(2);
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
it("sends the correct add action on submit", async () => {
|
it("sends the correct add action on submit", async () => {
|
||||||
|
|
||||||
wrapper.setData({name: "Test Policy"});
|
wrapper.setData({name: "Test Policy"});
|
||||||
|
|||||||
@@ -7,23 +7,21 @@ import "../../utils/quasar.js";
|
|||||||
const localVue = createLocalVue();
|
const localVue = createLocalVue();
|
||||||
localVue.use(Vuex);
|
localVue.use(Vuex);
|
||||||
|
|
||||||
const related = [
|
const related = {
|
||||||
{
|
|
||||||
id: 1,
|
id: 1,
|
||||||
name: "Test Policy"
|
name: "Test Policy"
|
||||||
},
|
};
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
name: "Test Policy 2"
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
let state, actions, getters, store;
|
let state, actions, getters, store;
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
policies: [
|
policies: [
|
||||||
...related,
|
related,
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
name: "Test Policy 2"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: 3,
|
id: 3,
|
||||||
name: "TestPolicy 3"
|
name: "TestPolicy 3"
|
||||||
@@ -34,7 +32,7 @@ beforeEach(() => {
|
|||||||
actions = {
|
actions = {
|
||||||
updateRelatedPolicies: jest.fn(),
|
updateRelatedPolicies: jest.fn(),
|
||||||
loadPolicies: jest.fn(),
|
loadPolicies: jest.fn(),
|
||||||
getRelatedPolicies: jest.fn(() => new Promise(res => res({ data: related }))),
|
getRelatedPolicies: jest.fn(() => Promise.resolve({ data: related })),
|
||||||
};
|
};
|
||||||
|
|
||||||
getters = {
|
getters = {
|
||||||
@@ -86,7 +84,7 @@ describe.each([
|
|||||||
|
|
||||||
it("renders title correctly", () => {
|
it("renders title correctly", () => {
|
||||||
|
|
||||||
expect(wrapper.find(".text-h6").text()).toBe(`Edit policies assigned to ${type}`);
|
expect(wrapper.find(".text-h6").text()).toBe(`Edit policy assigned to ${type}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("renders correct amount of policies in dropdown", async () => {
|
it("renders correct amount of policies in dropdown", async () => {
|
||||||
@@ -95,10 +93,10 @@ describe.each([
|
|||||||
expect(wrapper.vm.options).toHaveLength(3);
|
expect(wrapper.vm.options).toHaveLength(3);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("renders correct amount of related policies in selected", async () => {
|
it("renders correct policy in selected", async () => {
|
||||||
|
|
||||||
await flushpromises();
|
await flushpromises();
|
||||||
expect(wrapper.vm.selected).toHaveLength(2);
|
expect(wrapper.vm.selected).toStrictEqual({label: related.name, value: related.id});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("sends correct data on form submit", async () => {
|
it("sends correct data on form submit", async () => {
|
||||||
@@ -109,7 +107,7 @@ describe.each([
|
|||||||
await form.vm.$emit("submit");
|
await form.vm.$emit("submit");
|
||||||
|
|
||||||
expect(actions.updateRelatedPolicies).toHaveBeenCalledWith(expect.anything(),
|
expect(actions.updateRelatedPolicies).toHaveBeenCalledWith(expect.anything(),
|
||||||
{ pk: pk, type: type, policies: [1,2] }
|
{ pk: pk, type: type, policy: related.id }
|
||||||
);
|
);
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -8,44 +8,44 @@ localVue.use(Vuex);
|
|||||||
|
|
||||||
describe("PolicyOverview.vue", () => {
|
describe("PolicyOverview.vue", () => {
|
||||||
|
|
||||||
const policyTreeData = {
|
const policyTreeData = [
|
||||||
// node 0
|
|
||||||
"Client Name 1": {
|
|
||||||
policies: [
|
|
||||||
{
|
{
|
||||||
|
// node 0
|
||||||
|
client: "Client Name 1",
|
||||||
|
policy: {
|
||||||
id: 1,
|
id: 1,
|
||||||
name: "Policy Name 1",
|
name: "Policy Name 1",
|
||||||
active: true
|
active: true
|
||||||
}
|
|
||||||
],
|
|
||||||
sites: {
|
|
||||||
// node -1
|
|
||||||
"Site Name 1": { policies: []}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
// node -2
|
// node -1
|
||||||
"Client Name 2": {
|
sites: [
|
||||||
policies: [
|
|
||||||
{
|
{
|
||||||
|
site: "Site Name 1",
|
||||||
|
policy: null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// node -2
|
||||||
|
client: "Client Name 2",
|
||||||
|
policy: {
|
||||||
id: 2,
|
id: 2,
|
||||||
name: "Policy Name 2",
|
name: "Policy Name 2",
|
||||||
active: true
|
active: true
|
||||||
}
|
},
|
||||||
],
|
sites: [
|
||||||
sites: {
|
|
||||||
// node -3
|
|
||||||
"Site Name 2": {
|
|
||||||
policies: [
|
|
||||||
{
|
{
|
||||||
|
// node -3
|
||||||
|
site: "Site Name 2",
|
||||||
|
policy: {
|
||||||
id: 3,
|
id: 3,
|
||||||
name: "Policy Name 3",
|
name: "Policy Name 3",
|
||||||
active: false
|
active: false
|
||||||
}
|
}
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
];
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let wrapper, actions, mutations, store;
|
let wrapper, actions, mutations, store;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user