Merge branch 'dev' into 'merge/api-v2'

# Conflicts:
#   app/classes/controllers/crafty_perms_controller.py
This commit is contained in:
Andrew 2022-05-18 21:56:39 +00:00
commit b7780682d5
12 changed files with 401 additions and 232 deletions

View File

@ -35,20 +35,16 @@ class CraftyPermsController:
) )
@staticmethod @staticmethod
def can_add_user(): # Add back argument 'user_id' when you work on this def can_add_user(user_id):
return True return PermissionsCrafty.can_add_in_crafty(
# TODO: Complete if we need a User Addition limit user_id, EnumPermissionsCrafty.USER_CONFIG
# return crafty_permissions.can_add_in_crafty( )
# user_id, EnumPermissionsCrafty.USER_CONFIG
# )
@staticmethod @staticmethod
def can_add_role(): # Add back argument 'user_id' when you work on this def can_add_role(user_id):
return True return PermissionsCrafty.can_add_in_crafty(
# TODO: Complete if we need a Role Addition limit user_id, EnumPermissionsCrafty.ROLES_CONFIG
# return crafty_permissions.can_add_in_crafty( )
# user_id, EnumPermissionsCrafty.ROLES_CONFIG
# )
@staticmethod @staticmethod
def list_all_crafty_permissions_quantity_limits(): def list_all_crafty_permissions_quantity_limits():
@ -76,6 +72,14 @@ class CraftyPermsController:
""" """
return PermissionsCrafty.add_server_creation(user_id) return PermissionsCrafty.add_server_creation(user_id)
@staticmethod
def add_user_creation(user_id):
return PermissionsCrafty.add_user_creation(user_id)
@staticmethod
def add_role_creation(user_id):
return PermissionsCrafty.add_role_creation(user_id)
@staticmethod @staticmethod
def get_api_key_permissions_list(key: ApiKeys): def get_api_key_permissions_list(key: ApiKeys):
return PermissionsCrafty.get_api_key_permissions_list(key) return PermissionsCrafty.get_api_key_permissions_list(key)

View File

@ -29,9 +29,8 @@ class ServerPermsController:
return permissions_mask return permissions_mask
@staticmethod @staticmethod
def get_role_permissions(role_id): def get_role_permissions_dict(role_id):
permissions_list = PermissionsServers.get_role_permissions_list(role_id) return PermissionsServers.get_role_permissions_dict(role_id)
return permissions_list
@staticmethod @staticmethod
def add_role_server(server_id, role_id, rs_permissions="00000000"): def add_role_server(server_id, role_id, rs_permissions="00000000"):
@ -71,10 +70,6 @@ class ServerPermsController:
permission_mask, permission_tested, value permission_mask, permission_tested, value
) )
@staticmethod
def get_role_permissions_list(role_id):
return PermissionsServers.get_role_permissions_list(role_id)
@staticmethod @staticmethod
def get_user_id_permissions_list(user_id: str, server_id: str): def get_user_id_permissions_list(user_id: str, server_id: str):
return PermissionsServers.get_user_id_permissions_list(user_id, server_id) return PermissionsServers.get_user_id_permissions_list(user_id, server_id)

View File

@ -145,11 +145,13 @@ class UsersController:
elif key == "password": elif key == "password":
if user_data["password"] is not None and user_data["password"] != "": if user_data["password"] is not None and user_data["password"] != "":
up_data["password"] = self.helper.encode_pass(user_data["password"]) up_data["password"] = self.helper.encode_pass(user_data["password"])
elif key == "lang":
up_data["lang"] = user_data["lang"]
elif key == "hints":
up_data["hints"] = user_data["hints"]
elif base_data[key] != user_data[key]: elif base_data[key] != user_data[key]:
up_data[key] = user_data[key] up_data[key] = user_data[key]
up_data["last_update"] = self.helper.get_time_as_string() up_data["last_update"] = self.helper.get_time_as_string()
up_data["lang"] = user_data["lang"]
up_data["hints"] = user_data["hints"]
logger.debug(f"user: {user_data} +role:{added_roles} -role:{removed_roles}") logger.debug(f"user: {user_data} +role:{added_roles} -role:{removed_roles}")
for role in added_roles: for role in added_roles:
HelperUsers.get_or_create(user_id=user_id, role_id=role) HelperUsers.get_or_create(user_id=user_id, role_id=role)

View File

@ -205,6 +205,20 @@ class PermissionsCrafty:
UserCrafty.save(user_crafty) UserCrafty.save(user_crafty)
return user_crafty.created_server return user_crafty.created_server
@staticmethod
def add_user_creation(user_id):
user_crafty = PermissionsCrafty.get_user_crafty(user_id)
user_crafty.created_user += 1
UserCrafty.save(user_crafty)
return user_crafty.created_user
@staticmethod
def add_role_creation(user_id):
user_crafty = PermissionsCrafty.get_user_crafty(user_id)
user_crafty.created_role += 1
UserCrafty.save(user_crafty)
return user_crafty.created_role
@staticmethod @staticmethod
def get_api_key_permissions_list(key: ApiKeys): def get_api_key_permissions_list(key: ApiKeys):
user = HelperUsers.get_user(key.user_id) user = HelperUsers.get_user(key.user_id)

