Merge branch 'dev' into tweak/http-timeout

This commit is contained in:
Zedifus 2024-02-02 20:41:59 +00:00
commit 559e5f44ee
27 changed files with 222 additions and 18 deletions

View File

@ -44,13 +44,17 @@ black:
# Code Climate/Quality Checking [https://pylint.pycqa.org/en/latest/]
pylint:
stage: lint
image: registry.gitlab.com/pipeline-components/pylint:0.21.1
image: registry.gitlab.com/pipeline-components/pylint:latest
tags:
- docker
rules:
- if: "$CODE_QUALITY_DISABLED"
when: never
- if: "$CI_COMMIT_TAG || $CI_COMMIT_BRANCH"
before_script:
- apk update
- apk add gcc python3-dev linux-headers build-base
- pip3 install --no-cache-dir -r requirements.txt
script:
- pylint --exit-zero --load-plugins=pylint_gitlab --output-format=gitlab-codeclimate:codeclimate.json $(find -type f -name "*.py" ! -path "**/.venv/**" ! -path "**/app/migrations/**")
artifacts:

View File

@ -3,6 +3,7 @@
### New features
- Use Papermc Group's API for `paper` & `folia` builds in server builder ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/688))
- Allow omission of player count from Dashboard (e.g. for proxy servers) ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/692))
- Add lockout user for forgot password ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/694))
### Refactor
- Refactor subpage perm checks ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/695))
### Bug fixes
@ -15,6 +16,7 @@
### Tweaks
- Refactor Forge server initialisation flow for newer versions ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/687))
- Remove scroll bars from player management ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/693))
- Add warning to wizard for unsupported mc ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/701))
### Lang
- Update `zh_CN, pl_PL, nl_BE, lv_LV, he_IL, fr_FR, de_DE, lol_EN` translations for `4.2.3` ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/696))
- New `uk_UA, tr_TR, th_TH` translations for `4.2.3` ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/696))

View File

