Compare commits
120 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8dd636b0eb | ||
|
|
6b5bda8ee1 | ||
|
|
ddc5597157 | ||
|
|
ae112c7257 | ||
|
|
c22f10f96a | ||
|
|
18d10c9bec | ||
|
|
890e430cb7 | ||
|
|
dadc3d4cd7 | ||
|
|
d98b4d7320 | ||
|
|
340f532238 | ||
|
|
7669f68e7c | ||
|
|
3557e5514f | ||
|
|
a9f09b7614 | ||
|
|
845b9e4568 | ||
|
|
24a6092dcf | ||
|
|
195ae7d8b1 | ||
|
|
a5c6ea7ffc | ||
|
|
eb7a4ac29f | ||
|
|
508ef73fde | ||
|
|
838d6d8076 | ||
|
|
762c3159b8 | ||
|
|
7a88a06bcf | ||
|
|
0b1e3d7de5 | ||
|
|
9a83c73f21 | ||
|
|
aa50c7b268 | ||
|
|
179a5a80f4 | ||
|
|
0ddae527ef | ||
|
|
ee7a46de26 | ||
|
|
95522fda74 | ||
|
|
e58881c2bd | ||
|
|
36a902a44e | ||
|
|
16b74549a2 | ||
|
|
da7ededfb1 | ||
|
|
790bb08718 | ||
|
|
e6765f421f | ||
|
|
7e8f1fe904 | ||
|
|
eacce4578a | ||
|
|
07b2543972 | ||
|
|
d1c3fc8493 | ||
|
|
f453b16010 | ||
|
|
05151d8978 | ||
|
|
8218e1acc3 | ||
|
|
30212fc89a | ||
|
|
b31c13fcae | ||
|
|
6b95fc6f1d | ||
|
|
369cf17eb2 | ||
|
|
4dd8f512cc | ||
|
|
26cfec7d80 | ||
|
|
67a87ccf00 | ||
|
|
667cebcf94 | ||
|
|
bc1747ca1c | ||
|
|
945d8647bf | ||
|
|
dfe2e94627 | ||
|
|
09a5591eec | ||
|
|
f2bf06a0ba | ||
|
|
eedad4ab1c | ||
|
|
336a62ab29 | ||
|
|
b5603a5233 | ||
|
|
73890f553c | ||
|
|
f6243b8968 | ||
|
|
3770dc74d4 | ||
|
|
45f4e947c5 | ||
|
|
9928d7c6e1 | ||
|
|
bf776eeb2b | ||
|
|
ae7c0e9195 | ||
|
|
e90b640602 | ||
|
|
ba7529d3f5 | ||
|
|
34667f252e | ||
|
|
d18bddcb7b | ||
|
|
96dff49d33 | ||
|
|
b389728338 | ||
|
|
cdc7da86f3 | ||
|
|
4745cc0378 | ||
|
|
434f132479 | ||
|
|
fb0f31ffc7 | ||
|
|
bb1d73c0ae | ||
|
|
0e823d1191 | ||
|
|
48f4199ff3 | ||
|
|
eaf379587b | ||
|
|
672446b7d1 | ||
|
|
dfe52c1b07 | ||
|
|
d63df03ad8 | ||
|
|
aba4f9f2ce | ||
|
|
ac5c1e7803 | ||
|
|
d521dbf50e | ||
|
|
f210ed3e6a | ||
|
|
df3cac4ea6 | ||
|
|
f778c5175b | ||
|
|
6c66ff28dd | ||
|
|
d5b6ec702b | ||
|
|
c62a5fcef2 | ||
|
|
59c47e9200 | ||
|
|
4ba44d8932 | ||
|
|
27dae05e1b | ||
|
|
a251ae9b90 | ||
|
|
7e960b2bde | ||
|
|
5df4825158 | ||
|
|
8984d06d93 | ||
|
|
eed7aac047 | ||
|
|
54b068de4a | ||
|
|
f0f33b00b6 | ||
|
|
1043405088 | ||
|
|
0131b10805 | ||
|
|
a19b441f62 | ||
|
|
28edc31d43 | ||
|
|
0f9872a818 | ||
|
|
76ce4296f3 | ||
|
|
3dd2671380 | ||
|
|
298ca31332 | ||
|
|
8f911aa6b9 | ||
|
|
82a5c7d9b1 | ||
|
|
7f013dcdba | ||
|
|
68e2e16076 | ||
|
|
ea23c763c9 | ||
|
|
5dcecb3206 | ||
|
|
5bd48e2d0e | ||
|
|
afd0a02589 | ||
|
|
2379192d53 | ||
|
|
a6489290c8 | ||
|
|
5f74c43415 |
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 3.2 on 2021-04-11 01:43
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('accounts', '0013_user_client_tree_sort'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='user',
|
||||
name='client_tree_splitter',
|
||||
field=models.PositiveIntegerField(default=11),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 3.2 on 2021-04-11 03:03
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('accounts', '0014_user_client_tree_splitter'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='user',
|
||||
name='loading_bar_color',
|
||||
field=models.CharField(default='red', max_length=255),
|
||||
),
|
||||
]
|
||||
@@ -36,6 +36,8 @@ class User(AbstractUser, BaseAuditModel):
|
||||
client_tree_sort = models.CharField(
|
||||
max_length=50, choices=CLIENT_TREE_SORT_CHOICES, default="alphafail"
|
||||
)
|
||||
client_tree_splitter = models.PositiveIntegerField(default=11)
|
||||
loading_bar_color = models.CharField(max_length=255, default="red")
|
||||
|
||||
agent = models.OneToOneField(
|
||||
"agents.Agent",
|
||||
|
||||
@@ -13,6 +13,8 @@ class UserUISerializer(ModelSerializer):
|
||||
"agent_dblclick_action",
|
||||
"default_agent_tbl_tab",
|
||||
"client_tree_sort",
|
||||
"client_tree_splitter",
|
||||
"loading_bar_color",
|
||||
]
|
||||
|
||||
|
||||
|
||||
@@ -278,6 +278,8 @@ class TestUserAction(TacticalTestCase):
|
||||
"agent_dblclick_action": "editagent",
|
||||
"default_agent_tbl_tab": "mixed",
|
||||
"client_tree_sort": "alpha",
|
||||
"client_tree_splitter": 14,
|
||||
"loading_bar_color": "green",
|
||||
}
|
||||
r = self.client.patch(url, data, format="json")
|
||||
self.assertEqual(r.status_code, 200)
|
||||
|
||||
@@ -166,7 +166,7 @@ class Agent(BaseAuditModel):
|
||||
|
||||
@property
|
||||
def checks(self):
|
||||
total, passing, failing = 0, 0, 0
|
||||
total, passing, failing, warning, info = 0, 0, 0, 0, 0
|
||||
|
||||
if self.agentchecks.exists(): # type: ignore
|
||||
for i in self.agentchecks.all(): # type: ignore
|
||||
@@ -174,13 +174,20 @@ class Agent(BaseAuditModel):
|
||||
if i.status == "passing":
|
||||
passing += 1
|
||||
elif i.status == "failing":
|
||||
failing += 1
|
||||
if i.alert_severity == "error":
|
||||
failing += 1
|
||||
elif i.alert_severity == "warning":
|
||||
warning += 1
|
||||
elif i.alert_severity == "info":
|
||||
info += 1
|
||||
|
||||
ret = {
|
||||
"total": total,
|
||||
"passing": passing,
|
||||
"failing": failing,
|
||||
"has_failing_checks": failing > 0,
|
||||
"warning": warning,
|
||||
"info": info,
|
||||
"has_failing_checks": failing > 0 or warning > 0,
|
||||
}
|
||||
return ret
|
||||
|
||||
@@ -195,6 +202,27 @@ class Agent(BaseAuditModel):
|
||||
except:
|
||||
return ["unknown cpu model"]
|
||||
|
||||
@property
|
||||
def graphics(self):
|
||||
ret, mrda = [], []
|
||||
try:
|
||||
graphics = self.wmi_detail["graphics"]
|
||||
for i in graphics:
|
||||
caption = [x["Caption"] for x in i if "Caption" in x][0]
|
||||
if "microsoft remote display adapter" in caption.lower():
|
||||
mrda.append("yes")
|
||||
continue
|
||||
|
||||
ret.append([x["Caption"] for x in i if "Caption" in x][0])
|
||||
|
||||
# only return this if no other graphics cards
|
||||
if not ret and mrda:
|
||||
return "Microsoft Remote Display Adapter"
|
||||
|
||||
return ", ".join(ret)
|
||||
except:
|
||||
return "Graphics info requires agent v1.4.14"
|
||||
|
||||
@property
|
||||
def local_ips(self):
|
||||
ret = []
|
||||
@@ -322,7 +350,7 @@ class Agent(BaseAuditModel):
|
||||
online = [
|
||||
agent
|
||||
for agent in Agent.objects.only(
|
||||
"pk", "last_seen", "overdue_time", "offline_time"
|
||||
"pk", "agent_id", "last_seen", "overdue_time", "offline_time"
|
||||
)
|
||||
if agent.status == "online"
|
||||
]
|
||||
@@ -819,12 +847,6 @@ class RecoveryAction(models.Model):
|
||||
def __str__(self):
|
||||
return f"{self.agent.hostname} - {self.mode}"
|
||||
|
||||
def send(self):
|
||||
ret = {"recovery": self.mode}
|
||||
if self.mode == "command":
|
||||
ret["cmd"] = self.command
|
||||
return ret
|
||||
|
||||
|
||||
class Note(models.Model):
|
||||
agent = models.ForeignKey(
|
||||
|
||||
@@ -16,6 +16,7 @@ class AgentSerializer(serializers.ModelSerializer):
|
||||
local_ips = serializers.ReadOnlyField()
|
||||
make_model = serializers.ReadOnlyField()
|
||||
physical_disks = serializers.ReadOnlyField()
|
||||
graphics = serializers.ReadOnlyField()
|
||||
checks = serializers.ReadOnlyField()
|
||||
timezone = serializers.ReadOnlyField()
|
||||
all_timezones = serializers.SerializerMethodField()
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import asyncio
|
||||
import datetime as dt
|
||||
import json
|
||||
import random
|
||||
import subprocess
|
||||
import tempfile
|
||||
import urllib.parse
|
||||
from time import sleep
|
||||
from typing import Union
|
||||
|
||||
@@ -13,21 +11,21 @@ from loguru import logger
|
||||
from packaging import version as pyver
|
||||
|
||||
from agents.models import Agent
|
||||
from core.models import CoreSettings
|
||||
from core.models import CodeSignToken, CoreSettings
|
||||
from logs.models import PendingAction
|
||||
from scripts.models import Script
|
||||
from tacticalrmm.celery import app
|
||||
from tacticalrmm.utils import run_nats_api_cmd
|
||||
|
||||
logger.configure(**settings.LOG_CONFIG)
|
||||
|
||||
|
||||
def agent_update(pk: int) -> str:
|
||||
def agent_update(pk: int, codesigntoken: str = None) -> str:
|
||||
from agents.utils import get_exegen_url
|
||||
|
||||
agent = Agent.objects.get(pk=pk)
|
||||
|
||||
if pyver.parse(agent.version) <= pyver.parse("1.1.11"):
|
||||
logger.warning(
|
||||
f"{agent.hostname} v{agent.version} is running an unsupported version. Refusing to auto update."
|
||||
)
|
||||
if pyver.parse(agent.version) <= pyver.parse("1.3.0"):
|
||||
return "not supported"
|
||||
|
||||
# skip if we can't determine the arch
|
||||
@@ -37,18 +35,15 @@ def agent_update(pk: int) -> str:
|
||||
)
|
||||
return "noarch"
|
||||
|
||||
# removed sqlite in 1.4.0 to get rid of cgo dependency
|
||||
# 1.3.0 has migration func to move from sqlite to win registry, so force an upgrade to 1.3.0 if old agent
|
||||
if pyver.parse(agent.version) >= pyver.parse("1.3.0"):
|
||||
version = settings.LATEST_AGENT_VER
|
||||
url = agent.winagent_dl
|
||||
inno = agent.win_inno_exe
|
||||
version = settings.LATEST_AGENT_VER
|
||||
inno = agent.win_inno_exe
|
||||
|
||||
if codesigntoken is not None and pyver.parse(version) >= pyver.parse("1.5.0"):
|
||||
base_url = get_exegen_url() + "/api/v1/winagents/?"
|
||||
params = {"version": version, "arch": agent.arch, "token": codesigntoken}
|
||||
url = base_url + urllib.parse.urlencode(params)
|
||||
else:
|
||||
version = "1.3.0"
|
||||
inno = (
|
||||
"winagent-v1.3.0.exe" if agent.arch == "64" else "winagent-v1.3.0-x86.exe"
|
||||
)
|
||||
url = f"https://github.com/wh1te909/rmmagent/releases/download/v1.3.0/{inno}"
|
||||
url = agent.winagent_dl
|
||||
|
||||
if agent.pendingactions.filter(
|
||||
action_type="agentupdate", status="pending"
|
||||
@@ -81,10 +76,15 @@ def agent_update(pk: int) -> str:
|
||||
|
||||
@app.task
|
||||
def send_agent_update_task(pks: list[int]) -> None:
|
||||
try:
|
||||
codesigntoken = CodeSignToken.objects.first().token
|
||||
except:
|
||||
codesigntoken = None
|
||||
|
||||
chunks = (pks[i : i + 30] for i in range(0, len(pks), 30))
|
||||
for chunk in chunks:
|
||||
for pk in chunk:
|
||||
agent_update(pk)
|
||||
agent_update(pk, codesigntoken)
|
||||
sleep(0.05)
|
||||
sleep(4)
|
||||
|
||||
@@ -95,6 +95,11 @@ def auto_self_agent_update_task() -> None:
|
||||
if not core.agent_auto_update:
|
||||
return
|
||||
|
||||
try:
|
||||
codesigntoken = CodeSignToken.objects.first().token
|
||||
except:
|
||||
codesigntoken = None
|
||||
|
||||
q = Agent.objects.only("pk", "version")
|
||||
pks: list[int] = [
|
||||
i.pk
|
||||
@@ -105,7 +110,7 @@ def auto_self_agent_update_task() -> None:
|
||||
chunks = (pks[i : i + 30] for i in range(0, len(pks), 30))
|
||||
for chunk in chunks:
|
||||
for pk in chunk:
|
||||
agent_update(pk)
|
||||
agent_update(pk, codesigntoken)
|
||||
sleep(0.05)
|
||||
sleep(4)
|
||||
|
||||
@@ -257,30 +262,13 @@ def run_script_email_results_task(
|
||||
logger.error(e)
|
||||
|
||||
|
||||
def _get_nats_config() -> dict:
|
||||
return {
|
||||
"key": settings.SECRET_KEY,
|
||||
"natsurl": f"tls://{settings.ALLOWED_HOSTS[0]}:4222",
|
||||
}
|
||||
|
||||
|
||||
@app.task
|
||||
def monitor_agents_task() -> None:
|
||||
agents = Agent.objects.only(
|
||||
"pk", "agent_id", "last_seen", "overdue_time", "offline_time"
|
||||
)
|
||||
ret = [i.agent_id for i in agents if i.status != "online"]
|
||||
config = _get_nats_config()
|
||||
config["agents"] = ret
|
||||
with tempfile.NamedTemporaryFile() as fp:
|
||||
with open(fp.name, "w") as f:
|
||||
json.dump(config, f)
|
||||
|
||||
cmd = ["/usr/local/bin/nats-api", "-c", fp.name, "-m", "monitor"]
|
||||
try:
|
||||
subprocess.run(cmd, capture_output=True, timeout=30)
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
ids = [i.agent_id for i in agents if i.status != "online"]
|
||||
run_nats_api_cmd("monitor", ids)
|
||||
|
||||
|
||||
@app.task
|
||||
@@ -288,15 +276,5 @@ def get_wmi_task() -> None:
|
||||
agents = Agent.objects.only(
|
||||
"pk", "agent_id", "last_seen", "overdue_time", "offline_time"
|
||||
)
|
||||
ret = [i.agent_id for i in agents if i.status == "online"]
|
||||
config = _get_nats_config()
|
||||
config["agents"] = ret
|
||||
with tempfile.NamedTemporaryFile() as fp:
|
||||
with open(fp.name, "w") as f:
|
||||
json.dump(config, f)
|
||||
|
||||
cmd = ["/usr/local/bin/nats-api", "-c", fp.name, "-m", "wmi"]
|
||||
try:
|
||||
subprocess.run(cmd, capture_output=True, timeout=30)
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
ids = [i.agent_id for i in agents if i.status == "online"]
|
||||
run_nats_api_cmd("wmi", ids)
|
||||
|
||||
@@ -914,8 +914,9 @@ class TestAgentTasks(TacticalTestCase):
|
||||
self.authenticate()
|
||||
self.setup_coresettings()
|
||||
|
||||
@patch("agents.utils.get_exegen_url")
|
||||
@patch("agents.models.Agent.nats_cmd")
|
||||
def test_agent_update(self, nats_cmd):
|
||||
def test_agent_update(self, nats_cmd, get_exe):
|
||||
from agents.tasks import agent_update
|
||||
|
||||
agent_noarch = baker.make_recipe(
|
||||
@@ -926,63 +927,96 @@ class TestAgentTasks(TacticalTestCase):
|
||||
r = agent_update(agent_noarch.pk)
|
||||
self.assertEqual(r, "noarch")
|
||||
|
||||
agent_1111 = baker.make_recipe(
|
||||
"agents.agent",
|
||||
operating_system="Windows 10 Pro, 64 bit (build 19041.450)",
|
||||
version="1.1.11",
|
||||
)
|
||||
r = agent_update(agent_1111.pk)
|
||||
self.assertEqual(r, "not supported")
|
||||
|
||||
agent64_1112 = baker.make_recipe(
|
||||
"agents.agent",
|
||||
operating_system="Windows 10 Pro, 64 bit (build 19041.450)",
|
||||
version="1.1.12",
|
||||
)
|
||||
|
||||
r = agent_update(agent64_1112.pk)
|
||||
self.assertEqual(r, "created")
|
||||
action = PendingAction.objects.get(agent__pk=agent64_1112.pk)
|
||||
self.assertEqual(action.action_type, "agentupdate")
|
||||
self.assertEqual(action.status, "pending")
|
||||
self.assertEqual(
|
||||
action.details["url"],
|
||||
"https://github.com/wh1te909/rmmagent/releases/download/v1.3.0/winagent-v1.3.0.exe",
|
||||
)
|
||||
self.assertEqual(action.details["inno"], "winagent-v1.3.0.exe")
|
||||
self.assertEqual(action.details["version"], "1.3.0")
|
||||
nats_cmd.assert_called_with(
|
||||
{
|
||||
"func": "agentupdate",
|
||||
"payload": {
|
||||
"url": "https://github.com/wh1te909/rmmagent/releases/download/v1.3.0/winagent-v1.3.0.exe",
|
||||
"version": "1.3.0",
|
||||
"inno": "winagent-v1.3.0.exe",
|
||||
},
|
||||
},
|
||||
wait=False,
|
||||
)
|
||||
|
||||
agent_64_130 = baker.make_recipe(
|
||||
agent_130 = baker.make_recipe(
|
||||
"agents.agent",
|
||||
operating_system="Windows 10 Pro, 64 bit (build 19041.450)",
|
||||
version="1.3.0",
|
||||
)
|
||||
nats_cmd.return_value = "ok"
|
||||
r = agent_update(agent_64_130.pk)
|
||||
r = agent_update(agent_130.pk)
|
||||
self.assertEqual(r, "not supported")
|
||||
|
||||
# test __without__ code signing
|
||||
agent64_nosign = baker.make_recipe(
|
||||
"agents.agent",
|
||||
operating_system="Windows 10 Pro, 64 bit (build 19041.450)",
|
||||
version="1.4.14",
|
||||
)
|
||||
|
||||
r = agent_update(agent64_nosign.pk, None)
|
||||
self.assertEqual(r, "created")
|
||||
action = PendingAction.objects.get(agent__pk=agent64_nosign.pk)
|
||||
self.assertEqual(action.action_type, "agentupdate")
|
||||
self.assertEqual(action.status, "pending")
|
||||
self.assertEqual(
|
||||
action.details["url"],
|
||||
f"https://github.com/wh1te909/rmmagent/releases/download/v{settings.LATEST_AGENT_VER}/winagent-v{settings.LATEST_AGENT_VER}.exe",
|
||||
)
|
||||
self.assertEqual(
|
||||
action.details["inno"], f"winagent-v{settings.LATEST_AGENT_VER}.exe"
|
||||
)
|
||||
self.assertEqual(action.details["version"], settings.LATEST_AGENT_VER)
|
||||
nats_cmd.assert_called_with(
|
||||
{
|
||||
"func": "agentupdate",
|
||||
"payload": {
|
||||
"url": settings.DL_64,
|
||||
"url": f"https://github.com/wh1te909/rmmagent/releases/download/v{settings.LATEST_AGENT_VER}/winagent-v{settings.LATEST_AGENT_VER}.exe",
|
||||
"version": settings.LATEST_AGENT_VER,
|
||||
"inno": f"winagent-v{settings.LATEST_AGENT_VER}.exe",
|
||||
},
|
||||
},
|
||||
wait=False,
|
||||
)
|
||||
action = PendingAction.objects.get(agent__pk=agent_64_130.pk)
|
||||
|
||||
# test __with__ code signing (64 bit)
|
||||
codesign = baker.make("core.CodeSignToken", token="testtoken123")
|
||||
agent64_sign = baker.make_recipe(
|
||||
"agents.agent",
|
||||
operating_system="Windows 10 Pro, 64 bit (build 19041.450)",
|
||||
version="1.4.14",
|
||||
)
|
||||
|
||||
nats_cmd.return_value = "ok"
|
||||
get_exe.return_value = "https://exe.tacticalrmm.io"
|
||||
r = agent_update(agent64_sign.pk, codesign.token) # type: ignore
|
||||
self.assertEqual(r, "created")
|
||||
nats_cmd.assert_called_with(
|
||||
{
|
||||
"func": "agentupdate",
|
||||
"payload": {
|
||||
"url": f"https://exe.tacticalrmm.io/api/v1/winagents/?version={settings.LATEST_AGENT_VER}&arch=64&token=testtoken123", # type: ignore
|
||||
"version": settings.LATEST_AGENT_VER,
|
||||
"inno": f"winagent-v{settings.LATEST_AGENT_VER}.exe",
|
||||
},
|
||||
},
|
||||
wait=False,
|
||||
)
|
||||
action = PendingAction.objects.get(agent__pk=agent64_sign.pk)
|
||||
self.assertEqual(action.action_type, "agentupdate")
|
||||
self.assertEqual(action.status, "pending")
|
||||
|
||||
# test __with__ code signing (32 bit)
|
||||
agent32_sign = baker.make_recipe(
|
||||
"agents.agent",
|
||||
operating_system="Windows 10 Pro, 32 bit (build 19041.450)",
|
||||
version="1.4.14",
|
||||
)
|
||||
|
||||
nats_cmd.return_value = "ok"
|
||||
get_exe.return_value = "https://exe.tacticalrmm.io"
|
||||
r = agent_update(agent32_sign.pk, codesign.token) # type: ignore
|
||||
self.assertEqual(r, "created")
|
||||
nats_cmd.assert_called_with(
|
||||
{
|
||||
"func": "agentupdate",
|
||||
"payload": {
|
||||
"url": f"https://exe.tacticalrmm.io/api/v1/winagents/?version={settings.LATEST_AGENT_VER}&arch=32&token=testtoken123", # type: ignore
|
||||
"version": settings.LATEST_AGENT_VER,
|
||||
"inno": f"winagent-v{settings.LATEST_AGENT_VER}-x86.exe",
|
||||
},
|
||||
},
|
||||
wait=False,
|
||||
)
|
||||
action = PendingAction.objects.get(agent__pk=agent32_sign.pk)
|
||||
self.assertEqual(action.action_type, "agentupdate")
|
||||
self.assertEqual(action.status, "pending")
|
||||
|
||||
|
||||
37
api/tacticalrmm/agents/utils.py
Normal file
37
api/tacticalrmm/agents/utils.py
Normal file
@@ -0,0 +1,37 @@
|
||||
import random
|
||||
import urllib.parse
|
||||
|
||||
import requests
|
||||
from django.conf import settings
|
||||
|
||||
|
||||
def get_exegen_url() -> str:
|
||||
urls: list[str] = settings.EXE_GEN_URLS
|
||||
for url in urls:
|
||||
try:
|
||||
r = requests.get(url, timeout=10)
|
||||
except:
|
||||
continue
|
||||
|
||||
if r.status_code == 200:
|
||||
return url
|
||||
|
||||
return random.choice(urls)
|
||||
|
||||
|
||||
def get_winagent_url(arch: str) -> str:
|
||||
from core.models import CodeSignToken
|
||||
|
||||
try:
|
||||
codetoken = CodeSignToken.objects.first().token
|
||||
base_url = get_exegen_url() + "/api/v1/winagents/?"
|
||||
params = {
|
||||
"version": settings.LATEST_AGENT_VER,
|
||||
"arch": arch,
|
||||
"token": codetoken,
|
||||
}
|
||||
dl_url = base_url + urllib.parse.urlencode(params)
|
||||
except:
|
||||
dl_url = settings.DL_64 if arch == "64" else settings.DL_32
|
||||
|
||||
return dl_url
|
||||
@@ -40,7 +40,7 @@ logger.configure(**settings.LOG_CONFIG)
|
||||
|
||||
@api_view()
|
||||
def get_agent_versions(request):
|
||||
agents = Agent.objects.only("pk")
|
||||
agents = Agent.objects.prefetch_related("site").only("pk", "hostname")
|
||||
return Response(
|
||||
{
|
||||
"versions": [settings.LATEST_AGENT_VER],
|
||||
@@ -355,6 +355,8 @@ class Reboot(APIView):
|
||||
def install_agent(request):
|
||||
from knox.models import AuthToken
|
||||
|
||||
from agents.utils import get_winagent_url
|
||||
|
||||
client_id = request.data["client"]
|
||||
site_id = request.data["site"]
|
||||
version = settings.LATEST_AGENT_VER
|
||||
@@ -375,7 +377,7 @@ def install_agent(request):
|
||||
inno = (
|
||||
f"winagent-v{version}.exe" if arch == "64" else f"winagent-v{version}-x86.exe"
|
||||
)
|
||||
download_url = settings.DL_64 if arch == "64" else settings.DL_32
|
||||
download_url = get_winagent_url(arch)
|
||||
|
||||
_, token = AuthToken.objects.create(
|
||||
user=request.user, expiry=dt.timedelta(hours=request.data["expires"])
|
||||
|
||||
@@ -633,12 +633,15 @@ class Check(BaseAuditModel):
|
||||
if self.error_threshold:
|
||||
text += f" Error Threshold: {self.error_threshold}%"
|
||||
|
||||
percent_used = [
|
||||
d["percent"] for d in self.agent.disks if d["device"] == self.disk
|
||||
][0]
|
||||
percent_free = 100 - percent_used
|
||||
try:
|
||||
percent_used = [
|
||||
d["percent"] for d in self.agent.disks if d["device"] == self.disk
|
||||
][0]
|
||||
percent_free = 100 - percent_used
|
||||
|
||||
body = subject + f" - Free: {percent_free}%, {text}"
|
||||
body = subject + f" - Free: {percent_free}%, {text}"
|
||||
except:
|
||||
body = subject + f" - Disk {self.disk} does not exist"
|
||||
|
||||
elif self.check_type == "script":
|
||||
|
||||
@@ -710,11 +713,15 @@ class Check(BaseAuditModel):
|
||||
if self.error_threshold:
|
||||
text += f" Error Threshold: {self.error_threshold}%"
|
||||
|
||||
percent_used = [
|
||||
d["percent"] for d in self.agent.disks if d["device"] == self.disk
|
||||
][0]
|
||||
percent_free = 100 - percent_used
|
||||
body = subject + f" - Free: {percent_free}%, {text}"
|
||||
try:
|
||||
percent_used = [
|
||||
d["percent"] for d in self.agent.disks if d["device"] == self.disk
|
||||
][0]
|
||||
percent_free = 100 - percent_used
|
||||
body = subject + f" - Free: {percent_free}%, {text}"
|
||||
except:
|
||||
body = subject + f" - Disk {self.disk} does not exist"
|
||||
|
||||
elif self.check_type == "script":
|
||||
body = subject + f" - Return code: {self.retcode}"
|
||||
elif self.check_type == "ping":
|
||||
|
||||
@@ -86,7 +86,10 @@ class GetUpdateDeleteCheck(APIView):
|
||||
check = get_object_or_404(Check, pk=pk)
|
||||
|
||||
# remove fields that should not be changed when editing a check from the frontend
|
||||
if "check_alert" not in request.data.keys():
|
||||
if (
|
||||
"check_alert" not in request.data.keys()
|
||||
and "check_reset" not in request.data.keys()
|
||||
):
|
||||
[request.data.pop(i) for i in check.non_editable_fields]
|
||||
|
||||
# set event id to 0 if wildcard because it needs to be an integer field for db
|
||||
@@ -108,6 +111,11 @@ class GetUpdateDeleteCheck(APIView):
|
||||
if check.policy:
|
||||
update_policy_check_fields_task(checkpk=pk)
|
||||
|
||||
# resolve any alerts that are open
|
||||
if "check_reset" in request.data.keys():
|
||||
if obj.alert.filter(resolved=False).exists():
|
||||
obj.alert.get(resolved=False).resolve()
|
||||
|
||||
return Response(f"{obj.readable_desc} was edited!")
|
||||
|
||||
def delete(self, request, pk):
|
||||
|
||||
@@ -65,6 +65,10 @@ class Client(BaseAuditModel):
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
@property
|
||||
def agent_count(self) -> int:
|
||||
return Agent.objects.filter(site__client=self).count()
|
||||
|
||||
@property
|
||||
def has_maintenanace_mode_agents(self):
|
||||
return (
|
||||
@@ -86,16 +90,24 @@ class Client(BaseAuditModel):
|
||||
.prefetch_related("agentchecks")
|
||||
)
|
||||
|
||||
failing = 0
|
||||
data = {"error": False, "warning": False}
|
||||
|
||||
for agent in agents:
|
||||
if agent.checks["has_failing_checks"]:
|
||||
failing += 1
|
||||
|
||||
if agent.checks["warning"]:
|
||||
data["warning"] = True
|
||||
|
||||
if agent.checks["failing"]:
|
||||
data["error"] = True
|
||||
break
|
||||
|
||||
if agent.overdue_email_alert or agent.overdue_text_alert:
|
||||
if agent.status == "overdue":
|
||||
failing += 1
|
||||
data["error"] = True
|
||||
break
|
||||
|
||||
return failing > 0
|
||||
return data
|
||||
|
||||
@staticmethod
|
||||
def serialize(client):
|
||||
@@ -165,6 +177,10 @@ class Site(BaseAuditModel):
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
@property
|
||||
def agent_count(self) -> int:
|
||||
return Agent.objects.filter(site=self).count()
|
||||
|
||||
@property
|
||||
def has_maintenanace_mode_agents(self):
|
||||
return Agent.objects.filter(site=self, maintenance_mode=True).count() > 0
|
||||
@@ -184,16 +200,24 @@ class Site(BaseAuditModel):
|
||||
.prefetch_related("agentchecks")
|
||||
)
|
||||
|
||||
failing = 0
|
||||
data = {"error": False, "warning": False}
|
||||
|
||||
for agent in agents:
|
||||
|
||||
if agent.checks["has_failing_checks"]:
|
||||
failing += 1
|
||||
if agent.checks["warning"]:
|
||||
data["warning"] = True
|
||||
|
||||
if agent.checks["failing"]:
|
||||
data["error"] = True
|
||||
break
|
||||
|
||||
if agent.overdue_email_alert or agent.overdue_text_alert:
|
||||
if agent.status == "overdue":
|
||||
failing += 1
|
||||
data["error"] = True
|
||||
break
|
||||
|
||||
return failing > 0
|
||||
return data
|
||||
|
||||
@staticmethod
|
||||
def serialize(site):
|
||||
|
||||
@@ -25,6 +25,7 @@ class SiteCustomFieldSerializer(ModelSerializer):
|
||||
class SiteSerializer(ModelSerializer):
|
||||
client_name = ReadOnlyField(source="client.name")
|
||||
custom_fields = SiteCustomFieldSerializer(many=True, read_only=True)
|
||||
agent_count = ReadOnlyField()
|
||||
|
||||
class Meta:
|
||||
model = Site
|
||||
@@ -37,6 +38,7 @@ class SiteSerializer(ModelSerializer):
|
||||
"client_name",
|
||||
"client",
|
||||
"custom_fields",
|
||||
"agent_count",
|
||||
)
|
||||
|
||||
def validate(self, val):
|
||||
@@ -68,6 +70,7 @@ class ClientCustomFieldSerializer(ModelSerializer):
|
||||
class ClientSerializer(ModelSerializer):
|
||||
sites = SiteSerializer(many=True, read_only=True)
|
||||
custom_fields = ClientCustomFieldSerializer(many=True, read_only=True)
|
||||
agent_count = ReadOnlyField()
|
||||
|
||||
class Meta:
|
||||
model = Client
|
||||
@@ -79,6 +82,7 @@ class ClientSerializer(ModelSerializer):
|
||||
"alert_template",
|
||||
"sites",
|
||||
"custom_fields",
|
||||
"agent_count",
|
||||
)
|
||||
|
||||
def validate(self, val):
|
||||
@@ -95,7 +99,6 @@ class SiteTreeSerializer(ModelSerializer):
|
||||
class Meta:
|
||||
model = Site
|
||||
fields = "__all__"
|
||||
ordering = ("failing_checks",)
|
||||
|
||||
|
||||
class ClientTreeSerializer(ModelSerializer):
|
||||
@@ -106,7 +109,6 @@ class ClientTreeSerializer(ModelSerializer):
|
||||
class Meta:
|
||||
model = Client
|
||||
fields = "__all__"
|
||||
ordering = ("failing_checks",)
|
||||
|
||||
|
||||
class DeploymentSerializer(ModelSerializer):
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
from django.contrib import admin
|
||||
|
||||
from .models import CoreSettings, CustomField
|
||||
from .models import CodeSignToken, CoreSettings, CustomField
|
||||
|
||||
admin.site.register(CoreSettings)
|
||||
admin.site.register(CustomField)
|
||||
admin.site.register(CodeSignToken)
|
||||
|
||||
@@ -9,6 +9,7 @@ $rdp = rdpchange
|
||||
$ping = pingchange
|
||||
$auth = '"tokenchange"'
|
||||
$downloadlink = 'downloadchange'
|
||||
$apilink = $downloadlink.split('/')
|
||||
|
||||
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
||||
|
||||
@@ -47,24 +48,35 @@ If (Get-Service $serviceName -ErrorAction SilentlyContinue) {
|
||||
# pass
|
||||
}
|
||||
|
||||
Try
|
||||
{
|
||||
Invoke-WebRequest -Uri $downloadlink -OutFile $OutPath\$output
|
||||
Start-Process -FilePath $OutPath\$output -ArgumentList ('/VERYSILENT /SUPPRESSMSGBOXES') -Wait
|
||||
write-host ('Extracting...')
|
||||
Start-Sleep -s 5
|
||||
Start-Process -FilePath "C:\Program Files\TacticalAgent\tacticalrmm.exe" -ArgumentList $installArgs -Wait
|
||||
exit 0
|
||||
}
|
||||
Catch
|
||||
{
|
||||
$ErrorMessage = $_.Exception.Message
|
||||
$FailedItem = $_.Exception.ItemName
|
||||
Write-Error -Message "$ErrorMessage $FailedItem"
|
||||
exit 1
|
||||
}
|
||||
Finally
|
||||
{
|
||||
Remove-Item -Path $OutPath\$output
|
||||
$X = 0
|
||||
do {
|
||||
Write-Output "Waiting for network"
|
||||
Start-Sleep -s 5
|
||||
$X += 1
|
||||
} until(($connectreult = Test-NetConnection $apilink[2] -Port 443 | ? { $_.TcpTestSucceeded }) -or $X -eq 3)
|
||||
|
||||
if ($connectreult.TcpTestSucceeded -eq $true){
|
||||
Try
|
||||
{
|
||||
Invoke-WebRequest -Uri $downloadlink -OutFile $OutPath\$output
|
||||
Start-Process -FilePath $OutPath\$output -ArgumentList ('/VERYSILENT /SUPPRESSMSGBOXES') -Wait
|
||||
write-host ('Extracting...')
|
||||
Start-Sleep -s 5
|
||||
Start-Process -FilePath "C:\Program Files\TacticalAgent\tacticalrmm.exe" -ArgumentList $installArgs -Wait
|
||||
exit 0
|
||||
}
|
||||
Catch
|
||||
{
|
||||
$ErrorMessage = $_.Exception.Message
|
||||
$FailedItem = $_.Exception.ItemName
|
||||
Write-Error -Message "$ErrorMessage $FailedItem"
|
||||
exit 1
|
||||
}
|
||||
Finally
|
||||
{
|
||||
Remove-Item -Path $OutPath\$output
|
||||
}
|
||||
} else {
|
||||
Write-Output "Unable to connect to server"
|
||||
}
|
||||
}
|
||||
|
||||
20
api/tacticalrmm/core/migrations/0019_codesigntoken.py
Normal file
20
api/tacticalrmm/core/migrations/0019_codesigntoken.py
Normal file
@@ -0,0 +1,20 @@
|
||||
# Generated by Django 3.2 on 2021-04-13 05:41
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('core', '0018_auto_20210329_1709'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='CodeSignToken',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('token', models.CharField(blank=True, max_length=255, null=True)),
|
||||
],
|
||||
),
|
||||
]
|
||||
@@ -266,3 +266,16 @@ class CustomField(models.Model):
|
||||
return self.default_value_bool
|
||||
else:
|
||||
return self.default_value_string
|
||||
|
||||
|
||||
class CodeSignToken(models.Model):
|
||||
token = models.CharField(max_length=255, null=True, blank=True)
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
if not self.pk and CodeSignToken.objects.exists():
|
||||
raise ValidationError("There can only be one CodeSignToken instance")
|
||||
|
||||
super(CodeSignToken, self).save(*args, **kwargs)
|
||||
|
||||
def __str__(self):
|
||||
return "Code signing token"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import pytz
|
||||
from rest_framework import serializers
|
||||
|
||||
from .models import CoreSettings, CustomField
|
||||
from .models import CodeSignToken, CoreSettings, CustomField
|
||||
|
||||
|
||||
class CoreSettingsSerializer(serializers.ModelSerializer):
|
||||
@@ -27,3 +27,9 @@ class CustomFieldSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = CustomField
|
||||
fields = "__all__"
|
||||
|
||||
|
||||
class CodeSignTokenSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = CodeSignToken
|
||||
fields = "__all__"
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from unittest.mock import patch
|
||||
|
||||
import requests
|
||||
from channels.db import database_sync_to_async
|
||||
from channels.testing import WebsocketCommunicator
|
||||
from model_bakery import baker
|
||||
@@ -12,6 +13,28 @@ from .serializers import CustomFieldSerializer
|
||||
from .tasks import core_maintenance_tasks
|
||||
|
||||
|
||||
class TestCodeSign(TacticalTestCase):
|
||||
def setUp(self):
|
||||
self.setup_coresettings()
|
||||
self.authenticate()
|
||||
self.url = "/core/codesign/"
|
||||
|
||||
def test_get_codesign(self):
|
||||
r = self.client.get(self.url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
|
||||
self.check_not_authenticated("get", self.url)
|
||||
|
||||
@patch("requests.post")
|
||||
def test_edit_codesign_timeout(self, mock_post):
|
||||
mock_post.side_effect = requests.exceptions.ConnectionError()
|
||||
data = {"token": "token123"}
|
||||
r = self.client.patch(self.url, data, format="json")
|
||||
self.assertEqual(r.status_code, 400)
|
||||
|
||||
self.check_not_authenticated("patch", self.url)
|
||||
|
||||
|
||||
class TestConsumers(TacticalTestCase):
|
||||
def setUp(self):
|
||||
self.setup_coresettings()
|
||||
|
||||
@@ -12,4 +12,5 @@ urlpatterns = [
|
||||
path("servermaintenance/", views.server_maintenance),
|
||||
path("customfields/", views.GetAddCustomFields.as_view()),
|
||||
path("customfields/<int:pk>/", views.GetUpdateDeleteCustomFields.as_view()),
|
||||
path("codesign/", views.CodeSign.as_view()),
|
||||
]
|
||||
|
||||
@@ -11,8 +11,12 @@ from rest_framework.views import APIView
|
||||
|
||||
from tacticalrmm.utils import notify_error
|
||||
|
||||
from .models import CoreSettings, CustomField
|
||||
from .serializers import CoreSettingsSerializer, CustomFieldSerializer
|
||||
from .models import CodeSignToken, CoreSettings, CustomField
|
||||
from .serializers import (
|
||||
CodeSignTokenSerializer,
|
||||
CoreSettingsSerializer,
|
||||
CustomFieldSerializer,
|
||||
)
|
||||
|
||||
|
||||
class UploadMeshAgent(APIView):
|
||||
@@ -65,6 +69,9 @@ def dashboard_info(request):
|
||||
"dbl_click_action": request.user.agent_dblclick_action,
|
||||
"default_agent_tbl_tab": request.user.default_agent_tbl_tab,
|
||||
"client_tree_sort": request.user.client_tree_sort,
|
||||
"client_tree_splitter": request.user.client_tree_splitter,
|
||||
"loading_bar_color": request.user.loading_bar_color,
|
||||
"no_code_sign": hasattr(settings, "NOCODESIGN") and settings.NOCODESIGN,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -177,3 +184,48 @@ class GetUpdateDeleteCustomFields(APIView):
|
||||
get_object_or_404(CustomField, pk=pk).delete()
|
||||
|
||||
return Response("ok")
|
||||
|
||||
|
||||
class CodeSign(APIView):
|
||||
def get(self, request):
|
||||
token = CodeSignToken.objects.first()
|
||||
return Response(CodeSignTokenSerializer(token).data)
|
||||
|
||||
def patch(self, request):
|
||||
import requests
|
||||
|
||||
errors = []
|
||||
for url in settings.EXE_GEN_URLS:
|
||||
try:
|
||||
r = requests.post(
|
||||
f"{url}/api/v1/checktoken",
|
||||
json={"token": request.data["token"]},
|
||||
headers={"Content-type": "application/json"},
|
||||
timeout=15,
|
||||
)
|
||||
except Exception as e:
|
||||
errors.append(str(e))
|
||||
else:
|
||||
errors = []
|
||||
break
|
||||
|
||||
if errors:
|
||||
return notify_error(", ".join(errors))
|
||||
|
||||
if r.status_code == 400 or r.status_code == 401: # type: ignore
|
||||
return notify_error(r.json()["ret"]) # type: ignore
|
||||
elif r.status_code == 200: # type: ignore
|
||||
t = CodeSignToken.objects.first()
|
||||
if t is None:
|
||||
CodeSignToken.objects.create(token=request.data["token"])
|
||||
else:
|
||||
serializer = CodeSignTokenSerializer(instance=t, data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
serializer.save()
|
||||
return Response("Token was saved")
|
||||
|
||||
try:
|
||||
ret = r.json()["ret"] # type: ignore
|
||||
except:
|
||||
ret = "Something went wrong"
|
||||
return notify_error(ret)
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
[
|
||||
{
|
||||
"guid": "6820cb5e-5a7f-4d9b-8c22-d54677e3cc04",
|
||||
"filename": "Win_Clear_Firefox_Cache.ps1",
|
||||
"filename": "Win_Firefox_Clear_Cache.ps1",
|
||||
"submittedBy": "https://github.com/Omnicef",
|
||||
"name": "Clear Firefox Cache",
|
||||
"name": "Firefox - Clean Cache",
|
||||
"description": "This script will clean up Mozilla Firefox for all users.",
|
||||
"shell": "powershell",
|
||||
"category": "TRMM (Win):Browsers"
|
||||
},
|
||||
{
|
||||
"guid": "3ff6a386-11d1-4f9d-8cca-1b0563bb6443",
|
||||
"filename": "Win_Clear_Google_Chrome_Cache.ps1",
|
||||
"filename": "Win_Google_Chrome_Clear_Cache.ps1",
|
||||
"submittedBy": "https://github.com/Omnicef",
|
||||
"name": "Clear Google Chrome Cache",
|
||||
"name": "Chrome - Clear Cache for All Users",
|
||||
"description": "This script will clean up Google Chrome for all users.",
|
||||
"shell": "powershell",
|
||||
"category": "TRMM (Win):Browsers"
|
||||
@@ -21,7 +21,7 @@
|
||||
"guid": "be1de837-f677-4ac5-aa0c-37a0fc9991fc",
|
||||
"filename": "Win_Install_Adobe_Reader.ps1",
|
||||
"submittedBy": "https://github.com/Omnicef",
|
||||
"name": "Install Adobe Reader DC",
|
||||
"name": "Adobe Reader DC - Install",
|
||||
"description": "Installs Adobe Reader DC.",
|
||||
"shell": "powershell",
|
||||
"category": "TRMM (Win):3rd Party Software>Chocolatey"
|
||||
@@ -30,7 +30,7 @@
|
||||
"guid": "2ee134d5-76aa-4160-b334-a1efbc62079f",
|
||||
"filename": "Win_Install_Duplicati.ps1",
|
||||
"submittedBy": "https://github.com/Omnicef",
|
||||
"name": "Install Duplicati",
|
||||
"name": "Duplicati - Install",
|
||||
"description": "This script installs Duplicati 2.0.5.1 as a service.",
|
||||
"shell": "powershell",
|
||||
"category": "TRMM (Win):3rd Party Software"
|
||||
@@ -39,7 +39,7 @@
|
||||
"guid": "81cc5bcb-01bf-4b0c-89b9-0ac0f3fe0c04",
|
||||
"filename": "Win_Reset_Windows_Update.ps1",
|
||||
"submittedBy": "https://github.com/Omnicef",
|
||||
"name": "Reset Windows Update",
|
||||
"name": "Windows Update - Reset",
|
||||
"description": "This script will reset all of the Windows Updates components to DEFAULT SETTINGS.",
|
||||
"shell": "powershell",
|
||||
"category": "TRMM (Win):Updates"
|
||||
@@ -48,16 +48,16 @@
|
||||
"guid": "8db87ff0-a9b4-4d9d-bc55-377bbcb85b6d",
|
||||
"filename": "Win_Start_Cleanup.ps1",
|
||||
"submittedBy": "https://github.com/Omnicef",
|
||||
"name": "Cleanup C: drive",
|
||||
"name": "Disk - Cleanup C: drive",
|
||||
"description": "Cleans the C: drive's Window Temperary files, Windows SoftwareDistribution folder, the local users Temperary folder, IIS logs (if applicable) and empties the recycling bin. All deleted files will go into a log transcript in $env:TEMP. By default this script leaves files that are newer than 7 days old however this variable can be edited.",
|
||||
"shell": "powershell",
|
||||
"category": "TRMM (Win):Other"
|
||||
"category": "TRMM (Win):Maintenance"
|
||||
},
|
||||
{
|
||||
"guid": "2f28e8c1-ae0f-4b46-a826-f513974526a3",
|
||||
"filename": "Win_Defender_FullScan_Background.ps1",
|
||||
"submittedBy": "https://github.com/Omnicef",
|
||||
"name": "Windows Defender Full Scan",
|
||||
"name": "Defender - Full Scan",
|
||||
"description": "Runs a Windows Defender Full background scan.",
|
||||
"shell": "powershell",
|
||||
"category": "TRMM (Win):Security>Antivirus"
|
||||
@@ -66,7 +66,7 @@
|
||||
"guid": "adf81ddb-3b77-415c-a89b-2ccc826b5aa7",
|
||||
"filename": "Win_Defender_QuickScan_Background.ps1",
|
||||
"submittedBy": "https://github.com/Omnicef",
|
||||
"name": "Windows Defender Quick Scan",
|
||||
"name": "Defender - Quick Scan",
|
||||
"description": "Runs a Quick Scan using Windows Defender in the Background.",
|
||||
"shell": "powershell",
|
||||
"category": "TRMM (Win):Security>Antivirus"
|
||||
@@ -75,16 +75,16 @@
|
||||
"guid": "3c46290b-85db-4cd2-93a2-943c8c93b3b1",
|
||||
"filename": "Speedtest.py",
|
||||
"submittedBy": "https://github.com/wh1te909",
|
||||
"name": "Speed Test",
|
||||
"name": "Speed Test - Python",
|
||||
"description": "Runs a Speed Test using Python",
|
||||
"shell": "python",
|
||||
"category": "TRMM (Win):Network"
|
||||
},
|
||||
{
|
||||
"guid": "9d34f482-1f0c-4b2f-b65f-a9cf3c13ef5f",
|
||||
"filename": "Win_Rename_Installed_App.ps1",
|
||||
"filename": "Win_TRMM_Rename_Installed_App.ps1",
|
||||
"submittedBy": "https://github.com/bradhawkins85",
|
||||
"name": "Rename Tactical RMM Agent",
|
||||
"name": "TacticalRMM Agent Rename",
|
||||
"description": "Updates the DisplayName registry entry for the Tactical RMM windows agent to your desired name. This script takes 1 required argument: the name you wish to set.",
|
||||
"shell": "powershell",
|
||||
"category": "TRMM (Win):TacticalRMM Related"
|
||||
@@ -93,7 +93,7 @@
|
||||
"guid": "525ae965-1dcf-4c17-92b3-5da3cf6819f5",
|
||||
"filename": "Win_Bitlocker_Encrypted_Drive_c.ps1",
|
||||
"submittedBy": "https://github.com/ThatsNASt",
|
||||
"name": "Check C Drive for Bitlocker Status",
|
||||
"name": "Bitlocker - Check C Drive for Status",
|
||||
"description": "Runs a check on drive C for Bitlocker status.",
|
||||
"shell": "powershell",
|
||||
"category": "TRMM (Win):Storage"
|
||||
@@ -102,7 +102,7 @@
|
||||
"guid": "2ea35fa2-c227-4d17-a40e-4d39f252e27a",
|
||||
"filename": "Win_Bitlocker_Create_Status_Report.ps1",
|
||||
"submittedBy": "https://github.com/ThatsNASt",
|
||||
"name": "Create Bitlocker Status Report",
|
||||
"name": "Bitlocker - Create Status Report",
|
||||
"description": "Creates a Bitlocker status report.",
|
||||
"shell": "powershell",
|
||||
"category": "TRMM (Win):Storage"
|
||||
@@ -111,34 +111,61 @@
|
||||
"guid": "9e5769c1-3873-4941-bf70-e851e0afbd6d",
|
||||
"filename": "Win_Bitlocker_Retrieve_Status_Report.ps1",
|
||||
"submittedBy": "https://github.com/ThatsNASt",
|
||||
"name": "Retreive Bitlocker Status Report",
|
||||
"name": "Bitlocker - Retrieve Status Report",
|
||||
"description": "Retreives a Bitlocker status report.",
|
||||
"shell": "powershell",
|
||||
"category": "TRMM (Win):Storage"
|
||||
},
|
||||
{
|
||||
"guid": "72b93487-0266-43f0-97cc-03d4c7ee0b44",
|
||||
"filename": "Win_Bitlocker_Get_Recovery_Keys.ps1",
|
||||
"submittedBy": "https://github.com/silversword411",
|
||||
"name": "Bitlocker - Get Recovery Keys",
|
||||
"description": "Retreives a Bitlocker Recovery Keys",
|
||||
"shell": "powershell",
|
||||
"category": "TRMM (Win):Storage"
|
||||
},
|
||||
{
|
||||
"guid": "cfa14c28-4dfc-4d4e-95ee-a380652e058d",
|
||||
"filename": "Win_Bios_Check.ps1",
|
||||
"submittedBy": "https://github.com/ThatsNASt",
|
||||
"name": "Check BIOS Information",
|
||||
"name": "BIOS - Check Information",
|
||||
"description": "Retreives and reports on BIOS make, version, and date.",
|
||||
"shell": "powershell",
|
||||
"category": "TRMM (Win):Hardware"
|
||||
},
|
||||
{
|
||||
"guid": "e1c27982-b955-4766-85b6-d92527a177cf",
|
||||
"filename": "Win_Hardware_Monitor_Get_Info.ps1",
|
||||
"submittedBy": "https://github.com/MaxAnderson95/",
|
||||
"name": "Monitor - Get Info",
|
||||
"description": "Retreives and reports on Monitor info: Manufacturer, Model, Serial",
|
||||
"shell": "powershell",
|
||||
"category": "TRMM (Win):Hardware"
|
||||
},
|
||||
{
|
||||
"guid": "ae231ac4-b01f-4a39-a9d2-3d817af75260",
|
||||
"filename": "Win_Hardware_RAM_Status.ps1",
|
||||
"submittedBy": "https://github.com/silversword411",
|
||||
"name": "RAM - Check Information",
|
||||
"description": "Retreives and reports on RAM info: DIMM's, total memory, slots total and used",
|
||||
"shell": "powershell",
|
||||
"category": "TRMM (Win):Hardware"
|
||||
},
|
||||
{
|
||||
"guid": "95a2ee6f-b89b-4551-856e-3081b041caa7",
|
||||
"filename": "Win_Reset_High_Performance_Power_Profile_to_Defaults.ps1",
|
||||
"filename": "Win_Power_Profile_Reset_High_Performance_to_Defaults.ps1",
|
||||
"submittedBy": "https://github.com/azulskyknight",
|
||||
"name": "Reset High Perf Power Profile",
|
||||
"name": "Power Profile - Reset High Perf Power Profile to defaults",
|
||||
"description": "Resets monitor, disk, standby, and hibernate timers in the default High Performance power profile to their default values. It also re-indexes the AC and DC power profiles into their default order.",
|
||||
"shell": "powershell",
|
||||
"category": "TRMM (Win):Power"
|
||||
},
|
||||
{
|
||||
"guid": "2cbd30b0-84dd-4388-a36d-2e2e980f1a3e",
|
||||
"filename": "Win_Set_High_Performance_Power_Profile.ps1",
|
||||
"filename": "Win_Power_Profile_Set_High_Performance.ps1",
|
||||
"submittedBy": "https://github.com/azulskyknight",
|
||||
"name": "Set High Perf Power Profile",
|
||||
"name": "Power Profile - Set High Performance",
|
||||
"description": "Sets the High Performance Power profile to the active power profile. Use this to keep machines from falling asleep.",
|
||||
"shell": "powershell",
|
||||
"category": "TRMM (Win):Power"
|
||||
@@ -156,7 +183,7 @@
|
||||
"guid": "375323e5-cac6-4f35-a304-bb7cef35902d",
|
||||
"filename": "Win_Disk_Status.ps1",
|
||||
"submittedBy": "https://github.com/dinger1986",
|
||||
"name": "Check Disk Hardware Health (using Event Viewer errors)",
|
||||
"name": "Disk Hardware Health Check (using Event Viewer errors)",
|
||||
"description": "Checks local disks for errors reported in event viewer within the last 24 hours",
|
||||
"shell": "powershell",
|
||||
"category": "TRMM (Win):Hardware"
|
||||
@@ -165,7 +192,7 @@
|
||||
"guid": "7c14beb4-d1c3-41aa-8e70-92a267d6e080",
|
||||
"filename": "Win_Duplicati_Status.ps1",
|
||||
"submittedBy": "https://github.com/dinger1986",
|
||||
"name": "Check Duplicati",
|
||||
"name": "Duplicati - Check Status",
|
||||
"description": "Checks Duplicati Backup is running properly over the last 24 hours",
|
||||
"shell": "powershell",
|
||||
"category": "TRMM (Win):3rd Party Software"
|
||||
@@ -174,7 +201,7 @@
|
||||
"guid": "da51111c-aff6-4d87-9d76-0608e1f67fe5",
|
||||
"filename": "Win_Defender_Enable.ps1",
|
||||
"submittedBy": "https://github.com/dinger1986",
|
||||
"name": "Enable Windows Defender",
|
||||
"name": "Defender - Enable",
|
||||
"description": "Enables Windows Defender and sets preferences",
|
||||
"shell": "powershell",
|
||||
"category": "TRMM (Win):Security>Antivirus"
|
||||
@@ -183,8 +210,8 @@
|
||||
"guid": "a223d03a-e22e-40e0-94f2-92dd8c481d14",
|
||||
"filename": "Win_Open_SSH_Server_Install.ps1",
|
||||
"submittedBy": "https://github.com/dinger1986",
|
||||
"name": "Install SSH",
|
||||
"description": "Installs and enabled OpenSSH Server",
|
||||
"name": "SSH - Install Feature and Enable",
|
||||
"description": "Installs and enabled OpenSSH Server Feature in Win10",
|
||||
"shell": "powershell",
|
||||
"category": "TRMM (Win):Windows Features"
|
||||
},
|
||||
@@ -192,7 +219,7 @@
|
||||
"guid": "2435297a-6263-4e90-8688-1847400d0e22",
|
||||
"filename": "Win_RDP_enable.bat",
|
||||
"submittedBy": "https://github.com/dinger1986",
|
||||
"name": "Enable RDP",
|
||||
"name": "RDP - Enable",
|
||||
"description": "Enables RDP",
|
||||
"shell": "cmd",
|
||||
"category": "TRMM (Win):Windows Features"
|
||||
@@ -201,7 +228,7 @@
|
||||
"guid": "24f19ead-fdfe-46b4-9dcb-4cd0e12a3940",
|
||||
"filename": "Win_Speedtest.ps1",
|
||||
"submittedBy": "https://github.com/dinger1986",
|
||||
"name": "Speed Test Powershell",
|
||||
"name": "Speed Test - Powershell",
|
||||
"description": "Speed Test with Powershell(win 10 or server2016+)",
|
||||
"shell": "powershell",
|
||||
"category": "TRMM (Win):Network"
|
||||
@@ -210,41 +237,69 @@
|
||||
"guid": "a821975c-60df-4d58-8990-6cf8a55b4ee0",
|
||||
"filename": "Win_Sync_Time.bat",
|
||||
"submittedBy": "https://github.com/dinger1986",
|
||||
"name": "Sync DC Time",
|
||||
"name": "ADDC - Sync DC Time",
|
||||
"description": "Syncs time with domain controller",
|
||||
"shell": "cmd",
|
||||
"category": "TRMM (Win):Active Directory"
|
||||
},
|
||||
{
|
||||
"guid": "b6b9912f-4274-4162-99cc-9fd47fbcb292",
|
||||
"filename": "Win_ADDC_Sync_Start.bat",
|
||||
"submittedBy": "https://github.com/silversword411",
|
||||
"name": "ADDC - Sync AD",
|
||||
"description": "Trigger AD Sync on domain controller",
|
||||
"shell": "cmd",
|
||||
"category": "TRMM (Win):Active Directory"
|
||||
},
|
||||
{
|
||||
"guid": "b720e320-7755-4c89-9992-e1a6c43699ed",
|
||||
"filename": "Win_Defender_Clear_Logs.ps1",
|
||||
"submittedBy": "https://github.com/dinger1986",
|
||||
"name": "Clear Defender Logs",
|
||||
"name": "Defender - Clear Logs",
|
||||
"description": "Clears Windows Defender Logs",
|
||||
"shell": "powershell",
|
||||
"category": "TRMM (Win):Security>Antivirus"
|
||||
},
|
||||
{
|
||||
"guid": "d980fda3-a068-47eb-8495-1aab07a24e64",
|
||||
"filename": "Win_Defender_Status.ps1",
|
||||
"filename": "Win_Defender_Status_Report_Last24hrs.ps1",
|
||||
"submittedBy": "https://github.com/dinger1986",
|
||||
"name": "Defender Status",
|
||||
"description": "This will check for Malware, Antispyware, that Windows Defender is Healthy, last scan etc within the last 24 hours",
|
||||
"name": "Defender - Status Report Last 24hr",
|
||||
"description": "Using Event Viewer, this will check for Malware and Antispyware within the last 24 hours and display, otherwise will report as Healthy",
|
||||
"shell": "powershell",
|
||||
"category": "TRMM (Win):Security>Antivirus"
|
||||
},
|
||||
{
|
||||
"guid": "6a0202fc-1b27-4905-95c0-0a03bb881893",
|
||||
"filename": "Win_Defender_Status_Report_LastYear.ps1",
|
||||
"submittedBy": "https://github.com/silversword411",
|
||||
"name": "Defender - Status Report Last Year",
|
||||
"description": "Using Event Viewer, this will check for Malware and Antispyware and display, otherwise will report as Healthy",
|
||||
"shell": "powershell",
|
||||
"category": "TRMM (Win):Security>Antivirus",
|
||||
"default_timeout": "300"
|
||||
},
|
||||
{
|
||||
"guid": "9956e936-6fdb-4488-a9d8-8b274658037f",
|
||||
"filename": "Win_Disable_Fast_Startup.bat",
|
||||
"submittedBy": "https://github.com/dinger1986",
|
||||
"name": "Disable Fast Startup",
|
||||
"name": "Power - Fast Startup Disable",
|
||||
"description": "Disables Faststartup on Windows 10",
|
||||
"shell": "cmd",
|
||||
"category": "TRMM (Win):Power"
|
||||
},
|
||||
{
|
||||
"guid": "f628a02b-16c3-4ab5-b788-dec5bc2af1d9",
|
||||
"filename": "Win_Power_Disable_Hibernation.bat",
|
||||
"submittedBy": "https://github.com/silversword411",
|
||||
"name": "Power - Hibernate Disable",
|
||||
"description": "Disables Hibernation",
|
||||
"shell": "cmd",
|
||||
"category": "TRMM (Win):Power"
|
||||
},
|
||||
{
|
||||
"guid": "2472bbaf-1941-4722-8a58-d1dd0f528801",
|
||||
"filename": "Win_Update_Tactical_Exclusion.ps1",
|
||||
"filename": "Win_TRMM_AV_Update_Exclusion.ps1",
|
||||
"submittedBy": "https://github.com/dinger1986",
|
||||
"name": "TRMM Defender Exclusions",
|
||||
"description": "Windows Defender Exclusions for Tactical RMM",
|
||||
@@ -255,7 +310,7 @@
|
||||
"guid": "b253dc76-41a0-48ca-9cea-bee4277402c4",
|
||||
"filename": "Win_Display_Message_To_User.ps1",
|
||||
"submittedBy": "https://github.com/bradhawkins85",
|
||||
"name": "Display Message To User",
|
||||
"name": "Message Popup To User",
|
||||
"description": "Displays a popup message to the currently logged on user",
|
||||
"shell": "powershell",
|
||||
"category": "TRMM (Win):Other"
|
||||
@@ -264,7 +319,7 @@
|
||||
"guid": "19224d21-bd39-44bc-b9cf-8f1ba3ca9c11",
|
||||
"filename": "Win_Antivirus_Verify.ps1",
|
||||
"submittedBy": "https://github.com/beejayzed",
|
||||
"name": "Verify Antivirus Status",
|
||||
"name": "Antivirus - Verify Status",
|
||||
"description": "Verify and display status for all installed Antiviruses",
|
||||
"shell": "powershell",
|
||||
"category": "TRMM (Win):Security>Antivirus"
|
||||
@@ -273,7 +328,7 @@
|
||||
"guid": "f88c5c52-c6fe-44db-b727-b7912a4279ed",
|
||||
"filename": "Win_Create_All_User_Logon_Script.ps1",
|
||||
"submittedBy": "https://github.com/nr-plaxon",
|
||||
"name": "Create User Logon Script",
|
||||
"name": "Template Example - 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",
|
||||
"category": "TRMM (Win):Other"
|
||||
@@ -282,7 +337,7 @@
|
||||
"guid": "5615aa90-0272-427b-8acf-0ca019612501",
|
||||
"filename": "Win_Chocolatey_Update_Installed.bat",
|
||||
"submittedBy": "https://github.com/silversword411",
|
||||
"name": "Chocolatey Update Installed Apps",
|
||||
"name": "Update Installed Apps",
|
||||
"description": "Update all apps that were installed using Chocolatey.",
|
||||
"shell": "cmd",
|
||||
"category": "TRMM (Win):3rd Party Software>Chocolatey"
|
||||
@@ -291,7 +346,7 @@
|
||||
"guid": "fff8024d-d72e-4457-84fa-6c780f69a16f",
|
||||
"filename": "Win_AD_Check_And_Enable_AD_Recycle_Bin.ps1",
|
||||
"submittedBy": "https://github.com/silversword411",
|
||||
"name": "AD - Check and Enable AD Recycle Bin",
|
||||
"name": "ADDC - 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",
|
||||
"category": "TRMM (Win):Active Directory"
|
||||
@@ -300,11 +355,38 @@
|
||||
"guid": "71090fc4-faa6-460b-adb0-95d7863544e1",
|
||||
"filename": "Win_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",
|
||||
"name": "Event Viewer - Bluescreen Notification",
|
||||
"description": "Event Viewer Monitor - Notify Bluescreen events on your system",
|
||||
"shell": "powershell",
|
||||
"category": "TRMM (Win):Monitoring"
|
||||
},
|
||||
{
|
||||
"guid": "8373846f-facc-49b9-9891-3a780a394c89",
|
||||
"filename": "Win_Local_User_Created_Monitor.ps1",
|
||||
"submittedBy": "https://github.com/dinger1986",
|
||||
"name": "Event Viewer - New User Notification",
|
||||
"description": "Event Viewer Monitor - Notify when new Local user is created",
|
||||
"shell": "powershell",
|
||||
"category": "TRMM (Win):Monitoring"
|
||||
},
|
||||
{
|
||||
"guid": "65e5cef1-8338-4180-a0bc-cd54e62de690",
|
||||
"filename": "Win_Task_Scheduler_New_Items_Monitor.ps1",
|
||||
"submittedBy": "https://github.com/dinger1986",
|
||||
"name": "Event Viewer - Task Scheduler New Item Notification",
|
||||
"description": "Event Viewer Monitor - Notify when new Task Scheduler item is created",
|
||||
"shell": "powershell",
|
||||
"category": "TRMM (Win):Monitoring"
|
||||
},
|
||||
{
|
||||
"guid": "08ca81f2-f044-4dfc-ad47-090b19b19d76",
|
||||
"filename": "Win_User_Logged_in_with_Temp_Profile.ps1",
|
||||
"submittedBy": "https://github.com/dinger1986",
|
||||
"name": "User Logged in with temp profile check",
|
||||
"description": "Check if users are logged in with a temp profile",
|
||||
"shell": "powershell",
|
||||
"category": "TRMM (Win):Other"
|
||||
},
|
||||
{
|
||||
"guid": "5d905886-9eb1-4129-8b81-a013f842eb24",
|
||||
"filename": "Win_Rename_Computer.ps1",
|
||||
@@ -317,21 +399,12 @@
|
||||
},
|
||||
{
|
||||
"guid": "f396dae2-c768-45c5-bd6c-176e56ed3614",
|
||||
"filename": "Win_Finish_updates_and_restart.ps1",
|
||||
"filename": "Win_Power_RestartorShutdown.ps1",
|
||||
"submittedBy": "https://github.com/tremor021",
|
||||
"name": "Finish updates and restart",
|
||||
"description": "Finish installing updates and restart PC",
|
||||
"name": "Power - Restart or Shutdown PC",
|
||||
"description": "Restart PC. Add parameter: shutdown if you want to shutdown computer",
|
||||
"shell": "powershell",
|
||||
"category": "TRMM (Win):Other"
|
||||
},
|
||||
{
|
||||
"guid": "63f89be0-a9c9-4c61-9b55-bce0b28b90b2",
|
||||
"filename": "Win_Finish_updates_and_shutdown.ps1",
|
||||
"submittedBy": "https://github.com/tremor021",
|
||||
"name": "Finish updates and shutdown",
|
||||
"description": "Finish installing updates and shutdown PC",
|
||||
"shell": "powershell",
|
||||
"category": "TRMM (Win):Other"
|
||||
"category": "TRMM (Win):Updates"
|
||||
},
|
||||
{
|
||||
"guid": "e09895d5-ca13-44a2-a38c-6e77c740f0e8",
|
||||
@@ -350,9 +423,9 @@
|
||||
},
|
||||
{
|
||||
"guid": "3abbb62a-3757-492c-8979-b4fc6174845d",
|
||||
"filename": "Win_Disable_AutoRun.bat",
|
||||
"filename": "Win_AutoRun_Disable.bat",
|
||||
"submittedBy": "https://github.com/silversword411",
|
||||
"name": "Disable Autorun",
|
||||
"name": "Autorun - Disable",
|
||||
"description": "Disable Autorun System Wide",
|
||||
"shell": "cmd",
|
||||
"category": "TRMM (Win):Other",
|
||||
@@ -360,9 +433,9 @@
|
||||
},
|
||||
{
|
||||
"guid": "4a11877a-7555-494c-ac74-29d6df3c1989",
|
||||
"filename": "Win_Disable_Cortana.bat",
|
||||
"filename": "Win_Cortana_Disable.bat",
|
||||
"submittedBy": "https://github.com/silversword411",
|
||||
"name": "Disable Cortana",
|
||||
"name": "Cortana - Disable",
|
||||
"description": "Disable Cortana System Wide",
|
||||
"shell": "cmd",
|
||||
"category": "TRMM (Win):Other",
|
||||
@@ -372,7 +445,7 @@
|
||||
"guid": "28ef1387-dd4f-4bab-b042-26250914e370",
|
||||
"filename": "Win_WOL_Enable_Status.ps1",
|
||||
"submittedBy": "https://github.com/silversword411",
|
||||
"name": "WoL - Enable function",
|
||||
"name": "BROKEN Network WoL - Enable function",
|
||||
"description": "Wake on Lan enable on Dell, HP, Lenovo",
|
||||
"shell": "powershell",
|
||||
"category": "TRMM (Win):Network",
|
||||
@@ -382,20 +455,159 @@
|
||||
"guid": "685d5432-0b84-46d5-98e8-3ec2054150fe",
|
||||
"filename": "Win_WOL_Test_State.ps1",
|
||||
"submittedBy": "https://github.com/silversword411",
|
||||
"name": "WoL - Test State",
|
||||
"name": "BROKEN Network WoL - Test State",
|
||||
"description": "Wake on Lan test status",
|
||||
"shell": "powershell",
|
||||
"category": "TRMM (Win):Network",
|
||||
"default_timeout": "90"
|
||||
},
|
||||
{
|
||||
"guid": "abe78170-7cf9-435b-9666-c5ef6c11a106",
|
||||
"filename": "Win_Network_IPv6_Disable.ps1",
|
||||
"submittedBy": "https://github.com/silversword411",
|
||||
"name": "Network IPv6 - Disable",
|
||||
"description": "Disable IPv6 on all adapters",
|
||||
"shell": "powershell",
|
||||
"category": "TRMM (Win):Network",
|
||||
"default_timeout": "90"
|
||||
},
|
||||
{
|
||||
"guid": "745bb7cd-b71a-4f2e-b6f2-c579b1828162",
|
||||
"filename": "Win_Network_DHCP_Set.bat",
|
||||
"submittedBy": "https://github.com/silversword411",
|
||||
"name": "Network - Set Primary NIC to DHCP",
|
||||
"description": "Enable DHCP on primary adapter",
|
||||
"shell": "cmd",
|
||||
"category": "TRMM (Win):Network",
|
||||
"default_timeout": "90"
|
||||
},
|
||||
{
|
||||
"guid": "83aa4d51-63ce-41e7-829f-3c16e6115bbf",
|
||||
"filename": "Win_Network_DNS_Set_to_1.1.1.2.ps1",
|
||||
"submittedBy": "https://github.com/silversword411",
|
||||
"name": "Network - Set all NICs to use DNS of 1.1.1.2",
|
||||
"description": "Domain computers skipped. Sets all NICs to have primary DNS server of 1.1.1.2, backup of 1.0.0.2 (Cloudflare malware blocking)",
|
||||
"shell": "powershell",
|
||||
"category": "TRMM (Win):Network",
|
||||
"default_timeout": "90"
|
||||
},
|
||||
{
|
||||
"guid": "6ce5682a-49db-4c0b-9417-609cf905ac43",
|
||||
"filename": "Win_Win10_Change_Key_and_Activate.ps1",
|
||||
"submittedBy": "https://github.com/silversword411",
|
||||
"name": "Change Win10 Product Key and Activate",
|
||||
"name": "Product Key in Win10 Change and Activate",
|
||||
"description": "Insert new product key and Activate. Requires 1 parameter the product key you want to use",
|
||||
"shell": "powershell",
|
||||
"category": "TRMM (Win):Other",
|
||||
"default_timeout": "90"
|
||||
},
|
||||
{
|
||||
"guid": "83f6c6ea-6120-4fd3-bec8-d3abc505dcdf",
|
||||
"filename": "Win_TRMM_Start_Menu_Delete_Shortcut.ps1",
|
||||
"submittedBy": "https://github.com/silversword411",
|
||||
"name": "TacticalRMM Delete Start Menu Shortcut for App",
|
||||
"description": "Delete its application shortcut that's installed in the start menu by default",
|
||||
"shell": "powershell",
|
||||
"category": "TRMM (Win):TacticalRMM Related",
|
||||
"default_timeout": "10"
|
||||
},
|
||||
{
|
||||
"guid": "60130fca-7636-446e-acd7-cc5d29d609c2",
|
||||
"filename": "Win_Firewall_Check_Status.ps1",
|
||||
"submittedBy": "https://github.com/dinger1986",
|
||||
"name": "Windows Firewall - Check Status",
|
||||
"description": "Windows Firewall - Check state, report status",
|
||||
"shell": "powershell",
|
||||
"category": "TRMM (Win):Network"
|
||||
},
|
||||
{
|
||||
"guid": "93379675-c01c-433f-87df-a11597c959f0",
|
||||
"filename": "Win_UAC_Check_Status.ps1",
|
||||
"submittedBy": "https://github.com/dinger1986",
|
||||
"name": "Windows UAC - Check Status",
|
||||
"description": "Windows UAC - Report status",
|
||||
"shell": "powershell",
|
||||
"category": "TRMM (Win):Security"
|
||||
},
|
||||
{
|
||||
"guid": "7ea6a11a-05c0-4151-b5c1-cb8af029299f",
|
||||
"filename": "Win_AzureAD_Check_Connection_Status.ps1",
|
||||
"submittedBy": "https://github.com/dinger1986",
|
||||
"name": "Azure AD - Check Status",
|
||||
"description": "Azure AD - Check if joined or not",
|
||||
"shell": "powershell",
|
||||
"category": "TRMM (Win):Azure>AD"
|
||||
},
|
||||
{
|
||||
"guid": "7d81859a-1ba3-42b0-8664-29844f0dd765",
|
||||
"filename": "Win_Azure_Mars_Cloud_Backup_Status.ps1",
|
||||
"submittedBy": "https://github.com/dinger1986",
|
||||
"name": "Azure - Mars Cloud backup Status",
|
||||
"description": "Azure - Mars Cloud backup Check Status",
|
||||
"shell": "powershell",
|
||||
"category": "TRMM (Win):Azure>Backup"
|
||||
},
|
||||
{
|
||||
"guid": "e18c64d0-b783-4b52-b44b-9bb7592b439b",
|
||||
"filename": "Win_FileSystem_Enable_Long_Paths.bat",
|
||||
"submittedBy": "https://github.com/silversword411",
|
||||
"name": "File System - Enable Long Paths",
|
||||
"description": "Enables NTFS Long paths greater than 260 characters",
|
||||
"shell": "cmd",
|
||||
"category": "TRMM (Win):Storage"
|
||||
},
|
||||
{
|
||||
"guid": "c6252ca8-5172-42ea-9114-e447f80868f5",
|
||||
"filename": "Win_USB_Disable_Access.bat",
|
||||
"submittedBy": "https://github.com/silversword411",
|
||||
"name": "USB - Disable Access",
|
||||
"description": "USB - Disable Plugged in USB devices",
|
||||
"shell": "cmd",
|
||||
"category": "TRMM (Win):Storage"
|
||||
},
|
||||
{
|
||||
"guid": "3785952f-69fb-4bda-b2fe-5e3e8642738a",
|
||||
"filename": "Win_USB_Enable_Access.bat",
|
||||
"submittedBy": "https://github.com/silversword411",
|
||||
"name": "USB - Enable Access",
|
||||
"description": "USB - Enable Plugged in USB devices",
|
||||
"shell": "cmd",
|
||||
"category": "TRMM (Win):Storage"
|
||||
},
|
||||
{
|
||||
"guid": "c6014da2-b188-4e1b-b96a-e3440ade3a6a",
|
||||
"filename": "Win_RecycleBin_Empty.ps1",
|
||||
"submittedBy": "https://github.com/silversword411",
|
||||
"name": "File System - Empty Recycle Bin",
|
||||
"description": "Empty the recycle bin",
|
||||
"shell": "powershell",
|
||||
"category": "TRMM (Win):Storage"
|
||||
},
|
||||
{
|
||||
"guid": "57997ec7-b293-4fd5-9f90-a25426d0eb90",
|
||||
"filename": "Win_Get_Computer_Users.ps1",
|
||||
"submittedBy": "https://github.com/tremor021",
|
||||
"name": "Get Computer Users",
|
||||
"description": "Get list of computer users and show which one is enabled",
|
||||
"shell": "powershell",
|
||||
"category": "TRMM (Win):Other"
|
||||
},
|
||||
{
|
||||
"guid": "77da9c87-5a7a-4ba1-bdde-3eeb3b01d62d",
|
||||
"filename": "Win_Network_Set_To_Private.ps1",
|
||||
"submittedBy": "https://github.com/tremor021",
|
||||
"name": "Network Category - Set Network To Private",
|
||||
"description": "Sets current network type to Private",
|
||||
"shell": "powershell",
|
||||
"category": "TRMM (Win):Network"
|
||||
},
|
||||
{
|
||||
"guid": "768f42d5-7b45-45ed-8233-254ae537aaa2",
|
||||
"filename": "Win_TaskScheduler_Add_Task.ps1",
|
||||
"submittedBy": "https://github.com/tremor021",
|
||||
"name": "Task Scheduler - Add a task",
|
||||
"description": "Add a task to Task Scheduler, needs editing",
|
||||
"shell": "powershell",
|
||||
"category": "TRMM (Win):Other"
|
||||
}
|
||||
]
|
||||
18
api/tacticalrmm/scripts/migrations/0008_script_guid.py
Normal file
18
api/tacticalrmm/scripts/migrations/0008_script_guid.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 3.1.7 on 2021-04-15 02:10
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('scripts', '0007_script_args'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='script',
|
||||
name='guid',
|
||||
field=models.CharField(blank=True, max_length=64, null=True),
|
||||
),
|
||||
]
|
||||
@@ -1,10 +1,11 @@
|
||||
import base64
|
||||
import re
|
||||
from loguru import logger
|
||||
from typing import Any, List, Union
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.postgres.fields import ArrayField
|
||||
from django.db import models
|
||||
from loguru import logger
|
||||
|
||||
from logs.models import BaseAuditModel
|
||||
|
||||
@@ -23,6 +24,7 @@ logger.configure(**settings.LOG_CONFIG)
|
||||
|
||||
|
||||
class Script(BaseAuditModel):
|
||||
guid = name = models.CharField(max_length=64, null=True, blank=True)
|
||||
name = models.CharField(max_length=255)
|
||||
description = models.TextField(null=True, blank=True)
|
||||
filename = models.CharField(max_length=255) # deprecated
|
||||
@@ -78,9 +80,7 @@ class Script(BaseAuditModel):
|
||||
|
||||
for script in info:
|
||||
if os.path.exists(os.path.join(scripts_dir, script["filename"])):
|
||||
s = cls.objects.filter(script_type="builtin").filter(
|
||||
name=script["name"]
|
||||
)
|
||||
s = cls.objects.filter(script_type="builtin", guid=script["guid"])
|
||||
|
||||
category = (
|
||||
script["category"] if "category" in script.keys() else "Community"
|
||||
@@ -120,6 +120,46 @@ class Script(BaseAuditModel):
|
||||
"args",
|
||||
]
|
||||
)
|
||||
|
||||
# check if script was added without a guid
|
||||
elif cls.objects.filter(
|
||||
script_type="builtin", name=script["name"]
|
||||
).exists():
|
||||
s = cls.objects.get(script_type="builtin", name=script["name"])
|
||||
|
||||
if not s.guid:
|
||||
print(f"Updating GUID for: {script['name']}")
|
||||
s.guid = script["guid"]
|
||||
s.name = script["name"]
|
||||
s.description = script["description"]
|
||||
s.category = category
|
||||
s.shell = script["shell"]
|
||||
s.default_timeout = default_timeout
|
||||
s.args = args
|
||||
|
||||
with open(
|
||||
os.path.join(scripts_dir, script["filename"]), "rb"
|
||||
) as f:
|
||||
script_bytes = (
|
||||
f.read().decode("utf-8").encode("ascii", "ignore")
|
||||
)
|
||||
s.code_base64 = base64.b64encode(script_bytes).decode(
|
||||
"ascii"
|
||||
)
|
||||
|
||||
s.save(
|
||||
update_fields=[
|
||||
"guid",
|
||||
"name",
|
||||
"description",
|
||||
"category",
|
||||
"default_timeout",
|
||||
"code_base64",
|
||||
"shell",
|
||||
"args",
|
||||
]
|
||||
)
|
||||
|
||||
else:
|
||||
print(f"Adding new community script: {script['name']}")
|
||||
|
||||
@@ -131,6 +171,7 @@ class Script(BaseAuditModel):
|
||||
|
||||
cls(
|
||||
code_base64=code_base64,
|
||||
guid=script["guid"],
|
||||
name=script["name"],
|
||||
description=script["description"],
|
||||
filename=script["filename"],
|
||||
@@ -141,6 +182,9 @@ class Script(BaseAuditModel):
|
||||
args=args,
|
||||
).save()
|
||||
|
||||
# delete community scripts that had their name changed
|
||||
cls.objects.filter(script_type="builtin", guid=None).delete()
|
||||
|
||||
@staticmethod
|
||||
def serialize(script):
|
||||
# serializes the script and returns json
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from email.policy import default
|
||||
import json
|
||||
import os
|
||||
from email.policy import default
|
||||
from pathlib import Path
|
||||
|
||||
from django.conf import settings
|
||||
@@ -245,12 +245,39 @@ class TestScriptViews(TacticalTestCase):
|
||||
|
||||
Script.load_community_scripts()
|
||||
|
||||
community_scripts = Script.objects.filter(script_type="builtin").count()
|
||||
self.assertEqual(len(info), community_scripts)
|
||||
community_scripts_count = Script.objects.filter(script_type="builtin").count()
|
||||
if len(info) != community_scripts_count:
|
||||
raise Exception(
|
||||
f"There are {len(info)} scripts in json file but only {community_scripts_count} in database"
|
||||
)
|
||||
|
||||
# test updating already added community scripts
|
||||
Script.load_community_scripts()
|
||||
self.assertEqual(len(info), community_scripts)
|
||||
community_scripts_count2 = Script.objects.filter(script_type="builtin").count()
|
||||
self.assertEqual(len(info), community_scripts_count2)
|
||||
|
||||
def test_community_script_has_jsonfile_entry(self):
|
||||
with open(
|
||||
os.path.join(settings.BASE_DIR, "scripts/community_scripts.json")
|
||||
) as f:
|
||||
info = json.load(f)
|
||||
|
||||
filenames = [i["filename"] for i in info]
|
||||
|
||||
# normal
|
||||
if not settings.DOCKER_BUILD:
|
||||
scripts_dir = os.path.join(Path(settings.BASE_DIR).parents[1], "scripts")
|
||||
# docker
|
||||
else:
|
||||
scripts_dir = settings.SCRIPTS_DIR
|
||||
|
||||
with os.scandir(scripts_dir) as it:
|
||||
for f in it:
|
||||
if not f.name.startswith(".") and f.is_file():
|
||||
if f.name not in filenames:
|
||||
raise Exception(
|
||||
f"{f.name} is missing an entry in community_scripts.json"
|
||||
)
|
||||
|
||||
def test_script_filenames_do_not_contain_spaces(self):
|
||||
with open(
|
||||
@@ -259,4 +286,5 @@ class TestScriptViews(TacticalTestCase):
|
||||
info = json.load(f)
|
||||
for script in info:
|
||||
fn: str = script["filename"]
|
||||
self.assertTrue(" " not in fn)
|
||||
if " " in fn:
|
||||
raise Exception(f"{fn} must not contain spaces in filename")
|
||||
|
||||
@@ -15,16 +15,16 @@ EXE_DIR = os.path.join(BASE_DIR, "tacticalrmm/private/exe")
|
||||
AUTH_USER_MODEL = "accounts.User"
|
||||
|
||||
# latest release
|
||||
TRMM_VERSION = "0.5.2"
|
||||
TRMM_VERSION = "0.6.2"
|
||||
|
||||
# bump this version everytime vue code is changed
|
||||
# to alert user they need to manually refresh their browser
|
||||
APP_VER = "0.0.127"
|
||||
APP_VER = "0.0.131"
|
||||
|
||||
# https://github.com/wh1te909/rmmagent
|
||||
LATEST_AGENT_VER = "1.4.13"
|
||||
LATEST_AGENT_VER = "1.5.2"
|
||||
|
||||
MESH_VER = "0.7.93"
|
||||
MESH_VER = "0.8.17"
|
||||
|
||||
# for the update script, bump when need to recreate venv or npm install
|
||||
PIP_VER = "15"
|
||||
@@ -34,8 +34,8 @@ DL_64 = f"https://github.com/wh1te909/rmmagent/releases/download/v{LATEST_AGENT_
|
||||
DL_32 = f"https://github.com/wh1te909/rmmagent/releases/download/v{LATEST_AGENT_VER}/winagent-v{LATEST_AGENT_VER}-x86.exe"
|
||||
|
||||
EXE_GEN_URLS = [
|
||||
"https://exe2.tacticalrmm.io/api/v1/exe",
|
||||
"https://exe.tacticalrmm.io/api/v1/exe",
|
||||
"https://exe2.tacticalrmm.io",
|
||||
"https://exe.tacticalrmm.io",
|
||||
]
|
||||
|
||||
DEFAULT_AUTO_FIELD = "django.db.models.AutoField"
|
||||
|
||||
@@ -12,6 +12,7 @@ from .utils import (
|
||||
generate_winagent_exe,
|
||||
get_bit_days,
|
||||
reload_nats,
|
||||
run_nats_api_cmd,
|
||||
)
|
||||
|
||||
|
||||
@@ -74,6 +75,12 @@ class TestUtils(TestCase):
|
||||
|
||||
mock_subprocess.assert_called_once()
|
||||
|
||||
@patch("subprocess.run")
|
||||
def test_run_nats_api_cmd(self, mock_subprocess):
|
||||
ids = ["a", "b", "c"]
|
||||
_ = run_nats_api_cmd("monitor", ids)
|
||||
mock_subprocess.assert_called_once()
|
||||
|
||||
def test_bitdays_to_string(self):
|
||||
a = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
|
||||
all_days = [
|
||||
|
||||
@@ -4,6 +4,7 @@ import string
|
||||
import subprocess
|
||||
import tempfile
|
||||
import time
|
||||
import urllib.parse
|
||||
from typing import Union
|
||||
|
||||
import pytz
|
||||
@@ -19,6 +20,7 @@ from rest_framework import status
|
||||
from rest_framework.response import Response
|
||||
|
||||
from agents.models import Agent
|
||||
from core.models import CodeSignToken
|
||||
|
||||
logger.configure(**settings.LOG_CONFIG)
|
||||
|
||||
@@ -50,12 +52,27 @@ def generate_winagent_exe(
|
||||
file_name: str,
|
||||
) -> Union[Response, FileResponse]:
|
||||
|
||||
from agents.utils import get_exegen_url
|
||||
|
||||
inno = (
|
||||
f"winagent-v{settings.LATEST_AGENT_VER}.exe"
|
||||
if arch == "64"
|
||||
else f"winagent-v{settings.LATEST_AGENT_VER}-x86.exe"
|
||||
)
|
||||
|
||||
try:
|
||||
codetoken = CodeSignToken.objects.first().token
|
||||
base_url = get_exegen_url() + "/api/v1/winagents/?"
|
||||
params = {
|
||||
"version": settings.LATEST_AGENT_VER,
|
||||
"arch": arch,
|
||||
"token": codetoken,
|
||||
}
|
||||
dl_url = base_url + urllib.parse.urlencode(params)
|
||||
except:
|
||||
codetoken = ""
|
||||
dl_url = settings.DL_64 if arch == "64" else settings.DL_32
|
||||
|
||||
data = {
|
||||
"client": client,
|
||||
"site": site,
|
||||
@@ -66,8 +83,9 @@ def generate_winagent_exe(
|
||||
"goarch": "amd64" if arch == "64" else "386",
|
||||
"token": token,
|
||||
"inno": inno,
|
||||
"url": settings.DL_64 if arch == "64" else settings.DL_32,
|
||||
"url": dl_url,
|
||||
"api": api,
|
||||
"codesigntoken": codetoken,
|
||||
}
|
||||
headers = {"Content-type": "application/json"}
|
||||
|
||||
@@ -76,7 +94,7 @@ def generate_winagent_exe(
|
||||
for url in settings.EXE_GEN_URLS:
|
||||
try:
|
||||
r = requests.post(
|
||||
url,
|
||||
f"{url}/api/v1/exe",
|
||||
json=data,
|
||||
headers=headers,
|
||||
stream=True,
|
||||
@@ -228,3 +246,20 @@ class KnoxAuthMiddlewareInstance:
|
||||
KnoxAuthMiddlewareStack = lambda inner: KnoxAuthMiddlewareInstance(
|
||||
AuthMiddlewareStack(inner)
|
||||
)
|
||||
|
||||
|
||||
def run_nats_api_cmd(mode: str, ids: list[str], timeout: int = 30) -> None:
|
||||
config = {
|
||||
"key": settings.SECRET_KEY,
|
||||
"natsurl": f"tls://{settings.ALLOWED_HOSTS[0]}:4222",
|
||||
"agents": ids,
|
||||
}
|
||||
with tempfile.NamedTemporaryFile() as fp:
|
||||
with open(fp.name, "w") as f:
|
||||
json.dump(config, f)
|
||||
|
||||
cmd = ["/usr/local/bin/nats-api", "-c", fp.name, "-m", mode]
|
||||
try:
|
||||
subprocess.run(cmd, capture_output=True, timeout=timeout)
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
|
||||
@@ -14,8 +14,29 @@ class TestWinUpdateViews(TacticalTestCase):
|
||||
self.authenticate()
|
||||
self.setup_coresettings()
|
||||
|
||||
def test_get_winupdates(self):
|
||||
@patch("agents.models.Agent.nats_cmd")
|
||||
def test_run_update_scan(self, nats_cmd):
|
||||
agent = baker.make_recipe("agents.agent")
|
||||
url = f"/winupdate/{agent.pk}/runupdatescan/"
|
||||
r = self.client.get(url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
nats_cmd.assert_called_with({"func": "getwinupdates"}, wait=False)
|
||||
|
||||
self.check_not_authenticated("get", url)
|
||||
|
||||
@patch("agents.models.Agent.nats_cmd")
|
||||
def test_install_updates(self, nats_cmd):
|
||||
agent = baker.make_recipe("agents.agent")
|
||||
baker.make("winupdate.WinUpdate", agent=agent, _quantity=4)
|
||||
baker.make("winupdate.WinUpdatePolicy", agent=agent)
|
||||
url = f"/winupdate/{agent.pk}/installnow/"
|
||||
r = self.client.get(url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
nats_cmd.assert_called_once()
|
||||
|
||||
self.check_not_authenticated("get", url)
|
||||
|
||||
def test_get_winupdates(self):
|
||||
agent = baker.make_recipe("agents.agent")
|
||||
baker.make("winupdate.WinUpdate", agent=agent, _quantity=4)
|
||||
|
||||
@@ -27,8 +48,8 @@ class TestWinUpdateViews(TacticalTestCase):
|
||||
resp = self.client.get(url, format="json")
|
||||
serializer = UpdateSerializer(agent)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
self.assertEqual(len(resp.data["winupdates"]), 4)
|
||||
self.assertEqual(resp.data, serializer.data)
|
||||
self.assertEqual(len(resp.data["winupdates"]), 4) # type: ignore
|
||||
self.assertEqual(resp.data, serializer.data) # type: ignore
|
||||
|
||||
self.check_not_authenticated("get", url)
|
||||
|
||||
@@ -99,7 +120,7 @@ class TestWinUpdateViews(TacticalTestCase):
|
||||
resp = self.client.patch(url, invalid_data, format="json")
|
||||
self.assertEqual(resp.status_code, 404)
|
||||
|
||||
data = {"pk": winupdate.pk, "policy": "inherit"}
|
||||
data = {"pk": winupdate.pk, "policy": "inherit"} # type: ignore
|
||||
|
||||
resp = self.client.patch(url, data, format="json")
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import asyncio
|
||||
|
||||
from django.shortcuts import get_object_or_404
|
||||
from packaging import version as pyver
|
||||
from rest_framework.decorators import api_view
|
||||
from rest_framework.response import Response
|
||||
|
||||
from agents.models import Agent
|
||||
from tacticalrmm.utils import get_default_timezone, notify_error
|
||||
from tacticalrmm.utils import get_default_timezone
|
||||
|
||||
from .models import WinUpdate
|
||||
from .serializers import UpdateSerializer
|
||||
@@ -24,9 +23,6 @@ def get_win_updates(request, pk):
|
||||
def run_update_scan(request, pk):
|
||||
agent = get_object_or_404(Agent, pk=pk)
|
||||
agent.delete_superseded_updates()
|
||||
if pyver.parse(agent.version) < pyver.parse("1.3.0"):
|
||||
return notify_error("Requires agent version 1.3.0 or greater")
|
||||
|
||||
asyncio.run(agent.nats_cmd({"func": "getwinupdates"}, wait=False))
|
||||
return Response("ok")
|
||||
|
||||
@@ -35,9 +31,6 @@ def run_update_scan(request, pk):
|
||||
def install_updates(request, pk):
|
||||
agent = get_object_or_404(Agent, pk=pk)
|
||||
agent.delete_superseded_updates()
|
||||
if pyver.parse(agent.version) < pyver.parse("1.3.0"):
|
||||
return notify_error("Requires agent version 1.3.0 or greater")
|
||||
|
||||
agent.approve_updates()
|
||||
nats_data = {
|
||||
"func": "installwinupdates",
|
||||
|
||||
@@ -4,7 +4,7 @@ A backup script is provided for quick and easy way to backup all settings into o
|
||||
|
||||
Download the backup script:
|
||||
```bash
|
||||
wget https://raw.githubusercontent.com/wh1te909/tacticalrmm/master/backup.sh
|
||||
wget -N https://raw.githubusercontent.com/wh1te909/tacticalrmm/master/backup.sh
|
||||
```
|
||||
|
||||
From the Web UI, click **Tools > Server Maintenance**
|
||||
|
||||
22
docs/docs/code_signing.md
Normal file
22
docs/docs/code_signing.md
Normal file
@@ -0,0 +1,22 @@
|
||||
# Code Signing
|
||||
|
||||
*Version added: Tactical RMM v0.6.0 / Agent v1.5.0*
|
||||
|
||||
Tactical RMM agents are now [code signed](https://comodosslstore.com/resources/what-is-microsoft-authenticode-code-signing-certificate/)!
|
||||
|
||||
To get access to code signed agents, you must be a [Github Sponsor](https://github.com/sponsors/wh1te909) with a minumum monthly donation of $50.00
|
||||
|
||||
Once you have become a sponsor, please email **support@amidaware.com** with your Github username (and Discord username if you're on our [Discord](https://discord.gg/upGTkWp))
|
||||
|
||||
Please allow up to 24 hours for a response
|
||||
|
||||
You will then be sent a code signing auth token, which you should enter into Tactical's web UI from *Settings > Code Signing*
|
||||
|
||||
|
||||
## How does it work?
|
||||
|
||||
Everytime you generate an agent or an agent does a self-update, your self-hosted instance sends a request to Tactical's code signing servers with your auth token.
|
||||
|
||||
If the token is valid, the server sends you back a code signed agent. If not, it sends you back the un-signed agent.
|
||||
|
||||
If you think your auth token has been compromised or stolen then please email support or contact wh1te909 on discord to get a new token / invalidate the old one.
|
||||
103
docs/docs/contributing_community_scripts.md
Normal file
103
docs/docs/contributing_community_scripts.md
Normal file
@@ -0,0 +1,103 @@
|
||||
## Script Library Naming Conventions
|
||||
|
||||
### File names
|
||||
|
||||
Under `/scripts` the file name should generally follow this format:
|
||||
|
||||
```
|
||||
(Platform)_(Category or Function)_(What It Does).xxx
|
||||
```
|
||||
|
||||
!!!info
|
||||
Although Tactical RMM only has a Windows agent for now, we're planning for a future with more platform support
|
||||
|
||||
Platform for now are:
|
||||
|
||||
```
|
||||
Win
|
||||
OSX
|
||||
Linux
|
||||
iOS
|
||||
Android
|
||||
```
|
||||
|
||||
|
||||
Good filename examples include:
|
||||
|
||||
```
|
||||
Win_Azure_Mars_Cloud_Backup_Status.ps1
|
||||
Win_AzureAD_Check_Connection_Status.ps1
|
||||
Win_Network_DHCP_Set.bat
|
||||
Win_Network_DNS_Set_to_1.1.1.2.ps1
|
||||
```
|
||||
|
||||
!!!info
|
||||
This is so that at a glance you can see if there is already a script with that function, and you can avoid duplication of functionality. If you can improve a script or allow Script Arguments/Parameters update existing if possible
|
||||
|
||||
### Name field (in community_scripts.json)
|
||||
|
||||
Consider they are viewed in 3 different locations:
|
||||
|
||||
Script Manager
|
||||
|
||||
- List View (sortable by any column)
|
||||
|
||||
- Folder View (Grouped by Categories)
|
||||
|
||||
Run or Add script
|
||||
|
||||
- Running scripts manually or adding tasks (or adding in Automation Manager)
|
||||
|
||||
!!!info
|
||||
A good max length is 50-60 chars or less for display in these 3 locations
|
||||
|
||||
Make sure your Name roughly follows the order of file naming as above
|
||||
|
||||
```
|
||||
Category or Function - What It Does
|
||||
```
|
||||
|
||||
Consider how the alphabetic sort will affect display
|
||||
|
||||

|
||||
|
||||
## Making Script Files
|
||||
|
||||
### Good Habits
|
||||
|
||||
- Try and make them fully self-contained.
|
||||
|
||||
- If they pull data from elsewhere, create comment notes at the top with references for others to audit/validate
|
||||
|
||||
- Good folder locations
|
||||
```
|
||||
c:\ProgramData\TacticalRMM\
|
||||
c:\ProgramData\TacticalRMM\scripts
|
||||
c:\ProgramData\TacticalRMM\toolbox
|
||||
c:\ProgramData\TacticalRMM\logs
|
||||
c:\ProgramData\TacticalRMM\temp
|
||||
c:\ProgramData\TacticalRMM\
|
||||
```
|
||||
|
||||
- Command Parameters are good. Optional command parameters for extra functions are better.
|
||||
|
||||
### Bad Habits
|
||||
|
||||
- Assumes non-standard configurations
|
||||
|
||||
- Doesn't play well with other community scripts (reused names etc.)
|
||||
|
||||
|
||||
## Useful Reference Script Examples
|
||||
|
||||
RunAsUser (since Tactical RMM runs as system)
|
||||
[https://github.com/wh1te909/tacticalrmm/blob/develop/scripts/Win_Display_Message_To_User.ps1](https://github.com/wh1te909/tacticalrmm/blob/develop/scripts/Win_Display_Message_To_User.ps1)
|
||||
|
||||
Command Paramater Ninja
|
||||
[https://github.com/wh1te909/tacticalrmm/blob/develop/scripts/Win_ScreenConnectAIO.ps1](https://github.com/wh1te909/tacticalrmm/blob/develop/scripts/Win_ScreenConnectAIO.ps1)
|
||||
|
||||
Optional Command Parameters and testing for errors
|
||||
[https://github.com/wh1te909/tacticalrmm/blob/develop/scripts/Win_Rename_Computer.ps1](https://github.com/wh1te909/tacticalrmm/blob/develop/scripts/Win_Rename_Computer.ps1)
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
|
||||
## Getting Started
|
||||
|
||||
### 1. Install vscode
|
||||
[https://code.visualstudio.com/download](https://code.visualstudio.com/download)
|
||||
@@ -19,7 +20,7 @@ Login to your Github
|
||||
|
||||
Choose local folder
|
||||
|
||||
### 3a. Install extra vscode Extensions
|
||||
#### 3a. Install extra vscode Extensions
|
||||
|
||||
GitLens
|
||||
|
||||
@@ -94,4 +95,16 @@ Then you're `push`ing that updated local repo to your online Github fork
|
||||
|
||||
### 8. Verify and Repeat
|
||||
|
||||
Check your Github fork in browser, should be up to date now with original. Repeat 6 or 7 as necessary
|
||||
Check your Github fork in browser, should be up to date now with original. Repeat 6 or 7 as necessary
|
||||
|
||||
## Reference
|
||||
|
||||
### Customizing the Admin Web Interface
|
||||
|
||||
Created using quasar, it's all your .vue files in `web/src/components/modals/agents/RunScript.vue`
|
||||
|
||||
Learn stuff here
|
||||
|
||||
https://quasar.dev/
|
||||
|
||||
|
||||
|
||||
BIN
docs/docs/images/community_scripts_name_field_example1.png
Normal file
BIN
docs/docs/images/community_scripts_name_field_example1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
@@ -1,6 +1,7 @@
|
||||
# Installing an agent
|
||||
|
||||
!!!warning
|
||||
If you don't want to deal with AV flagging/deleting your agents, check the instructions for getting [code signed agents](code_signing.md)<br/><br />
|
||||
You must add antivirus exlusions for the tactical agent.<br/>
|
||||
Any decent AV will flag the agent as a virus, since it technically is one due to the nature of this software.<br/>
|
||||
Adding the following exlucions will make sure everything works, including agent update:<br/>
|
||||
|
||||
@@ -7,7 +7,8 @@
|
||||
The provided install script assumes a fresh server with no software installed on it. Attempting to run it on an existing server with other services **will** break things and the install will fail.<br/><br/>
|
||||
The install script has been tested on the following public cloud providers: DigitalOcean, Linode, Vultr, BuyVM (highly recommended), Hetzner, AWS, Google Cloud and Azure, as well as behind NAT on Hyper-V, Proxmox and ESXi.
|
||||
|
||||
- A real domain is needed to generate a Let's Encrypt wildcard cert. <br/>If you cannot afford to purchase a domain ($12 a year) then you can get one for free at [freenom.com](https://www.freenom.com/)<br/><br/>
|
||||
- A real (internet resolvable) domain is needed to generate a Let's Encrypt wildcard cert. <br/>If you cannot afford to purchase a domain ($12 a year) then you can get one for free at [freenom.com](https://www.freenom.com/)
|
||||
- example.local is __NOT__ a real domain. No you [don't have to expose your server](faq.md#can-i-run-tactical-rmm-locally-behind-nat-without-exposing-anything-to-the-internet) to the internet<br/><br/>
|
||||
|
||||
- A TOTP based authenticator app. Some popular ones are Google Authenticator, Authy and Microsoft Authenticator.<br/><br/>
|
||||
|
||||
|
||||
@@ -30,6 +30,9 @@ If you have agents that are relatively old, you will need to uninstall them manu
|
||||
|
||||
#### Agents not checking in or showing up / General agent issues
|
||||
|
||||
First, reload NATS from tactical's web UI:<br />
|
||||
*Tools > Server Maintenance > Reload Nats Configuration*
|
||||
|
||||
Open CMD as admin on the problem computer and stop the agent services:
|
||||
|
||||
```cmd
|
||||
@@ -51,7 +54,7 @@ This will print out a ton of info. You should be able to see the error from the
|
||||
|
||||
Please then copy/paste the logs and post them either in our [Discord support chat](https://discord.gg/upGTkWp), or create a [github issue](https://github.com/wh1te909/tacticalrmm/issues).
|
||||
|
||||
<br/>
|
||||
If all else fails, simply uninstall the agent either from control panel or silently with `"C:\Program Files\TacticalAgent\unins000.exe" /VERYSILENT` and then reinstall the agent.
|
||||
|
||||
#### All other errors
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ site_name: "Tactical RMM Documentation"
|
||||
nav:
|
||||
- Home: index.md
|
||||
- Sponsor: sponsor.md
|
||||
- Code Signing: code_signing.md
|
||||
- RMM Installation:
|
||||
- "Traditional Install": install_server.md
|
||||
- "Docker Install": install_docker.md
|
||||
@@ -23,9 +24,10 @@ nav:
|
||||
- FAQ: faq.md
|
||||
- Management Commands: management_cmds.md
|
||||
- MeshCentral Integration: mesh_integration.md
|
||||
- Contributing:
|
||||
- Contributing:
|
||||
- "Contributing to Docs": contributing.md
|
||||
- "Contributing using VSCode": contributing_using_vscode.md
|
||||
- "Contributing to Community Scripts": contributing_community_scripts.md
|
||||
- License: license.md
|
||||
site_description: "A remote monitoring and management tool"
|
||||
site_author: "wh1te909"
|
||||
|
||||
2
scripts/Win_ADDC_Sync_Start.bat
Normal file
2
scripts/Win_ADDC_Sync_Start.bat
Normal file
@@ -0,0 +1,2 @@
|
||||
net start ADSync
|
||||
exit
|
||||
15
scripts/Win_AzureAD_Check_Connection_Status.ps1
Normal file
15
scripts/Win_AzureAD_Check_Connection_Status.ps1
Normal file
@@ -0,0 +1,15 @@
|
||||
$ErrorActionPreference = 'silentlycontinue'
|
||||
$aadchk = dsregcmd /status | Where-Object { $_ -match 'AzureAdJoined : ' } | ForEach-Object { $_.Trim() }
|
||||
|
||||
if ($aadchk -Eq 'AzureAdJoined : Yes') {
|
||||
Write-Output "Machine is Azure Ad Joined"
|
||||
exit 0
|
||||
}
|
||||
|
||||
else {
|
||||
Write-Output "Machine is not Azure Ad Joined"
|
||||
exit 1
|
||||
}
|
||||
|
||||
|
||||
Exit $LASTEXITCODE
|
||||
23
scripts/Win_Azure_Mars_Cloud_Backup_Status.ps1
Normal file
23
scripts/Win_Azure_Mars_Cloud_Backup_Status.ps1
Normal file
@@ -0,0 +1,23 @@
|
||||
$ErrorActionPreference= 'silentlycontinue'
|
||||
$TimeSpan = (Get-Date) - (New-TimeSpan -Day 1)
|
||||
|
||||
##Check for Errors in Backup
|
||||
if (Get-WinEvent -FilterHashtable @{LogName='CloudBackup/Operational';ID='11','18';StartTime=$TimeSpan})
|
||||
|
||||
{
|
||||
Write-Host "Cloud Backup Mars Ended with Errors"
|
||||
Get-WinEvent -FilterHashtable @{LogName='CloudBackup/Operational';ID='1','14','11','18','16';StartTime=$TimeSpan}
|
||||
exit 1
|
||||
}
|
||||
|
||||
|
||||
else
|
||||
|
||||
{
|
||||
Write-Host "Cloud Backup Mars Backup Is Working Correctly"
|
||||
Get-WinEvent -FilterHashtable @{LogName='CloudBackup/Operational';ID='1','14','16'}
|
||||
exit 0
|
||||
}
|
||||
|
||||
|
||||
Exit $LASTEXITCODE
|
||||
1
scripts/Win_Bitlocker_Get_Recovery_Keys.ps1
Normal file
1
scripts/Win_Bitlocker_Get_Recovery_Keys.ps1
Normal file
@@ -0,0 +1 @@
|
||||
manage-bde -protectors C: -get
|
||||
@@ -1,24 +1,24 @@
|
||||
# This will check for Malware, Antispyware, that Windows Defender is Healthy, last scan etc within the last 24 hours
|
||||
|
||||
$ErrorActionPreference= 'silentlycontinue'
|
||||
$TimeSpan = (Get-Date) - (New-TimeSpan -Day 1)
|
||||
|
||||
if (Get-WinEvent -FilterHashtable @{LogName='Microsoft-Windows-Windows Defender/Operational';ID='1116','1118','1015','1006','5010','5012','5001','1123';StartTime=$TimeSpan})
|
||||
|
||||
{
|
||||
Write-Output "Virus Found or Issue with Defender"
|
||||
Get-WinEvent -FilterHashtable @{LogName='Microsoft-Windows-Windows Defender/Operational';ID='1116','1118','1015','1006','5010','5012','5001','1123';StartTime=$TimeSpan}
|
||||
exit 1
|
||||
}
|
||||
|
||||
|
||||
else
|
||||
|
||||
{
|
||||
Write-Output "No Virus Found, Defender is Healthy"
|
||||
Get-WinEvent -FilterHashtable @{LogName='Microsoft-Windows-Windows Defender/Operational';ID='1150','1001';StartTime=$TimeSpan}
|
||||
exit 0
|
||||
}
|
||||
|
||||
|
||||
Exit $LASTEXITCODE
|
||||
# This will check for Malware, Antispyware, that Windows Defender is Healthy, last scan etc within the last 24 hours
|
||||
|
||||
$ErrorActionPreference= 'silentlycontinue'
|
||||
$TimeSpan = (Get-Date) - (New-TimeSpan -Day 1)
|
||||
|
||||
if (Get-WinEvent -FilterHashtable @{LogName='Microsoft-Windows-Windows Defender/Operational';ID='1116','1118','1015','1006','5010','5012','5001','1123';StartTime=$TimeSpan})
|
||||
|
||||
{
|
||||
Write-Output "Virus Found or Issue with Defender"
|
||||
Get-WinEvent -FilterHashtable @{LogName='Microsoft-Windows-Windows Defender/Operational';ID='1116','1118','1015','1006','5010','5012','5001','1123';StartTime=$TimeSpan}
|
||||
exit 1
|
||||
}
|
||||
|
||||
|
||||
else
|
||||
|
||||
{
|
||||
Write-Output "No Virus Found, Defender is Healthy"
|
||||
Get-WinEvent -FilterHashtable @{LogName='Microsoft-Windows-Windows Defender/Operational';ID='1150','1001';StartTime=$TimeSpan}
|
||||
exit 0
|
||||
}
|
||||
|
||||
|
||||
Exit $LASTEXITCODE
|
||||
22
scripts/Win_Defender_Status_Report_LastYear.ps1
Normal file
22
scripts/Win_Defender_Status_Report_LastYear.ps1
Normal file
@@ -0,0 +1,22 @@
|
||||
# This will check for Malware, Antispyware, that Windows Defender is Healthy, last scan etc within the last 24 hours
|
||||
|
||||
$ErrorActionPreference = 'silentlycontinue'
|
||||
$TimeSpan = (Get-Date) - (New-TimeSpan -Day 365)
|
||||
|
||||
if (Get-WinEvent -FilterHashtable @{LogName = 'Microsoft-Windows-Windows Defender/Operational'; ID = '1116', '1118', '1015', '1006', '5010', '5012', '5001', '1123'; StartTime = $TimeSpan })
|
||||
{
|
||||
Write-Output "Virus Found or Issue with Defender"
|
||||
Get-WinEvent -FilterHashtable @{LogName = 'Microsoft-Windows-Windows Defender/Operational'; ID = '1116', '1118', '1015', '1006', '5010', '5012', '5001', '1123'; StartTime = $TimeSpan }
|
||||
exit 1
|
||||
}
|
||||
|
||||
|
||||
else
|
||||
{
|
||||
Write-Output "No Virus Found, Defender is Healthy"
|
||||
Get-WinEvent -FilterHashtable @{LogName = 'Microsoft-Windows-Windows Defender/Operational'; ID = '1150', '1001'; StartTime = $TimeSpan }
|
||||
exit 0
|
||||
}
|
||||
|
||||
|
||||
Exit $LASTEXITCODE
|
||||
@@ -1,20 +1,18 @@
|
||||
# Checks local disks for errors reported in event viewer within the last 24 hours
|
||||
|
||||
$ErrorActionPreference= 'silentlycontinue'
|
||||
$ErrorActionPreference = 'silentlycontinue'
|
||||
$TimeSpan = (Get-Date) - (New-TimeSpan -Day 1)
|
||||
if (Get-WinEvent -FilterHashtable @{LogName='system';ID='11','9','15','52','129','7','98';Level=2,3;ProviderName='*disk*','*storsvc*','*ntfs*';StartTime=$TimeSpan})
|
||||
|
||||
if (Get-WinEvent -FilterHashtable @{LogName = 'system'; ID = '11', '9', '15', '52', '129', '7', '98'; Level = 2, 3; ProviderName = '*disk*', '*storsvc*', '*ntfs*'; StartTime = $TimeSpan } -MaxEvents 10 | Where-Object -Property Message -Match Volume*)
|
||||
{
|
||||
Write-Output "Disk errors detected please investigate"
|
||||
Get-WinEvent -FilterHashtable @{LogName='system';ID='11','9','15','52','129','7','98';Level=2,3;ProviderName='*disk*','*storsvc*','*ntfs*';StartTime=$TimeSpan}
|
||||
exit 1
|
||||
Write-Output "Disk errors detected please investigate"
|
||||
Get-WinEvent -FilterHashtable @{LogName = 'system'; ID = '11', '9', '15', '52', '129', '7', '98'; Level = 2, 3; ProviderName = '*disk*', '*storsvc*', '*ntfs*'; StartTime = $TimeSpan }
|
||||
exit 1
|
||||
}
|
||||
|
||||
|
||||
else
|
||||
{
|
||||
Write-Output "Disks are Healthy"
|
||||
exit 0
|
||||
else {
|
||||
Write-Output "Disks are Healthy"
|
||||
exit 0
|
||||
}
|
||||
|
||||
|
||||
|
||||
1
scripts/Win_FileSystem_Enable_Long_Paths.bat
Normal file
1
scripts/Win_FileSystem_Enable_Long_Paths.bat
Normal file
@@ -0,0 +1 @@
|
||||
REG ADD "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem" /V LongPathsEnabled /T REG_DWORD /D 1 /F
|
||||
@@ -1 +0,0 @@
|
||||
Restart-Computer -ComputerName $env:COMPUTERNAME -Force
|
||||
@@ -1 +0,0 @@
|
||||
Stop-Computer -ComputerName $env:COMPUTERNAME -Force
|
||||
17
scripts/Win_Firewall_Check_Status.ps1
Normal file
17
scripts/Win_Firewall_Check_Status.ps1
Normal file
@@ -0,0 +1,17 @@
|
||||
$ErrorActionPreference = 'silentlycontinue'
|
||||
$fwenabled = (get-netfirewallprofile -policystore activestore).Enabled
|
||||
|
||||
if ($fwenabled.Contains('True')) {
|
||||
Write-Output "Firewall is Enabled"
|
||||
netsh advfirewall show currentprofile
|
||||
exit 0
|
||||
}
|
||||
|
||||
|
||||
else {
|
||||
Write-Host "Firewall is Disabled"
|
||||
exit 1
|
||||
}
|
||||
|
||||
|
||||
Exit $LASTEXITCODE
|
||||
5
scripts/Win_Get_Computer_Users.ps1
Normal file
5
scripts/Win_Get_Computer_Users.ps1
Normal file
@@ -0,0 +1,5 @@
|
||||
# This script return the list of all users and checks
|
||||
# if they are enabled or disabled
|
||||
|
||||
get-localuser | Select name,Enabled > $env:TEMP\users.txt
|
||||
Get-Content $env:TEMP\users.txt | foreach {Write-Output $_}
|
||||
154
scripts/Win_Hardware_Monitor_Get_Info.ps1
Normal file
154
scripts/Win_Hardware_Monitor_Get_Info.ps1
Normal file
@@ -0,0 +1,154 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
This powershell function gets information about the monitors attached to any computer. It uses EDID information provided by WMI. If this value is not specified it pulls the monitors of the computer that the script is being run on.
|
||||
.DESCRIPTION
|
||||
The function begins by looping through each computer specified. For each computer it gets a litst of monitors.
|
||||
It then gets all of the necessary data from each monitor object and converts and cleans the data and places it in a custom PSObject. It then adds
|
||||
the data to an array. At the end the array is displayed.
|
||||
.PARAMETER ComputerName
|
||||
Use this to specify the computer(s) which you'd like to retrieve information about monitors from.
|
||||
.EXAMPLE
|
||||
PS C:/> Get-Monitor.ps1 -ComputerName SSL1-F1102-1G2Z
|
||||
Manufacturer Model SerialNumber AttachedComputer
|
||||
------------ ----- ------------ ----------------
|
||||
HP HP E241i CN12345678 SSL1-F1102-1G2Z
|
||||
HP HP E241i CN91234567 SSL1-F1102-1G2Z
|
||||
HP HP E241i CN89123456 SSL1-F1102-1G2Z
|
||||
.EXAMPLE
|
||||
PS C:/> $Computers = @("SSL7-F108F-9D4Z","SSL1-F1102-1G2Z","SSA7-F1071-0T7F")
|
||||
PS C:/> Get-Monitor.ps1 -ComputerName $Computers
|
||||
Manufacturer Model SerialNumber AttachedComputer
|
||||
------------ ----- ------------ ----------------
|
||||
HP HP LA2405x CN12345678 SSL7-F108F-9D4Z
|
||||
HP HP E241i CN91234567 SSL1-F1102-1G2Z
|
||||
HP HP E241i CN89123456 SSL1-F1102-1G2Z
|
||||
HP HP E241i CN78912345 SSL1-F1102-1G2Z
|
||||
HP HP ZR22w CN67891234 SSA7-F1071-0T7F
|
||||
#>
|
||||
|
||||
|
||||
[CmdletBinding()]
|
||||
PARAM (
|
||||
[Parameter(ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)]
|
||||
[String[]]$ComputerName = $env:ComputerName
|
||||
)
|
||||
|
||||
#List of Manufacture Codes that could be pulled from WMI and their respective full names. Used for translating later down.
|
||||
$ManufacturerHash = @{
|
||||
"AAC" = "AcerView";
|
||||
"ACR" = "Acer";
|
||||
"AOC" = "AOC";
|
||||
"AIC" = "AG Neovo";
|
||||
"APP" = "Apple Computer";
|
||||
"AST" = "AST Research";
|
||||
"AUO" = "Asus";
|
||||
"BNQ" = "BenQ";
|
||||
"CMO" = "Acer";
|
||||
"CPL" = "Compal";
|
||||
"CPQ" = "Compaq";
|
||||
"CPT" = "Chunghwa Pciture Tubes, Ltd.";
|
||||
"CTX" = "CTX";
|
||||
"DEC" = "DEC";
|
||||
"DEL" = "Dell";
|
||||
"DPC" = "Delta";
|
||||
"DWE" = "Daewoo";
|
||||
"EIZ" = "EIZO";
|
||||
"ELS" = "ELSA";
|
||||
"ENC" = "EIZO";
|
||||
"EPI" = "Envision";
|
||||
"FCM" = "Funai";
|
||||
"FUJ" = "Fujitsu";
|
||||
"FUS" = "Fujitsu-Siemens";
|
||||
"GSM" = "LG Electronics";
|
||||
"GWY" = "Gateway 2000";
|
||||
"HEI" = "Hyundai";
|
||||
"HIT" = "Hyundai";
|
||||
"HSL" = "Hansol";
|
||||
"HTC" = "Hitachi/Nissei";
|
||||
"HWP" = "HP";
|
||||
"IBM" = "IBM";
|
||||
"ICL" = "Fujitsu ICL";
|
||||
"IVM" = "Iiyama";
|
||||
"KDS" = "Korea Data Systems";
|
||||
"LEN" = "Lenovo";
|
||||
"LGD" = "Asus";
|
||||
"LPL" = "Fujitsu";
|
||||
"MAX" = "Belinea";
|
||||
"MEI" = "Panasonic";
|
||||
"MEL" = "Mitsubishi Electronics";
|
||||
"MS_" = "Panasonic";
|
||||
"NAN" = "Nanao";
|
||||
"NEC" = "NEC";
|
||||
"NOK" = "Nokia Data";
|
||||
"NVD" = "Fujitsu";
|
||||
"OPT" = "Optoma";
|
||||
"PHL" = "Philips";
|
||||
"REL" = "Relisys";
|
||||
"SAN" = "Samsung";
|
||||
"SAM" = "Samsung";
|
||||
"SBI" = "Smarttech";
|
||||
"SGI" = "SGI";
|
||||
"SNY" = "Sony";
|
||||
"SRC" = "Shamrock";
|
||||
"SUN" = "Sun Microsystems";
|
||||
"SEC" = "Hewlett-Packard";
|
||||
"TAT" = "Tatung";
|
||||
"TOS" = "Toshiba";
|
||||
"TSB" = "Toshiba";
|
||||
"VSC" = "ViewSonic";
|
||||
"ZCM" = "Zenith";
|
||||
"UNK" = "Unknown";
|
||||
"_YV" = "Fujitsu";
|
||||
}
|
||||
|
||||
|
||||
#Takes each computer specified and runs the following code:
|
||||
ForEach ($Computer in $ComputerName) {
|
||||
|
||||
#Grabs the Monitor objects from WMI
|
||||
$Monitors = Get-WmiObject -Namespace "root\WMI" -Class "WMIMonitorID" -ComputerName $Computer -ErrorAction SilentlyContinue
|
||||
|
||||
#Creates an empty array to hold the data
|
||||
$Monitor_Array = @()
|
||||
|
||||
|
||||
#Takes each monitor object found and runs the following code:
|
||||
ForEach ($Monitor in $Monitors) {
|
||||
|
||||
#Grabs respective data and converts it from ASCII encoding and removes any trailing ASCII null values
|
||||
If ([System.Text.Encoding]::ASCII.GetString($Monitor.UserFriendlyName) -ne $null) {
|
||||
$Mon_Model = ([System.Text.Encoding]::ASCII.GetString($Monitor.UserFriendlyName)).Replace("$([char]0x0000)", "")
|
||||
}
|
||||
else {
|
||||
$Mon_Model = $null
|
||||
}
|
||||
$Mon_Serial_Number = ([System.Text.Encoding]::ASCII.GetString($Monitor.SerialNumberID)).Replace("$([char]0x0000)", "")
|
||||
$Mon_Attached_Computer = ($Monitor.PSComputerName).Replace("$([char]0x0000)", "")
|
||||
$Mon_Manufacturer = ([System.Text.Encoding]::ASCII.GetString($Monitor.ManufacturerName)).Replace("$([char]0x0000)", "")
|
||||
|
||||
#Filters out "non monitors". Place any of your own filters here. These two are all-in-one computers with built in displays. I don't need the info from these.
|
||||
If ($Mon_Model -like "*800 AIO*" -or $Mon_Model -like "*8300 AiO*") { Break }
|
||||
|
||||
#Sets a friendly name based on the hash table above. If no entry found sets it to the original 3 character code
|
||||
$Mon_Manufacturer_Friendly = $ManufacturerHash.$Mon_Manufacturer
|
||||
If ($Mon_Manufacturer_Friendly -eq $null) {
|
||||
$Mon_Manufacturer_Friendly = $Mon_Manufacturer
|
||||
}
|
||||
|
||||
#Creates a custom monitor object and fills it with 4 NoteProperty members and the respective data
|
||||
$Monitor_Obj = [PSCustomObject]@{
|
||||
Manufacturer = $Mon_Manufacturer_Friendly
|
||||
Model = $Mon_Model
|
||||
SerialNumber = $Mon_Serial_Number
|
||||
AttachedComputer = $Mon_Attached_Computer
|
||||
}
|
||||
|
||||
#Appends the object to the array
|
||||
$Monitor_Array += $Monitor_Obj
|
||||
|
||||
} #End ForEach Monitor
|
||||
|
||||
#Outputs the Array
|
||||
$Monitor_Array
|
||||
|
||||
} #End ForEach Computer
|
||||
26
scripts/Win_Hardware_RAM_Status.ps1
Normal file
26
scripts/Win_Hardware_RAM_Status.ps1
Normal file
@@ -0,0 +1,26 @@
|
||||
#Identifies Computer RAM capacity and status
|
||||
|
||||
[Cmdletbinding()]
|
||||
Param(
|
||||
[string]$Computername = "localhost"
|
||||
)
|
||||
cls
|
||||
$PysicalMemory = Get-WmiObject -class "win32_physicalmemory" -namespace "root\CIMV2" -ComputerName $Computername
|
||||
|
||||
Write-Host "RAM Modules:" -ForegroundColor Green
|
||||
$PysicalMemory | Format-Table Tag, BankLabel, @{n = "Capacity(GB)"; e = { $_.Capacity / 1GB } }, Manufacturer, PartNumber, Speed -AutoSize
|
||||
|
||||
Write-Host "Total Memory:" -ForegroundColor Green
|
||||
Write-Host "$((($PysicalMemory).Capacity | Measure-Object -Sum).Sum/1GB)GB"
|
||||
|
||||
$TotalSlots = ((Get-WmiObject -Class "win32_PhysicalMemoryArray" -namespace "root\CIMV2" -ComputerName $Computername).MemoryDevices | Measure-Object -Sum).Sum
|
||||
Write-Host "`nTotal Memory Slots:" -ForegroundColor Green
|
||||
Write-Host $TotalSlots
|
||||
|
||||
$UsedSlots = (($PysicalMemory) | Measure-Object).Count
|
||||
Write-Host "`nUsed Memory Slots:" -ForegroundColor Green
|
||||
Write-Host $UsedSlots
|
||||
|
||||
If ($UsedSlots -eq $TotalSlots) {
|
||||
Write-Host "All memory slots are filled up, none is empty!" -ForegroundColor Yellow
|
||||
}
|
||||
17
scripts/Win_Local_User_Created_Monitor.ps1
Normal file
17
scripts/Win_Local_User_Created_Monitor.ps1
Normal file
@@ -0,0 +1,17 @@
|
||||
$ErrorActionPreference= 'silentlycontinue'
|
||||
$TimeSpan = (Get-Date) - (New-TimeSpan -Day 1)
|
||||
if (Get-WinEvent -FilterHashtable @{LogName='security';ID='4720','4720','4728','4732','4756','4767';StartTime=$TimeSpan})
|
||||
{
|
||||
Write-Output "A change has been made to local users"
|
||||
Get-WinEvent -FilterHashtable @{LogName='security';ID='4720','4720','4728','4732','4756','4767';StartTime=$TimeSpan}
|
||||
exit 1
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
Write-Output "No changes all looks fine"
|
||||
exit 0
|
||||
}
|
||||
|
||||
|
||||
Exit $LASTEXITCODE
|
||||
13
scripts/Win_Network_DHCP_Set.bat
Normal file
13
scripts/Win_Network_DHCP_Set.bat
Normal file
@@ -0,0 +1,13 @@
|
||||
@echo off
|
||||
for /f "tokens=4-5 delims=. " %%i in ('ver') do set VERSION=%%i.%%j
|
||||
|
||||
if "%version%" == "6.1" (
|
||||
rem Windows 7
|
||||
netsh interface ip set address "Local Area Connection" dhcp
|
||||
netsh interface ip set dns "Local Area Connection" dhcp
|
||||
)
|
||||
if "%version%" == "10.0" (
|
||||
rem Windows 10
|
||||
netsh interface ip set address Ethernet dhcp
|
||||
netsh interface ip set dns Ethernet dhcp
|
||||
)
|
||||
39
scripts/Win_Network_DNS_Set_to_1.1.1.2.ps1
Normal file
39
scripts/Win_Network_DNS_Set_to_1.1.1.2.ps1
Normal file
@@ -0,0 +1,39 @@
|
||||
# Cloudflare Family DNS see https://blog.cloudflare.com/introducing-1-1-1-1-for-families/
|
||||
|
||||
$ErrorActionPreference = 'SilentlyContinue'
|
||||
|
||||
if ((Get-WmiObject -Class Win32_ComputerSystem).PartOfDomain){
|
||||
write-host "Domain member, we better not update the DNS!!"
|
||||
exit
|
||||
}
|
||||
|
||||
$PrimaryDNS = '1.1.1.2'
|
||||
$SecondaryDNS = '1.0.0.2'
|
||||
|
||||
$DNSServers = $PrimaryDNS,$SecondaryDNS
|
||||
|
||||
$NICs = Get-WMIObject Win32_NetworkAdapterConfiguration | where{$_.IPEnabled -eq "TRUE"}
|
||||
|
||||
function get-return-status {
|
||||
Param ($code)
|
||||
If ($code -eq 0) {
|
||||
return "Success."
|
||||
} elseif ($code -eq 1) {
|
||||
return "Success, but Restart Required."
|
||||
} else {
|
||||
return "Error with Code $($code)!"
|
||||
}
|
||||
}
|
||||
|
||||
Foreach($NIC in $NICs) {
|
||||
""
|
||||
"-------"
|
||||
"Attempting to modify DNS Servers for the following NIC:"
|
||||
$NIC
|
||||
$returnValue = $NIC.SetDNSServerSearchOrder($DNSServers).ReturnValue
|
||||
$response = get-return-status($returnValue)
|
||||
Write-Host "Setting DNS Servers to ${$NICs}...$($response)"
|
||||
$returnValue = $NIC.SetDynamicDNSRegistration("True").ReturnValue
|
||||
$response = get-return-status($returnValue)
|
||||
Write-Host "Setting Dynamic DNS Registration to True...$($response)"
|
||||
}
|
||||
8
scripts/Win_Network_IPv6_Disable.ps1
Normal file
8
scripts/Win_Network_IPv6_Disable.ps1
Normal file
@@ -0,0 +1,8 @@
|
||||
#CVE-2020-16898 | Windows TCP/IP Remote Code Execution Vulnerability
|
||||
#https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2020-16898
|
||||
|
||||
#Disable IPv6 on All Adapers
|
||||
Disable-NetAdapterBinding -Name "*" -ComponentID ms_tcpip6
|
||||
|
||||
#Confirm That all NIC's no longer have IPv6 Enabled
|
||||
(Get-NetAdapterBinding -Name '*' -ComponentID ms_tcpip6).Enabled
|
||||
3
scripts/Win_Network_Set_To_Private.ps1
Normal file
3
scripts/Win_Network_Set_To_Private.ps1
Normal file
@@ -0,0 +1,3 @@
|
||||
#This script sets current network profile to Private
|
||||
|
||||
$net = get-netconnectionprofile;Set-NetConnectionProfile -Name $net.Name -NetworkCategory Private
|
||||
1
scripts/Win_Power_Disable_Hibernation.bat
Normal file
1
scripts/Win_Power_Disable_Hibernation.bat
Normal file
@@ -0,0 +1 @@
|
||||
%SYSTEMROOT%\System32\powercfg.exe -H OFF
|
||||
11
scripts/Win_Power_RestartorShutdown.ps1
Normal file
11
scripts/Win_Power_RestartorShutdown.ps1
Normal file
@@ -0,0 +1,11 @@
|
||||
# This script will force restart computer. Add command paramter: shutdown to shutdown instead
|
||||
# Normal restart doesn't install updates before issuing
|
||||
|
||||
$param1 = $args[0]
|
||||
|
||||
if ($param1 -eq 'shutdown') {
|
||||
Stop-Computer -ComputerName $env:COMPUTERNAME -Force
|
||||
}
|
||||
else {
|
||||
Restart-Computer -ComputerName $env:COMPUTERNAME -Force
|
||||
}
|
||||
1
scripts/Win_RecycleBin_Empty.ps1
Normal file
1
scripts/Win_RecycleBin_Empty.ps1
Normal file
@@ -0,0 +1 @@
|
||||
Clear-RecycleBin -Force
|
||||
@@ -1,5 +1,5 @@
|
||||
#Windows Defender Exclusions for Tactical
|
||||
Add-MpPreference -ExclusionPath "C:\Program Files\Mesh Agent\*"
|
||||
Add-MpPreference -ExclusionPath "C:\Program Files\TacticalAgent\*"
|
||||
Add-MpPreference -ExclusionPath "C:\Windows\Temp\trmm\*"
|
||||
Add-MpPreference -ExclusionPath "C:\Windows\Temp\winagent-v*.exe"
|
||||
#Windows Defender Exclusions for Tactical
|
||||
Add-MpPreference -ExclusionPath "C:\Program Files\Mesh Agent\*"
|
||||
Add-MpPreference -ExclusionPath "C:\Program Files\TacticalAgent\*"
|
||||
Add-MpPreference -ExclusionPath "C:\Windows\Temp\trmm\*"
|
||||
Add-MpPreference -ExclusionPath "C:\Windows\Temp\winagent-v*.exe"
|
||||
1
scripts/Win_TRMM_Start_Menu_Delete_Shortcut.ps1
Normal file
1
scripts/Win_TRMM_Start_Menu_Delete_Shortcut.ps1
Normal file
@@ -0,0 +1 @@
|
||||
Remove-Item -Path "C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Tactical RMM Agent.lnk" -Force
|
||||
6
scripts/Win_TaskScheduler_Add_Task.ps1
Normal file
6
scripts/Win_TaskScheduler_Add_Task.ps1
Normal file
@@ -0,0 +1,6 @@
|
||||
# Add a task to Task Scheduler
|
||||
|
||||
$Trigger = New-ScheduledTaskTrigger -At 10:00am –Daily # Specify the trigger settings
|
||||
$User = "NT AUTHORITY\SYSTEM" # Specify the account to run the script
|
||||
$Action = New-ScheduledTaskAction -Execute "PowerShell.exe" -Argument "YOUR COMMAND HERE" # Specify what program to run and with its parameters
|
||||
Register-ScheduledTask -TaskName "SomeTaskName" -Trigger $Trigger -User $User -Action $Action -RunLevel Highest –Force # Specify the name of the task
|
||||
18
scripts/Win_Task_Scheduler_New_Items_Monitor.ps1
Normal file
18
scripts/Win_Task_Scheduler_New_Items_Monitor.ps1
Normal file
@@ -0,0 +1,18 @@
|
||||
$ErrorActionPreference= 'silentlycontinue'
|
||||
$TimeSpan = (Get-Date) - (New-TimeSpan -Day 1)
|
||||
if (Get-WinEvent -FilterHashtable @{LogName='Microsoft-Windows-TaskScheduler/Operational';ID='106';StartTime=$TimeSpan} | Where-Object -Property Message -notlike *$env:COMPUTERNAME*)
|
||||
{
|
||||
Write-Output "New Task Has Been Added"
|
||||
Get-WinEvent -FilterHashtable @{LogName='Microsoft-Windows-TaskScheduler/Operational';ID='106';StartTime=$TimeSpan}
|
||||
Get-WinEvent -FilterHashtable @{LogName='Microsoft-Windows-TaskScheduler/Operational';ID='141';StartTime=$TimeSpan}
|
||||
exit 1
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
Write-Output "No changes with Task Scheduler"
|
||||
exit 0
|
||||
}
|
||||
|
||||
|
||||
Exit $LASTEXITCODE
|
||||
22
scripts/Win_UAC_Check_Status.ps1
Normal file
22
scripts/Win_UAC_Check_Status.ps1
Normal file
@@ -0,0 +1,22 @@
|
||||
$ErrorActionPreference = 'silentlycontinue'
|
||||
$PSDenabled = (Get-ItemProperty HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System).PromptOnSecureDesktop
|
||||
$CPAenabled = (Get-ItemProperty HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System).ConsentPromptBehaviorAdmin
|
||||
|
||||
|
||||
if ($PSDenabled -Eq 1 -And $CPAenabled -Eq 5) {
|
||||
Write-Output "UAC is Enabled"
|
||||
exit 0
|
||||
}
|
||||
|
||||
elseif ($PSDenabled -Eq 1 -And $CPAenabled -Eq 2) {
|
||||
Write-Output "UAC is Enabled"
|
||||
exit 0
|
||||
}
|
||||
|
||||
else {
|
||||
Write-Output "UAC is Disabled"
|
||||
exit 1
|
||||
}
|
||||
|
||||
|
||||
Exit $LASTEXITCODE
|
||||
1
scripts/Win_USB_Disable_Access.bat
Normal file
1
scripts/Win_USB_Disable_Access.bat
Normal file
@@ -0,0 +1 @@
|
||||
reg add HKLM\SYSTEM\CurrentControlSet\Services\UsbStor /v "Start" /t REG_DWORD /d "4" /f
|
||||
1
scripts/Win_USB_Enable_Access.bat
Normal file
1
scripts/Win_USB_Enable_Access.bat
Normal file
@@ -0,0 +1 @@
|
||||
reg add HKLM\SYSTEM\CurrentControlSet\Services\UsbStor /v "Start" /t REG_DWORD /d "3" /f
|
||||
17
scripts/Win_User_Logged_in_with_Temp_Profile.ps1
Normal file
17
scripts/Win_User_Logged_in_with_Temp_Profile.ps1
Normal file
@@ -0,0 +1,17 @@
|
||||
$ErrorActionPreference= 'silentlycontinue'
|
||||
$TimeSpan = (Get-Date) - (New-TimeSpan -Day 1)
|
||||
if (Get-WinEvent -FilterHashtable @{LogName='Application';ID='1511';StartTime=$TimeSpan})
|
||||
{
|
||||
Write-Output "An account has been logged in with a Temporary profile"
|
||||
Get-WinEvent -FilterHashtable @{LogName='Application';ID='1511';StartTime=$TimeSpan}
|
||||
exit 1
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
Write-Output "All looks fine"
|
||||
exit 0
|
||||
}
|
||||
|
||||
|
||||
Exit $LASTEXITCODE
|
||||
@@ -89,7 +89,6 @@ module.exports = function () {
|
||||
],
|
||||
config: {
|
||||
loadingBar: {
|
||||
color: "red",
|
||||
size: "4px"
|
||||
},
|
||||
notify: {
|
||||
|
||||
@@ -303,17 +303,18 @@
|
||||
/>
|
||||
</q-td>
|
||||
<q-td key="checks-status" :props="props">
|
||||
<q-icon v-if="props.row.maintenance_mode" name="fas fa-check-double" size="1.2em" color="warning">
|
||||
<q-icon v-if="props.row.maintenance_mode" name="construction" size="1.2em" color="green">
|
||||
<q-tooltip>Maintenance Mode Enabled</q-tooltip>
|
||||
</q-icon>
|
||||
<q-icon
|
||||
v-else-if="props.row.checks.has_failing_checks"
|
||||
name="fas fa-check-double"
|
||||
size="1.2em"
|
||||
color="negative"
|
||||
>
|
||||
<q-icon v-else-if="props.row.checks.failing > 0" name="fas fa-check-double" size="1.2em" color="negative">
|
||||
<q-tooltip>Checks failing</q-tooltip>
|
||||
</q-icon>
|
||||
<q-icon v-else-if="props.row.checks.warning > 0" name="fas fa-check-double" size="1.2em" color="warning">
|
||||
<q-tooltip>Checks warning</q-tooltip>
|
||||
</q-icon>
|
||||
<q-icon v-else-if="props.row.checks.info > 0" name="fas fa-check-double" size="1.2em" color="info">
|
||||
<q-tooltip>Checks info</q-tooltip>
|
||||
</q-icon>
|
||||
<q-icon v-else name="fas fa-check-double" size="1.2em" color="positive">
|
||||
<q-tooltip>Checks passing</q-tooltip>
|
||||
</q-icon>
|
||||
@@ -384,7 +385,7 @@
|
||||
</q-dialog>
|
||||
</div>
|
||||
<!-- send command modal -->
|
||||
<q-dialog v-model="showSendCommand">
|
||||
<q-dialog v-model="showSendCommand" persistent>
|
||||
<SendCommand @close="showSendCommand = false" :pk="selectedAgentPk" />
|
||||
</q-dialog>
|
||||
<!-- agent recovery modal -->
|
||||
@@ -392,7 +393,7 @@
|
||||
<AgentRecovery @close="showAgentRecovery = false" :pk="selectedAgentPk" />
|
||||
</q-dialog>
|
||||
<!-- run script modal -->
|
||||
<q-dialog v-model="showRunScript">
|
||||
<q-dialog v-model="showRunScript" persistent>
|
||||
<RunScript @close="showRunScript = false" :pk="selectedAgentPk" />
|
||||
</q-dialog>
|
||||
</div>
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
<q-tab name="base_board" label="Motherboard" />
|
||||
<q-tab name="comp_sys_prod" label="Computer System Product" />
|
||||
<q-tab name="network_config" label="Network Config" />
|
||||
<q-tab name="graphics" label="Graphics" />
|
||||
<q-tab name="desktop_monitor" label="Monitors" />
|
||||
<q-tab name="network_adapter" label="Network Adapters" />
|
||||
</q-tabs>
|
||||
@@ -63,6 +64,9 @@
|
||||
<q-tab-panel name="desktop_monitor">
|
||||
<WmiDetail :info="desktop_monitor" />
|
||||
</q-tab-panel>
|
||||
<q-tab-panel name="graphics">
|
||||
<WmiDetail :info="graphics" />
|
||||
</q-tab-panel>
|
||||
<q-tab-panel name="network_adapter">
|
||||
<WmiDetail :info="network_adapter" />
|
||||
</q-tab-panel>
|
||||
@@ -125,6 +129,9 @@ export default {
|
||||
network_adapter() {
|
||||
return this.assets.network_adapter;
|
||||
},
|
||||
graphics() {
|
||||
return this.assets.graphics;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
<template v-else>
|
||||
<q-table
|
||||
dense
|
||||
:table-class="{ 'table-bgcolor': !$q.dark.isActive, 'table-bgcolor-dark': $q.dark.isActive }"
|
||||
class="tabs-tbl-sticky"
|
||||
:style="{ 'max-height': tabsTableHeight }"
|
||||
:data="tasks"
|
||||
|
||||
@@ -124,6 +124,13 @@
|
||||
<q-item-section>Delete</q-item-section>
|
||||
</q-item>
|
||||
<q-separator></q-separator>
|
||||
<q-item clickable v-close-popup @click="resetCheck(props.row.id)">
|
||||
<q-item-section side>
|
||||
<q-icon name="info" />
|
||||
</q-item-section>
|
||||
<q-item-section>Reset Check Status</q-item-section>
|
||||
</q-item>
|
||||
<q-separator></q-separator>
|
||||
<q-item clickable v-close-popup>
|
||||
<q-item-section>Close</q-item-section>
|
||||
</q-item>
|
||||
@@ -431,7 +438,25 @@ export default {
|
||||
});
|
||||
});
|
||||
},
|
||||
resetCheck(check) {
|
||||
const data = {
|
||||
check_reset: true,
|
||||
status: "passing",
|
||||
};
|
||||
|
||||
axios
|
||||
.patch(`/checks/${check}/check/`, data)
|
||||
.then(r => {
|
||||
this.$emit("refreshEdit");
|
||||
this.$store.dispatch("loadChecks", this.selectedAgentPk);
|
||||
this.notifySuccess("The check was reset");
|
||||
})
|
||||
.catch(e => {
|
||||
this.notifyError("There was an issue resetting the check");
|
||||
});
|
||||
},
|
||||
onRefresh(id) {
|
||||
this.$emit("refreshEdit");
|
||||
this.$store.dispatch("loadChecks", id);
|
||||
this.$store.dispatch("loadAutomatedTasks", id);
|
||||
},
|
||||
|
||||
@@ -89,6 +89,10 @@
|
||||
<q-item clickable v-close-popup @click="showEditCoreSettingsModal = true">
|
||||
<q-item-section>Global Settings</q-item-section>
|
||||
</q-item>
|
||||
<!-- code sign -->
|
||||
<q-item v-if="!noCodeSigning" clickable v-close-popup @click="showCodeSign = true">
|
||||
<q-item-section>Code Signing</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</q-menu>
|
||||
</q-btn>
|
||||
@@ -179,6 +183,10 @@
|
||||
<q-dialog v-model="showServerMaintenance">
|
||||
<ServerMaintenance @close="showMaintenance = false" />
|
||||
</q-dialog>
|
||||
<!-- Code Sign -->
|
||||
<q-dialog v-model="showCodeSign">
|
||||
<CodeSign @close="showCodeSign = false" />
|
||||
</q-dialog>
|
||||
</q-bar>
|
||||
</div>
|
||||
</template>
|
||||
@@ -201,6 +209,7 @@ import AuditManager from "@/components/AuditManager";
|
||||
import BulkAction from "@/components/modals/agents/BulkAction";
|
||||
import Deployment from "@/components/Deployment";
|
||||
import ServerMaintenance from "@/components/modals/core/ServerMaintenance";
|
||||
import CodeSign from "@/components/modals/coresettings/CodeSign";
|
||||
|
||||
export default {
|
||||
name: "FileBar",
|
||||
@@ -217,6 +226,7 @@ export default {
|
||||
BulkAction,
|
||||
Deployment,
|
||||
ServerMaintenance,
|
||||
CodeSign,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -233,8 +243,14 @@ export default {
|
||||
showDeployment: false,
|
||||
showDebugLog: false,
|
||||
showScriptManager: false,
|
||||
showCodeSign: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
noCodeSigning() {
|
||||
return this.$store.state.noCodeSign;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
showBulkActionModal(mode) {
|
||||
this.bulkMode = mode;
|
||||
|
||||
@@ -283,10 +283,18 @@
|
||||
<q-tooltip> Batch </q-tooltip>
|
||||
</q-icon>
|
||||
</q-td>
|
||||
<!-- name -->
|
||||
<q-td>{{ props.row.name }}</q-td>
|
||||
<!-- args -->
|
||||
<q-td>
|
||||
<span v-if="props.row.args.length > 0">{{ props.row.args }}</span>
|
||||
<span v-if="props.row.args.length > 0">
|
||||
{{ truncateText(props.row.args.toString()) }}
|
||||
<q-tooltip v-if="props.row.args.toString().length >= 60" content-style="font-size: 12px">
|
||||
{{ props.row.args }}
|
||||
</q-tooltip>
|
||||
</span>
|
||||
</q-td>
|
||||
|
||||
<q-td>{{ props.row.category }}</q-td>
|
||||
<q-td>
|
||||
{{ truncateText(props.row.description) }}
|
||||
@@ -363,6 +371,7 @@ export default {
|
||||
label: "Default Args",
|
||||
field: "args",
|
||||
align: "left",
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
name: "category",
|
||||
@@ -516,11 +525,9 @@ export default {
|
||||
},
|
||||
categories() {
|
||||
let list = [];
|
||||
this.scripts.forEach(script => {
|
||||
this.visibleScripts.forEach(script => {
|
||||
if (!!script.category && !list.includes(script.category)) {
|
||||
if (script.category !== "Community") {
|
||||
list.push(script.category);
|
||||
}
|
||||
list.push(script.category);
|
||||
}
|
||||
});
|
||||
return list;
|
||||
@@ -538,8 +545,7 @@ export default {
|
||||
let scriptsTemp = Object.assign([], this.visibleScripts);
|
||||
let categoriesTemp = Object.assign([], this.categories);
|
||||
|
||||
// add Community and Unassigned values and categories array
|
||||
if (this.showCommunityScripts) categoriesTemp.push("Community");
|
||||
// add Unassigned category
|
||||
categoriesTemp.push("Unassigned");
|
||||
|
||||
const sorted = categoriesTemp.sort();
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
<SummaryTab />
|
||||
</q-tab-panel>
|
||||
<q-tab-panel name="checks" class="q-pb-xs q-pt-none">
|
||||
<ChecksTab />
|
||||
<ChecksTab @refreshEdit="$emit('refreshEdit')" />
|
||||
</q-tab-panel>
|
||||
<q-tab-panel name="tasks" class="q-pb-xs q-pt-none">
|
||||
<AutomatedTasksTab />
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<q-btn class="q-mr-sm" dense flat push icon="refresh" @click="refreshSummary" />
|
||||
<span>
|
||||
<b>{{ summary.hostname }}</b>
|
||||
<span v-if="summary.maintenance_mode"> • <q-badge color="warning"> Maintenance Mode </q-badge> </span>
|
||||
<span v-if="summary.maintenance_mode"> • <q-badge color="green"> Maintenance Mode </q-badge> </span>
|
||||
• {{ summary.operating_system }} • Agent v{{ summary.version }}
|
||||
</span>
|
||||
<hr />
|
||||
@@ -39,6 +39,13 @@
|
||||
</q-item-section>
|
||||
<q-item-section>{{ disk }}</q-item-section>
|
||||
</q-item>
|
||||
<!-- graphics -->
|
||||
<q-item>
|
||||
<q-item-section avatar>
|
||||
<q-icon name="fas fa-tv" />
|
||||
</q-item-section>
|
||||
<q-item-section>{{ summary.graphics }}</q-item-section>
|
||||
</q-item>
|
||||
<q-item>
|
||||
<q-item-section avatar>
|
||||
<q-icon name="fas fa-globe-americas" />
|
||||
@@ -65,7 +72,24 @@
|
||||
<q-avatar size="lg" square icon="cancel" color="red" text-color="white" />
|
||||
<small>{{ summary.checks.failing }} checks failing</small>
|
||||
</q-chip>
|
||||
<span v-if="awaitingSync(summary.checks.total, summary.checks.passing, summary.checks.failing)"
|
||||
<q-chip v-if="summary.checks.warning" square size="lg">
|
||||
<q-avatar size="lg" square icon="warning" color="warning" text-color="white" />
|
||||
<small>{{ summary.checks.warning }} checks warning</small>
|
||||
</q-chip>
|
||||
<q-chip v-if="summary.checks.info" square size="lg">
|
||||
<q-avatar size="lg" square icon="info" color="info" text-color="white" />
|
||||
<small>{{ summary.checks.info }} checks info</small>
|
||||
</q-chip>
|
||||
<span
|
||||
v-if="
|
||||
awaitingSync(
|
||||
summary.checks.total,
|
||||
summary.checks.passing,
|
||||
summary.checks.failing,
|
||||
summary.checks.warning,
|
||||
summary.checks.info
|
||||
)
|
||||
"
|
||||
>{{ summary.checks.total }} checks awaiting first synchronization</span
|
||||
>
|
||||
</template>
|
||||
@@ -98,8 +122,8 @@ export default {
|
||||
return {};
|
||||
},
|
||||
methods: {
|
||||
awaitingSync(total, passing, failing) {
|
||||
return total !== 0 && passing === 0 && failing === 0 ? true : false;
|
||||
awaitingSync(total, passing, failing, warning, info) {
|
||||
return total !== 0 && passing === 0 && failing === 0 && warning === 0 && info === 0;
|
||||
},
|
||||
refreshSummary() {
|
||||
this.$q.loading.show();
|
||||
|
||||
@@ -47,7 +47,11 @@
|
||||
<q-card-section>
|
||||
<div class="q-gutter-sm">
|
||||
<q-checkbox v-model="rdp" dense label="Enable RDP" />
|
||||
<q-checkbox v-model="ping" dense label="Enable Ping" />
|
||||
<q-checkbox v-model="ping" dense label="Enable Ping">
|
||||
<q-tooltip>
|
||||
Enable ICMP echo requests in the local firewall
|
||||
</q-tooltip>
|
||||
</q-checkbox>
|
||||
<q-checkbox v-model="power" dense v-show="agenttype === 'workstation'" label="Disable sleep/hibernate" />
|
||||
</div>
|
||||
</q-card-section>
|
||||
|
||||
@@ -93,7 +93,7 @@
|
||||
<div class="col-3">Day of month to run:</div>
|
||||
<div class="col-4"></div>
|
||||
<q-select
|
||||
:disable="winupdatepolicy.run_time_frequency === 'inherit'"
|
||||
v-show="winupdatepolicy.run_time_frequency !== 'inherit'"
|
||||
dense
|
||||
class="col-5"
|
||||
outlined
|
||||
@@ -103,11 +103,10 @@
|
||||
map-options
|
||||
/>
|
||||
</q-card-section>
|
||||
<q-card-section class="row">
|
||||
<q-card-section class="row" v-show="winupdatepolicy.run_time_frequency !== 'inherit'">
|
||||
<div class="col-3">Scheduled Time:</div>
|
||||
<div class="col-4"></div>
|
||||
<q-select
|
||||
:disable="winupdatepolicy.run_time_frequency === 'inherit'"
|
||||
dense
|
||||
class="col-5"
|
||||
outlined
|
||||
@@ -118,51 +117,17 @@
|
||||
/>
|
||||
</q-card-section>
|
||||
<q-card-section
|
||||
v-if="winupdatepolicy.run_time_frequency === 'daily' || winupdatepolicy.run_time_frequency === 'inherit'"
|
||||
v-if="winupdatepolicy.run_time_frequency === 'daily'"
|
||||
v-show="winupdatepolicy.run_time_frequency !== 'inherit'"
|
||||
>
|
||||
<div class="q-gutter-sm">
|
||||
<q-checkbox
|
||||
:disable="winupdatepolicy.run_time_frequency === 'inherit'"
|
||||
v-model="winupdatepolicy.run_time_days"
|
||||
:val="1"
|
||||
label="Monday"
|
||||
/>
|
||||
<q-checkbox
|
||||
:disable="winupdatepolicy.run_time_frequency === 'inherit'"
|
||||
v-model="winupdatepolicy.run_time_days"
|
||||
:val="2"
|
||||
label="Tuesday"
|
||||
/>
|
||||
<q-checkbox
|
||||
:disable="winupdatepolicy.run_time_frequency === 'inherit'"
|
||||
v-model="winupdatepolicy.run_time_days"
|
||||
:val="3"
|
||||
label="Wednesday"
|
||||
/>
|
||||
<q-checkbox
|
||||
:disable="winupdatepolicy.run_time_frequency === 'inherit'"
|
||||
v-model="winupdatepolicy.run_time_days"
|
||||
:val="4"
|
||||
label="Thursday"
|
||||
/>
|
||||
<q-checkbox
|
||||
:disable="winupdatepolicy.run_time_frequency === 'inherit'"
|
||||
v-model="winupdatepolicy.run_time_days"
|
||||
:val="5"
|
||||
label="Friday"
|
||||
/>
|
||||
<q-checkbox
|
||||
:disable="winupdatepolicy.run_time_frequency === 'inherit'"
|
||||
v-model="winupdatepolicy.run_time_days"
|
||||
:val="6"
|
||||
label="Saturday"
|
||||
/>
|
||||
<q-checkbox
|
||||
:disable="winupdatepolicy.run_time_frequency === 'inherit'"
|
||||
v-model="winupdatepolicy.run_time_days"
|
||||
:val="0"
|
||||
label="Sunday"
|
||||
/>
|
||||
<q-checkbox v-model="winupdatepolicy.run_time_days" :val="1" label="Monday" />
|
||||
<q-checkbox v-model="winupdatepolicy.run_time_days" :val="2" label="Tuesday" />
|
||||
<q-checkbox v-model="winupdatepolicy.run_time_days" :val="3" label="Wednesday" />
|
||||
<q-checkbox v-model="winupdatepolicy.run_time_days" :val="4" label="Thursday" />
|
||||
<q-checkbox v-model="winupdatepolicy.run_time_days" :val="5" label="Friday" />
|
||||
<q-checkbox v-model="winupdatepolicy.run_time_days" :val="6" label="Saturday" />
|
||||
<q-checkbox v-model="winupdatepolicy.run_time_days" :val="0" label="Sunday" />
|
||||
</div>
|
||||
</q-card-section>
|
||||
<!-- Reboot After Installation -->
|
||||
@@ -189,20 +154,15 @@
|
||||
<q-checkbox v-model="winupdatepolicy.reprocess_failed_inherit" label="Inherit failed patch settings" />
|
||||
</div>
|
||||
</q-card-section>
|
||||
<q-card-section class="row">
|
||||
<q-card-section class="row" v-show="!winupdatepolicy.reprocess_failed_inherit">
|
||||
<div class="col-5">
|
||||
<q-checkbox
|
||||
:disable="winupdatepolicy.reprocess_failed_inherit"
|
||||
v-model="winupdatepolicy.reprocess_failed"
|
||||
label="Reprocess failed patches"
|
||||
/>
|
||||
<q-checkbox v-model="winupdatepolicy.reprocess_failed" label="Reprocess failed patches" />
|
||||
</div>
|
||||
|
||||
<div class="col-3">
|
||||
<q-input
|
||||
dense
|
||||
v-model.number="winupdatepolicy.reprocess_failed_times"
|
||||
:disable="winupdatepolicy.reprocess_failed_inherit"
|
||||
type="number"
|
||||
filled
|
||||
label="Times"
|
||||
@@ -210,11 +170,7 @@
|
||||
/>
|
||||
</div>
|
||||
<div class="col-3"></div>
|
||||
<q-checkbox
|
||||
v-model="winupdatepolicy.email_if_fail"
|
||||
:disable="winupdatepolicy.reprocess_failed_inherit"
|
||||
label="Send an email when patch installation fails"
|
||||
/>
|
||||
<q-checkbox v-model="winupdatepolicy.email_if_fail" label="Send an email when patch installation fails" />
|
||||
</q-card-section>
|
||||
<q-card-actions align="left" v-if="policy">
|
||||
<q-btn label="Submit" color="primary" @click="submit" />
|
||||
@@ -249,6 +205,7 @@ export default {
|
||||
run_time_hour: 3,
|
||||
run_time_frequency: "daily",
|
||||
run_time_days: [],
|
||||
run_time_day: 1,
|
||||
reboot_after_install: "never",
|
||||
reprocess_failed_inherit: false,
|
||||
reprocess_failed: false,
|
||||
|
||||
@@ -55,6 +55,7 @@ export default {
|
||||
return {
|
||||
siteOptions: [],
|
||||
selectedSite: null,
|
||||
agentCount: 0,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
@@ -112,6 +113,7 @@ export default {
|
||||
},
|
||||
getSites() {
|
||||
this.$axios.get("/clients/clients/").then(r => {
|
||||
this.agentCount = this.getAgentCount(r.data, this.type, this.object.id);
|
||||
r.data.forEach(client => {
|
||||
// remove client that is being deleted from options
|
||||
if (this.type === "client") {
|
||||
|
||||
67
web/src/components/modals/coresettings/CodeSign.vue
Normal file
67
web/src/components/modals/coresettings/CodeSign.vue
Normal file
@@ -0,0 +1,67 @@
|
||||
<template>
|
||||
<q-card style="min-width: 85vh">
|
||||
<q-card-section class="row items-center">
|
||||
<div class="text-h6">Code Signing</div>
|
||||
<q-space />
|
||||
<q-btn icon="close" flat round dense v-close-popup />
|
||||
</q-card-section>
|
||||
<q-form @submit.prevent="editToken">
|
||||
<q-card-section class="row">
|
||||
<div class="col-2">Token:</div>
|
||||
<div class="col-1"></div>
|
||||
<q-input
|
||||
outlined
|
||||
dense
|
||||
v-model="settings.token"
|
||||
class="col-9 q-pa-none"
|
||||
:rules="[val => !!val || 'Token is required']"
|
||||
/>
|
||||
</q-card-section>
|
||||
<q-card-section class="row items-center">
|
||||
<q-btn label="Save" color="primary" type="submit" />
|
||||
</q-card-section>
|
||||
</q-form>
|
||||
</q-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import mixins from "@/mixins/mixins";
|
||||
export default {
|
||||
name: "CodeSign",
|
||||
mixins: [mixins],
|
||||
data() {
|
||||
return {
|
||||
settings: {
|
||||
token: "",
|
||||
},
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
getToken() {
|
||||
this.$axios
|
||||
.get("/core/codesign/")
|
||||
.then(r => {
|
||||
this.settings = r.data;
|
||||
})
|
||||
.catch(e => this.notifyError(e.response.data));
|
||||
},
|
||||
editToken() {
|
||||
this.$q.loading.show();
|
||||
this.$axios
|
||||
.patch("/core/codesign/", this.settings)
|
||||
.then(r => {
|
||||
this.$q.loading.hide();
|
||||
this.notifySuccess(r.data);
|
||||
this.$emit("close");
|
||||
})
|
||||
.catch(e => {
|
||||
this.$q.loading.hide();
|
||||
this.notifyError(e.response.data, 4000);
|
||||
});
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.getToken();
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -46,6 +46,18 @@
|
||||
class="col-4"
|
||||
/>
|
||||
</q-card-section>
|
||||
<q-card-section class="row">
|
||||
<div class="col-4">Loading Bar Color:</div>
|
||||
<div class="col-4"></div>
|
||||
<q-select
|
||||
outlined
|
||||
dense
|
||||
options-dense
|
||||
v-model="loading_bar_color"
|
||||
:options="loadingBarColors"
|
||||
class="col-4"
|
||||
/>
|
||||
</q-card-section>
|
||||
<q-card-section class="row">
|
||||
<div class="col-2">Client Sort:</div>
|
||||
<div class="col-2"></div>
|
||||
@@ -73,6 +85,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { loadingBarColors } from "@/mixins/data";
|
||||
import mixins from "@/mixins/mixins";
|
||||
|
||||
export default {
|
||||
@@ -80,11 +93,13 @@ export default {
|
||||
mixins: [mixins],
|
||||
data() {
|
||||
return {
|
||||
loadingBarColors,
|
||||
agentDblClickAction: "",
|
||||
defaultAgentTblTab: "",
|
||||
clientTreeSort: "",
|
||||
tab: "ui",
|
||||
splitterModel: 20,
|
||||
loading_bar_color: "",
|
||||
clientTreeSortOptions: [
|
||||
{
|
||||
label: "Sort alphabetically, moving failing clients to the top",
|
||||
@@ -131,6 +146,7 @@ export default {
|
||||
this.agentDblClickAction = r.data.dbl_click_action;
|
||||
this.defaultAgentTblTab = r.data.default_agent_tbl_tab;
|
||||
this.clientTreeSort = r.data.client_tree_sort;
|
||||
this.loading_bar_color = r.data.loading_bar_color;
|
||||
});
|
||||
},
|
||||
editUserPrefs() {
|
||||
@@ -138,6 +154,7 @@ export default {
|
||||
agent_dblclick_action: this.agentDblClickAction,
|
||||
default_agent_tbl_tab: this.defaultAgentTblTab,
|
||||
client_tree_sort: this.clientTreeSort,
|
||||
loading_bar_color: this.loading_bar_color,
|
||||
};
|
||||
this.$axios.patch("/accounts/users/ui/", data).then(r => {
|
||||
this.notifySuccess("Preferences were saved!");
|
||||
|
||||
@@ -20,21 +20,6 @@
|
||||
label="Select script"
|
||||
map-options
|
||||
emit-value
|
||||
/>
|
||||
</q-card-section>
|
||||
<q-card-section>
|
||||
<q-select
|
||||
dense
|
||||
label="Script Arguments (press Enter after typing each argument)"
|
||||
filled
|
||||
v-model="localTask.script_args"
|
||||
use-input
|
||||
use-chips
|
||||
multiple
|
||||
hide-dropdown-icon
|
||||
input-debounce="0"
|
||||
new-value-mode="add"
|
||||
@input="setScriptDefaults"
|
||||
>
|
||||
<template v-slot:option="scope">
|
||||
<q-item v-if="!scope.opt.category" v-bind="scope.itemProps" v-on="scope.itemEvents" class="q-pl-lg">
|
||||
@@ -48,6 +33,21 @@
|
||||
</template>
|
||||
</q-select>
|
||||
</q-card-section>
|
||||
<q-card-section>
|
||||
<q-select
|
||||
dense
|
||||
label="Script Arguments (press Enter after typing each argument)"
|
||||
filled
|
||||
v-model="localTask.script_args"
|
||||
use-input
|
||||
use-chips
|
||||
multiple
|
||||
hide-dropdown-icon
|
||||
input-debounce="0"
|
||||
new-value-mode="add"
|
||||
@input="setScriptDefaults"
|
||||
/>
|
||||
</q-card-section>
|
||||
<q-card-section>
|
||||
<q-input
|
||||
:rules="[val => !!val || '*Required']"
|
||||
@@ -55,6 +55,7 @@
|
||||
dense
|
||||
v-model="localTask.name"
|
||||
label="Descriptive name of task"
|
||||
class="q-pb-none"
|
||||
/>
|
||||
</q-card-section>
|
||||
<q-card-section>
|
||||
@@ -77,6 +78,7 @@
|
||||
v-model.number="localTask.timeout"
|
||||
type="number"
|
||||
label="Maximum permitted execution time (seconds)"
|
||||
class="q-pb-none"
|
||||
/>
|
||||
</q-card-section>
|
||||
<q-card-actions align="right">
|
||||
|
||||
@@ -59,4 +59,6 @@ const monthDays = [
|
||||
"31"
|
||||
]
|
||||
|
||||
export { scheduledTimes, monthDays };
|
||||
const loadingBarColors = ["red", "pink", "purple", "deep-purple", "indigo", "blue", "light-blue", "cyan", "teal", "green", "light-green", "lime", "yellow", "amber", "orange", "deep-orange", "brown", "grey", "blue-grey"]
|
||||
|
||||
export { scheduledTimes, monthDays, loadingBarColors };
|
||||
|
||||
@@ -147,6 +147,19 @@ export default {
|
||||
this.notifyError("There was an issue getting Custom Fields");
|
||||
});
|
||||
},
|
||||
getAgentCount(data, type, id) {
|
||||
if (type === "client") {
|
||||
return data.find(i => id === i.id).agent_count
|
||||
} else {
|
||||
const sites = data.map(i => i.sites)
|
||||
for (let i of sites) {
|
||||
for (let k of i) {
|
||||
if (k.id === id) return k.agent_count;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
},
|
||||
formatCustomFields(fields, values) {
|
||||
let tempArray = [];
|
||||
|
||||
|
||||
@@ -33,8 +33,13 @@ export default function () {
|
||||
agentDblClickAction: "",
|
||||
defaultAgentTblTab: "server",
|
||||
clientTreeSort: "alphafail",
|
||||
clientTreeSplitter: 11,
|
||||
noCodeSign: false,
|
||||
},
|
||||
getters: {
|
||||
clientTreeSplitterModel(state) {
|
||||
return state.clientTreeSplitter;
|
||||
},
|
||||
loggedIn(state) {
|
||||
return state.token !== null;
|
||||
},
|
||||
@@ -132,6 +137,9 @@ export default function () {
|
||||
agentHeight <= 15.0 ? state.tableHeight = "15vh" : state.tableHeight = `${agentHeight}vh`;
|
||||
tabsHeight <= 15.0 ? state.tabHeight = "15vh" : state.tabHeight = `${tabsHeight}vh`;
|
||||
},
|
||||
SET_CLIENT_SPLITTER(state, val) {
|
||||
state.clientTreeSplitter = val;
|
||||
},
|
||||
SET_NOTES(state, notes) {
|
||||
state.notes = notes;
|
||||
},
|
||||
@@ -146,9 +154,17 @@ export default function () {
|
||||
},
|
||||
SET_CLIENT_TREE_SORT(state, val) {
|
||||
state.clientTreeSort = val
|
||||
},
|
||||
SET_NO_CODE_SIGN(state, val) {
|
||||
state.noCodeSign = val
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
setClientTreeSplitter(context, val) {
|
||||
axios.patch("/accounts/users/ui/", { client_tree_splitter: Math.trunc(val) }).then(r => {
|
||||
context.commit("SET_CLIENT_SPLITTER", val)
|
||||
})
|
||||
},
|
||||
setShowCommunityScripts(context, data) {
|
||||
axios.patch("/accounts/users/ui/", { show_community_scripts: data }).then(r => {
|
||||
context.commit("setShowCommunityScripts", data)
|
||||
@@ -244,8 +260,9 @@ export default function () {
|
||||
alert_template: site.alert_template
|
||||
}
|
||||
|
||||
if (site.maintenance_mode) { siteNode["color"] = "warning" }
|
||||
else if (site.failing_checks) { siteNode["color"] = "negative" }
|
||||
if (site.maintenance_mode) { siteNode["color"] = "green" }
|
||||
else if (site.failing_checks.error) { siteNode["color"] = "negative" }
|
||||
else if (site.failing_checks.warning) { siteNode["color"] = "warning" }
|
||||
|
||||
childSites.push(siteNode);
|
||||
}
|
||||
@@ -262,8 +279,9 @@ export default function () {
|
||||
children: childSites
|
||||
}
|
||||
|
||||
if (client.maintenance_mode) clientNode["color"] = "warning"
|
||||
else if (client.failing_checks) clientNode["color"] = "negative"
|
||||
if (client.maintenance_mode) clientNode["color"] = "green"
|
||||
else if (client.failing_checks.error) { clientNode["color"] = "negative" }
|
||||
else if (client.failing_checks.warning) { clientNode["color"] = "warning" }
|
||||
|
||||
output.push(clientNode);
|
||||
}
|
||||
@@ -271,7 +289,9 @@ export default function () {
|
||||
|
||||
if (state.clientTreeSort === "alphafail") {
|
||||
// move failing clients to the top
|
||||
const sortedByFailing = output.sort(a => a.color === "negative" ? -1 : 1);
|
||||
const failing = output.filter(i => i.color === "negative" || i.color === "warning");
|
||||
const ok = output.filter(i => i.color !== "negative" && i.color !== "warning");
|
||||
const sortedByFailing = [...failing, ...ok];
|
||||
commit("loadTree", sortedByFailing);
|
||||
} else {
|
||||
commit("loadTree", output);
|
||||
|
||||
@@ -90,7 +90,7 @@
|
||||
|
||||
<q-page-container>
|
||||
<FileBar />
|
||||
<q-splitter v-model="outsideModel">
|
||||
<q-splitter v-model="clientTreeSplitter">
|
||||
<template v-slot:before>
|
||||
<div v-if="!treeReady" class="q-pa-sm q-gutter-sm text-center" style="height: 30vh">
|
||||
<q-spinner size="40px" color="primary" />
|
||||
@@ -343,7 +343,7 @@
|
||||
<q-avatar color="primary" text-color="white" size="30px" icon="drag_indicator" />
|
||||
</template>
|
||||
<template v-slot:after>
|
||||
<SubTableTabs />
|
||||
<SubTableTabs @refreshEdit="refreshEntireSite" />
|
||||
</template>
|
||||
</q-splitter>
|
||||
</template>
|
||||
@@ -397,7 +397,6 @@ export default {
|
||||
serverOfflineCount: 0,
|
||||
workstationCount: 0,
|
||||
workstationOfflineCount: 0,
|
||||
outsideModel: 11,
|
||||
selectedTree: "",
|
||||
innerModel: 50,
|
||||
clientActive: "",
|
||||
@@ -712,24 +711,27 @@ export default {
|
||||
getDashInfo(edited = true) {
|
||||
this.$store.dispatch("getDashInfo").then(r => {
|
||||
if (edited) {
|
||||
this.$q.loadingBar.setDefaults({ color: r.data.loading_bar_color });
|
||||
this.$store.commit("SET_DEFAULT_AGENT_TBL_TAB", r.data.default_agent_tbl_tab);
|
||||
this.$store.commit("SET_CLIENT_TREE_SORT", r.data.client_tree_sort);
|
||||
this.$store.commit("SET_CLIENT_SPLITTER", r.data.client_tree_splitter);
|
||||
}
|
||||
this.darkMode = r.data.dark_mode;
|
||||
this.$q.dark.set(this.darkMode);
|
||||
this.currentTRMMVersion = r.data.trmm_version;
|
||||
this.$store.commit("SET_AGENT_DBLCLICK_ACTION", r.data.dbl_click_action);
|
||||
this.$store.commit("setShowCommunityScripts", r.data.show_community_scripts);
|
||||
this.$store.commit("SET_NO_CODE_SIGN", r.data.no_code_sign);
|
||||
});
|
||||
},
|
||||
showToggleMaintenance(node) {
|
||||
let data = {
|
||||
id: node.id,
|
||||
type: node.raw.split("|")[0],
|
||||
action: node.color === "warning" ? false : true,
|
||||
action: node.color === "green" ? false : true,
|
||||
};
|
||||
|
||||
const text = node.color === "warning" ? "Maintenance mode was disabled" : "Maintenance mode was enabled";
|
||||
const text = node.color === "green" ? "Maintenance mode was disabled" : "Maintenance mode was enabled";
|
||||
this.$store
|
||||
.dispatch("toggleMaintenanceMode", data)
|
||||
.then(response => {
|
||||
@@ -741,7 +743,7 @@ export default {
|
||||
});
|
||||
},
|
||||
menuMaintenanceText(node) {
|
||||
return node.color === "warning" ? "Disable Maintenance Mode" : "Enable Maintenance Mode";
|
||||
return node.color === "green" ? "Disable Maintenance Mode" : "Enable Maintenance Mode";
|
||||
},
|
||||
clearFilter() {
|
||||
this.filterTextLength = 0;
|
||||
@@ -802,13 +804,21 @@ export default {
|
||||
treeReady: state => state.treeReady,
|
||||
clients: state => state.clients,
|
||||
}),
|
||||
...mapGetters(["selectedAgentPk", "needRefresh"]),
|
||||
...mapGetters(["selectedAgentPk", "needRefresh", "clientTreeSplitterModel"]),
|
||||
wsUrl() {
|
||||
return getBaseUrl().split("://")[1];
|
||||
},
|
||||
token() {
|
||||
return this.$store.state.token;
|
||||
},
|
||||
clientTreeSplitter: {
|
||||
get: function () {
|
||||
return this.clientTreeSplitterModel;
|
||||
},
|
||||
set: function (newVal) {
|
||||
this.$store.dispatch("setClientTreeSplitter", newVal);
|
||||
},
|
||||
},
|
||||
tab: {
|
||||
get: function () {
|
||||
return this.$store.state.defaultAgentTblTab;
|
||||
|
||||
@@ -72,10 +72,11 @@ export default {
|
||||
this.title = `${r.data.hostname} - ${r.data.client} - ${r.data.site} | Remote Background`;
|
||||
});
|
||||
},
|
||||
getDark() {
|
||||
getUI() {
|
||||
this.$store.dispatch("getDashInfo").then(r => {
|
||||
this.darkMode = r.data.dark_mode;
|
||||
this.$q.dark.set(this.darkMode);
|
||||
this.$q.loadingBar.setDefaults({ color: r.data.loading_bar_color });
|
||||
});
|
||||
},
|
||||
},
|
||||
@@ -90,7 +91,7 @@ export default {
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.getDark();
|
||||
this.getUI();
|
||||
this.genURLS();
|
||||
},
|
||||
};
|
||||
|
||||
@@ -54,6 +54,12 @@ export default {
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
getUI() {
|
||||
this.$store.dispatch("getDashInfo").then(r => {
|
||||
this.$q.dark.set(r.data.dark_mode);
|
||||
this.$q.loadingBar.setDefaults({ color: r.data.loading_bar_color });
|
||||
});
|
||||
},
|
||||
genURL() {
|
||||
this.$q.loading.show();
|
||||
this.visible = false;
|
||||
@@ -117,6 +123,7 @@ export default {
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.getUI();
|
||||
this.genURL();
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user