diff --git a/app/classes/controllers/crafty_perms_controller.py b/app/classes/controllers/crafty_perms_controller.py index b555b97f..9c79c33a 100644 --- a/app/classes/controllers/crafty_perms_controller.py +++ b/app/classes/controllers/crafty_perms_controller.py @@ -35,20 +35,16 @@ class CraftyPermsController: ) @staticmethod - def can_add_user(): # Add back argument 'user_id' when you work on this - return True - # TODO: Complete if we need a User Addition limit - # return crafty_permissions.can_add_in_crafty( - # user_id, EnumPermissionsCrafty.USER_CONFIG - # ) + def can_add_user(user_id): + return PermissionsCrafty.can_add_in_crafty( + user_id, EnumPermissionsCrafty.USER_CONFIG + ) @staticmethod - def can_add_role(): # Add back argument 'user_id' when you work on this - return True - # TODO: Complete if we need a Role Addition limit - # return crafty_permissions.can_add_in_crafty( - # user_id, EnumPermissionsCrafty.ROLES_CONFIG - # ) + def can_add_role(user_id): + return PermissionsCrafty.can_add_in_crafty( + user_id, EnumPermissionsCrafty.ROLES_CONFIG + ) @staticmethod def list_all_crafty_permissions_quantity_limits(): @@ -76,6 +72,14 @@ class CraftyPermsController: """ 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 def get_api_key_permissions_list(key: ApiKeys): return PermissionsCrafty.get_api_key_permissions_list(key) diff --git a/app/classes/controllers/server_perms_controller.py b/app/classes/controllers/server_perms_controller.py index 3ee5ea18..95395fec 100644 --- a/app/classes/controllers/server_perms_controller.py +++ b/app/classes/controllers/server_perms_controller.py @@ -29,9 +29,8 @@ class ServerPermsController: return permissions_mask @staticmethod - def get_role_permissions(role_id): - permissions_list = PermissionsServers.get_role_permissions_list(role_id) - return permissions_list + def get_role_permissions_dict(role_id): + return PermissionsServers.get_role_permissions_dict(role_id) @staticmethod def add_role_server(server_id, role_id, rs_permissions="00000000"): @@ -71,10 +70,6 @@ class ServerPermsController: permission_mask, permission_tested, value ) - @staticmethod - def get_role_permissions_list(role_id): - return PermissionsServers.get_role_permissions_list(role_id) - @staticmethod def get_user_id_permissions_list(user_id: str, server_id: str): return PermissionsServers.get_user_id_permissions_list(user_id, server_id) diff --git a/app/classes/controllers/users_controller.py b/app/classes/controllers/users_controller.py index 20faf4d6..569de4a6 100644 --- a/app/classes/controllers/users_controller.py +++ b/app/classes/controllers/users_controller.py @@ -145,11 +145,13 @@ class UsersController: elif key == "password": if user_data["password"] is not None and 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]: up_data[key] = user_data[key] 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}") for role in added_roles: HelperUsers.get_or_create(user_id=user_id, role_id=role) diff --git a/app/classes/models/crafty_permissions.py b/app/classes/models/crafty_permissions.py index b53ae930..936d88bd 100644 --- a/app/classes/models/crafty_permissions.py +++ b/app/classes/models/crafty_permissions.py @@ -205,6 +205,20 @@ class PermissionsCrafty: UserCrafty.save(user_crafty) 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 def get_api_key_permissions_list(key: ApiKeys): user = HelperUsers.get_user(key.user_id) diff --git a/app/classes/models/server_permissions.py b/app/classes/models/server_permissions.py index 6eab88be..38773bdb 100644 --- a/app/classes/models/server_permissions.py +++ b/app/classes/models/server_permissions.py @@ -1,3 +1,4 @@ +import typing as t from enum import Enum import logging import typing as t @@ -167,6 +168,18 @@ class PermissionsServers: permissions_list = PermissionsServers.get_permissions(permissions_mask) 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 def update_role_permission(role_id, server_id, permissions_mask): role_server = ( diff --git a/app/classes/shared/command.py b/app/classes/shared/command.py index f8fa8b48..2071c645 100644 --- a/app/classes/shared/command.py +++ b/app/classes/shared/command.py @@ -3,6 +3,7 @@ import cmd import time import threading import logging +import getpass from app.classes.shared.console import Console from app.classes.shared.import3 import Import3 @@ -11,11 +12,13 @@ logger = logging.getLogger(__name__) class MainPrompt(cmd.Cmd): - def __init__(self, helper, tasks_manager, migration_manager): + def __init__(self, helper, tasks_manager, migration_manager, main_controller): super().__init__() self.helper = helper self.tasks_manager = tasks_manager self.migration_manager = migration_manager + self.controller = main_controller + # overrides the default Prompt self.prompt = f"Crafty Controller v{self.helper.get_version_string()} > " @@ -49,6 +52,37 @@ class MainPrompt(cmd.Cmd): else: 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 def do_threads(_line): for thread in threading.enumerate(): diff --git a/app/classes/web/panel_handler.py b/app/classes/web/panel_handler.py index 4fa4982a..c22656a3 100644 --- a/app/classes/web/panel_handler.py +++ b/app/classes/web/panel_handler.py @@ -15,9 +15,13 @@ from tornado import iostream # TZLocal is set as a hidden import on win pipeline 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.management import HelpersManagement from app.classes.shared.helpers import Helpers @@ -39,15 +43,21 @@ class PanelHandler(BaseHandler): def get_role_servers(self) -> t.Set[int]: servers = set() for server in self.controller.list_defined_servers(): - argument = int( - float( - bleach.clean( - self.get_argument(f"server_{server['server_id']}_access", "0") - ) + argument = self.get_argument(f"server_{server['server_id']}_access", "0") + if argument == "0": + continue + + 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: - servers.add(server["server_id"]) + if argument == "1": + permission_mask = self.controller.server_perms.set_permission( + permission_mask, permission, "1" + ) + + servers.add((server["server_id"], permission_mask)) return servers def get_perms_quantity(self) -> t.Tuple[str, dict]: @@ -85,19 +95,9 @@ class PanelHandler(BaseHandler): permission ) in self.controller.crafty_perms.list_defined_crafty_permissions(): 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, permission, 1 if argument == "1" else 0 - ) - 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 + permissions_mask, permission, "1" ) return permissions_mask @@ -158,7 +158,7 @@ class PanelHandler(BaseHandler): if not self.controller.servers.server_id_authorized_api_key( server_id, api_key ): - print( + logger.debug( f"API key {api_key.name} (id: {api_key.token_id}) " f"does not have permission" ) @@ -168,7 +168,9 @@ class PanelHandler(BaseHandler): if not self.controller.servers.server_id_authorized( 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") return None return server_id @@ -770,6 +772,7 @@ class PanelHandler(BaseHandler): page_data["user"]["last_update"] = "N/A" page_data["user"]["roles"] = set() page_data["user"]["hints"] = True + page_data["superuser"] = superuser if EnumPermissionsCrafty.USER_CONFIG not in exec_user_crafty_permissions: self.redirect( @@ -957,6 +960,7 @@ class PanelHandler(BaseHandler): page_data["role-servers"] = page_role_servers page_data["roles_all"] = self.controller.roles.get_all_roles() page_data["servers_all"] = self.controller.list_defined_servers() + page_data["superuser"] = superuser page_data[ "permissions_all" ] = self.controller.crafty_perms.list_defined_crafty_permissions() @@ -1087,7 +1091,7 @@ class PanelHandler(BaseHandler): page_data[ "permissions_all" ] = self.controller.server_perms.list_defined_permissions() - page_data["permissions_list"] = set() + page_data["permissions_dict"] = {} template = "panel/panel_edit_role.html" elif page == "edit_role": @@ -1100,8 +1104,8 @@ class PanelHandler(BaseHandler): "permissions_all" ] = self.controller.server_perms.list_defined_permissions() page_data[ - "permissions_list" - ] = self.controller.server_perms.get_role_permissions(role_id) + "permissions_dict" + ] = self.controller.server_perms.get_role_permissions_dict(role_id) page_data["user-roles"] = user_roles page_data["users"] = self.controller.users.get_all_users() @@ -1449,11 +1453,9 @@ class PanelHandler(BaseHandler): else: interval_type = "" cron_string = bleach.clean(self.get_argument("cron", "")) - try: - CronValidator.parse(cron_string) - except Exception as e: + if not croniter.is_valid(cron_string): self.redirect( - f"/panel/error?error=INVALID FORMAT: Invalid Cron Format. {e}" + "/panel/error?error=INVALID FORMAT: Invalid Cron Format." ) return action = bleach.clean(self.get_argument("action", None)) @@ -1607,11 +1609,9 @@ class PanelHandler(BaseHandler): interval_type = "" cron_string = bleach.clean(self.get_argument("cron", "")) sch_id = self.get_argument("sch_id", None) - try: - CronValidator.parse(cron_string) - except Exception as e: + if not croniter.is_valid(cron_string): self.redirect( - f"/panel/error?error=INVALID FORMAT: Invalid Cron Format. {e}" + "/panel/error?error=INVALID FORMAT: Invalid Cron Format." ) return action = bleach.clean(self.get_argument("action", None)) @@ -1933,6 +1933,15 @@ class PanelHandler(BaseHandler): "/panel/error?error=Unauthorized access: not a user editor" ) 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 == "": self.redirect("/panel/error?error=Invalid username") return @@ -1977,6 +1986,7 @@ class PanelHandler(BaseHandler): server_id=0, source_ip=self.get_remote_ip(), ) + self.controller.crafty_perms.add_user_creation(exec_user["user_id"]) self.redirect("/panel/panel_config") elif page == "edit_role": @@ -2001,16 +2011,40 @@ class PanelHandler(BaseHandler): return servers = self.get_role_servers() - permissions_mask = self.get_perms_server() - role_data = {"role_name": role_name, "servers": servers} - self.controller.roles.update_role( - role_id, role_data=role_data, permissions_mask=permissions_mask + # TODO: use update_role_advanced when API v2 gets merged + base_data = self.controller.roles.get_role_with_servers(role_id) + + 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( 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, source_ip=self.get_remote_ip(), ) @@ -2024,6 +2058,14 @@ class PanelHandler(BaseHandler): "/panel/error?error=Unauthorized access: not a role editor" ) 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 == "": self.redirect("/panel/error?error=Invalid role name") return @@ -2034,25 +2076,19 @@ class PanelHandler(BaseHandler): return servers = self.get_role_servers() - permissions_mask = self.get_perms_server() role_id = self.controller.roles.add_role(role_name) - self.controller.roles.update_role( - role_id, {"servers": servers}, permissions_mask - ) + # TODO: use add_role_advanced when API v2 gets merged + for server in servers: + PermissionsServers.get_or_create(role_id, server[0], server[1]) self.controller.management.add_to_audit_log( exec_user["user_id"], - f"Added 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}", + f"created role {role_name} (RID:{role_id})", server_id=0, source_ip=self.get_remote_ip(), ) + self.controller.crafty_perms.add_role_creation(exec_user["user_id"]) self.redirect("/panel/panel_config") else: diff --git a/app/frontend/templates/panel/panel_edit_role.html b/app/frontend/templates/panel/panel_edit_role.html index e97e081f..49559591 100644 --- a/app/frontend/templates/panel/panel_edit_role.html +++ b/app/frontend/templates/panel/panel_edit_role.html @@ -37,180 +37,238 @@