Merge branch 'dev' into bugfix/creation-formatting

This commit is contained in:
Zedifus 2023-07-18 21:43:54 +01:00
commit 1beb86bc8f
19 changed files with 133 additions and 177 deletions

View File

@ -3,6 +3,7 @@ docker/
.dockerignore
Dockerfile
docker-compose.yml
docker-compose.yml.example
# git & gitlab related
.git/
@ -17,6 +18,8 @@ docker-compose.yml
.venv
.vscode
crafty_commander.exe
CHANGELOG.md
CONTRIBUTING.md
DBCHANGES.md
docker-compose.yml.example
README.md
sonar-project.properties

View File

@ -57,3 +57,27 @@ pylint:
reports:
codequality: codeclimate.json
when: always
# SonarQube/SonarCloud - Code Climate & QA [https://www.sonarsource.com]
sonarcloud-check:
stage: lint
image:
name: sonarsource/sonar-scanner-cli:latest
entrypoint: [""]
tags:
- docker
rules:
- exists:
- $SONAR_TOKEN
- if: "$CODE_QUALITY_DISABLED"
when: never
- if: "$CI_COMMIT_TAG || $CI_COMMIT_BRANCH"
variables:
SONAR_USER_HOME: "${CI_PROJECT_DIR}/.sonar" # Defines the location of the analysis task cache
GIT_DEPTH: "0" # Tells git to fetch all the branches of the project, required by the analysis task
cache:
key: "${CI_JOB_NAME}"
paths:
- .sonar/cache
script:
- sonar-scanner

View File

@ -1,17 +1,33 @@
# Changelog
## --- [4.1.2] - 2023/TBD
## --- [4.1.3] - 2023/TBD
### New features
TBD
### Bug fixes
- Include tzdata in Docker image ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/604))
- Fix text/formatting issue on server config page ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/602))
- Bump required version of PyYAML to 6.0.1 ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/609))
- Fix enable/disable schedule toggles on schedule list ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/606))
### Refactor
- Replace "in_file" helper method ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/605))
### Tweaks
- Add public status link to login ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/608))
### Lang
TBD
<br><br>
## --- [4.1.2] - 2023/06/18
### Bug fixes
- Fix upload root files being hidden ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/590))
- Send empty json for no banned/cached players ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/589))
- Bump Tornado from 6.0 to 6.3.2 in response to CVE-2023-28370 ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/591))
- Fix bug where commands would show "command_server" when initially created ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/592))
- Add ID autofield to management CraftySettings class ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/599))
### Refactor
- Optimize player management page ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/593))
### Tweaks
- Remove bedrock servers in serverjars options ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/595))
- Bump cryptography & pyOpenSSL ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/596))
- Bump requests ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/600))
### Lang
- Update es_ES & pl_PL lang, thank you `.lucyy_` & `terrariadlc` ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/597))
<br><br>

View File

@ -25,6 +25,7 @@ RUN apt-get update \
openjdk-8-jre-headless \
openjdk-11-jre-headless \
openjdk-17-jre-headless \
tzdata \
&& apt-get autoremove \
&& apt-get clean

View File