@ -1,5 +1,9 @@
import logging
import typing as t
import datetime
from datetime import timedelta
from zoneinfo import ZoneInfo
from apscheduler.schedulers.background import BackgroundScheduler
from app.classes.models.servers import HelperServers
from app.classes.models.users import HelperUsers
@ -8,6 +12,7 @@ from app.classes.models.crafty_permissions import (
PermissionsCrafty,
EnumPermissionsCrafty,
)
from app.classes.shared.console import Console
logger = logging.getLogger(__name__)
@ -22,6 +27,8 @@ class UsersController:
self.helper = helper
self.users_helper = users_helper
self.authentication = authentication
self.scheduler = BackgroundScheduler(timezone="Etc/UTC")
self.scheduler.start()
_permissions_props = {
"name": {
@ -169,7 +176,8 @@ class UsersController:
# create sets to store role data
added_roles = set()
removed_roles = set()
if user_data.get("username", None) == "anti-lockout-user":
raise ValueError("Invalid Username")
# search for changes in user data
for key in user_data:
if key == "user_id":
@ -245,6 +253,8 @@ class UsersController:
superuser: bool = False,
theme="default",
):
if username == "anti-lockout-user":
raise ValueError("Username is not valid")
return self.users_helper.add_user(
username,
manager,
@ -353,3 +363,37 @@ class UsersController:
def delete_user_api_key(self, key_id: str):
return self.users_helper.delete_user_api_key(key_id)
# **********************************************************************************
# Lockout Methods
# **********************************************************************************
def start_anti_lockout(self):
lockout_pass = self.helper.create_pass()
self.users_helper.add_user(
"anti-lockout-user",
None,
password=lockout_pass,
email="",
enabled=True,
superuser=True,
theme="anti-lockout",
)
Console.yellow(
f"""
Anti-lockout recovery account enabled!
{'/' * 74}
Username: anti-lockout-user
Password: {lockout_pass}
{'/' * 74}"""
)
self.scheduler.add_job(
self.stop_anti_lockout,
"date",
id="anti-lockout-watcher",
run_date=datetime.datetime.now(ZoneInfo("Etc/UTC")) + timedelta(hours=1),
)
def stop_anti_lockout(self):
self.scheduler.remove_all_jobs()
self.users_helper.remove_user(self.get_id_by_name("anti-lockout-user"))

View File

@ -103,7 +103,9 @@ class HelperUsers:
@staticmethod
def get_all_users():
query = Users.select().where(Users.username != "system")
query = Users.select().where(
Users.username != "system", Users.username != "anti-lockout-user"
)
return query
@staticmethod

View File

@ -12,6 +12,7 @@ from app.classes.shared.file_helpers import FileHelpers
from app.classes.shared.main_controller import Controller
from app.classes.shared.translation import Translation
from app.classes.shared.main_models import DatabaseShortcuts
from app.classes.models.users import DoesNotExist
logger = logging.getLogger(__name__)
auth_log = logging.getLogger("auth")
@ -91,7 +92,10 @@ class BaseHandler(tornado.web.RequestHandler):
t.Dict[str, t.Any]: The token's payload.
t.Dict[str, t.Any]: The user's data from the database.
"""
try:
return self.controller.authentication.check(self.get_cookie("token"))
except DoesNotExist:
return None
def autobleach(self, name, text):
for r in self.redactables:
@ -102,7 +106,7 @@ class BaseHandler(tornado.web.RequestHandler):
if type(text) in self.nobleach:
logger.debug("Auto-bleaching - bypass type")
return text
return nh3.clean(text)
return nh3.clean(text) # pylint: disable=no-member
def get_argument(
self,

View File

@ -80,6 +80,7 @@ class PanelHandler(BaseHandler):
) in self.controller.crafty_perms.list_defined_crafty_permissions():
argument = int(
float(
# pylint: disable=no-member
nh3.clean(self.get_argument(f"permission_{permission.name}", "0"))
)
)
@ -89,7 +90,10 @@ class PanelHandler(BaseHandler):
)
q_argument = int(
float(nh3.clean(self.get_argument(f"quantity_{permission.name}", "0")))
float(
# pylint: disable=no-member
nh3.clean(self.get_argument(f"quantity_{permission.name}", "0"))
)
)
if q_argument:
server_quantity[permission.name] = q_argument
@ -302,6 +306,8 @@ class PanelHandler(BaseHandler):
"Could not capture time zone from system. Falling back to Europe/London"
)
tz = "Europe/London"
if exec_user["username"] == "anti-lockout-user":
page = "panel_config"
page_data: t.Dict[str, t.Any] = {
# todo: make this actually pull and compare version data
@ -501,7 +507,9 @@ class PanelHandler(BaseHandler):
template = "panel/dashboard.html"
elif page == "server_detail":
# pylint: disable=no-member
subpage = nh3.clean(self.get_argument("subpage", ""))
# pylint: enable=no-member
server_id = self.check_server_id()
# load page the user was on last
@ -1360,7 +1368,9 @@ class PanelHandler(BaseHandler):
template = "panel/panel_edit_user_apikeys.html"
elif page == "remove_user":
# pylint: disable=no-member
user_id = nh3.clean(self.get_argument("id", None))
# pylint: enable=no-member
if (
not superuser

View File

@ -29,8 +29,10 @@ class PublicHandler(BaseHandler):
# self.clear_cookie("user_data")
def get(self, page=None):
# pylint: disable=no-member
error = nh3.clean(self.get_argument("error", "Invalid Login!"))
error_msg = nh3.clean(self.get_argument("error_msg", ""))
# pylint: enable=no-member
page_data = {
"version": self.helper.get_version_string(),
@ -61,7 +63,11 @@ class PublicHandler(BaseHandler):
template = "public/offline.html"
elif page == "logout":
exec_user = self.get_current_user()
self.clear_cookie("token")
# Delete anti-lockout-user on lockout...it's one time use
if exec_user[2]["username"] == "anti-lockout-user":
self.controller.users.stop_anti_lockout()
# self.clear_cookie("user")
# self.clear_cookie("user_data")
self.redirect("/login")
@ -83,8 +89,10 @@ class PublicHandler(BaseHandler):
)
def post(self, page=None):
# pylint: disable=no-member
error = nh3.clean(self.get_argument("error", "Invalid Login!"))
error_msg = nh3.clean(self.get_argument("error_msg", ""))
# pylint: enable=no-member
page_data = {
"version": self.helper.get_version_string(),
@ -104,10 +112,11 @@ class PublicHandler(BaseHandler):
if self.request.query:
next_page = "/login?" + self.request.query
# pylint: disable=no-member
entered_username = nh3.clean(self.get_argument("username"))
entered_password = self.get_argument("password")
# pylint: enable=no-member
# pylint: disable=no-member
try:
user_id = HelperUsers.get_user_id_by_name(entered_username.lower())
user_data = HelperUsers.get_user_model(user_id)

View File

@ -79,6 +79,7 @@ from app.classes.web.routes.api.crafty.stats.stats import ApiCraftyHostStatsHand
from app.classes.web.routes.api.crafty.clogs.index import ApiCraftyLogIndexHandler
from app.classes.web.routes.api.crafty.imports.index import ApiImportFilesIndexHandler
from app.classes.web.routes.api.crafty.exe_cache import ApiCraftyJarCacheIndexHandler
from app.classes.web.routes.api.crafty.antilockout.index import ApiCraftyLockoutHandler
def api_handlers(handler_args):
@ -94,6 +95,11 @@ def api_handlers(handler_args):
ApiAuthInvalidateTokensHandler,
handler_args,
),
(
r"/api/v2/crafty/resetPass/?",
ApiCraftyLockoutHandler,
handler_args,
),
(
r"/api/v2/crafty/announcements/?",
ApiAnnounceIndexHandler,

View File

@ -0,0 +1,24 @@
import logging
from app.classes.web.base_api_handler import BaseApiHandler
logger = logging.getLogger(__name__)
class ApiCraftyLockoutHandler(BaseApiHandler):
def get(self):
if self.controller.users.get_id_by_name("anti-lockout-user"):
return self.finish_json(
425, {"status": "error", "data": "Lockout recovery already in progress"}
)
self.controller.users.start_anti_lockout()
lockout_msg = (
"Lockout account has been activated for 1 hour."
" Please find temporary credentials in the terminal"
)
return self.finish_json(
200,
{
"status": "ok",
"data": lockout_msg,
},
)

View File

@ -55,6 +55,49 @@ root,
--font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
}
:root.anti-lockout {
/*CHANGE THESE FOR THEMES*/
--tooltip-bg: rgb(215, 82, 0);
--select-bg: #b8772c;
--ram-bg: #4d4d4e;
--base-text: white;
--outline: #c73929;
--card-banner-bg: #de7c26;
--deep-bg: #912f2f;
--dropdown-bg: #c83b3b;
/*END THEME VARIATION*/
--blue: #00aeef;
--indigo: #6610f2;
--purple: #ab8ce4;
--pink: #E91E63;
--red: #ff0017;
--orange: #fb9678;
--yellow: #ffd500;
--green: #3bd949;
--teal: #58d8a3;
--cyan: #57c7d4;
--white: #ffffff;
--white-smoke: #f3f5f6;
--gray: #6c757d;
--gray-light: #8ba2b5;
--gray-lightest: #f7f7f9;
--primary: #dbc900;
--secondary: #dde4eb;
--success: #adff84;
--info: #dbc900;
--warning: #ffaf00;
--danger: #ff6258;
--light: #fbfbfb;
--dark: #252C46;
--breakpoint-xs: 0;
--breakpoint-sm: 576px;
--breakpoint-md: 768px;
--breakpoint-lg: 992px;
--breakpoint-xl: 1200px;
--font-family-sans-serif: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
--font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
}
:root.light {
/*CHANGE THESE FOR THEMES*/
--tooltip-bg: white;
@ -322,7 +365,7 @@ sup {
}
a {
color: #007bff;
color: var(--primary);
text-decoration: none;
background-color: transparent;
}

View File

@ -1,5 +1,5 @@
<!DOCTYPE html>
<html lang="{{ data['lang_page'] }}" class="{{data['user_data'].get('theme', 'default')}}">
<html lang="{{ data['lang_page'] }}" class="{{data['user_data'].get('theme', 'default')}}" data-username="{{data['user_data'].get('username', None)}}">
<head>
<!-- Required meta tags -->
@ -256,8 +256,9 @@
const sendWssError = () => wsOpen || warn(
'WebSockets are required for Crafty to work. This websocket connection has been closed. Are you using a reverse proxy?',
'https://docs.craftycontrol.com/pages/getting-started/proxies/',
'wssError'
link='https://docs.craftycontrol.com/pages/getting-started/proxies/',
link_msg="See our documentation for details",
className='wssError'
)
function startWebSocket() {
@ -459,7 +460,7 @@
}
function warn(message, link = null, className = null) {
function warn(message, link = null, link_msg=null, className = null, bg_color="#f7970f") {
var closeEl = document.createElement('span');
var strongEL = document.createElement('strong');
var msgEl = document.createElement('div');
@ -481,14 +482,14 @@
var parentEl = document.createElement('div');
parentEl.style.padding = '20px';
parentEl.style.backgroundColor = '#f7970f';
parentEl.style.backgroundColor = bg_color;
parentEl.appendChild(closeEl);
parentEl.appendChild(msgEl);
if (link) {
let linkEl = document.createElement('a')
linkEl.href = link;
linkEl.innerHTML = "See our documentation for details.";
linkEl.innerHTML = link_msg;
linkEl.style.color = 'white';
linkEl.style.textDecoration = 'underline';
linkEl.target = "_blank";
@ -580,6 +581,15 @@
$(document).ready(function () {
console.log('%c[Crafty Controller] %cReady for JS!', 'font-weight: 900; color: #800080;', 'font-weight: 900; color: #eee;');
if ($(document.documentElement).data("username") === "anti-lockout-user"){
warn(
'⚠You are in a recovery account. Access is limited!',
link='/logout',
link_msg="Click here to log out after you change your password. ⚠️",
className='anti-lockout',
bg_color='#6887dc'
)
}
$('#support_logs').click(function () {
var dialog = bootbox.dialog({
message: "<p class='text- center mb - 0'><i class='fa fa - spin fa - cog'></i>{{ translate('notify', 'preparingLogs', data['lang']) }}</p>",

View File

@ -281,7 +281,7 @@
<style>
.clickable {
color: #007bff;
color: var(--primary);
}
.clickable:hover {
cursor: pointer;

View File

@ -112,8 +112,8 @@
<div class="form-check form-check-flat mt-0">
&nbsp;
</div>
<a href="#" class="text-small forgot-password ">{{ translate('login', 'forgotPassword',
data['lang']) }}</a>
<button onclick="resetPass()" id="#resetPass" form="" class="btn btn-outline-primary btn-sm forgot-password ">{{ translate('login', 'forgotPassword',
data['lang']) }}</button>
</div>
<div class="text-block text-center my-3">
@ -146,6 +146,7 @@
<script src="/static/assets/js/shared/misc.js"></script>
<script src="/static/assets/js/shared/settings.js"></script>
<script src="/static/assets/js/shared/todolist.js"></script>
<script src="../static/assets/vendors/js/bootbox.min.js"></script>
<!-- endinject -->
<script>
$(document).ready(function () {
@ -160,8 +161,24 @@
});
}
});
async function resetPass(){
let res = await fetch(`/api/v2/crafty/resetPass/`, {
method: 'GET',
});
let responseData = await res.json();
console.log(responseData);
bootbox.alert(responseData.data)
}
</script>
<style>
.modal-content {
background-color: rgb(34, 36, 55) !important;
color: lightgray;
text-align: center;
}
</style>
</body>
</html>

View File

@ -107,7 +107,8 @@
</select>
</div>
</div>
<span data-html="true" class="version-hint text-center"
data-content="⚠️ {{ translate('serverWizard', 'unsupported', data['lang']) }} ⚠️" , data-placement="right"></span>
<div class="col-sm-12">
<div class="form-group">
<label for="server_name">{{ translate('serverWizard', 'serverName', data['lang']) }}</label>
@ -1233,6 +1234,20 @@
}
})
}
$("#server").change(function (){
let selected_version = $("#server :selected").text().split(".");
if(parseInt(selected_version[0]) === 1 && parseInt(selected_version[1]) < 8 ){
$('[data-toggle="popover"]').popover();
if ($(window).width() < 1000) {
$('.version-hint').attr("data-placement", "top")
} else {
$('.version-hint').attr("data-placement", "right")
}
$('.version-hint').popover("show");
}else{
$('.version-hint').popover("hide");
}
});
function serverJarChange(selectObj) {
const type_select = document.getElementById('server_jar')

View File

@ -579,6 +579,7 @@
"serverUpload": "Als Zip-Datei Gepackten Server hochladen",
"serverVersion": "Server Version",
"sizeInGB": "Größe in GB",
"unsupported": "Minecraft-Versionen vor 1.8 sind nicht vollständig auf Kompatibilität getestet. Bei einer Installation verhalten diese sich möglicherweise nicht wie erwartet.",
"uploadButton": "Hochladen",
"uploadZip": "Zip-Datei für Serverimport hochladen",
"zipPath": "Server Pfad"

View File

@ -578,6 +578,7 @@
"serverUpload": "Upload Zipped Server",
"serverVersion": "Server Version",
"sizeInGB": "Size in GB",
"unsupported": "Minecraft versions lower than 1.8 are not supported by Crafty. You may still install it. Results will vary.",
"uploadButton": "Upload",
"uploadZip": "Upload Zip File For Server Import",
"zipPath": "Server Path"

View File

@ -579,6 +579,7 @@
"serverUpload": "Charger le fichier Zippé",
"serverVersion": "Version du Serveur",
"sizeInGB": "Taille en GB",
"unsupported": "Les versions Minecraft inférieures à 1.8 ne sont pas supportées par Crafty. Elles peuvent être installées, mais leur fonctionnement peut varier.",
"uploadButton": "Chargement",
"uploadZip": "Charger le fichier pour l'importation du Serveur",
"zipPath": "Chemin du Serveur"

View File

@ -579,6 +579,7 @@
"serverUpload": "העלה שרת מכווץ",
"serverVersion": "גרסת השרת",
"sizeInGB": "גודל ב-GB",
"unsupported": "גרסאות מיינקראפט מתחת ל1.8 לא נתמכות על ידי קראפטי. אפשר להתקין אותם אבל הביצועים לא יהיו עקביים.",
"uploadButton": "העלה",
"uploadZip": "העלה קובץ Zip לייבוא שרת",
"zipPath": "נתיב שרת"

View File

@ -579,6 +579,7 @@
"serverUpload": "UPLOADZ ZIPPED SERVR",
"serverVersion": "SERVR VERZHUN",
"sizeInGB": "SIEZ IN GIGABITEZ",
"unsupported": "MINECRAFTZ OLDR THAN 1.8 NOT FRENDS WIF CWAFTY. U CAN STILL TRY INSTALLIN'. IT MIGHT WORKS, MIGHT NOT.",
"uploadButton": "UPLOADZ",
"uploadZip": "UPLOADZ ZIP FISH FOR SERVR BRING IN",
"zipPath": "WER IZ ZIP FISH (PETH)"

View File

@ -580,6 +580,7 @@
"serverUpload": "Augšupielādē Saspiestu (Zipotu) Serveri",
"serverVersion": "Servera Versija",
"sizeInGB": "lielums, GB",
"unsupported": "Minecraft versijas zem 1.8 netiek atbalstītas iekš Crafty. Jūs tāpat variet tās instalēt, bet rezultāti atškirsies.",
"uploadButton": "Augšupielādēt",
"uploadZip": "Augšupielādē Zip Failu Priekš Serveru Importa",
"zipPath": "Servera Ceļš"

View File

@ -579,6 +579,7 @@
"serverUpload": "Gezipt bestand uploaden",
"serverVersion": "Serverversie",
"sizeInGB": "Grootte in GB",
"unsupported": "Minecraft-versies lager dan 1.8 worden niet ondersteund door Crafty. Je kunt het nog steeds installeren. De resultaten kunnen variëren.",
"uploadButton": "Uploaden",
"uploadZip": "Upload zip-bestand voor serverimport",
"zipPath": "Serverpad"

View File

@ -578,6 +578,7 @@
"serverUpload": "Wgraj ZIP Serwera",
"serverVersion": "Wersja serwera",
"sizeInGB": "Wielkość w GB",
"unsupported": "Wersje Minecrafta poniżej 1.8 nie są wspierane przez Crafty. Mimo to możesz je zainstalować - jednakże nie jesteśmy w stanie zagwarantować ich działanie.",
"uploadButton": "Wgraj",
"uploadZip": "Wgraj plik Zip dla imprtowania serwera",
"zipPath": "Server Path"

View File

@ -578,6 +578,7 @@
"serverUpload": "อัปโหลดเซิร์ฟเวอร์ซิป",
"serverVersion": "เวอร์ชันเซิร์ฟเวอร์",
"sizeInGB": "ขนาดเป็นกิกะไบต์",
"unsupported": "Crafty ไม่รองรับ Minecraft เวอร์ชั่นต่ำกว่า 1.8 คุณยังสามารถติดตั้งได้ ผลลัพธ์จะแตกต่างกันออกไป",
"uploadButton": "อัปโหลด",
"uploadZip": "อัปโหลดไฟล์ซิปสำหรับการนำเข้าเซิร์ฟเวอร์",
"zipPath": "เส้นทางเซิร์ฟเวอร์"

View File

@ -578,6 +578,7 @@
"serverUpload": "Sıkıştırılmış Sunucu Yükle",
"serverVersion": "Sunucu Sürümü",
"sizeInGB": "GB cinsinden boyut",
"unsupported": "Minecraft'ın 1.8'den eski sürümleri Crafty tarafından desteklenmemektedir. Yine de yükleyebilirsiniz. Sonuçlar değişkenlik gösterecektir.",
"uploadButton": "Dosya Yükle",
"uploadZip": "Sunucu İçe Aktarımı İçin Zip Dosyası Yükle",
"zipPath": "Sunucu Konumu"

View File

@ -578,6 +578,7 @@
"serverUpload": "Вивантажити архівований сервер",
"serverVersion": "Версія сервера",
"sizeInGB": "Об'єм пам'яті в ГБ",
"unsupported": "Minecraft версії старіші ніж 1.8, не підтримуються Crafty. Ви все ще можете встановити їх, але результат буде непередбачуваний.",
"uploadButton": "Вивантажити",
"uploadZip": "Вивантажити архів(.zip) для імпорту сервера",
"zipPath": "Шлях архіву сервера"

View File

@ -579,6 +579,7 @@
"serverUpload": "上传打包为 Zip 文件的服务器",
"serverVersion": "服务器版本",
"sizeInGB": "大小(以 GB 为单位)",
"unsupported": "Crafty 不支持低于 1.8 的 Minecraft 版本。你仍可以安装,但结果无法保证。",
"uploadButton": "上传",
"uploadZip": "上传 Zip 文件以导入服务器",
"zipPath": "服务器路径"

View File

@ -388,6 +388,8 @@ if __name__ == "__main__":
# Master config.json in helpers.py
Console.info("Checking for remote changes to config.json")
controller.get_config_diff()
# Delete anti-lockout-user
controller.users.stop_anti_lockout()
Console.info("Remote change complete.")
# startup the web server