Compare commits
33 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f82b589d03 | ||
|
|
cddac4d0fb | ||
|
|
ff41bbd0e5 | ||
|
|
4bdb6ae84e | ||
|
|
58fe14bd31 | ||
|
|
97f362ed1e | ||
|
|
b63e87ecb6 | ||
|
|
ac3550dfd7 | ||
|
|
8278a4cfd9 | ||
|
|
f161a2bbc8 | ||
|
|
6a94489df0 | ||
|
|
c3a0b9192f | ||
|
|
69ff70a9ce | ||
|
|
5284eb0af8 | ||
|
|
58384ae136 | ||
|
|
054cc78e65 | ||
|
|
8c283281d6 | ||
|
|
241fe41756 | ||
|
|
e50e0626fa | ||
|
|
c9135f1573 | ||
|
|
ec2663a152 | ||
|
|
7567042c8a | ||
|
|
c99ceb155f | ||
|
|
f44c92f0d3 | ||
|
|
492701ec62 | ||
|
|
a6d0acaa4d | ||
|
|
f84b4e7274 | ||
|
|
b7ef5b82d8 | ||
|
|
a854d2c38c | ||
|
|
5140499bbd | ||
|
|
7183e9ee85 | ||
|
|
11885e0aca | ||
|
|
2bda4e822c |
66
.github/workflows/ci-tests.yml
vendored
Normal file
66
.github/workflows/ci-tests.yml
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
name: Tests CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- "*"
|
||||
pull_request:
|
||||
branches:
|
||||
- "*"
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: self-hosted
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Setup virtual env and install requirements
|
||||
run: |
|
||||
sudo -u postgres psql -c 'DROP DATABASE IF EXISTS pipeline'
|
||||
sudo -u postgres psql -c 'DROP DATABASE IF EXISTS test_pipeline'
|
||||
sudo -u postgres psql -c 'CREATE DATABASE pipeline'
|
||||
sudo -u postgres psql -c "SET client_encoding = 'UTF8'" pipeline
|
||||
pwd
|
||||
rm -rf /actions-runner/_work/trmm-actions/trmm-actions/api/env
|
||||
cd api
|
||||
python3.10 -m venv env
|
||||
source env/bin/activate
|
||||
cd tacticalrmm
|
||||
python --version
|
||||
SETTINGS_FILE="tacticalrmm/settings.py"
|
||||
SETUPTOOLS_VER=$(grep "^SETUPTOOLS_VER" "$SETTINGS_FILE" | awk -F'[= "]' '{print $5}')
|
||||
WHEEL_VER=$(grep "^WHEEL_VER" "$SETTINGS_FILE" | awk -F'[= "]' '{print $5}')
|
||||
pip install --upgrade pip
|
||||
pip install setuptools==${SETUPTOOLS_VER} wheel==${WHEEL_VER}
|
||||
pip install -r requirements.txt -r requirements-test.txt
|
||||
|
||||
- name: Run django tests
|
||||
env:
|
||||
GHACTIONS: "yes"
|
||||
run: |
|
||||
cd api/tacticalrmm
|
||||
source ../env/bin/activate
|
||||
rm -f .coverage coverage.lcov
|
||||
coverage run --concurrency=multiprocessing manage.py test -v 2 --parallel
|
||||
coverage combine
|
||||
coverage lcov
|
||||
if [ $? -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Codestyle black
|
||||
run: |
|
||||
cd api
|
||||
source env/bin/activate
|
||||
black --exclude migrations/ --check tacticalrmm
|
||||
if [ $? -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Coveralls
|
||||
uses: coverallsapp/github-action@master
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
path-to-lcov: ./api/tacticalrmm/coverage.lcov
|
||||
base-path: ./api/tacticalrmm
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -51,3 +51,4 @@ reset_db.sh
|
||||
run_go_cmd.py
|
||||
nats-api.conf
|
||||
ignore/
|
||||
coverage.lcov
|
||||
|
||||
11
README.md
11
README.md
@@ -1,7 +1,7 @@
|
||||
# Tactical RMM
|
||||
|
||||
[](https://dev.azure.com/dcparsi/Tactical%20RMM/_build/latest?definitionId=4&branchName=develop)
|
||||
[](https://coveralls.io/github/wh1te909/tacticalrmm?branch=develop)
|
||||

|
||||
[](https://coveralls.io/github/amidaware/tacticalrmm?branch=develop)
|
||||
[](https://github.com/python/black)
|
||||
|
||||
Tactical RMM is a remote monitoring & management tool, built with Django and Vue.\
|
||||
@@ -28,9 +28,12 @@ Demo database resets every hour. A lot of features are disabled for obvious reas
|
||||
- Remote software installation via chocolatey
|
||||
- Software and hardware inventory
|
||||
|
||||
## Windows versions supported
|
||||
## Windows agent versions supported
|
||||
|
||||
- Windows 7, 8.1, 10, Server 2008R2, 2012R2, 2016, 2019
|
||||
- Windows 7, 8.1, 10, 11, Server 2008R2, 2012R2, 2016, 2019, 2022
|
||||
|
||||
## Linux agent versions supported
|
||||
- Any distro with systemd
|
||||
|
||||
## Installation / Backup / Restore / Usage
|
||||
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
|
||||
| Version | Supported |
|
||||
| ------- | ------------------ |
|
||||
| 0.12.0 | :white_check_mark: |
|
||||
| < 0.12.0 | :x: |
|
||||
| 0.12.2 | :white_check_mark: |
|
||||
| < 0.12.2 | :x: |
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import asyncio
|
||||
import base64
|
||||
import re
|
||||
import time
|
||||
from collections import Counter
|
||||
from distutils.version import LooseVersion
|
||||
from typing import Any
|
||||
@@ -11,10 +9,6 @@ import nats
|
||||
import validators
|
||||
from asgiref.sync import sync_to_async
|
||||
from core.models import TZ_CHOICES, CoreSettings
|
||||
from Crypto.Cipher import AES
|
||||
from Crypto.Hash import SHA3_384
|
||||
from Crypto.Random import get_random_bytes
|
||||
from Crypto.Util.Padding import pad
|
||||
from django.conf import settings
|
||||
from django.contrib.postgres.fields import ArrayField
|
||||
from django.db import models
|
||||
@@ -219,7 +213,8 @@ class Agent(BaseAuditModel):
|
||||
try:
|
||||
if not self.wmi_detail["gpus"]:
|
||||
return "No graphics cards"
|
||||
return self.wmi_detail["gpus"]
|
||||
|
||||
return ", ".join(self.wmi_detail["gpus"])
|
||||
except:
|
||||
return "Error getting graphics cards"
|
||||
|
||||
@@ -613,30 +608,6 @@ class Agent(BaseAuditModel):
|
||||
# Generate tasks based on policies
|
||||
Policy.generate_policy_tasks(self)
|
||||
|
||||
# https://github.com/Ylianst/MeshCentral/issues/59#issuecomment-521965347
|
||||
def get_login_token(self, key, user, action=3):
|
||||
try:
|
||||
key = bytes.fromhex(key)
|
||||
key1 = key[0:48]
|
||||
key2 = key[48:]
|
||||
msg = '{{"a":{}, "u":"{}","time":{}}}'.format(
|
||||
action, user.lower(), int(time.time())
|
||||
)
|
||||
iv = get_random_bytes(16)
|
||||
|
||||
# sha
|
||||
h = SHA3_384.new()
|
||||
h.update(key1)
|
||||
hashed_msg = h.digest() + msg.encode()
|
||||
|
||||
# aes
|
||||
cipher = AES.new(key2, AES.MODE_CBC, iv)
|
||||
msg = cipher.encrypt(pad(hashed_msg, 16))
|
||||
|
||||
return base64.b64encode(iv + msg, altchars=b"@$").decode("utf-8")
|
||||
except Exception:
|
||||
return "err"
|
||||
|
||||
def _do_nats_debug(self, agent, message):
|
||||
DebugLog.error(agent=agent, log_type="agent_issues", message=message)
|
||||
|
||||
|
||||
@@ -519,7 +519,7 @@ class TestAgentViews(TacticalTestCase):
|
||||
|
||||
self.check_not_authenticated("post", url)
|
||||
|
||||
@patch("agents.models.Agent.get_login_token")
|
||||
@patch("meshctrl.utils.get_auth_token")
|
||||
def test_meshcentral_tabs(self, mock_token):
|
||||
url = f"{base_url}/{self.agent.agent_id}/meshcentral/"
|
||||
mock_token.return_value = "askjh1k238uasdhk487234jadhsajksdhasd"
|
||||
@@ -547,10 +547,6 @@ class TestAgentViews(TacticalTestCase):
|
||||
|
||||
self.assertEqual(r.status_code, 200)
|
||||
|
||||
mock_token.return_value = "err"
|
||||
r = self.client.get(url)
|
||||
self.assertEqual(r.status_code, 400)
|
||||
|
||||
self.check_not_authenticated("get", url)
|
||||
|
||||
@patch("agents.models.Agent.nats_cmd")
|
||||
|
||||
@@ -4,6 +4,7 @@ import os
|
||||
import random
|
||||
import string
|
||||
import time
|
||||
from meshctrl.utils import get_auth_token
|
||||
|
||||
from core.models import CodeSignToken, CoreSettings
|
||||
from core.utils import get_mesh_ws_url, remove_mesh_agent, send_command_with_mesh
|
||||
@@ -208,13 +209,7 @@ class AgentMeshCentral(APIView):
|
||||
agent = get_object_or_404(Agent, agent_id=agent_id)
|
||||
core = CoreSettings.objects.first()
|
||||
|
||||
token = agent.get_login_token(
|
||||
key=core.mesh_token,
|
||||
user=f"user//{core.mesh_username.lower()}", # type:ignore
|
||||
)
|
||||
|
||||
if token == "err":
|
||||
return notify_error("Invalid mesh token")
|
||||
token = get_auth_token(user=core.mesh_username, key=core.mesh_token)
|
||||
|
||||
control = f"{core.mesh_site}/?login={token}&gotonode={agent.mesh_node_id}&viewmode=11&hide=31" # type:ignore
|
||||
terminal = f"{core.mesh_site}/?login={token}&gotonode={agent.mesh_node_id}&viewmode=12&hide=31" # type:ignore
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import asyncio
|
||||
from meshctrl.utils import get_auth_token
|
||||
|
||||
from django.core.management.base import BaseCommand
|
||||
|
||||
from core.models import CoreSettings
|
||||
from core.utils import get_auth_token, get_mesh_device_id, get_mesh_ws_url
|
||||
from core.utils import get_mesh_device_id, get_mesh_ws_url
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
|
||||
@@ -12,6 +12,13 @@ class Command(BaseCommand):
|
||||
|
||||
self.stdout.write("Creating configuration for nats-api...")
|
||||
db = settings.DATABASES["default"]
|
||||
if hasattr(settings, "DB_SSL"):
|
||||
ssl = settings.DB_SSL
|
||||
elif "DB_SSL" in os.environ:
|
||||
ssl = os.getenv("DB_SSL")
|
||||
else:
|
||||
ssl = "disable"
|
||||
|
||||
config = {
|
||||
"key": settings.SECRET_KEY,
|
||||
"natsurl": f"tls://{settings.ALLOWED_HOSTS[0]}:4222",
|
||||
@@ -20,6 +27,7 @@ class Command(BaseCommand):
|
||||
"host": db["HOST"],
|
||||
"port": int(db["PORT"]),
|
||||
"dbname": db["NAME"],
|
||||
"sslmode": ssl,
|
||||
}
|
||||
conf = os.path.join(settings.BASE_DIR, "nats-api.conf")
|
||||
with open(conf, "w") as f:
|
||||
|
||||
@@ -45,9 +45,9 @@ class Command(BaseCommand):
|
||||
# Check for Mesh Username
|
||||
if (
|
||||
not mesh_settings.mesh_username
|
||||
or settings.MESH_USERNAME != mesh_settings.mesh_username
|
||||
or settings.MESH_USERNAME.lower() != mesh_settings.mesh_username
|
||||
):
|
||||
mesh_settings.mesh_username = settings.MESH_USERNAME
|
||||
mesh_settings.mesh_username = settings.MESH_USERNAME.lower()
|
||||
|
||||
# Check for Mesh Site
|
||||
if (
|
||||
|
||||
@@ -97,7 +97,7 @@ class CoreSettings(BaseAuditModel):
|
||||
if not self.pk:
|
||||
try:
|
||||
self.mesh_site = settings.MESH_SITE
|
||||
self.mesh_username = settings.MESH_USERNAME
|
||||
self.mesh_username = settings.MESH_USERNAME.lower()
|
||||
self.mesh_token = settings.MESH_TOKEN_KEY
|
||||
except:
|
||||
pass
|
||||
|
||||
@@ -1,30 +1,14 @@
|
||||
import json
|
||||
import tempfile
|
||||
import time
|
||||
from base64 import b64encode
|
||||
from meshctrl.utils import get_auth_token
|
||||
|
||||
import requests
|
||||
import websockets
|
||||
from Crypto.Cipher import AES
|
||||
from Crypto.Random import get_random_bytes
|
||||
from django.conf import settings
|
||||
from django.http import FileResponse
|
||||
|
||||
|
||||
def get_auth_token(user, key):
|
||||
key = bytes.fromhex(key)
|
||||
key1 = key[0:32]
|
||||
msg = '{{"userid":"{}", "domainid":"{}", "time":{}}}'.format(
|
||||
f"user//{user}", "", int(time.time())
|
||||
)
|
||||
iv = get_random_bytes(12)
|
||||
|
||||
a = AES.new(key1, AES.MODE_GCM, iv)
|
||||
msg, tag = a.encrypt_and_digest(bytes(msg, "utf-8")) # type: ignore
|
||||
|
||||
return b64encode(iv + tag + msg, altchars=b"@$").decode("utf-8")
|
||||
|
||||
|
||||
def get_mesh_ws_url() -> str:
|
||||
from core.models import CoreSettings
|
||||
|
||||
|
||||
@@ -3,3 +3,4 @@ Werkzeug
|
||||
django-extensions
|
||||
isort
|
||||
types-pytz
|
||||
django-silk
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
coverage==6.3.2
|
||||
coveralls==3.3.1
|
||||
model_bakery
|
||||
coverage
|
||||
coveralls
|
||||
model_bakery
|
||||
black
|
||||
tblib
|
||||
@@ -1,5 +1,5 @@
|
||||
asgiref==3.5.0
|
||||
celery==5.2.3
|
||||
celery==5.2.6
|
||||
certifi==2021.10.8
|
||||
cffi==1.15.0
|
||||
channels==3.0.4
|
||||
@@ -14,8 +14,7 @@ django-rest-knox==4.2.0
|
||||
djangorestframework==3.13.1
|
||||
future==0.18.2
|
||||
msgpack==1.0.3
|
||||
nats-py==2.0.0
|
||||
packaging==21.3
|
||||
nats-py==2.1.0
|
||||
psycopg2-binary==2.9.3
|
||||
pycparser==2.21
|
||||
pycryptodome==3.14.1
|
||||
@@ -23,15 +22,16 @@ pyotp==2.6.0
|
||||
pyparsing==3.0.7
|
||||
pytz==2022.1
|
||||
qrcode==7.3.1
|
||||
redis==4.1.4
|
||||
redis==4.2.2
|
||||
requests==2.27.1
|
||||
six==1.16.0
|
||||
sqlparse==0.4.2
|
||||
twilio==7.8.0
|
||||
twilio==7.8.1
|
||||
urllib3==1.26.9
|
||||
uWSGI==2.0.20
|
||||
validators==0.18.2
|
||||
vine==5.0.0
|
||||
websockets==10.2
|
||||
zipp==3.7.0
|
||||
drf_spectacular==0.21.2
|
||||
zipp==3.8.0
|
||||
drf_spectacular==0.21.2
|
||||
meshctrl==0.1.13
|
||||
@@ -17,22 +17,22 @@ LINUX_AGENT_SCRIPT = BASE_DIR / "core" / "agent_linux.sh"
|
||||
AUTH_USER_MODEL = "accounts.User"
|
||||
|
||||
# latest release
|
||||
TRMM_VERSION = "0.12.1"
|
||||
TRMM_VERSION = "0.12.3"
|
||||
|
||||
# bump this version everytime vue code is changed
|
||||
# to alert user they need to manually refresh their browser
|
||||
APP_VER = "0.0.159"
|
||||
APP_VER = "0.0.160"
|
||||
|
||||
# https://github.com/amidaware/rmmagent
|
||||
LATEST_AGENT_VER = "2.0.1"
|
||||
LATEST_AGENT_VER = "2.0.2"
|
||||
|
||||
MESH_VER = "0.9.98"
|
||||
MESH_VER = "1.0.2"
|
||||
|
||||
NATS_SERVER_VER = "2.7.4"
|
||||
|
||||
# for the update script, bump when need to recreate venv or npm install
|
||||
PIP_VER = "27"
|
||||
NPM_VER = "30"
|
||||
PIP_VER = "28"
|
||||
NPM_VER = "31"
|
||||
|
||||
SETUPTOOLS_VER = "59.6.0"
|
||||
WHEEL_VER = "0.37.1"
|
||||
@@ -52,6 +52,29 @@ REST_KNOX = {
|
||||
"MIN_REFRESH_INTERVAL": 600,
|
||||
}
|
||||
|
||||
if "GHACTIONS" in os.environ:
|
||||
print("-----------------------PIPELINE----------------------------")
|
||||
DATABASES = {
|
||||
"default": {
|
||||
"ENGINE": "django.db.backends.postgresql",
|
||||
"NAME": "pipeline",
|
||||
"USER": "pipeline",
|
||||
"PASSWORD": "pipeline123456",
|
||||
"HOST": "127.0.0.1",
|
||||
"PORT": "",
|
||||
}
|
||||
}
|
||||
SECRET_KEY = "abcdefghijklmnoptravis123456789"
|
||||
DEBUG = False
|
||||
ALLOWED_HOSTS = ["api.example.com"]
|
||||
ADMIN_URL = "abc123456/"
|
||||
CORS_ORIGIN_WHITELIST = ["https://rmm.example.com"]
|
||||
MESH_USERNAME = "pipeline"
|
||||
MESH_SITE = "https://example.com"
|
||||
MESH_TOKEN_KEY = "bd65e957a1e70c622d32523f61508400d6cd0937001a7ac12042227eba0b9ed625233851a316d4f489f02994145f74537a331415d00047dbbf13d940f556806dffe7a8ce1de216dc49edbad0c1a7399c"
|
||||
REDIS_HOST = "localhost"
|
||||
ADMIN_ENABLED = False
|
||||
|
||||
try:
|
||||
from .local_settings import *
|
||||
except ImportError:
|
||||
@@ -74,11 +97,34 @@ SPECTACULAR_SETTINGS = {
|
||||
"AUTHENTICATION_WHITELIST": ["tacticalrmm.auth.APIAuthentication"],
|
||||
}
|
||||
|
||||
if not "AZPIPELINE" in os.environ:
|
||||
if not DEBUG: # type: ignore
|
||||
REST_FRAMEWORK.update(
|
||||
{"DEFAULT_RENDERER_CLASSES": ("rest_framework.renderers.JSONRenderer",)}
|
||||
)
|
||||
|
||||
if not DEBUG: # type: ignore
|
||||
REST_FRAMEWORK.update(
|
||||
{"DEFAULT_RENDERER_CLASSES": ("rest_framework.renderers.JSONRenderer",)}
|
||||
)
|
||||
|
||||
MIDDLEWARE = [
|
||||
"django.middleware.security.SecurityMiddleware",
|
||||
"django.contrib.sessions.middleware.SessionMiddleware",
|
||||
"corsheaders.middleware.CorsMiddleware", ##
|
||||
"tacticalrmm.middleware.LogIPMiddleware",
|
||||
"django.middleware.common.CommonMiddleware",
|
||||
"django.middleware.csrf.CsrfViewMiddleware",
|
||||
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
||||
"tacticalrmm.middleware.AuditMiddleware",
|
||||
"tacticalrmm.middleware.LinuxMiddleware",
|
||||
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
||||
]
|
||||
|
||||
|
||||
if ADMIN_ENABLED: # type: ignore
|
||||
MIDDLEWARE += ("django.contrib.messages.middleware.MessageMiddleware",)
|
||||
|
||||
try:
|
||||
if DEMO: # type: ignore
|
||||
MIDDLEWARE += ("tacticalrmm.middleware.DemoMiddleware",)
|
||||
except:
|
||||
pass
|
||||
|
||||
INSTALLED_APPS = [
|
||||
"django.contrib.auth",
|
||||
@@ -107,21 +153,23 @@ INSTALLED_APPS = [
|
||||
"drf_spectacular",
|
||||
]
|
||||
|
||||
if not "AZPIPELINE" in os.environ:
|
||||
if DEBUG: # type: ignore
|
||||
INSTALLED_APPS += ("django_extensions",)
|
||||
|
||||
CHANNEL_LAYERS = {
|
||||
"default": {
|
||||
"BACKEND": "channels_redis.core.RedisChannelLayer",
|
||||
"CONFIG": {
|
||||
"hosts": [(REDIS_HOST, 6379)], # type: ignore
|
||||
},
|
||||
if DEBUG: # type: ignore
|
||||
INSTALLED_APPS += (
|
||||
"django_extensions",
|
||||
"silk",
|
||||
)
|
||||
|
||||
MIDDLEWARE.insert(0, "silk.middleware.SilkyMiddleware")
|
||||
|
||||
CHANNEL_LAYERS = {
|
||||
"default": {
|
||||
"BACKEND": "channels_redis.core.RedisChannelLayer",
|
||||
"CONFIG": {
|
||||
"hosts": [(REDIS_HOST, 6379)], # type: ignore
|
||||
},
|
||||
}
|
||||
|
||||
if "AZPIPELINE" in os.environ:
|
||||
ADMIN_ENABLED = False
|
||||
},
|
||||
}
|
||||
|
||||
if ADMIN_ENABLED: # type: ignore
|
||||
INSTALLED_APPS += (
|
||||
@@ -130,28 +178,6 @@ if ADMIN_ENABLED: # type: ignore
|
||||
)
|
||||
|
||||
|
||||
MIDDLEWARE = [
|
||||
"django.middleware.security.SecurityMiddleware",
|
||||
"django.contrib.sessions.middleware.SessionMiddleware",
|
||||
"corsheaders.middleware.CorsMiddleware", ##
|
||||
"tacticalrmm.middleware.LogIPMiddleware",
|
||||
"django.middleware.common.CommonMiddleware",
|
||||
"django.middleware.csrf.CsrfViewMiddleware",
|
||||
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
||||
"tacticalrmm.middleware.AuditMiddleware",
|
||||
"tacticalrmm.middleware.LinuxMiddleware",
|
||||
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
||||
]
|
||||
|
||||
if ADMIN_ENABLED: # type: ignore
|
||||
MIDDLEWARE += ("django.contrib.messages.middleware.MessageMiddleware",)
|
||||
|
||||
try:
|
||||
if DEMO: # type: ignore
|
||||
MIDDLEWARE += ("tacticalrmm.middleware.DemoMiddleware",)
|
||||
except:
|
||||
pass
|
||||
|
||||
ROOT_URLCONF = "tacticalrmm.urls"
|
||||
|
||||
|
||||
@@ -226,38 +252,3 @@ LOGGING = {
|
||||
"django.request": {"handlers": ["file"], "level": "ERROR", "propagate": True}
|
||||
},
|
||||
}
|
||||
|
||||
if "AZPIPELINE" in os.environ:
|
||||
print("PIPELINE")
|
||||
DATABASES = {
|
||||
"default": {
|
||||
"ENGINE": "django.db.backends.postgresql",
|
||||
"NAME": "pipeline",
|
||||
"USER": "pipeline",
|
||||
"PASSWORD": "pipeline123456",
|
||||
"HOST": "127.0.0.1",
|
||||
"PORT": "",
|
||||
}
|
||||
}
|
||||
|
||||
REST_FRAMEWORK = {
|
||||
"DATETIME_FORMAT": "%b-%d-%Y - %H:%M",
|
||||
"DEFAULT_PERMISSION_CLASSES": ("rest_framework.permissions.IsAuthenticated",),
|
||||
"DEFAULT_AUTHENTICATION_CLASSES": (
|
||||
"knox.auth.TokenAuthentication",
|
||||
"tacticalrmm.auth.APIAuthentication",
|
||||
),
|
||||
"DEFAULT_RENDERER_CLASSES": ("rest_framework.renderers.JSONRenderer",),
|
||||
}
|
||||
|
||||
ALLOWED_HOSTS = ["api.example.com"]
|
||||
DEBUG = True
|
||||
SECRET_KEY = "abcdefghijklmnoptravis123456789"
|
||||
|
||||
ADMIN_URL = "abc123456/"
|
||||
|
||||
SCRIPTS_DIR = os.path.join(Path(BASE_DIR).parents[1], "scripts")
|
||||
MESH_USERNAME = "pipeline"
|
||||
MESH_SITE = "https://example.com"
|
||||
MESH_TOKEN_KEY = "bd65e957a1e70c622d32523f61508400d6cd0937001a7ac12042227eba0b9ed625233851a316d4f489f02994145f74537a331415d00047dbbf13d940f556806dffe7a8ce1de216dc49edbad0c1a7399c"
|
||||
REDIS_HOST = "localhost"
|
||||
|
||||
@@ -43,6 +43,9 @@ if getattr(settings, "ADMIN_ENABLED", False):
|
||||
|
||||
urlpatterns += (path(settings.ADMIN_URL, admin.site.urls),)
|
||||
|
||||
if getattr(settings, "DEBUG", False):
|
||||
urlpatterns += [path("silk/", include("silk.urls", namespace="silk"))]
|
||||
|
||||
if getattr(settings, "SWAGGER_ENABLED", False):
|
||||
from drf_spectacular.views import SpectacularAPIView, SpectacularSwaggerView
|
||||
|
||||
|
||||
@@ -161,14 +161,28 @@ def convert_to_iso_duration(string: str) -> str:
|
||||
|
||||
|
||||
def reload_nats():
|
||||
users = [{"user": "tacticalrmm", "password": settings.SECRET_KEY}]
|
||||
users = [
|
||||
{
|
||||
"user": "tacticalrmm",
|
||||
"password": settings.SECRET_KEY,
|
||||
"permissions": {"publish": ">", "subscribe": ">"},
|
||||
}
|
||||
]
|
||||
agents = Agent.objects.prefetch_related("user").only(
|
||||
"pk", "agent_id"
|
||||
) # type:ignore
|
||||
for agent in agents:
|
||||
try:
|
||||
users.append(
|
||||
{"user": agent.agent_id, "password": agent.user.auth_token.key}
|
||||
{
|
||||
"user": agent.agent_id,
|
||||
"password": agent.user.auth_token.key,
|
||||
"permissions": {
|
||||
"publish": {"allow": agent.agent_id},
|
||||
"subscribe": {"allow": agent.agent_id},
|
||||
"allow_responses": True,
|
||||
},
|
||||
}
|
||||
)
|
||||
except:
|
||||
DebugLog.critical(
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
trigger:
|
||||
- master
|
||||
- develop
|
||||
|
||||
jobs:
|
||||
- job: setup_env
|
||||
displayName: "Setup"
|
||||
strategy:
|
||||
matrix:
|
||||
Debian10:
|
||||
AGENT_NAME: "az-pipeline-fran"
|
||||
|
||||
pool:
|
||||
name: linux-vms
|
||||
demands:
|
||||
- agent.name -equals $(AGENT_NAME)
|
||||
|
||||
steps:
|
||||
- script: |
|
||||
sudo -u postgres psql -c 'DROP DATABASE IF EXISTS pipeline'
|
||||
sudo -u postgres psql -c 'DROP DATABASE IF EXISTS test_pipeline'
|
||||
sudo -u postgres psql -c 'CREATE DATABASE pipeline'
|
||||
sudo -u postgres psql -c "SET client_encoding = 'UTF8'" pipeline
|
||||
SETTINGS_FILE="/myagent/_work/1/s/api/tacticalrmm/tacticalrmm/settings.py"
|
||||
rm -rf /myagent/_work/1/s/api/env
|
||||
cd /myagent/_work/1/s/api
|
||||
python3.10 -m venv env
|
||||
source env/bin/activate
|
||||
cd /myagent/_work/1/s/api/tacticalrmm
|
||||
pip install --trusted-host pypi.org --trusted-host pypi.python.org --trusted-host files.pythonhosted.org --upgrade pip
|
||||
SETUPTOOLS_VER=$(grep "^SETUPTOOLS_VER" "$SETTINGS_FILE" | awk -F'[= "]' '{print $5}')
|
||||
WHEEL_VER=$(grep "^WHEEL_VER" "$SETTINGS_FILE" | awk -F'[= "]' '{print $5}')
|
||||
pip install --trusted-host pypi.org --trusted-host pypi.python.org --trusted-host files.pythonhosted.org setuptools==${SETUPTOOLS_VER} wheel==${WHEEL_VER}
|
||||
pip install --trusted-host pypi.org --trusted-host pypi.python.org --trusted-host files.pythonhosted.org -r requirements.txt -r requirements-test.txt -r requirements-dev.txt
|
||||
displayName: "Install Python Dependencies"
|
||||
|
||||
- script: |
|
||||
cd /myagent/_work/1/s/api
|
||||
source env/bin/activate
|
||||
cd /myagent/_work/1/s/api/tacticalrmm
|
||||
coverage run manage.py test -v 2
|
||||
if [ $? -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
displayName: "Run django tests"
|
||||
|
||||
- script: |
|
||||
cd /myagent/_work/1/s/api
|
||||
source env/bin/activate
|
||||
black --exclude migrations/ --check tacticalrmm
|
||||
if [ $? -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
displayName: "Codestyle black"
|
||||
|
||||
- script: |
|
||||
cd /myagent/_work/1/s/api
|
||||
source env/bin/activate
|
||||
cd /myagent/_work/1/s/api/tacticalrmm
|
||||
export CIRCLE_BRANCH=$BUILD_SOURCEBRANCH
|
||||
coveralls
|
||||
displayName: "coveralls"
|
||||
env:
|
||||
CIRCLECI: 1
|
||||
CIRCLE_BUILD_NUM: $(Build.BuildNumber)
|
||||
@@ -129,11 +129,13 @@ processes = ${uwsgiprocs}
|
||||
threads = ${uwsgiprocs}
|
||||
enable-threads = true
|
||||
socket = 0.0.0.0:8080
|
||||
harakiri = 300
|
||||
chmod-socket = 660
|
||||
buffer-size = 65535
|
||||
vacuum = true
|
||||
die-on-term = true
|
||||
max-requests = 2000
|
||||
max-requests = 500
|
||||
disable-logging = true
|
||||
EOF
|
||||
)"
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
SCRIPT_VERSION="59"
|
||||
SCRIPT_VERSION="61"
|
||||
SCRIPT_URL='https://raw.githubusercontent.com/amidaware/tacticalrmm/master/install.sh'
|
||||
|
||||
sudo apt install -y curl wget dirmngr gnupg lsb-release
|
||||
@@ -406,6 +406,7 @@ buffer-size = 65535
|
||||
vacuum = true
|
||||
die-on-term = true
|
||||
max-requests = 500
|
||||
disable-logging = true
|
||||
EOF
|
||||
)"
|
||||
echo "${uwsgini}" > /rmm/api/tacticalrmm/app.ini
|
||||
@@ -657,7 +658,7 @@ CELERY_APP="tacticalrmm"
|
||||
|
||||
CELERYD_MULTI="multi"
|
||||
|
||||
CELERYD_OPTS="--time-limit=86400 --autoscale=50,3"
|
||||
CELERYD_OPTS="--time-limit=86400 --autoscale=20,2"
|
||||
|
||||
CELERYD_PID_FILE="/rmm/api/tacticalrmm/%n.pid"
|
||||
CELERYD_LOG_FILE="/var/log/celery/%n%I.log"
|
||||
|
||||
2
main.go
2
main.go
@@ -11,7 +11,7 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
version = "3.0.1"
|
||||
version = "3.0.2"
|
||||
log = logrus.New()
|
||||
)
|
||||
|
||||
|
||||
Binary file not shown.
@@ -13,4 +13,5 @@ type DjangoConfig struct {
|
||||
Host string `json:"host"`
|
||||
Port int `json:"port"`
|
||||
DBName string `json:"dbname"`
|
||||
SSLMode string `json:"sslmode"`
|
||||
}
|
||||
|
||||
@@ -41,8 +41,8 @@ func GetConfig(cfg string) (db *sqlx.DB, r DjangoConfig, err error) {
|
||||
}
|
||||
|
||||
psqlInfo := fmt.Sprintf("host=%s port=%d user=%s "+
|
||||
"password=%s dbname=%s sslmode=disable",
|
||||
r.Host, r.Port, r.User, r.Pass, r.DBName)
|
||||
"password=%s dbname=%s sslmode=%s",
|
||||
r.Host, r.Port, r.User, r.Pass, r.DBName, r.SSLMode)
|
||||
|
||||
db, err = sqlx.Connect("postgres", psqlInfo)
|
||||
if err != nil {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
SCRIPT_VERSION="34"
|
||||
SCRIPT_VERSION="35"
|
||||
SCRIPT_URL='https://raw.githubusercontent.com/amidaware/tacticalrmm/master/restore.sh'
|
||||
|
||||
sudo apt update
|
||||
@@ -276,6 +276,7 @@ buffer-size = 65535
|
||||
vacuum = true
|
||||
die-on-term = true
|
||||
max-requests = 500
|
||||
disable-logging = true
|
||||
EOF
|
||||
)"
|
||||
echo "${uwsgini}" > /rmm/api/tacticalrmm/app.ini
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
SCRIPT_VERSION="131"
|
||||
SCRIPT_VERSION="133"
|
||||
SCRIPT_URL='https://raw.githubusercontent.com/amidaware/tacticalrmm/master/update.sh'
|
||||
LATEST_SETTINGS_URL='https://raw.githubusercontent.com/amidaware/tacticalrmm/master/api/tacticalrmm/tacticalrmm/settings.py'
|
||||
YELLOW='\033[1;33m'
|
||||
@@ -144,6 +144,7 @@ buffer-size = 65535
|
||||
vacuum = true
|
||||
die-on-term = true
|
||||
max-requests = 500
|
||||
disable-logging = true
|
||||
EOF
|
||||
)"
|
||||
echo "${uwsgini}" > /rmm/api/tacticalrmm/app.ini
|
||||
@@ -257,6 +258,11 @@ sudo chown ${USER}:${USER} -R /etc/conf.d/
|
||||
sudo chown ${USER}:${USER} -R /etc/letsencrypt
|
||||
sudo chmod 775 -R /etc/letsencrypt
|
||||
|
||||
CHECK_CELERY_CONFIG=$(grep "autoscale=20,2" /etc/conf.d/celery.conf)
|
||||
if ! [[ $CHECK_CELERY_CONFIG ]]; then
|
||||
sed -i 's/CELERYD_OPTS=.*/CELERYD_OPTS="--time-limit=86400 --autoscale=20,2"/g' /etc/conf.d/celery.conf
|
||||
fi
|
||||
|
||||
CHECK_ADMIN_ENABLED=$(grep ADMIN_ENABLED /rmm/api/tacticalrmm/tacticalrmm/local_settings.py)
|
||||
if ! [[ $CHECK_ADMIN_ENABLED ]]; then
|
||||
adminenabled="$(cat << EOF
|
||||
|
||||
987
web/package-lock.json
generated
987
web/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -10,12 +10,12 @@
|
||||
"test:e2e:ci": "cross-env E2E_TEST=true start-test \"quasar dev\" http-get://localhost:8080 \"cypress run\""
|
||||
},
|
||||
"dependencies": {
|
||||
"@quasar/extras": "^1.13.3",
|
||||
"@quasar/extras": "^1.13.5",
|
||||
"apexcharts": "^3.33.2",
|
||||
"axios": "^0.26.1",
|
||||
"dotenv": "^16.0.0",
|
||||
"qrcode.vue": "^3.3.3",
|
||||
"quasar": "^2.6.1",
|
||||
"quasar": "^2.6.6",
|
||||
"vue": "^3.2.31",
|
||||
"vue3-ace-editor": "^2.2.2",
|
||||
"vue3-apexcharts": "^1.4.1",
|
||||
@@ -23,7 +23,7 @@
|
||||
"vuex": "^4.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@quasar/app-webpack": "^3.4.5",
|
||||
"@quasar/app-webpack": "^3.5.0",
|
||||
"@quasar/cli": "^1.3.2"
|
||||
},
|
||||
"browserslist": [
|
||||
|
||||
@@ -304,7 +304,15 @@
|
||||
<q-card-section class="row">
|
||||
<div class="col-4">Username:</div>
|
||||
<div class="col-2"></div>
|
||||
<q-input dense outlined v-model="settings.mesh_username" class="col-6" />
|
||||
<q-input
|
||||
dense
|
||||
outlined
|
||||
v-model="settings.mesh_username"
|
||||
class="col-6"
|
||||
:rules="[
|
||||
val => (val == val.toLowerCase() && val != val.toUpperCase()) || 'Username must be all lowercase',
|
||||
]"
|
||||
/>
|
||||
</q-card-section>
|
||||
<q-card-section class="row">
|
||||
<div class="col-4">Mesh Site:</div>
|
||||
|
||||
Reference in New Issue
Block a user