@ -1,5 +1,5 @@
[![Crafty Logo](app/frontend/static/assets/images/logo_long.svg)](https://craftycontrol.com)
# Crafty Controller 4.1.2
# Crafty Controller 4.1.3
> Python based Control Panel for your Minecraft Server
## What is Crafty Controller?

View File

@ -43,6 +43,7 @@ class AuditLog(BaseModel):
# Crafty Settings Class
# **********************************************************************************
class CraftySettings(BaseModel):
id = AutoField()
secret_api_key = CharField(default="")
cookie_secret = CharField(default="")
login_photo = CharField(default="login_1.jpg")

View File

@ -507,9 +507,9 @@ class Helpers:
return mounts
def is_subdir(self, server_path, root_dir):
server_path = os.path.realpath(server_path)
root_dir = os.path.realpath(root_dir)
def is_subdir(self, child_path, parent_path):
server_path = os.path.realpath(child_path)
root_dir = os.path.realpath(parent_path)
if self.is_os_windows():
try:
@ -1244,22 +1244,6 @@ class Helpers:
return temp_dir
return False
@staticmethod
def in_path(parent_path, child_path):
# Smooth out relative path names, note: if you are concerned about
# symbolic links, you should use os.path.realpath too
parent_path = os.path.abspath(parent_path)
child_path = os.path.abspath(child_path)
# Compare the common path of the parent and child path with the
# common path of just the parent path. Using the commonpath method
# on just the parent path will regularise the path name in the same way
# as the comparison that deals with both paths, removing any trailing
# path separator
return os.path.commonpath([parent_path]) == os.path.commonpath(
[parent_path, child_path]
)
@staticmethod
def download_file(executable_url, jar_path):
try:

View File

@ -656,12 +656,12 @@ class AjaxHandler(BaseHandler):
server_info = self.controller.servers.get_server_data_by_id(server_id)
if not (
Helpers.in_path(
Helpers.get_os_understandable_path(server_info["path"]), file_path
self.helper.is_subdir(
file_path, Helpers.get_os_understandable_path(server_info["path"])
)
or Helpers.in_path(
Helpers.get_os_understandable_path(server_info["backup_path"]),
or self.helper.is_subdir(
file_path,
Helpers.get_os_understandable_path(server_info["backup_path"]),
)
) or not Helpers.check_file_exists(os.path.abspath(file_path)):
logger.warning(f"Invalid path in del_backup ajax call ({file_path})")

View File

@ -57,11 +57,11 @@ class FileHandler(BaseHandler):
return
server_id = bleach.clean(server_id)
if not Helpers.in_path(
if not self.helper.is_subdir(
file_path,
Helpers.get_os_understandable_path(
self.controller.servers.get_server_data_by_id(server_id)["path"]
),
file_path,
) or not Helpers.check_file_exists(os.path.abspath(file_path)):
logger.warning(
f"Invalid path in get_file file file ajax call ({file_path})"
@ -163,11 +163,11 @@ class FileHandler(BaseHandler):
return
server_id = bleach.clean(server_id)
if not Helpers.in_path(
if not self.helper.is_subdir(
file_path,
Helpers.get_os_understandable_path(
self.controller.servers.get_server_data_by_id(server_id)["path"]
),
file_path,
) or Helpers.check_file_exists(os.path.abspath(file_path)):
logger.warning(
f"Invalid path in create_file file ajax call ({file_path})"
@ -196,11 +196,11 @@ class FileHandler(BaseHandler):
return
server_id = bleach.clean(server_id)
if not Helpers.in_path(
if not self.helper.is_subdir(
dir_path,
Helpers.get_os_understandable_path(
self.controller.servers.get_server_data_by_id(server_id)["path"]
),
dir_path,
) or Helpers.check_path_exists(os.path.abspath(dir_path)):
logger.warning(
f"Invalid path in create_dir file ajax call ({dir_path})"
@ -263,12 +263,12 @@ class FileHandler(BaseHandler):
server_info = self.controller.servers.get_server_data_by_id(server_id)
if not (
Helpers.in_path(
Helpers.get_os_understandable_path(server_info["path"]), file_path
self.helper.is_subdir(
file_path, Helpers.get_os_understandable_path(server_info["path"])
)
or Helpers.in_path(
Helpers.get_os_understandable_path(server_info["backup_path"]),
or self.helper.is_subdir(
file_path,
Helpers.get_os_understandable_path(server_info["backup_path"]),
)
) or not Helpers.check_file_exists(os.path.abspath(file_path)):
logger.warning(f"Invalid path in del_file file ajax call ({file_path})")
@ -296,8 +296,8 @@ class FileHandler(BaseHandler):
server_id = bleach.clean(server_id)
server_info = self.controller.servers.get_server_data_by_id(server_id)
if not Helpers.in_path(
Helpers.get_os_understandable_path(server_info["path"]), dir_path
if not self.helper.is_subdir(
dir_path, Helpers.get_os_understandable_path(server_info["path"])
) or not Helpers.check_path_exists(os.path.abspath(dir_path)):
logger.warning(f"Invalid path in del_file file ajax call ({dir_path})")
Console.warning(f"Invalid path in del_file file ajax call ({dir_path})")
@ -348,11 +348,11 @@ class FileHandler(BaseHandler):
return
server_id = bleach.clean(server_id)
if not Helpers.in_path(
if not self.helper.is_subdir(
file_path,
Helpers.get_os_understandable_path(
self.controller.servers.get_server_data_by_id(server_id)["path"]
),
file_path,
) or not Helpers.check_file_exists(os.path.abspath(file_path)):
logger.warning(
f"Invalid path in save_file file ajax call ({file_path})"
@ -366,60 +366,6 @@ class FileHandler(BaseHandler):
with open(file_path, "w", encoding="utf-8") as file_object:
file_object.write(file_contents)
elif page == "rename_file":
if not permissions["Files"] in user_perms:
if not superuser:
self.redirect("/panel/error?error=Unauthorized access to Files")
return
item_path = Helpers.get_os_understandable_path(
self.get_body_argument("item_path", default=None, strip=True)
)
new_item_name = self.get_body_argument(
"new_item_name", default=None, strip=True
)
if not self.check_server_id(server_id, "rename_file"):
return
server_id = bleach.clean(server_id)
if item_path is None or new_item_name is None:
logger.warning("Invalid path(s) in rename_file file ajax call")
Console.warning("Invalid path(s) in rename_file file ajax call")
return
if not Helpers.in_path(
Helpers.get_os_understandable_path(
self.controller.servers.get_server_data_by_id(server_id)["path"]
),
item_path,
) or not Helpers.check_path_exists(os.path.abspath(item_path)):
logger.warning(
f"Invalid old name path in rename_file file ajax call ({server_id})"
)
Console.warning(
f"Invalid old name path in rename_file file ajax call ({server_id})"
)
return
new_item_path = os.path.join(os.path.split(item_path)[0], new_item_name)
if not Helpers.in_path(
Helpers.get_os_understandable_path(
self.controller.servers.get_server_data_by_id(server_id)["path"]
),
new_item_path,
) or Helpers.check_path_exists(os.path.abspath(new_item_path)):
logger.warning(
f"Invalid new name path in rename_file file ajax call ({server_id})"
)
Console.warning(
f"Invalid new name path in rename_file file ajax call ({server_id})"
)
return
# RENAME
os.rename(item_path, new_item_path)
@tornado.web.authenticated
def patch(self, page):
api_key, _, exec_user = self.current_user
@ -462,11 +408,11 @@ class FileHandler(BaseHandler):
Console.warning("Invalid path(s) in rename_file file ajax call")
return
if not Helpers.in_path(
if not self.helper.is_subdir(
item_path,
Helpers.get_os_understandable_path(
self.controller.servers.get_server_data_by_id(server_id)["path"]
),
item_path,
) or not Helpers.check_path_exists(os.path.abspath(item_path)):
logger.warning(
f"Invalid old name path in rename_file file ajax call ({server_id})"
@ -478,11 +424,11 @@ class FileHandler(BaseHandler):
new_item_path = os.path.join(os.path.split(item_path)[0], new_item_name)
if not Helpers.in_path(
if not self.helper.is_subdir(
new_item_path,
Helpers.get_os_understandable_path(
self.controller.servers.get_server_data_by_id(server_id)["path"]
),
new_item_path,
) or Helpers.check_path_exists(os.path.abspath(new_item_path)):
logger.warning(
f"Invalid new name path in rename_file file ajax call ({server_id})"

View File

@ -808,9 +808,9 @@ class PanelHandler(BaseHandler):
Helpers.get_os_understandable_path(server_info["backup_path"]), file
)
)
if not Helpers.in_path(
Helpers.get_os_understandable_path(server_info["backup_path"]),
if not self.helper.is_subdir(
backup_file,
Helpers.get_os_understandable_path(server_info["backup_path"]),
) or not os.path.isfile(backup_file):
self.redirect("/panel/error?error=Invalid path detected")
return
@ -1463,8 +1463,9 @@ class PanelHandler(BaseHandler):
server_info = self.controller.servers.get_server_data_by_id(server_id)
if not Helpers.in_path(
Helpers.get_os_understandable_path(server_info["path"]), file
if not self.helper.is_subdir(
file,
Helpers.get_os_understandable_path(server_info["path"]),
) or not os.path.isfile(file):
self.redirect("/panel/error?error=Invalid path detected")
return

View File

@ -92,6 +92,7 @@ class ApiServersServerTasksIndexHandler(BaseApiHandler):
data["start_time"] = "00:00"
# validate cron string
if "cron_string" in data:
if data["cron_string"] != "" and not croniter.is_valid(data["cron_string"]):
return self.finish_json(
405,

View File

@ -136,6 +136,7 @@ class ApiServersServerTasksTaskIndexHandler(BaseApiHandler):
data["parent"] = None
data["server_id"] = server_id
if "cron_string" in data:
if data["cron_string"] != "" and not croniter.is_valid(data["cron_string"]):
return self.finish_json(
405,

View File

@ -278,11 +278,11 @@ class UploadHandler(BaseHandler):
filename = self.request.headers.get("X-FileName", None)
full_path = os.path.join(path, filename)
if not Helpers.in_path(
if not self.helper.is_subdir(
full_path,
Helpers.get_os_understandable_path(
self.controller.servers.get_server_data_by_id(server_id)["path"]
),
full_path,
):
logger.warning(
f"User {user_id} tried to upload a file to {server_id} "

View File

@ -1,5 +1,5 @@
{
"major": 4,
"minor": 1,
"sub": 2
"sub": 3
}

View File

@ -166,48 +166,6 @@
<input type="number" class="form-control" name="logs_delete_after" id="logs_delete_after" value="{{ data['server_stats']['server_id']['logs_delete_after'] }}" step="1" max="365" min="0" required>
</div>
<!--
<div class="form-check-flat">
<label for="auto_start" class="form-check-label ml-4 mb-4">
{% if data['server_stats']['server_id']['auto_start'] %}
<input type="checkbox" class="form-check-input" id="auto_start" name="auto_start" checked=""
data-toggle="toggle" value="1">&nbsp;&nbsp;{{ translate('serverConfig', 'serverAutoStart',
data['lang']) }}
{% else %}
<input type="checkbox" class="form-check-input" id="auto_start" name="auto_start" value="1"
data-toggle="toggle">&nbsp;&nbsp;{{
translate('serverConfig', 'serverAutoStart', data['lang']) }}
{% end %}
</label>
<label for="crash_detection" class="form-check-label ml-4 mb-4">
{% if data['server_stats']['server_id']['crash_detection'] %}
<input type="checkbox" class="form-check-input" id="crash_detection" name="crash_detection"
data-toggle="toggle" checked="" value="1">&nbsp;&nbsp;{{ translate('serverConfig',
'serverCrashDetection', data['lang']) }}
{% else %}
<input type="checkbox" class="form-check-input" id="crash_detection" name="crash_detection"
data-toggle="toggle" value="1">&nbsp;&nbsp;{{ translate('serverConfig', 'serverCrashDetection',
data['lang']) }}
{% end %}
</label>
{% if data['super_user'] %}
<label for="show_status" class="form-check-label ml-4 mb-4">
{% if data['server_stats']['server_id']['show_status'] %}
<input type="checkbox" class="form-check-input" id="show_status" name="show_status"
data-toggle="toggle" checked="" value="1">&nbsp;&nbsp;{{ translate('serverConfig', 'showStatus',
data['lang']) }}
{% else %}
<input type="checkbox" class="form-check-input" id="show_status" name="show_status"
data-toggle="toggle" value="1">&nbsp;&nbsp;{{ translate('serverConfig', 'showStatus',
data['lang']) }}
{% end %}
</label>
{% end %}
</div>
-->
<div class="form-group">
<div class="custom-control custom-switch">
{% if data['server_stats']['server_id']['auto_start'] %}
@ -237,7 +195,7 @@
<input type="checkbox" class="custom-control-input" id="show_status" name="show_status" checked="" value="1">
<label class="custom-control-label" for="show_status">&nbsp;&nbsp;{{ translate('serverConfig', 'showStatus', data['lang']) }}</label>
{% else %}
<input type="checkbox" class="custom-control-input" id="show_status" name="show_status" value="1">&nbsp;&nbsp;{{ translate('serverConfig', 'showStatus', data['lang']) }}
<input type="checkbox" class="custom-control-input" id="show_status" name="show_status" value="1">&nbsp;&nbsp;
<label class="custom-control-label" for="show_status">&nbsp;&nbsp;{{ translate('serverConfig', 'showStatus', data['lang']) }}</label>
{% end %}
{% end %}

View File

@ -120,6 +120,11 @@
<span class="text-small font-weight-semibold"><a href="https://craftycontrol.com/">Crafty Control
{{data['version'] }}</a> </span>
</div>
<div class="text-block text-center my-3">
<a href="/status" class="text-small forgot-password">{{ translate('login', 'viewStatus',
data['lang']) }}</a>
</div>
</form>
</div>

View File

@ -203,7 +203,8 @@
"forgotPassword": "Forgot Password",
"login": "Log In",
"password": "Password",
"username": "Username"
"username": "Username",
"viewStatus": "View Public Status Page"
},
"notify": {
"activityLog": "Activity Logs",

View File

@ -12,8 +12,8 @@ pexpect==4.8
psutil==5.9
pyOpenSSL==23.2.0
pyjwt==2.4.0
PyYAML==5.4
requests==2.26
PyYAML==6.0.1
requests==2.31
termcolor==1.1
tornado==6.3.2
tzlocal==4.0

14
sonar-project.properties Normal file
View File

@ -0,0 +1,14 @@
sonar.projectKey=crafty-controller_crafty-4
sonar.organization=crafty-controller
# This is the name and version displayed in the SonarCloud UI.
sonar.projectName=Crafty 4
sonar.projectVersion=4.1.3
sonar.python.version=3.9, 3.10, 3.11
sonar.exclusions=app/migrations/**, app/frontend/static/assets/vendors/**
# Path is relative to the sonar-project.properties file. Replace "\" by "/" on Windows.
#sonar.sources=.
# Encoding of the source code. Default is default system encoding
#sonar.sourceEncoding=UTF-8