Merge branch 'dev' into tweak/unsupported-mc

This commit is contained in:
Zedifus 2024-02-02 17:53:02 +00:00
commit dc13706a02
13 changed files with 172 additions and 13 deletions

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

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.
"""
return self.controller.authentication.check(self.get_cookie("token"))
try:
return self.controller.authentication.check(self.get_cookie("token"))
except DoesNotExist:
return None
def autobleach(self, name, text):
for r in self.redactables:

View File

@ -302,6 +302,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

View File

@ -61,7 +61,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")

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

@ -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