Skip to content

Commit

Permalink
Release 0.4.23
Browse files Browse the repository at this point in the history
  • Loading branch information
wh1te909 committed Mar 11, 2021
2 parents 72d55a0 + 176c85d commit 48375f3
Show file tree
Hide file tree
Showing 44 changed files with 26,132 additions and 5,822 deletions.
18 changes: 18 additions & 0 deletions api/tacticalrmm/accounts/migrations/0013_user_client_tree_sort.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 3.1.7 on 2021-03-09 02:33

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('accounts', '0012_user_agents_per_page'),
]

operations = [
migrations.AddField(
model_name='user',
name='client_tree_sort',
field=models.CharField(choices=[('alphafail', 'Move failing clients to the top'), ('alpha', 'Sort alphabetically')], default='alphafail', max_length=50),
),
]
10 changes: 9 additions & 1 deletion api/tacticalrmm/accounts/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@
("mixed", "Mixed"),
]

CLIENT_TREE_SORT_CHOICES = [
("alphafail", "Move failing clients to the top"),
("alpha", "Sort alphabetically"),
]


class User(AbstractUser, BaseAuditModel):
is_active = models.BooleanField(default=True)
Expand All @@ -27,7 +32,10 @@ class User(AbstractUser, BaseAuditModel):
default_agent_tbl_tab = models.CharField(
max_length=50, choices=AGENT_TBL_TAB_CHOICES, default="server"
)
agents_per_page = models.PositiveIntegerField(default=50)
agents_per_page = models.PositiveIntegerField(default=50) # not currently used
client_tree_sort = models.CharField(
max_length=50, choices=CLIENT_TREE_SORT_CHOICES, default="alphafail"
)