View File

@ -1,3 +1,4 @@
import typing as t
from enum import Enum from enum import Enum
import logging import logging
import typing as t import typing as t
@ -167,6 +168,18 @@ class PermissionsServers:
permissions_list = PermissionsServers.get_permissions(permissions_mask) permissions_list = PermissionsServers.get_permissions(permissions_mask)
return permissions_list return permissions_list
@staticmethod
def get_role_permissions_dict(role_id):
permissions_dict: t.Dict[str, t.List[EnumPermissionsServer]] = {}
role_servers = RoleServers.select(
RoleServers.server_id, RoleServers.permissions
).where(RoleServers.role_id == role_id)
for role_server in role_servers:
permissions_dict[
role_server.server_id_id
] = PermissionsServers.get_permissions(role_server.permissions)
return permissions_dict
@staticmethod @staticmethod
def update_role_permission(role_id, server_id, permissions_mask): def update_role_permission(role_id, server_id, permissions_mask):
role_server = ( role_server = (

View File

@ -3,6 +3,7 @@ import cmd
import time import time
import threading import threading
import logging import logging
import getpass
from app.classes.shared.console import Console from app.classes.shared.console import Console
from app.classes.shared.import3 import Import3 from app.classes.shared.import3 import Import3
@ -11,11 +12,13 @@ logger = logging.getLogger(__name__)
class MainPrompt(cmd.Cmd): class MainPrompt(cmd.Cmd):
def __init__(self, helper, tasks_manager, migration_manager): def __init__(self, helper, tasks_manager, migration_manager, main_controller):
super().__init__() super().__init__()
self.helper = helper self.helper = helper
self.tasks_manager = tasks_manager self.tasks_manager = tasks_manager
self.migration_manager = migration_manager self.migration_manager = migration_manager
self.controller = main_controller
# overrides the default Prompt # overrides the default Prompt
self.prompt = f"Crafty Controller v{self.helper.get_version_string()} > " self.prompt = f"Crafty Controller v{self.helper.get_version_string()} > "
@ -49,6 +52,37 @@ class MainPrompt(cmd.Cmd):
else: else:
Console.info("Unknown migration command") Console.info("Unknown migration command")
def do_set_passwd(self, line):
try:
username = str(line).lower()
# If no user is found it returns None
user_id = self.controller.users.get_id_by_name(username)
if not username:
Console.error("You must enter a username. Ex: `set_passwd admin'")
return False
if not user_id:
Console.error(f"No user found by the name of {username}")
return False
except:
Console.error(f"User: {line} Not Found")
return False
new_pass = getpass.getpass(prompt=f"NEW password for: {username} > ")
new_pass_conf = getpass.getpass(prompt="Re-enter your password: > ")
if new_pass != new_pass_conf:
Console.error("Passwords do not match. Please try again.")
return False
if len(new_pass) > 512:
Console.warning("Passwords must be greater than 6char long and under 512")
return False
if len(new_pass) < 6:
Console.warning("Passwords must be greater than 6char long and under 512")
return False
self.controller.users.update_user(user_id, {"password": new_pass})
@staticmethod @staticmethod
def do_threads(_line): def do_threads(_line):
for thread in threading.enumerate(): for thread in threading.enumerate():

View File

@ -15,9 +15,13 @@ from tornado import iostream
# TZLocal is set as a hidden import on win pipeline # TZLocal is set as a hidden import on win pipeline
from tzlocal import get_localzone from tzlocal import get_localzone
from cron_validator import CronValidator from croniter import croniter
from app.classes.models.server_permissions import EnumPermissionsServer from app.classes.models.roles import HelperRoles
from app.classes.models.server_permissions import (
EnumPermissionsServer,
PermissionsServers,
)
from app.classes.models.crafty_permissions import EnumPermissionsCrafty from app.classes.models.crafty_permissions import EnumPermissionsCrafty
from app.classes.models.management import HelpersManagement from app.classes.models.management import HelpersManagement
from app.classes.shared.helpers import Helpers from app.classes.shared.helpers import Helpers
@ -39,15 +43,21 @@ class PanelHandler(BaseHandler):
def get_role_servers(self) -> t.Set[int]: def get_role_servers(self) -> t.Set[int]:
servers = set() servers = set()
for server in self.controller.list_defined_servers(): for server in self.controller.list_defined_servers():
argument = int( argument = self.get_argument(f"server_{server['server_id']}_access", "0")
float( if argument == "0":
bleach.clean( continue
self.get_argument(f"server_{server['server_id']}_access", "0")
) permission_mask = "0" * len(EnumPermissionsServer)
for permission in self.controller.server_perms.list_defined_permissions():
argument = self.get_argument(
f"permission_{server['server_id']}_{permission.name}", "0"
) )
) if argument == "1":
if argument: permission_mask = self.controller.server_perms.set_permission(
servers.add(server["server_id"]) permission_mask, permission, "1"
)
servers.add((server["server_id"], permission_mask))
return servers return servers
def get_perms_quantity(self) -> t.Tuple[str, dict]: def get_perms_quantity(self) -> t.Tuple[str, dict]:
@ -85,19 +95,9 @@ class PanelHandler(BaseHandler):
permission permission
) in self.controller.crafty_perms.list_defined_crafty_permissions(): ) in self.controller.crafty_perms.list_defined_crafty_permissions():
argument = self.get_argument(f"permission_{permission.name}", None) argument = self.get_argument(f"permission_{permission.name}", None)
if argument is not None: if argument is not None and argument == "1":
permissions_mask = self.controller.crafty_perms.set_permission( permissions_mask = self.controller.crafty_perms.set_permission(
permissions_mask, permission, 1 if argument == "1" else 0 permissions_mask, permission, "1"
)
return permissions_mask
def get_perms_server(self) -> str:
permissions_mask = "00000000"
for permission in self.controller.server_perms.list_defined_permissions():
argument = self.get_argument(f"permission_{permission.name}", None)
if argument is not None:
permissions_mask = self.controller.server_perms.set_permission(
permissions_mask, permission, 1 if argument == "1" else 0
) )
return permissions_mask return permissions_mask
@ -158,7 +158,7 @@ class PanelHandler(BaseHandler):
if not self.controller.servers.server_id_authorized_api_key( if not self.controller.servers.server_id_authorized_api_key(
server_id, api_key server_id, api_key
): ):
print( logger.debug(
f"API key {api_key.name} (id: {api_key.token_id}) " f"API key {api_key.name} (id: {api_key.token_id}) "
f"does not have permission" f"does not have permission"
) )
@ -168,7 +168,9 @@ class PanelHandler(BaseHandler):
if not self.controller.servers.server_id_authorized( if not self.controller.servers.server_id_authorized(
server_id, exec_user["user_id"] server_id, exec_user["user_id"]
): ):
print(f'User {exec_user["user_id"]} does not have permission') logger.debug(
f'User {exec_user["user_id"]} does not have permission'
)
self.redirect("/pandel/error?error=Invalid Server ID") self.redirect("/pandel/error?error=Invalid Server ID")
return None return None
return server_id return server_id
@ -770,6 +772,7 @@ class PanelHandler(BaseHandler):
page_data["user"]["last_update"] = "N/A" page_data["user"]["last_update"] = "N/A"
page_data["user"]["roles"] = set() page_data["user"]["roles"] = set()
page_data["user"]["hints"] = True page_data["user"]["hints"] = True
page_data["superuser"] = superuser
if EnumPermissionsCrafty.USER_CONFIG not in exec_user_crafty_permissions: if EnumPermissionsCrafty.USER_CONFIG not in exec_user_crafty_permissions:
self.redirect( self.redirect(
@ -957,6 +960,7 @@ class PanelHandler(BaseHandler):
page_data["role-servers"] = page_role_servers page_data["role-servers"] = page_role_servers
page_data["roles_all"] = self.controller.roles.get_all_roles() page_data["roles_all"] = self.controller.roles.get_all_roles()
page_data["servers_all"] = self.controller.list_defined_servers() page_data["servers_all"] = self.controller.list_defined_servers()
page_data["superuser"] = superuser
page_data[ page_data[
"permissions_all" "permissions_all"
] = self.controller.crafty_perms.list_defined_crafty_permissions() ] = self.controller.crafty_perms.list_defined_crafty_permissions()
@ -1087,7 +1091,7 @@ class PanelHandler(BaseHandler):
page_data[ page_data[
"permissions_all" "permissions_all"
] = self.controller.server_perms.list_defined_permissions() ] = self.controller.server_perms.list_defined_permissions()
page_data["permissions_list"] = set() page_data["permissions_dict"] = {}
template = "panel/panel_edit_role.html" template = "panel/panel_edit_role.html"
elif page == "edit_role": elif page == "edit_role":
@ -1100,8 +1104,8 @@ class PanelHandler(BaseHandler):
"permissions_all" "permissions_all"
] = self.controller.server_perms.list_defined_permissions() ] = self.controller.server_perms.list_defined_permissions()
page_data[ page_data[
"permissions_list" "permissions_dict"
] = self.controller.server_perms.get_role_permissions(role_id) ] = self.controller.server_perms.get_role_permissions_dict(role_id)
page_data["user-roles"] = user_roles page_data["user-roles"] = user_roles
page_data["users"] = self.controller.users.get_all_users() page_data["users"] = self.controller.users.get_all_users()
@ -1449,11 +1453,9 @@ class PanelHandler(BaseHandler):
else: else:
interval_type = "" interval_type = ""
cron_string = bleach.clean(self.get_argument("cron", "")) cron_string = bleach.clean(self.get_argument("cron", ""))
try: if not croniter.is_valid(cron_string):
CronValidator.parse(cron_string)
except Exception as e:
self.redirect( self.redirect(
f"/panel/error?error=INVALID FORMAT: Invalid Cron Format. {e}" "/panel/error?error=INVALID FORMAT: Invalid Cron Format."
) )
return return
action = bleach.clean(self.get_argument("action", None)) action = bleach.clean(self.get_argument("action", None))
@ -1607,11 +1609,9 @@ class PanelHandler(BaseHandler):
interval_type = "" interval_type = ""
cron_string = bleach.clean(self.get_argument("cron", "")) cron_string = bleach.clean(self.get_argument("cron", ""))
sch_id = self.get_argument("sch_id", None) sch_id = self.get_argument("sch_id", None)
try: if not croniter.is_valid(cron_string):
CronValidator.parse(cron_string)
except Exception as e:
self.redirect( self.redirect(
f"/panel/error?error=INVALID FORMAT: Invalid Cron Format. {e}" "/panel/error?error=INVALID FORMAT: Invalid Cron Format."
) )
return return
action = bleach.clean(self.get_argument("action", None)) action = bleach.clean(self.get_argument("action", None))
@ -1933,6 +1933,15 @@ class PanelHandler(BaseHandler):
"/panel/error?error=Unauthorized access: not a user editor" "/panel/error?error=Unauthorized access: not a user editor"
) )
return return
if (
not self.controller.crafty_perms.can_add_user(exec_user["user_id"])
and not exec_user["superuser"]
):
self.redirect(
"/panel/error?error=Unauthorized access: quantity limit reached"
)
return
elif username is None or username == "": elif username is None or username == "":
self.redirect("/panel/error?error=Invalid username") self.redirect("/panel/error?error=Invalid username")
return return
@ -1977,6 +1986,7 @@ class PanelHandler(BaseHandler):
server_id=0, server_id=0,
source_ip=self.get_remote_ip(), source_ip=self.get_remote_ip(),
) )
self.controller.crafty_perms.add_user_creation(exec_user["user_id"])
self.redirect("/panel/panel_config") self.redirect("/panel/panel_config")
elif page == "edit_role": elif page == "edit_role":
@ -2001,16 +2011,40 @@ class PanelHandler(BaseHandler):
return return
servers = self.get_role_servers() servers = self.get_role_servers()
permissions_mask = self.get_perms_server()
role_data = {"role_name": role_name, "servers": servers} # TODO: use update_role_advanced when API v2 gets merged
self.controller.roles.update_role( base_data = self.controller.roles.get_role_with_servers(role_id)
role_id, role_data=role_data, permissions_mask=permissions_mask
server_ids = {server[0] for server in servers}
server_permissions_map = {server[0]: server[1] for server in servers}
added_servers = server_ids.difference(set(base_data["servers"]))
removed_servers = set(base_data["servers"]).difference(server_ids)
same_servers = server_ids.intersection(set(base_data["servers"]))
logger.debug(
f"role: {role_id} +server:{added_servers} -server{removed_servers}"
) )
for server_id in added_servers:
PermissionsServers.get_or_create(
role_id, server_id, server_permissions_map[server_id]
)
for server_id in same_servers:
PermissionsServers.update_role_permission(
role_id, server_id, server_permissions_map[server_id]
)
if len(removed_servers) != 0:
PermissionsServers.delete_roles_permissions(role_id, removed_servers)
up_data = {
"role_name": role_name,
"last_update": Helpers.get_time_as_string(),
}
# TODO: do the last_update on the db side
HelperRoles.update_role(role_id, up_data)
self.controller.management.add_to_audit_log( self.controller.management.add_to_audit_log(
exec_user["user_id"], exec_user["user_id"],
f"Edited role {role_name} (RID:{role_id}) with servers {servers}", f"edited role {role_name} (RID:{role_id}) with servers {servers}",
server_id=0, server_id=0,
source_ip=self.get_remote_ip(), source_ip=self.get_remote_ip(),
) )
@ -2024,6 +2058,14 @@ class PanelHandler(BaseHandler):
"/panel/error?error=Unauthorized access: not a role editor" "/panel/error?error=Unauthorized access: not a role editor"
) )
return return
elif (
not self.controller.crafty_perms.can_add_role(exec_user["user_id"])
and not exec_user["superuser"]
):
self.redirect(
"/panel/error?error=Unauthorized access: quantity limit reached"
)
return
elif role_name is None or role_name == "": elif role_name is None or role_name == "":
self.redirect("/panel/error?error=Invalid role name") self.redirect("/panel/error?error=Invalid role name")
return return
@ -2034,25 +2076,19 @@ class PanelHandler(BaseHandler):
return return
servers = self.get_role_servers() servers = self.get_role_servers()
permissions_mask = self.get_perms_server()
role_id = self.controller.roles.add_role(role_name) role_id = self.controller.roles.add_role(role_name)
self.controller.roles.update_role( # TODO: use add_role_advanced when API v2 gets merged
role_id, {"servers": servers}, permissions_mask for server in servers:
) PermissionsServers.get_or_create(role_id, server[0], server[1])
self.controller.management.add_to_audit_log( self.controller.management.add_to_audit_log(
exec_user["user_id"], exec_user["user_id"],
f"Added role {role_name} (RID:{role_id})", f"created role {role_name} (RID:{role_id})",
server_id=0,
source_ip=self.get_remote_ip(),
)
self.controller.management.add_to_audit_log(
exec_user["user_id"],
f"Edited role {role_name} (RID:{role_id}) with servers {servers}",
server_id=0, server_id=0,
source_ip=self.get_remote_ip(), source_ip=self.get_remote_ip(),
) )
self.controller.crafty_perms.add_role_creation(exec_user["user_id"])
self.redirect("/panel/panel_config") self.redirect("/panel/panel_config")
else: else:

