Skip to content

Commit

Permalink
Release 0.4.26
Browse files Browse the repository at this point in the history
  • Loading branch information
wh1te909 committed Mar 17, 2021
2 parents d7868e9 + ded5437 commit 8eb91c0
Show file tree
Hide file tree
Showing 27 changed files with 143 additions and 121 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
Tactical RMM is a remote monitoring & management tool for Windows computers, built with Django and Vue.\
It uses an [agent](https://github.com/wh1te909/rmmagent) written in golang and integrates with [MeshCentral](https://github.com/Ylianst/MeshCentral)

# [LIVE DEMO](https://rmm.xlawgaming.com/)
# [LIVE DEMO](https://rmm.tacticalrmm.io/)
Demo database resets every hour. Alot of features are disabled for obvious reasons due to the nature of this app.

*Tactical RMM is currently in alpha and subject to breaking changes. Use in production at your own risk.*
Expand Down
8 changes: 0 additions & 8 deletions api/tacticalrmm/agents/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,14 +110,6 @@ def __str__(self):
def client(self):
return self.site.client

@property
def has_nats(self):
return pyver.parse(self.version) >= pyver.parse("1.1.0")

@property
def has_gotasks(self):
return pyver.parse(self.version) >= pyver.parse("1.1.1")

@property
def timezone(self):
# return the default timezone unless the timezone is explicity set per agent
Expand Down
6 changes: 1 addition & 5 deletions api/tacticalrmm/agents/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,11 +198,6 @@ def test_uninstall(self, reload_nats, nats_cmd):

@patch("agents.models.Agent.nats_cmd")
def test_get_processes(self, mock_ret):
agent_old = baker.make_recipe("agents.online_agent", version="1.1.12")
url_old = f"/agents/{agent_old.pk}/getprocs/"
r = self.client.get(url_old)
self.assertEqual(r.status_code, 400)

agent = baker.make_recipe("agents.online_agent", version="1.2.0")
url = f"/agents/{agent.pk}/getprocs/"

Expand Down Expand Up @@ -340,6 +335,7 @@ def test_reboot_later(self, nats_cmd):
"func": "schedtask",
"schedtaskpayload": {
"type": "schedreboot",
"deleteafter": True,
"trigger": "once",
"name": r.data["task_name"], # type: ignore
"year": 2025,
Expand Down
35 changes: 5 additions & 30 deletions api/tacticalrmm/agents/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,19 +69,17 @@ def update_agents(request):
def ping(request, pk):
agent = get_object_or_404(Agent, pk=pk)
status = "offline"
if agent.has_nats:
r = asyncio.run(agent.nats_cmd({"func": "ping"}, timeout=5))
if r == "pong":
status = "online"
r = asyncio.run(agent.nats_cmd({"func": "ping"}, timeout=5))
if r == "pong":
status = "online"

return Response({"name": agent.hostname, "status": status})


@api_view(["DELETE"])
def uninstall(request):
agent = get_object_or_404(Agent, pk=request.data["pk"])
if agent.has_nats:
asyncio.run(agent.nats_cmd({"func": "uninstall"}, wait=False))
asyncio.run(agent.nats_cmd({"func": "uninstall"}, wait=False))

name = agent.hostname
agent.delete()
Expand Down Expand Up @@ -147,9 +145,6 @@ def agent_detail(request, pk):
@api_view()
def get_processes(request, pk):
agent = get_object_or_404(Agent, pk=pk)
if pyver.parse(agent.version) < pyver.parse("1.2.0"):
return notify_error("Requires agent version 1.2.0 or greater")

r = asyncio.run(agent.nats_cmd(data={"func": "procs"}, timeout=5))
if r == "timeout":
return notify_error("Unable to contact the agent")
Expand All @@ -159,9 +154,6 @@ def get_processes(request, pk):
@api_view()
def kill_proc(request, pk, pid):
agent = get_object_or_404(Agent, pk=pk)
if not agent.has_nats:
return notify_error("Requires agent version 1.1.0 or greater")

r = asyncio.run(
agent.nats_cmd({"func": "killproc", "procpid": int(pid)}, timeout=15)
)
Expand All @@ -177,8 +169,6 @@ def kill_proc(request, pk, pid):
@api_view()
def get_event_log(request, pk, logtype, days):
agent = get_object_or_404(Agent, pk=pk)
if not agent.has_nats:
return notify_error("Requires agent version 1.1.0 or greater")
timeout = 180 if logtype == "Security" else 30
data = {
"func": "eventlog",
Expand All @@ -198,8 +188,6 @@ def get_event_log(request, pk, logtype, days):
@api_view(["POST"])
def send_raw_cmd(request):
agent = get_object_or_404(Agent, pk=request.data["pk"])
if not agent.has_nats:
return notify_error("Requires agent version 1.1.0 or greater")
timeout = int(request.data["timeout"])
data = {
"func": "rawcmd",
Expand Down Expand Up @@ -296,9 +284,6 @@ class Reboot(APIView):
# reboot now
def post(self, request):
agent = get_object_or_404(Agent, pk=request.data["pk"])
if not agent.has_nats:
return notify_error("Requires agent version 1.1.0 or greater")

r = asyncio.run(agent.nats_cmd({"func": "rebootnow"}, timeout=10))
if r != "ok":
return notify_error("Unable to contact the agent")
Expand All @@ -308,8 +293,6 @@ def post(self, request):
# reboot later
def patch(self, request):
agent = get_object_or_404(Agent, pk=request.data["pk"])
if not agent.has_gotasks:
return notify_error("Requires agent version 1.1.1 or greater")

try:
obj = dt.datetime.strptime(request.data["datetime"], "%Y-%m-%d %H:%M")
Expand All @@ -324,6 +307,7 @@ def patch(self, request):
"func": "schedtask",
"schedtaskpayload": {
"type": "schedreboot",
"deleteafter": True,
"trigger": "once",
"name": task_name,
"year": int(dt.datetime.strftime(obj, "%Y")),
Expand All @@ -334,9 +318,6 @@ def patch(self, request):
},
}

if pyver.parse(agent.version) >= pyver.parse("1.1.2"):
nats_data["schedtaskpayload"]["deleteafter"] = True

r = asyncio.run(agent.nats_cmd(nats_data, timeout=10))
if r != "ok":
return notify_error(r)
Expand Down Expand Up @@ -561,9 +542,6 @@ def run_script(request):
@api_view()
def recover_mesh(request, pk):
agent = get_object_or_404(Agent, pk=pk)
if not agent.has_nats:
return notify_error("Requires agent version 1.1.0 or greater")

data = {"func": "recover", "payload": {"mode": "mesh"}}
r = asyncio.run(agent.nats_cmd(data, timeout=45))
if r != "ok":
Expand Down Expand Up @@ -743,9 +721,6 @@ def agent_maintenance(request):
class WMI(APIView):
def get(self, request, pk):
agent = get_object_or_404(Agent, pk=pk)
if pyver.parse(agent.version) < pyver.parse("1.1.2"):
return notify_error("Requires agent version 1.1.2 or greater")

r = asyncio.run(agent.nats_cmd({"func": "sysinfo"}, timeout=20))
if r != "ok":
return notify_error("Unable to contact the agent")
Expand Down
17 changes: 17 additions & 0 deletions api/tacticalrmm/apiv3/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,23 @@ def test_checkrunner_interval(self):
{"agent": self.agent.pk, "check_interval": 15},
)

def test_run_checks(self):
# force run all checks regardless of interval
agent = baker.make_recipe("agents.online_agent")
baker.make_recipe("checks.ping_check", agent=agent)
baker.make_recipe("checks.diskspace_check", agent=agent)
baker.make_recipe("checks.cpuload_check", agent=agent)
baker.make_recipe("checks.memory_check", agent=agent)
baker.make_recipe("checks.eventlog_check", agent=agent)
for _ in range(10):
baker.make_recipe("checks.script_check", agent=agent)

url = f"/api/v3/{agent.agent_id}/runchecks/"
r = self.client.get(url)
self.assertEqual(r.json()["agent"], agent.pk)
self.assertIsInstance(r.json()["check_interval"], int)
self.assertEqual(len(r.json()["checks"]), 15)

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

Expand Down
17 changes: 0 additions & 17 deletions api/tacticalrmm/autotasks/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ def test_add_autotask(
agent = baker.make_recipe("agents.agent")
policy = baker.make("automation.Policy")
check = baker.make_recipe("checks.diskspace_check", agent=agent)
old_agent = baker.make_recipe("agents.agent", version="1.1.0")

# test script set to invalid pk
data = {"autotask": {"script": 500}}
Expand All @@ -52,15 +51,6 @@ def test_add_autotask(
resp = self.client.post(url, data, format="json")
self.assertEqual(resp.status_code, 404)

# test old agent version
data = {
"autotask": {"script": script.id},
"agent": old_agent.id,
}

resp = self.client.post(url, data, format="json")
self.assertEqual(resp.status_code, 400)

# test add task to agent
data = {
"autotask": {
Expand Down Expand Up @@ -203,13 +193,6 @@ def test_run_autotask(self, nats_cmd):
nats_cmd.assert_called_with({"func": "runtask", "taskpk": task.id}, wait=False)
nats_cmd.reset_mock()

old_agent = baker.make_recipe("agents.agent", version="1.0.2")
task2 = baker.make("autotasks.AutomatedTask", agent=old_agent)
url = f"/tasks/runwintask/{task2.id}/"
resp = self.client.get(url, format="json")
self.assertEqual(resp.status_code, 400)
nats_cmd.assert_not_called()

self.check_not_authenticated("get", url)


Expand Down
6 changes: 0 additions & 6 deletions api/tacticalrmm/autotasks/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,6 @@ def post(self, request):
parent = {"policy": policy}
else:
agent = get_object_or_404(Agent, pk=data["agent"])
if not agent.has_gotasks:
return notify_error("Requires agent version 1.1.1 or greater")

parent = {"agent": agent}

check = None
Expand Down Expand Up @@ -128,8 +125,5 @@ def delete(self, request, pk):
@api_view()
def run_task(request, pk):
task = get_object_or_404(AutomatedTask, pk=pk)
if not task.agent.has_nats:
return notify_error("Requires agent version 1.1.0 or greater")

asyncio.run(task.agent.nats_cmd({"func": "runtask", "taskpk": task.pk}, wait=False))
return Response(f"{task.name} will now be run on {task.agent.hostname}")
6 changes: 0 additions & 6 deletions api/tacticalrmm/checks/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -310,14 +310,8 @@ def test_edit_check_alert(self):
@patch("agents.models.Agent.nats_cmd")
def test_run_checks(self, nats_cmd):
agent = baker.make_recipe("agents.agent", version="1.4.1")
agent_old = baker.make_recipe("agents.agent", version="1.0.2")
agent_b4_141 = baker.make_recipe("agents.agent", version="1.4.0")

url = f"/checks/runchecks/{agent_old.pk}/"
r = self.client.get(url)
self.assertEqual(r.status_code, 400)
self.assertEqual(r.json(), "Requires agent version 1.1.0 or greater")

url = f"/checks/runchecks/{agent_b4_141.pk}/"
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
Expand Down
2 changes: 0 additions & 2 deletions api/tacticalrmm/checks/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,8 +161,6 @@ def patch(self, request, checkpk):
@api_view()
def run_checks(request, pk):
agent = get_object_or_404(Agent, pk=pk)
if not agent.has_nats:
return notify_error("Requires agent version 1.1.0 or greater")

if pyver.parse(agent.version) >= pyver.parse("1.4.1"):
r = asyncio.run(agent.nats_cmd({"func": "runchecks"}, timeout=15))
Expand Down
23 changes: 22 additions & 1 deletion api/tacticalrmm/scripts/community_scripts.json
Original file line number Diff line number Diff line change
Expand Up @@ -215,5 +215,26 @@
"name": "Create User Logon Script",
"description": "Creates a powershell script that runs at logon of any user on the machine in the security context of the user.",
"shell": "powershell"
},
{
"filename": "Chocolatey_Update_Installed.bat",
"submittedBy": "https://github.com/silversword411",
"name": "Chocolatey Update Installed Apps",
"description": "Update all apps that were installed using Chocolatey.",
"shell": "cmd"
},
{
"filename": "AD_Check_And_Enable_AD_Recycle_Bin.ps1",
"submittedBy": "https://github.com/silversword411",
"name": "AD - Check and Enable AD Recycle Bin",
"description": "Only run on Domain Controllers, checks for Active Directory Recycle Bin and enables if not already enabled",
"shell": "powershell"
},
{
"filename": "Check_Events_for_Bluescreens.ps1",
"submittedBy": "https://github.com/dinger1986",
"name": "Event Viewer - Check for Bluescreens",
"description": "This will check for Bluescreen events on your system",
"shell": "powershell"
}
]
]
8 changes: 2 additions & 6 deletions api/tacticalrmm/scripts/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@

@app.task
def handle_bulk_command_task(agentpks, cmd, shell, timeout) -> None:
agents = Agent.objects.filter(pk__in=agentpks)
agents_nats = [agent for agent in agents if agent.has_nats]
nats_data = {
"func": "rawcmd",
"timeout": timeout,
Expand All @@ -17,15 +15,13 @@ def handle_bulk_command_task(agentpks, cmd, shell, timeout) -> None:
"shell": shell,
},
}
for agent in agents_nats:
for agent in Agent.objects.filter(pk__in=agentpks):
asyncio.run(agent.nats_cmd(nats_data, wait=False))


@app.task
def handle_bulk_script_task(scriptpk, agentpks, args, timeout) -> None:
script = Script.objects.get(pk=scriptpk)
agents = Agent.objects.filter(pk__in=agentpks)
agents_nats = [agent for agent in agents if agent.has_nats]
nats_data = {
"func": "runscript",
"timeout": timeout,
Expand All @@ -35,5 +31,5 @@ def handle_bulk_script_task(scriptpk, agentpks, args, timeout) -> None:
"shell": script.shell,
},
}
for agent in agents_nats:
for agent in Agent.objects.filter(pk__in=agentpks):
asyncio.run(agent.nats_cmd(nats_data, wait=False))
8 changes: 0 additions & 8 deletions api/tacticalrmm/services/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@
@api_view()
def get_services(request, pk):
agent = get_object_or_404(Agent, pk=pk)
if not agent.has_nats:
return notify_error("Requires agent version 1.1.0 or greater")
r = asyncio.run(agent.nats_cmd(data={"func": "winservices"}, timeout=10))

if r == "timeout":
Expand All @@ -38,8 +36,6 @@ def default_services(request):
@api_view(["POST"])
def service_action(request):
agent = get_object_or_404(Agent, pk=request.data["pk"])
if not agent.has_nats:
return notify_error("Requires agent version 1.1.0 or greater")
action = request.data["sv_action"]
data = {
"func": "winsvcaction",
Expand Down Expand Up @@ -80,8 +76,6 @@ def service_action(request):
@api_view()
def service_detail(request, pk, svcname):
agent = get_object_or_404(Agent, pk=pk)
if not agent.has_nats:
return notify_error("Requires agent version 1.1.0 or greater")
data = {"func": "winsvcdetail", "payload": {"name": svcname}}
r = asyncio.run(agent.nats_cmd(data, timeout=10))
if r == "timeout":
Expand All @@ -93,8 +87,6 @@ def service_detail(request, pk, svcname):
@api_view(["POST"])
def edit_service(request):
agent = get_object_or_404(Agent, pk=request.data["pk"])
if not agent.has_nats:
return notify_error("Requires agent version 1.1.0 or greater")
data = {
"func": "editwinsvc",
"payload": {
Expand Down
2 changes: 0 additions & 2 deletions api/tacticalrmm/software/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,6 @@ def get_installed(request, pk):
@api_view()
def refresh_installed(request, pk):
agent = get_object_or_404(Agent, pk=pk)
if not agent.has_nats:
return notify_error("Requires agent version 1.1.0 or greater")

r: Any = asyncio.run(agent.nats_cmd({"func": "softwarelist"}, timeout=15))
if r == "timeout" or r == "natsdown":
Expand Down
6 changes: 3 additions & 3 deletions api/tacticalrmm/tacticalrmm/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,16 @@
AUTH_USER_MODEL = "accounts.User"

# latest release
TRMM_VERSION = "0.4.25"
TRMM_VERSION = "0.4.26"

# bump this version everytime vue code is changed
# to alert user they need to manually refresh their browser
APP_VER = "0.0.121"
APP_VER = "0.0.122"

# https://github.com/wh1te909/rmmagent
LATEST_AGENT_VER = "1.4.12"

MESH_VER = "0.7.84"
MESH_VER = "0.7.88"

# for the update script, bump when need to recreate venv or npm install
PIP_VER = "11"
Expand Down
Loading

0 comments on commit 8eb91c0

Please sign in to comment.