mirror of
https://gitlab.com/crafty-controller/crafty-4.git
synced 2024-08-30 18:23:09 +00:00
Merge branch 'dev' into tweak/unsupported-mc
This commit is contained in:
commit
dc13706a02
@ -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
|
||||
|
@ -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"))
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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")
|
||||
|
@ -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,
|
||||
|
24
app/classes/web/routes/api/crafty/antilockout/index.py
Normal file
24
app/classes/web/routes/api/crafty/antilockout/index.py
Normal 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,
|
||||
},
|
||||
)
|
@ -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;
|
||||
}
|
||||
|
@ -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>",
|
||||
|
@ -281,7 +281,7 @@
|
||||
|
||||
<style>
|
||||
.clickable {
|
||||
color: #007bff;
|
||||
color: var(--primary);
|
||||
}
|
||||
.clickable:hover {
|
||||
cursor: pointer;
|
||||
|
@ -112,8 +112,8 @@
|
||||
<div class="form-check form-check-flat mt-0">
|
||||
|
||||
</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>
|
2
main.py
2
main.py
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user