diff --git a/.pylintrc b/.pylintrc index ba047ba8..a7573c8e 100644 --- a/.pylintrc +++ b/.pylintrc @@ -443,7 +443,7 @@ ignored-classes=optparse.Values,thread._local,_thread._local # (useful for modules/projects where namespaces are manipulated during runtime # and thus existing member attributes cannot be deduced by static analysis). It # supports qualified module names, as well as Unix pattern matching. -ignored-modules= +ignored-modules=jsonschema,orjson # Show a hint with possible names when a member name was not found. The aspect # of finding the hint is based on edit distance. diff --git a/app/classes/controllers/users_controller.py b/app/classes/controllers/users_controller.py index 569de4a6..8874804e 100644 --- a/app/classes/controllers/users_controller.py +++ b/app/classes/controllers/users_controller.py @@ -11,6 +11,11 @@ logger = logging.getLogger(__name__) class UsersController: + class ApiPermissionDict(t.TypedDict): + name: str + quantity: int + enabled: bool + def __init__(self, helper, users_helper, authentication): self.helper = helper self.users_helper = users_helper diff --git a/app/classes/models/crafty_permissions.py b/app/classes/models/crafty_permissions.py index 73b59fd8..74d9e572 100644 --- a/app/classes/models/crafty_permissions.py +++ b/app/classes/models/crafty_permissions.py @@ -1,4 +1,5 @@ import logging +import typing as t from enum import Enum from peewee import ( ForeignKeyField, @@ -99,7 +100,7 @@ class PermissionsCrafty: try: user_crafty = UserCrafty.get(UserCrafty.user_id == user_id) except DoesNotExist: - user_crafty = UserCrafty.insert( + UserCrafty.insert( { UserCrafty.user_id: user_id, UserCrafty.permissions: "000", @@ -114,6 +115,13 @@ class PermissionsCrafty: user_crafty = PermissionsCrafty.get_user_crafty(user_id) return user_crafty + @staticmethod + def get_user_crafty_optional(user_id) -> t.Optional[UserCrafty]: + try: + return UserCrafty.get(UserCrafty.user_id == user_id) + except DoesNotExist: + return None + @staticmethod def add_user_crafty(user_id, uc_permissions): user_crafty = UserCrafty.insert( diff --git a/app/classes/models/roles.py b/app/classes/models/roles.py index 4bd58906..60338475 100644 --- a/app/classes/models/roles.py +++ b/app/classes/models/roles.py @@ -66,10 +66,9 @@ class HelperRoles: @staticmethod def get_role_column(role_id: t.Union[str, int], column_name: str) -> t.Any: column = getattr(Roles, column_name) - return model_to_dict( - Roles.select(column).where(Roles.role_id == role_id).get(), - only=[column], - )[column_name] + return getattr( + Roles.select(column).where(Roles.role_id == role_id).get(), column_name + ) @staticmethod def add_role(role_name): diff --git a/app/classes/models/server_permissions.py b/app/classes/models/server_permissions.py index afdf428c..529e279d 100644 --- a/app/classes/models/server_permissions.py +++ b/app/classes/models/server_permissions.py @@ -205,7 +205,7 @@ class PermissionsServers: @staticmethod def get_user_permissions_mask(user: Users, server_id: str): if user.superuser: - permissions_mask = "1" * len(PermissionsServers.get_permissions_list()) + permissions_mask = "1" * len(EnumPermissionsServer) else: roles_list = HelperUsers.get_user_roles_id(user.user_id) role_server = ( @@ -217,7 +217,7 @@ class PermissionsServers: try: permissions_mask = role_server[0].permissions except IndexError: - permissions_mask = "0" * len(PermissionsServers.get_permissions_list()) + permissions_mask = "0" * len(EnumPermissionsServer) return permissions_mask @staticmethod diff --git a/app/classes/models/servers.py b/app/classes/models/servers.py index c2c449d7..42b119a4 100644 --- a/app/classes/models/servers.py +++ b/app/classes/models/servers.py @@ -143,10 +143,10 @@ class HelperServers: @staticmethod def get_server_column(server_id: t.Union[str, int], column_name: str) -> t.Any: column = getattr(Servers, column_name) - return model_to_dict( + return getattr( Servers.select(column).where(Servers.server_id == server_id).get(), - only=[column], - )[column_name] + column_name, + ) # ********************************************************************************** # Servers Methods diff --git a/app/classes/models/users.py b/app/classes/models/users.py index 235df1cd..620a6688 100644 --- a/app/classes/models/users.py +++ b/app/classes/models/users.py @@ -165,19 +165,10 @@ class HelperUsers: @staticmethod def get_user_column(user_id: t.Union[str, int], column_name: str) -> t.Any: column = getattr(Users, column_name) - return model_to_dict( + return getattr( Users.select(column).where(Users.user_id == user_id).get(), - only=[column], - )[column_name] - - @staticmethod - def check_system_user(user_id): - try: - result = Users.get(Users.user_id == user_id).user_id == user_id - if result: - return True - except: - return False + column_name, + ) @staticmethod def get_user_model(user_id: str) -> Users: diff --git a/app/classes/shared/main_controller.py b/app/classes/shared/main_controller.py index c2cf04c4..d3a7bf7b 100644 --- a/app/classes/shared/main_controller.py +++ b/app/classes/shared/main_controller.py @@ -147,10 +147,7 @@ class Controller: @staticmethod def check_system_user(): - if HelperUsers.get_user_id_by_name("system") is not None: - return True - else: - return False + return HelperUsers.get_user_id_by_name("system") is not None def set_project_root(self, root_dir): self.project_root = root_dir @@ -393,12 +390,12 @@ class Controller: + ("" if empty else f"\nserver-port={port}") ) + server_file = "server.jar" # HACK: Throw this horrible default out of here root_create_data = data[data["create_type"] + "_create_data"] create_data = root_create_data[root_create_data["create_type"] + "_create_data"] if data["create_type"] == "minecraft_java": if root_create_data["create_type"] == "download_jar": server_file = f"{create_data['type']}-{create_data['version']}.jar" - full_jar_path = os.path.join(new_server_path, server_file) # Create an EULA file with open( @@ -409,16 +406,18 @@ class Controller: ) elif root_create_data["create_type"] == "import_server": _copy_import_dir_files(create_data["existing_server_path"]) - full_jar_path = os.path.join(new_server_path, create_data["jarfile"]) + server_file = create_data["jarfile"] elif root_create_data["create_type"] == "import_zip": # TODO: Copy files from the zip file to the new server directory - full_jar_path = os.path.join(new_server_path, create_data["jarfile"]) + server_file = create_data["jarfile"] raise Exception("Not yet implemented") _create_server_properties_if_needed(create_data["server_properties_port"]) min_mem = create_data["mem_min"] max_mem = create_data["mem_max"] + full_jar_path = os.path.join(new_server_path, server_file) + def _gibs_to_mibs(gibs: float) -> str: return str(int(gibs * 1024)) @@ -448,7 +447,9 @@ class Controller: _create_server_properties_if_needed(0, True) server_command = create_data["command"] - server_file = "" + server_file = ( + "./bedrock_server" # HACK: This is a hack to make the server start + ) elif data["create_type"] == "custom": # TODO: working_directory, executable_update if root_create_data["create_type"] == "raw_exec": @@ -468,7 +469,11 @@ class Controller: _create_server_properties_if_needed(0, True) server_command = create_data["command"] - server_file = root_create_data["executable_update"].get("file", "") + + server_file_new = root_create_data["executable_update"].get("file", "") + if server_file_new != "": + # HACK: Horrible hack to make the server start + server_file = server_file_new stop_command = data.get("stop_command", "") if stop_command == "": @@ -478,7 +483,7 @@ class Controller: log_location = data.get("log_location", "") if log_location == "": # TODO: different default log locations for server creation types - log_location = "/logs/latest.log" + log_location = "./logs/latest.log" if data["monitoring_type"] == "minecraft_java": monitoring_port = data["minecraft_java_monitoring_data"]["port"] @@ -490,7 +495,7 @@ class Controller: monitoring_type = "minecraft-bedrock" elif data["monitoring_type"] == "none": # TODO: this needs to be NUKED.. - # There shouldn't be anything set if there are nothing to monitor + # There shouldn't be anything set if there is nothing to monitor monitoring_port = 25565 monitoring_host = "127.0.0.1" monitoring_type = "minecraft-java" diff --git a/app/classes/web/base_handler.py b/app/classes/web/base_handler.py index c7aca58c..8a649d78 100644 --- a/app/classes/web/base_handler.py +++ b/app/classes/web/base_handler.py @@ -238,4 +238,4 @@ class BaseHandler(tornado.web.RequestHandler): def finish_json(self, status: int, data: t.Dict[str, t.Any]): self.set_status(status) self.set_header("Content-Type", "application/json") - self.finish(orjson.dumps(data)) # pylint: disable=no-member + self.finish(orjson.dumps(data)) diff --git a/app/classes/web/panel_handler.py b/app/classes/web/panel_handler.py index 4512cf48..deac2db1 100644 --- a/app/classes/web/panel_handler.py +++ b/app/classes/web/panel_handler.py @@ -16,12 +16,9 @@ from tornado import iostream # TZLocal is set as a hidden import on win pipeline from tzlocal import get_localzone from croniter import croniter +from app.classes.controllers.roles_controller import RolesController -from app.classes.models.roles import HelperRoles -from app.classes.models.server_permissions import ( - EnumPermissionsServer, - PermissionsServers, -) +from app.classes.models.server_permissions import EnumPermissionsServer from app.classes.models.crafty_permissions import EnumPermissionsCrafty from app.classes.models.management import HelpersManagement from app.classes.shared.helpers import Helpers @@ -40,8 +37,8 @@ class PanelHandler(BaseHandler): user_roles[user_id] = user_roles_list return user_roles - def get_role_servers(self) -> t.Set[int]: - servers = set() + def get_role_servers(self) -> t.List[RolesController.RoleServerJsonType]: + servers = [] for server in self.controller.list_defined_servers(): argument = self.get_argument(f"server_{server['server_id']}_access", "0") if argument == "0": @@ -57,7 +54,9 @@ class PanelHandler(BaseHandler): permission_mask, permission, "1" ) - servers.add((server["server_id"], permission_mask)) + servers.append( + {"server_id": server["server_id"], "permissions": permission_mask} + ) return servers def get_perms_quantity(self) -> t.Tuple[str, dict]: @@ -2016,35 +2015,7 @@ class PanelHandler(BaseHandler): servers = self.get_role_servers() - # 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.roles.update_role_advanced(role_id, role_name, servers) self.controller.management.add_to_audit_log( exec_user["user_id"], @@ -2081,10 +2052,7 @@ class PanelHandler(BaseHandler): servers = self.get_role_servers() - role_id = self.controller.roles.add_role(role_name) - # TODO: use add_role_advanced when API v2 gets merged - for server in servers: - PermissionsServers.get_or_create(role_id, server[0], server[1]) + role_id = self.controller.roles.add_role_advanced(role_name, servers) self.controller.management.add_to_audit_log( exec_user["user_id"], diff --git a/app/classes/web/routes/api/api_handlers.py b/app/classes/web/routes/api/api_handlers.py index e9218830..6c032811 100644 --- a/app/classes/web/routes/api/api_handlers.py +++ b/app/classes/web/routes/api/api_handlers.py @@ -25,6 +25,9 @@ from app.classes.web.routes.api.servers.server.stats import ApiServersServerStat from app.classes.web.routes.api.servers.server.users import ApiServersServerUsersHandler from app.classes.web.routes.api.users.index import ApiUsersIndexHandler from app.classes.web.routes.api.users.user.index import ApiUsersUserIndexHandler +from app.classes.web.routes.api.users.user.permissions import ( + ApiUsersUserPermissionsHandler, +) from app.classes.web.routes.api.users.user.pfp import ApiUsersUserPfpHandler from app.classes.web.routes.api.users.user.public import ApiUsersUserPublicHandler @@ -58,6 +61,16 @@ def api_handlers(handler_args): ApiUsersUserIndexHandler, handler_args, ), + ( + r"/api/v2/users/([0-9]+)/permissions/?", + ApiUsersUserPermissionsHandler, + handler_args, + ), + ( + r"/api/v2/users/(@me)/permissions/?", + ApiUsersUserPermissionsHandler, + handler_args, + ), ( r"/api/v2/users/([0-9]+)/pfp/?", ApiUsersUserPfpHandler, diff --git a/app/classes/web/routes/api/auth/invalidate_tokens.py b/app/classes/web/routes/api/auth/invalidate_tokens.py index 6308afcd..f15bf60d 100644 --- a/app/classes/web/routes/api/auth/invalidate_tokens.py +++ b/app/classes/web/routes/api/auth/invalidate_tokens.py @@ -1,6 +1,5 @@ import datetime import logging -from app.classes.shared.console import Console from app.classes.web.base_api_handler import BaseApiHandler logger = logging.getLogger(__name__) @@ -12,8 +11,7 @@ class ApiAuthInvalidateTokensHandler(BaseApiHandler): if not auth_data: return - # TODO: Invalidate tokens - Console.info("invalidate_tokens") + logger.debug(f"Invalidate tokens for user {auth_data[4]['user_id']}") self.controller.users.raw_update_user( auth_data[4]["user_id"], {"valid_tokens_from": datetime.datetime.now()} ) diff --git a/app/classes/web/routes/api/roles/index.py b/app/classes/web/routes/api/roles/index.py index d98d8d53..11142ebc 100644 --- a/app/classes/web/routes/api/roles/index.py +++ b/app/classes/web/routes/api/roles/index.py @@ -79,8 +79,8 @@ class ApiRolesIndexHandler(BaseApiHandler): return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"}) try: - data = orjson.loads(self.request.body) # pylint: disable=no-member - except orjson.decoder.JSONDecodeError as e: # pylint: disable=no-member + data = orjson.loads(self.request.body) + except orjson.decoder.JSONDecodeError as e: return self.finish_json( 400, {"status": "error", "error": "INVALID_JSON", "error_data": str(e)} ) diff --git a/app/classes/web/routes/api/roles/role/index.py b/app/classes/web/routes/api/roles/role/index.py index 8dbb7373..43abbd55 100644 --- a/app/classes/web/routes/api/roles/role/index.py +++ b/app/classes/web/routes/api/roles/role/index.py @@ -105,8 +105,8 @@ class ApiRolesRoleIndexHandler(BaseApiHandler): return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"}) try: - data = orjson.loads(self.request.body) # pylint: disable=no-member - except orjson.decoder.JSONDecodeError as e: # pylint: disable=no-member + data = orjson.loads(self.request.body) + except orjson.decoder.JSONDecodeError as e: return self.finish_json( 400, {"status": "error", "error": "INVALID_JSON", "error_data": str(e)} ) diff --git a/app/classes/web/routes/api/servers/index.py b/app/classes/web/routes/api/servers/index.py index 04172386..f3789f62 100644 --- a/app/classes/web/routes/api/servers/index.py +++ b/app/classes/web/routes/api/servers/index.py @@ -17,24 +17,6 @@ new_server_schema = { "monitoring_type", "create_type", ], - "examples": [ - { - "name": "My Server", - "monitoring_type": "minecraft_java", - "minecraft_java_monitoring_data": {"host": "127.0.0.1", "port": 25565}, - "create_type": "minecraft_java", - "minecraft_java_create_data": { - "create_type": "download_jar", - "download_jar_create_data": { - "type": "Paper", - "version": "1.18.2", - "mem_min": 1, - "mem_max": 2, - "server_properties_port": 25565, - }, - }, - } - ], "properties": { "name": { "title": "Name", @@ -665,8 +647,8 @@ class ApiServersIndexHandler(BaseApiHandler): return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"}) try: - data = orjson.loads(self.request.body) # pylint: disable=no-member - except orjson.decoder.JSONDecodeError as e: # pylint: disable=no-member + data = orjson.loads(self.request.body) + except orjson.decoder.JSONDecodeError as e: return self.finish_json( 400, {"status": "error", "error": "INVALID_JSON", "error_data": str(e)} ) diff --git a/app/classes/web/routes/api/servers/server/index.py b/app/classes/web/routes/api/servers/server/index.py index 962f9380..ea8c71de 100644 --- a/app/classes/web/routes/api/servers/server/index.py +++ b/app/classes/web/routes/api/servers/server/index.py @@ -110,7 +110,7 @@ class ApiServersServerIndexHandler(BaseApiHandler): server_obj = self.controller.servers.get_server_obj(server_id) for key in data: # If we don't validate the input there could be security issues - setattr(self, key, data[key]) + setattr(server_obj, key, data[key]) self.controller.servers.update_server(server_obj) self.controller.management.add_to_audit_log( diff --git a/app/classes/web/routes/api/users/index.py b/app/classes/web/routes/api/users/index.py index d2a9724e..4c5a85a2 100644 --- a/app/classes/web/routes/api/users/index.py +++ b/app/classes/web/routes/api/users/index.py @@ -99,7 +99,7 @@ class ApiUsersIndexHandler(BaseApiHandler): email = data.get("email", "default@example.com") enabled = data.get("enabled", True) lang = data.get("lang", self.helper.get_setting("language")) - superuser = data.get("superuser", False) + new_superuser = data.get("superuser", False) permissions = data.get("permissions", None) roles = data.get("roles", None) hints = data.get("hints", True) @@ -134,13 +134,24 @@ class ApiUsersIndexHandler(BaseApiHandler): ) permissions_mask = "".join(permissions_mask) + if new_superuser and not superuser: + return self.finish_json( + 400, {"status": "error", "error": "INVALID_SUPERUSER_CREATE"} + ) + + if len(roles) != 0 and not superuser: + # HACK: This should check if the user has the roles or something + return self.finish_json( + 400, {"status": "error", "error": "INVALID_ROLES_CREATE"} + ) + # TODO: do this in the most efficient way user_id = self.controller.users.add_user( username, password, email, enabled, - superuser, + new_superuser, ) self.controller.users.update_user( user_id, diff --git a/app/classes/web/routes/api/users/user/index.py b/app/classes/web/routes/api/users/user/index.py index 7dbcbff3..7274611a 100644 --- a/app/classes/web/routes/api/users/user/index.py +++ b/app/classes/web/routes/api/users/user/index.py @@ -1,8 +1,13 @@ import json import logging +import typing as t from jsonschema import ValidationError, validate -from app.classes.models.crafty_permissions import EnumPermissionsCrafty +from app.classes.controllers.users_controller import UsersController +from app.classes.models.crafty_permissions import ( + EnumPermissionsCrafty, + PermissionsCrafty, +) from app.classes.models.roles import HelperRoles from app.classes.models.users import HelperUsers from app.classes.web.base_api_handler import BaseApiHandler @@ -170,7 +175,7 @@ class ApiUsersUserIndexHandler(BaseApiHandler): }, ) - if data.get("username", None) is not None: + if "username" in data: if data["username"].lower() in ["system", ""]: return self.finish_json( 400, {"status": "error", "error": "INVALID_USERNAME"} @@ -180,10 +185,10 @@ class ApiUsersUserIndexHandler(BaseApiHandler): 400, {"status": "error", "error": "USER_EXISTS"} ) - if data.get("superuser", None) is not None: - if str(user["user_id"]) == str(user_id): - # Checks if user is trying to change super user status of self. - # We don't want that. + if "superuser" in data: + if str(user["user_id"]) == str(user_id) and not superuser: + # Checks if user is trying to change super user status + # of self without superuser. We don't want that. return self.finish_json( 400, {"status": "error", "error": "INVALID_SUPERUSER_MODIFY"} ) @@ -191,10 +196,10 @@ class ApiUsersUserIndexHandler(BaseApiHandler): # The user is not superuser so they can't change the superuser status data.pop("superuser") - if data.get("permissions", None) is not None: - if str(user["user_id"]) == str(user_id): - # Checks if user is trying to change permissions of self. - # We don't want that. + if "permissions" in data: + if str(user["user_id"]) == str(user_id) and not superuser: + # Checks if user is trying to change permissions + # of self without superuser. We don't want that. return self.finish_json( 400, {"status": "error", "error": "INVALID_PERMISSIONS_MODIFY"} ) @@ -205,10 +210,10 @@ class ApiUsersUserIndexHandler(BaseApiHandler): 400, {"status": "error", "error": "INVALID_PERMISSIONS_MODIFY"} ) - if data.get("roles", None) is not None: - if str(user["user_id"]) == str(user_id): - # Checks if user is trying to change roles of self. - # We don't want that. + if "roles" in data: + if str(user["user_id"]) == str(user_id) and not superuser: + # Checks if user is trying to change roles of + # self without superuser. We don't want that. return self.finish_json( 400, {"status": "error", "error": "INVALID_ROLES_MODIFY"} ) @@ -219,10 +224,66 @@ class ApiUsersUserIndexHandler(BaseApiHandler): 400, {"status": "error", "error": "INVALID_ROLES_MODIFY"} ) - # TODO: make this more efficient - # TODO: add permissions and roles because I forgot + if "password" in data and str(user["user_id"] == str(user_id)): + # TODO: edit your own password + return self.finish_json( + 400, {"status": "error", "error": "INVALID_PASSWORD_MODIFY"} + ) + user_obj = HelperUsers.get_user_model(user_id) + if "roles" in data: + roles: t.Set[str] = set(data.pop("roles")) + base_roles: t.Set[str] = set(user_obj.roles) + added_roles = roles.difference(base_roles) + removed_roles = base_roles.difference(roles) + logger.debug( + f"updating user {user_id}'s roles: " + f"+role:{added_roles} -role:{removed_roles}" + ) + + for role_id in added_roles: + HelperUsers.get_or_create(user_id, role_id) + + if len(removed_roles) != 0: + self.controller.users.users_helper.delete_user_roles( + user_id, removed_roles + ) + + if "permissions" in data: + permissions: t.List[UsersController.ApiPermissionDict] = data.pop( + "permissions" + ) + permissions_mask = "0" * len(EnumPermissionsCrafty) + limit_server_creation = 0 + limit_user_creation = 0 + limit_role_creation = 0 + + for permission in permissions: + self.controller.crafty_perms.set_permission( + permissions_mask, + EnumPermissionsCrafty.__members__[permission["name"]], + "1" if permission["enabled"] else "0", + ) + + PermissionsCrafty.add_or_update_user( + user_id, + permissions_mask, + limit_server_creation, + limit_user_creation, + limit_role_creation, + ) + + # TODO: make this more efficient + if len(data) != 0: + for key in data: + # If we don't validate the input there could be security issues + value = data[key] + if key == "password": + value = self.helper.encode_pass(value) + setattr(user_obj, key, value) + user_obj.save() + self.controller.management.add_to_audit_log( user["user_id"], ( @@ -233,9 +294,4 @@ class ApiUsersUserIndexHandler(BaseApiHandler): source_ip=self.get_remote_ip(), ) - for key in data: - # If we don't validate the input there could be security issues - setattr(user_obj, key, data[key]) - user_obj.save() - return self.finish_json(200, {"status": "ok"}) diff --git a/app/classes/web/routes/api/users/user/permissions.py b/app/classes/web/routes/api/users/user/permissions.py new file mode 100644 index 00000000..b6c8703a --- /dev/null +++ b/app/classes/web/routes/api/users/user/permissions.py @@ -0,0 +1,73 @@ +import logging +import typing as t + +from app.classes.models.crafty_permissions import ( + EnumPermissionsCrafty, + PermissionsCrafty, +) +from app.classes.web.base_api_handler import BaseApiHandler + + +logger = logging.getLogger(__name__) + + +SERVER_CREATION: t.Final[str] = EnumPermissionsCrafty.SERVER_CREATION.name +USER_CONFIG: t.Final[str] = EnumPermissionsCrafty.USER_CONFIG.name +ROLES_CONFIG: t.Final[str] = EnumPermissionsCrafty.ROLES_CONFIG.name + + +class ApiUsersUserPermissionsHandler(BaseApiHandler): + def get(self, user_id: str): + auth_data = self.authenticate_user() + if not auth_data: + return + ( + _, + exec_user_crafty_permissions, + _, + _, + user, + ) = auth_data + + if user_id in ["@me", user["user_id"]]: + user_id = user["user_id"] + res_data = PermissionsCrafty.get_user_crafty(user_id) + elif EnumPermissionsCrafty.USER_CONFIG not in exec_user_crafty_permissions: + return self.finish_json( + 400, + { + "status": "error", + "error": "NOT_AUTHORIZED", + }, + ) + else: + # has User_Config permission and isn't viewing self + res_data = PermissionsCrafty.get_user_crafty_optional(user_id) + if res_data is None: + return self.finish_json( + 404, + { + "status": "error", + "error": "USER_NOT_FOUND", + }, + ) + + self.finish_json( + 200, + { + "status": "ok", + "data": { + "permissions": res_data.permissions, + "counters": { + SERVER_CREATION: res_data.created_server, + USER_CONFIG: res_data.created_user, + ROLES_CONFIG: res_data.created_role, + }, + "limits": { + SERVER_CREATION: res_data.limit_server_creation, + USER_CONFIG: res_data.limit_user_creation, + ROLES_CONFIG: res_data.limit_role_creation, + }, + }, + }, + ) diff --git a/requirements.txt b/requirements.txt index d57a8b2f..d57479e9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -17,5 +17,5 @@ requests==2.26 termcolor==1.1 tornado==6.0 tzlocal==4.0 -jsonschema==4.4.0 +jsonschema==4.5.1 orjson==3.6.7