View File

@ -37,180 +37,238 @@
<div class="col-sm-12 grid-margin"> <div class="col-sm-12 grid-margin">
<div class="card"> <div class="card">
<div class="card-body pt-0"> <div class="card-body pt-0">
<ul class="nav nav-tabs col-md-12 tab-simple-styled " role="tablist"> <ul class="nav nav-tabs col-md-12 tab-simple-styled " role="tablist">
<li class="nav-item"> <li class="nav-item">
<a class="nav-link active" href="/panel/edit_role?id={{ data['role']['role_id'] }}&subpage=config" role="tab" aria-selected="true"> <a class="nav-link active" href="/panel/edit_role?id={{ data['role']['role_id'] }}&subpage=config" role="tab" aria-selected="true">
<i class="fas fa-cogs"></i>{{ translate('rolesConfig', 'config', data['lang']) }}</a> <i class="fas fa-cogs"></i>{{ translate('rolesConfig', 'config', data['lang']) }}</a>
</li> </li>
<!-- <li class="nav-item"> <!-- <li class="nav-item">
<a class="nav-link" href="/panel/edit_role?id={{ data['role']['role_id'] }}&subpage=other" role="tab" aria-selected="false"> <a class="nav-link" href="/panel/edit_role?id={{ data['role']['role_id'] }}&subpage=other" role="tab" aria-selected="false">
<i class="fas fa-folder-tree"></i>Other</a> <i class="fas fa-folder-tree"></i>Other</a>
</li> --> </li> -->
</ul> </ul>
<div class="row"> <div class="">
<div class="col-md-6 col-sm-12"> <div class="">
{% if data['new_role'] %} <form class="forms-sample" method="post" action="{{ '/panel/add_role' if data['new_role'] else '/panel/edit_role' }}">
<form class="forms-sample" method="post" action="/panel/add_role"> {% raw xsrf_form_html() %}
{% else %} <input type="hidden" name="id" value="{{ data['role']['role_id'] }}">
<form class="forms-sample" method="post" action="/panel/edit_role"> <input type="hidden" name="subpage" value="config">
{% end %}
{% raw xsrf_form_html() %}
<input type="hidden" name="id" value="{{ data['role']['role_id'] }}">
<input type="hidden" name="subpage" value="config">
<div class="card"> <div class="card">
<div class="card-header header-sm d-flex justify-content-between align-items-center"> <div class="card-header header-sm d-flex justify-content-between align-items-center">
<h4 class="card-title"><i class="fas fa-user-tag"></i> {{ translate('rolesConfig', 'roleTitle', data['lang']) }}</h4> <h4 class="card-title"><i class="fas fa-user-tag"></i> {{ translate('rolesConfig', 'roleTitle', data['lang']) }}</h4>
</div> </div>
<div class="card-body"> <div class="card-body">
<div class="form-group"> <div class="form-group">
<label for="role_name">{{ translate('rolesConfig', 'roleName', data['lang']) }} <small class="text-muted ml-1"> - {{ translate('rolesConfig', 'roleDesc', data['lang']) }}</small> </label> <label for="role_name">{{ translate('rolesConfig', 'roleName', data['lang']) }} <small class="text-muted ml-1"> - {{ translate('rolesConfig', 'roleDesc', data['lang']) }}</small> </label>
<input type="text" class="form-control" name="role_name" id="role_name" value="{{ data['role']['role_name'] }}" placeholder="Role Name" > <input type="text" class="form-control" name="role_name" id="role_name" value="{{ data['role']['role_name'] }}" placeholder="Role Name" >
</div>
</div>
</div>
<div class="card">
<div class="card-header header-sm d-flex justify-content-between align-items-center">
<h4 class="card-title"><i class="fas fa-server"></i> {{ translate('rolesConfig', 'roleServers', data['lang']) }} <small class="text-muted ml-1"> {{ translate('rolesConfig', 'serversDesc', data['lang']) }}</small> </h4>
</div>
<div class="card-body">
<div class="form-group">
<div class="table-responsive">
<table class="table table-hover">
<thead>
<tr class="rounded">
<th>{{ translate('rolesConfig', 'serverName', data['lang']) }}</th>
<th>{{ translate('rolesConfig', 'serverAccess', data['lang']) }}</th>
</tr>
</thead>
<tbody>
{% for server in data['servers_all'] %}
<tr>
<td>{{ server['server_name'] }}</td>
<td>
{% if server['server_id'] in data['role']['servers'] %}
<input type="checkbox" class="" id="server_{{ server['server_id'] }}_access" name="server_{{ server['server_id'] }}_access" checked="" value="1">
{% else %}
<input type="checkbox" class="" id="server_{{ server['server_id'] }}_access" name="server_{{ server['server_id'] }}_access" value="1">
{% end %}
</td>
</tr>
{% end %}
</tbody>
</table>
</div>
</div>
</div>
</div>
<div class="card">
<div class="card-header header-sm d-flex justify-content-between align-items-center">
<h4 class="card-title"><i class="fas fa-user-lock"></i> {{ translate('rolesConfig', 'rolePerms', data['lang']) }}<small class="text-muted ml-1"> - {{ translate('rolesConfig', 'permsServer', data['lang']) }} </small></h4>
</div>
<div class="card-body">
<div class="form-group">
<div class="table-responsive">
<table class="table table-hover">
<thead>
<tr class="rounded">
<th>{{ translate('rolesConfig', 'permName', data['lang']) }}</th>
<th>{{ translate('rolesConfig', 'permAccess', data['lang']) }}</th>
</tr>
</thead>
<tbody>
{% for permission in data['permissions_all'] %}
<tr>
<td>{{ permission.name }}</td>
<td>
{% if permission in data['permissions_list'] %}
<input type="checkbox" class="" id="permission_{{ permission.name }}" name="permission_{{ permission.name }}" checked="" value="1">
{% else %}
<input type="checkbox" class="" id="permission_{{ permission.name }}" name="permission_{{ permission.name }}" value="1">
{% end %}
</td>
</tr>
{% end %}
</tbody>
</table>
</div>
</div>
</div>
</div>
<button type="submit" class="btn btn-success mr-2"><i class="fas fa-save"></i> {{ translate('panelConfig', 'save', data['lang']) }}</button>
<button type="reset" onclick="location.href='/panel/panel_config'" class="btn btn-light"><i class="fas fa-undo-alt"></i> {{ translate('panelConfig', 'cancel', data['lang']) }}</button>
</form>
</div>
<div class="col-md-3 col-sm-12">
<div class="card">
<div class="card-header header-sm d-flex justify-content-between align-items-center">
<h4 class="card-title"><i class="fas fa-users"></i> {{ translate('rolesConfig', 'roleUsers', data['lang']) }}</h4>
</div> </div>
<div class="card-body"> </div>
<div class="table-responsive"> </div>
<table class="table table-hover">
<div class="card">
<div class="card-header header-sm d-flex justify-content-between align-items-center">
<h4 class="card-title"><i class="fas fa-server"></i> {{ translate('rolesConfig', 'roleServers', data['lang']) }} <small class="text-muted ml-1"> {{ translate('rolesConfig', 'serversDesc', data['lang']) }}</small> </h4>
</div>
<div class="card-body">
<div class="form-group">
<div class="table-responsive rotate-table-parent">
<table class="table table-hover rotate-table">
<thead> <thead>
<style>
.rotate-table-parent {
padding-top: 2.5rem;
padding-right: 4rem;
}
/* https://css-tricks.com/rotated-table-column-headers-now-with-fewer-magic-numbers/ */
table.rotate-table {
--table-border-width: 1px;
border-collapse: collapse;
}
th.rotate-column-header {
/* Something you can count on */
height: 140px;
white-space: nowrap;
}
th.rotate-column-header > div {
transform:
/* Magic Numbers */
translate(0px, 51px)
/* 315 is 360 - 45 */
rotate(315deg);
width: 30px;
}
th.rotate-column-header > div > span {
border-bottom: 1px solid #ccc;
padding: 5px 10px;
}
th.rotate {
white-space: nowrap;
position: relative;
}
th.rotate > div {
/* place div at bottom left of the th parent */
position: absolute;
bottom: 0;
left: 0;
/* Make sure short labels still meet the corner of the parent otherwise you'll get a gap */
text-align: left;
/* Move the top left corner of the span's bottom-border to line up with the top left corner of the td's border-right border so that the border corners are matched
* Rotate 315 (-45) degrees about matched border corners */
transform:
translate(calc(100% - var(--table-border-width) / 2), var(--table-border-width))
rotate(-45deg);
transform-origin: 0% calc(100% - var(--table-border-width));
transition: transform 500ms;
width: 100%;
}
th.rotate > div > span {
/* make sure the bottom of the span is matched up with the bottom of the parent div */
position: absolute;
bottom: 0;
left: 0;
border-bottom: var(--table-border-width) solid #383e5d;
transition: border-bottom-color 500ms;
padding-bottom: 5px;
user-select: none;
}
table.rotate-table > tbody td {
border-right: var(--table-border-width) solid #383e5d;
/* make sure this is at least as wide as sqrt(2) * height of the tallest letter in your font or the headers will overlap each other*/
min-width: 30px;
padding-top: 2px;
padding-left: 5px;
text-align: right;
}
@media screen and (min-width: 1650px) {
th.rotate > div {
transform: translate(15px, 0px) rotate(0deg);
}
th.rotate > div > span {
border-bottom-color: transparent;
}
}
</style>
<tr class="rounded"> <tr class="rounded">
<th>{{ translate('rolesConfig', 'roleUserName', data['lang']) }}</th> <th>{{ translate('rolesConfig', 'serverName', data['lang']) }}</th>
<th></th> <th class="rotate"><div><span>{{ translate('rolesConfig', 'serverAccess', data['lang']) }}</span></div></th>
{% for permission in data['permissions_all'] %}
<th class="rotate"><div><span>{{ permission.name }}</span></div></th>
{% end %}
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for user in data['users'] %} {% for server in data['servers_all'] %}
{% for ruser in data['user-roles'][user.user_id] %} <tr>
{% if ruser == data['role']['role_name'] %} <td>{{ server['server_name'] }}</td>
<tr> <td>
<td>{{ user.username }}</td> <input type="checkbox" class="" onclick="enable_disable(event)" data-id="{{server['server_id']}}"
<td> id="server_{{ server['server_id'] }}_access"
<a href="/panel/edit_user?id={{user.user_id}}"><i class="fas fa-user-edit"></i></a> name="server_{{ server['server_id'] }}_access"
</td> {{ 'checked' if server['server_id'] in data['role']['servers'] else '' }}
</tr> autocomplete="off" value="1">
</td>
{% for permission in data['permissions_all'] %}
{% if server['server_id'] in data['role']['servers'] %}
<td>
<input type="checkbox" class="{{server['server_id']}}_perms"
id="permission_{{ server['server_id'] }}_{{ permission.name }}"
name="permission_{{ server['server_id'] }}_{{ permission.name }}"
{{ 'checked' if permission in data['permissions_dict'].get(server['server_id'], []) else '' }}
autocomplete="off" value="1">
</td>
{% else %}
<td>
<input type="checkbox" class="{{server['server_id']}}_perms"
id="permission_{{ server['server_id'] }}_{{ permission.name }}"
name="permission_{{ server['server_id'] }}_{{ permission.name }}"
autocomplete="off" value="1" disabled>
</td>
{% end %} {% end %}
{% end %} {% end %}
</tr>
{% end %} {% end %}
</tbody> </tbody>
</table> </table>
</div> </div>
</div> </div>
</div> </div>
<br> </div>
</div>
<div class="col-md-3 col-sm-12"> <div class="card">
<div class="card"> <div class="card-header header-sm d-flex justify-content-between align-items-center">
<div class="card-body"> <h4 class="card-title"><i class="fas fa-settings"></i> {{ translate('panelConfig', 'save', data['lang']) }}</h4>
<h4 class="card-title">{{ translate('rolesConfig', 'roleConfigArea', data['lang']) }}</h4>
<p class="card-description"> {{ translate('rolesConfig', 'configDesc', data['lang']) }}</p>
<blockquote class="blockquote">
<p class="mb-0">
{{ translate('rolesConfig', 'created', data['lang']) }} {{ str(data['role']['created']) }}
<br />
{{ translate('rolesConfig', 'configUpdate', data['lang']) }} {{ str(data['role']['last_update']) }}
<br />
</p>
</blockquote>
<div class="text-center">
{% if data['new_role'] %}
<a class="btn btn-sm btn-danger disabled"><i class="fas fa-trash"></i>{{ translate('rolesConfig', 'delRole', data['lang']) }}</a><br />
<small>{{ translate('rolesConfig', 'doesNotExist', data['lang']) }}</small>
{% else %}
<a href="/panel/remove_role?id={{ data['role']['role_id'] }}" class="btn btn-sm btn-danger"><i class="fas fa-trash"></i>{{ translate('rolesConfig', 'delRole', data['lang']) }}</a>
{% end %}
</div>
</div>
</div> </div>
<div class="card-body">
<button type="submit" class="btn btn-success mr-2"><i class="fas fa-save"></i> {{ translate('panelConfig', 'save', data['lang']) }}</button>
<button type="reset" onclick="location.href='/panel/panel_config'" class="btn btn-light"><i class="fas fa-undo-alt"></i> {{ translate('panelConfig', 'cancel', data['lang']) }}</button>
</div>
</div>
</form>
<div class="card">
<div class="card-header header-sm d-flex justify-content-between align-items-center">
<h4 class="card-title"><i class="fas fa-users"></i> {{ translate('rolesConfig', 'roleUsers', data['lang']) }}</h4>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-hover">
<thead>
<tr class="rounded">
<th>{{ translate('rolesConfig', 'roleUserName', data['lang']) }}</th>
<th></th>
</tr>
</thead>
<tbody>
{% for user in data['users'] %}
{% for ruser in data['user-roles'][user.user_id] %}
{% if ruser == data['role']['role_name'] %}
<tr>
<td>{{ user.username }}</td>
<td>
<a href="/panel/edit_user?id={{user.user_id}}"><i class="fas fa-user-edit"></i></a>
</td>
</tr>
{% end %}
{% end %}
{% end %}
</tbody>
</table>
</div>
</div>
</div>
<div class="card">
<div class="card-body">
<h4 class="card-title">{{ translate('rolesConfig', 'roleConfigArea', data['lang']) }}</h4>
<p class="card-description"> {{ translate('rolesConfig', 'configDesc', data['lang']) }}</p>
<blockquote class="blockquote">
<p class="mb-0">
{{ translate('rolesConfig', 'created', data['lang']) }} {{ str(data['role']['created']) }}
<br />
{{ translate('rolesConfig', 'configUpdate', data['lang']) }} {{ str(data['role']['last_update']) }}
<br />
</p>
</blockquote>
<div class="text-center">
{% if data['new_role'] %}
<a class="btn btn-sm btn-danger disabled"><i class="fas fa-trash"></i>{{ translate('rolesConfig', 'delRole', data['lang']) }}</a><br />
<small>{{ translate('rolesConfig', 'doesNotExist', data['lang']) }}</small>
{% else %}
<a href="/panel/remove_role?id={{ data['role']['role_id'] }}" class="btn btn-sm btn-danger"><i class="fas fa-trash"></i>{{ translate('rolesConfig', 'delRole', data['lang']) }}</a>
{% end %}
</div>
</div> </div>
</div> </div>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<!-- content-wrapper ends --> <!-- content-wrapper ends -->
@ -219,7 +277,19 @@
{% block js %} {% block js %}
<script> <script>
function enable_disable(event) {
let server_id = event.target.getAttribute('data-id');
console.log(server_id);
if (document.getElementById("server_" + server_id + "_access").checked) {
$('.'+server_id+'_perms').attr('disabled', false);
$('.'+server_id+'_perms').attr('enabled', true);
}else{
$('.'+server_id+'_perms').prop('checked', false);
$('.'+server_id+'_perms').attr('disabled', true);
$('.'+server_id+'_perms').attr('enabled', false);
}
}
//used to get cookies from browser - this is part of tornados xsrf protection - it's for extra security //used to get cookies from browser - this is part of tornados xsrf protection - it's for extra security
function getCookie(name) { function getCookie(name) {
var r = document.cookie.match("\\b" + name + "=([^;]*)\\b"); var r = document.cookie.match("\\b" + name + "=([^;]*)\\b");
@ -233,4 +303,4 @@
</script> </script>
{% end %} {% end %}

View File

@ -158,13 +158,14 @@
</div> </div>
<!-- Put Permissions Crafty part here --> <!-- Put Permissions Crafty part here -->
{% if data['superuser'] %}
<div class="card"> <div class="card">
<div class="card-header header-sm d-flex justify-content-between align-items-center"> <div class="card-header header-sm d-flex justify-content-between align-items-center">
<h4 class="card-title"><i class="fas fa-user-lock"></i> {{ translate('userConfig', 'craftyPerms', <h4 class="card-title"><i class="fas fa-user-lock"></i> {{ translate('userConfig', 'craftyPerms',
data['lang']) }} <small class="text-muted ml-1"> - {{ translate('userConfig', 'craftyPermDesc', data['lang']) }} <small class="text-muted ml-1"> - {{ translate('userConfig', 'craftyPermDesc',
data['lang']) }}</small></h4> data['lang']) }}</small></h4>
</div> </div>
<div class="card-body"> <div class="card-body">
<div class="form-group"> <div class="form-group">
<div class="table-responsive"> <div class="table-responsive">
@ -200,7 +201,7 @@
</div> </div>
</div> </div>
</div> </div>
{% end %}
<div class="form-check-flat"> <div class="form-check-flat">
<label for="enabled" class="form-check-label ml-4 mb-4"> <label for="enabled" class="form-check-label ml-4 mb-4">
{% if data['user']['enabled'] %} {% if data['user']['enabled'] %}

View File

@ -389,7 +389,7 @@
"command": "Command", "command": "Command",
"command-explain": "What command do you want us to execute? Do not include the '/'", "command-explain": "What command do you want us to execute? Do not include the '/'",
"cron": "Cron", "cron": "Cron",
"cron-explain": "Enter your cron string", "cron-explain": "Enter your cron string -- NOTE: 0 = Monday on last option.",
"custom": "Custom Command", "custom": "Custom Command",
"days": "Days", "days": "Days",
"enabled": "Enabled", "enabled": "Enabled",

View File

@ -168,7 +168,7 @@ if __name__ == "__main__":
if not controller.check_system_user(): if not controller.check_system_user():
controller.add_system_user() controller.add_system_user()
Crafty = MainPrompt(helper, tasks_manager, migration_manager) Crafty = MainPrompt(helper, tasks_manager, migration_manager, controller)
project_root = os.path.dirname(__file__) project_root = os.path.dirname(__file__)
controller.set_project_root(project_root) controller.set_project_root(project_root)

View File

@ -4,7 +4,7 @@ argon2-cffi==20.1
bleach==4.1 bleach==4.1
cached_property==1.5.2 cached_property==1.5.2
colorama==0.4 colorama==0.4
cron-validator==1.0.3 crontier==1.3.5
cryptography==3.4.8 cryptography==3.4.8
libgravatar==1.0.0 libgravatar==1.0.0
peewee==3.13 peewee==3.13