agent = models.OneToOneField(
"agents.Agent",
Expand Down
12 changes: 12 additions & 0 deletions api/tacticalrmm/accounts/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,18 @@
from .models import User


class UserUISerializer(ModelSerializer):
class Meta:
model = User
fields = [
"dark_mode",
"show_community_scripts",
"agent_dblclick_action",
"default_agent_tbl_tab",
"client_tree_sort",
]


class UserSerializer(ModelSerializer):
class Meta:
model = User
Expand Down
12 changes: 3 additions & 9 deletions api/tacticalrmm/accounts/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -271,19 +271,13 @@ def test_put_non_root_user(self):

def test_user_ui(self):
url = "/accounts/users/ui/"
data = {"dark_mode": False}
r = self.client.patch(url, data, format="json")
self.assertEqual(r.status_code, 200)

data = {"show_community_scripts": True}
r = self.client.patch(url, data, format="json")
self.assertEqual(r.status_code, 200)

data = {
"userui": True,
"dark_mode": True,
"show_community_scripts": True,
"agent_dblclick_action": "editagent",
"default_agent_tbl_tab": "mixed",
"agents_per_page": 1000,
"client_tree_sort": "alpha",
}
r = self.client.patch(url, data, format="json")
self.assertEqual(r.status_code, 200)
Expand Down
58 changes: 18 additions & 40 deletions api/tacticalrmm/accounts/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,15 @@
from tacticalrmm.utils import notify_error

from .models import User
from .serializers import TOTPSetupSerializer, UserSerializer
from .serializers import TOTPSetupSerializer, UserSerializer, UserUISerializer


def _is_root_user(request, user) -> bool:
return (
hasattr(settings, "ROOT_USER")
and request.user != user
and user.username == settings.ROOT_USER
)


class CheckCreds(KnoxLoginView):
Expand Down Expand Up @@ -105,11 +113,7 @@ def get(self, request, pk):
def put(self, request, pk):
user = get_object_or_404(User, pk=pk)

if (
hasattr(settings, "ROOT_USER")
and request.user != user
and user.username == settings.ROOT_USER
):
if _is_root_user(request, user):
return notify_error("The root user cannot be modified from the UI")

serializer = UserSerializer(instance=user, data=request.data, partial=True)
Expand All @@ -120,11 +124,7 @@ def put(self, request, pk):

def delete(self, request, pk):
user = get_object_or_404(User, pk=pk)
if (
hasattr(settings, "ROOT_USER")
and request.user != user
and user.username == settings.ROOT_USER
):
if _is_root_user(request, user):
return notify_error("The root user cannot be deleted from the UI")

user.delete()
Expand All @@ -137,11 +137,7 @@ class UserActions(APIView):
# reset password
def post(self, request):
user = get_object_or_404(User, pk=request.data["id"])
if (
hasattr(settings, "ROOT_USER")
and request.user != user
and user.username == settings.ROOT_USER
):
if _is_root_user(request, user):
return notify_error("The root user cannot be modified from the UI")

user.set_password(request.data["password"])
Expand All @@ -152,11 +148,7 @@ def post(self, request):
# reset two factor token
def put(self, request):
user = get_object_or_404(User, pk=request.data["id"])
if (
hasattr(settings, "ROOT_USER")
and request.user != user
and user.username == settings.ROOT_USER
):
if _is_root_user(request, user):
return notify_error("The root user cannot be modified from the UI")

user.totp_key = ""
Expand Down Expand Up @@ -184,23 +176,9 @@ def post(self, request):

class UserUI(APIView):
def patch(self, request):
user = request.user

if "dark_mode" in request.data.keys():
user.dark_mode = request.data["dark_mode"]
user.save(update_fields=["dark_mode"])

if "show_community_scripts" in request.data.keys():
user.show_community_scripts = request.data["show_community_scripts"]
user.save(update_fields=["show_community_scripts"])

if "userui" in request.data.keys():
user.agent_dblclick_action = request.data["agent_dblclick_action"]
user.default_agent_tbl_tab = request.data["default_agent_tbl_tab"]
user.save(update_fields=["agent_dblclick_action", "default_agent_tbl_tab"])

if "agents_per_page" in request.data.keys():
user.agents_per_page = request.data["agents_per_page"]
user.save(update_fields=["agents_per_page"])

serializer = UserUISerializer(
instance=request.user, data=request.data, partial=True
)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response("ok")
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Generated by Django 3.1.7 on 2021-03-04 03:57

from django.db import migrations, models
import django.db.models.deletion
from django.db import migrations, models


class Migration(migrations.Migration):
Expand Down
14 changes: 14 additions & 0 deletions api/tacticalrmm/agents/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,20 @@ def physical_disks(self):
except:
return ["unknown disk"]

def check_run_interval(self) -> int:
interval = self.check_interval
# determine if any agent checks have a custom interval and set the lowest interval
for check in self.agentchecks.filter(overriden_by_policy=False): # type: ignore
if check.run_interval and check.run_interval < interval:

# don't allow check runs less than 15s
if check.run_interval < 15:
interval = 15
else:
interval = check.run_interval

return interval

def run_script(
self,
scriptpk: int,
Expand Down
8 changes: 3 additions & 5 deletions api/tacticalrmm/alerts/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
from django.conf import settings
from django.utils import timezone as djangotime
from model_bakery import baker, seq

from alerts.tasks import cache_agents_alert_template
from autotasks.models import AutomatedTask

from core.models import CoreSettings
from tacticalrmm.test import TacticalTestCase

Expand Down Expand Up @@ -727,6 +727,7 @@ def test_handle_check_alerts(
send_email,
sleep,
):
from alerts.tasks import cache_agents_alert_template
from checks.models import Check
from checks.tasks import (
handle_check_email_alert_task,
Expand All @@ -735,8 +736,6 @@ def test_handle_check_alerts(
handle_resolved_check_sms_alert_task,
)

from alerts.tasks import cache_agents_alert_template

# create test data
agent = baker.make_recipe("agents.agent")
agent_no_settings = baker.make_recipe("agents.agent")
Expand Down Expand Up @@ -1012,6 +1011,7 @@ def test_handle_task_alerts(
send_email,
sleep,
):
from alerts.tasks import cache_agents_alert_template
from autotasks.models import AutomatedTask
from autotasks.tasks import (
handle_resolved_task_email_alert,
Expand All @@ -1020,8 +1020,6 @@ def test_handle_task_alerts(
handle_task_sms_alert,
)

from alerts.tasks import cache_agents_alert_template

# create test data
agent = baker.make_recipe("agents.agent")
agent_no_settings = baker.make_recipe("agents.agent")
Expand Down
1 change: 0 additions & 1 deletion api/tacticalrmm/alerts/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
AlertTemplateRelationSerializer,
AlertTemplateSerializer,
)

from .tasks import cache_agents_alert_template


Expand Down
59 changes: 59 additions & 0 deletions api/tacticalrmm/apiv3/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from unittest.mock import patch

from django.conf import settings
from django.utils import timezone as djangotime
from model_bakery import baker

from tacticalrmm.test import TacticalTestCase
Expand All @@ -17,8 +18,44 @@ def setUp(self):
def test_get_checks(self):
url = f"/api/v3/{self.agent.agent_id}/checkrunner/"

# add a check
check1 = baker.make_recipe("checks.ping_check", agent=self.agent)
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
self.assertEqual(r.data["check_interval"], self.agent.check_interval) # type: ignore
self.assertEqual(len(r.data["checks"]), 1) # type: ignore

# override check run interval
check2 = baker.make_recipe(
"checks.ping_check", agent=self.agent, run_interval=20
)

r = self.client.get(url)
self.assertEqual(r.status_code, 200)
self.assertEqual(r.data["check_interval"], 20) # type: ignore
self.assertEqual(len(r.data["checks"]), 2) # type: ignore

# Set last_run on both checks and should return an empty list
check1.last_run = djangotime.now()
check1.save()
check2.last_run = djangotime.now()
check2.save()

r = self.client.get(url)
self.assertEqual(r.status_code, 200)
self.assertEqual(r.data["check_interval"], 20) # type: ignore
self.assertFalse(r.data["checks"]) # type: ignore

# set last_run greater than interval
check1.last_run = djangotime.now() - djangotime.timedelta(seconds=200)
check1.save()
check2.last_run = djangotime.now() - djangotime.timedelta(seconds=200)
check2.save()

r = self.client.get(url)
self.assertEqual(r.status_code, 200)
self.assertEqual(r.data["check_interval"], 20) # type: ignore
self.assertEquals(len(r.data["checks"]), 2) # type: ignore

url = "/api/v3/Maj34ACb324j234asdj2n34kASDjh34-DESKTOPTEST123/checkrunner/"
r = self.client.get(url)
Expand Down Expand Up @@ -53,6 +90,28 @@ def test_checkrunner_interval(self):
{"agent": self.agent.pk, "check_interval": self.agent.check_interval},
)

# add check to agent with check interval set
check = baker.make_recipe(
"checks.ping_check", agent=self.agent, run_interval=30
)

r = self.client.get(url, format="json")
self.assertEqual(r.status_code, 200)
self.assertEqual(
r.json(),
{"agent": self.agent.pk, "check_interval": 30},
)

# minimum check run interval is 15 seconds
check = baker.make_recipe("checks.ping_check", agent=self.agent, run_interval=5)

r = self.client.get(url, format="json")
self.assertEqual(r.status_code, 200)
self.assertEqual(
r.json(),
{"agent": self.agent.pk, "check_interval": 15},
)

def test_checkin_patch(self):
from logs.models import PendingAction

Expand Down
33 changes: 28 additions & 5 deletions api/tacticalrmm/apiv3/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -266,12 +266,32 @@ class CheckRunner(APIView):

def get(self, request, agentid):
agent = get_object_or_404(Agent, agent_id=agentid)
checks = Check.objects.filter(agent__pk=agent.pk, overriden_by_policy=False)

checks = agent.agentchecks.filter(overriden_by_policy=False) # type: ignore

run_list = [
check
for check in checks
# always run if check hasn't run yet
if not check.last_run
# if a check interval is set, see if the correct amount of seconds have passed
or (
check.run_interval
and (
check.last_run
< djangotime.now()
- djangotime.timedelta(seconds=check.run_interval)
)
# if check interval isn't set, make sure the agent's check interval has passed before running
)
or (
check.last_run
< djangotime.now() - djangotime.timedelta(seconds=agent.check_interval)
)
]
ret = {
"agent": agent.pk,
"check_interval": agent.check_interval,
"checks": CheckRunnerGetSerializer(checks, many=True).data,
"check_interval": agent.check_run_interval(),
"checks": CheckRunnerGetSerializer(run_list, many=True).data,
}
return Response(ret)

Expand All @@ -290,7 +310,10 @@ class CheckRunnerInterval(APIView):

def get(self, request, agentid):
agent = get_object_or_404(Agent, agent_id=agentid)
return Response({"agent": agent.pk, "check_interval": agent.check_interval})

return Response(
{"agent": agent.pk, "check_interval": agent.check_run_interval()}
)


class TaskRunner(APIView):
Expand Down
Loading

0 comments on commit 48375f3

Please sign in to comment.