diff --git a/app/classes/controllers/users_controller.py b/app/classes/controllers/users_controller.py index 667e01b4..99147a63 100644 --- a/app/classes/controllers/users_controller.py +++ b/app/classes/controllers/users_controller.py @@ -31,7 +31,7 @@ class UsersController: for permission in PermissionsCrafty.get_permissions_list() ], }, - "quantity": {"type": "number", "minimum": 0}, + "quantity": {"type": "number", "minimum": -1}, "enabled": {"type": "boolean"}, } self.user_jsonschema_props: t.Final = { @@ -46,7 +46,7 @@ class UsersController: "password": { "type": "string", "maxLength": 20, - "minLength": 4, + "minLength": 6, "examples": ["crafty"], "title": "Password", }, @@ -73,6 +73,8 @@ class UsersController: "examples": [False], "title": "Superuser", }, + "manager": {"type": ["integer", "null"]}, + "theme": {"type": "string"}, "permissions": { "type": "array", "items": { @@ -84,7 +86,7 @@ class UsersController: "roles": { "type": "array", "items": { - "type": "string", + "type": "integer", "minLength": 1, }, }, diff --git a/app/classes/web/routes/api/users/index.py b/app/classes/web/routes/api/users/index.py index a1f849ef..f7341d38 100644 --- a/app/classes/web/routes/api/users/index.py +++ b/app/classes/web/routes/api/users/index.py @@ -93,10 +93,17 @@ class ApiUsersIndexHandler(BaseApiHandler): "error_data": str(e), }, ) - username = data["username"] username = str(username).lower() - manager = int(user["user_id"]) + manager = data.get("manager", None) + if user["superuser"]: + if ( + manager == self.controller.users.get_id_by_name("SYSTEM") + or manager == 0 + ): + manager = None + else: + manager = int(user["user_id"]) password = data["password"] email = data.get("email", "default@example.com") enabled = data.get("enabled", True) diff --git a/app/classes/web/routes/api/users/user/index.py b/app/classes/web/routes/api/users/user/index.py index 47d8dd68..898a9fda 100644 --- a/app/classes/web/routes/api/users/user/index.py +++ b/app/classes/web/routes/api/users/user/index.py @@ -166,7 +166,13 @@ class ApiUsersUserIndexHandler(BaseApiHandler): return self.finish_json( 400, {"status": "error", "error": "INVALID_USERNAME"} ) - if self.controller.users.get_id_by_name(data["username"]) is not None: + if self.controller.users.get_id_by_name( + data["username"] + ) is not None and self.controller.users.get_id_by_name( + data["username"] + ) != int( + user_id + ): return self.finish_json( 400, {"status": "error", "error": "USER_EXISTS"} ) @@ -210,13 +216,13 @@ class ApiUsersUserIndexHandler(BaseApiHandler): 400, {"status": "error", "error": "INVALID_ROLES_MODIFY"} ) - 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 "password" in data and str(user["user_id"]) != str(user_id): + if str(user["user_id"]) != str(user_obj.manager): + # TODO: edit your own password + return self.finish_json( + 400, {"status": "error", "error": "INVALID_PASSWORD_MODIFY"} + ) if "roles" in data: roles: t.Set[str] = set(data.pop("roles")) @@ -236,6 +242,13 @@ class ApiUsersUserIndexHandler(BaseApiHandler): user_id, removed_roles ) + if "manager" in data: + if ( + data["manager"] == self.controller.users.get_id_by_name("SYSTEM") + or data["manager"] == 0 + ): + data["manager"] = None + if "permissions" in data: permissions: t.List[UsersController.ApiPermissionDict] = data.pop( "permissions" @@ -246,7 +259,7 @@ class ApiUsersUserIndexHandler(BaseApiHandler): limit_role_creation = 0 for permission in permissions: - self.controller.crafty_perms.set_permission( + permissions_mask = self.controller.crafty_perms.set_permission( permissions_mask, EnumPermissionsCrafty.__members__[permission["name"]], "1" if permission["enabled"] else "0", diff --git a/app/frontend/templates/panel/panel_edit_role.html b/app/frontend/templates/panel/panel_edit_role.html index db760201..f614b7a3 100644 --- a/app/frontend/templates/panel/panel_edit_role.html +++ b/app/frontend/templates/panel/panel_edit_role.html @@ -49,7 +49,7 @@
-
+
diff --git a/app/frontend/templates/panel/panel_edit_user.html b/app/frontend/templates/panel/panel_edit_user.html index a40d2903..b61c6619 100644 --- a/app/frontend/templates/panel/panel_edit_user.html +++ b/app/frontend/templates/panel/panel_edit_user.html @@ -58,13 +58,11 @@ data['lang']) }}{% end %}
{% if data['new_user'] %} - + {% else %} - + {% end %} - {% raw xsrf_form_html() %} - - +
@@ -85,7 +83,7 @@ data['lang']) }}{% end %} }} - {{ translate('userConfig', 'leaveBlank', data['lang']) }} + autocomplete="new-password" data-lpignore="true" placeholder="Password" form="dummy"> @@ -95,7 +93,7 @@ data['lang']) }}{% end %} - {{ translate('userConfig', 'leaveBlank', data['lang']) }} + autocomplete="new-password" data-lpignore="true" placeholder="Repeat Password" form="dummy"> @@ -111,7 +109,7 @@ data['lang']) }}{% end %} + checked="" value="{{role.role_id}}" form="dummy"> {% else %} - + checked="" value="{{role.role_id}}" disabled form="dummy"> {% end %} {% elif data['superuser'] or role.manager == data['exec_user'] %} - + value="{{role.role_id}}" form="dummy"> {% end %} @@ -219,7 +217,7 @@ data['lang']) }}{% end %}
- +
@@ -233,16 +231,16 @@ data['lang']) }}{% end %} + value="{{ data['quantity_server'][permission.name] }}" data-perm="{{permission.name}}" form="dummy"> {% end %} @@ -287,7 +285,7 @@ data['lang']) }}{% end %} - @@ -363,9 +361,12 @@ data['lang']) }}{% end %} } } function validateForm() { - let password0 = document.getElementById("password0").value - let password1 = document.getElementById("password1").value - if (password0 != password1) { + let password0 = document.getElementById("password0").value; + let password1 = document.getElementById("password1").value; + if (password0 === "" && password1 === "" && userId){ + return true + } + else if (password0 != password1) { $('.passwords-match').popover('show'); $('.popover-body').click(function () { $('.passwords-match').popover("hide"); @@ -376,11 +377,103 @@ data['lang']) }}{% end %} $("#password1").css("outline", "1px solid red"); return false; } else { - return true; + return password1; } } + function replacer(key, value) { + if (typeof value == "boolean" || key === "email" || key === "permissions" || key === "roles") { + return value + } else { + console.log(key, value) + return (isNaN(value) ? value : +value); + } + } const userId = new URLSearchParams(document.location.search).get('id') + $("#user_form").on("submit", async function (e) { + e.preventDefault(); + let password = validateForm(); + if (!password){ + return; + } + var token = getCookie("_xsrf") + let userForm = document.getElementById("user_form"); + let disabled_flag = false; + let roles = $('.role_check').map(function() { + if ($(this).attr("disabled")){ + disabled_flag = true; + } + if ($(this).is(':checked')){ + return $(this).val(); + } + }).get(); + + let avail_permissions = $('.perm-name').map(function() { + return $(this).data("perm"); + }).get(); + + permissions = [] + for(i=0; i < avail_permissions.length; i++){ + permissions.push({"name": avail_permissions[i], "quantity": $(`#quantity_${avail_permissions[i]}`).val(), "enabled": $(`#permission_${avail_permissions[i]}`).is(':checked')}) + } + console.log(permissions); + + let formData = new FormData(userForm); + //Create an object from the form data entries + let formDataObject = Object.fromEntries(formData.entries()); + if (!disabled_flag){ + formDataObject.roles = roles; + } + if ($("#permissions").length){ + formDataObject.permissions = permissions; + } + if(typeof password === "string"){ + formDataObject.password = password; + } + formDataObject.enabled = $("#enabled").is(":checked"); + if ($("#superuser").is(":enabled")){ + formDataObject.superuser = $("#superuser").is(":checked"); + } + formDataObject.hints = $("#hints").is(":checked"); + console.log(formDataObject); + + //We need to make sure these are sent regardless of whether or not they're checked + + // Format the plain form data as JSON + let formDataJsonString = JSON.stringify(formDataObject, replacer); + + console.log(formDataJsonString); + if (userId){ + url = `/api/v2/users/${userId}` + method = 'PATCH' + }else{ + url = `/api/v2/users/` + method = 'POST' + } + let res = await fetch(url, { + method: method, + headers: { + 'X-XSRFToken': token + }, + body: formDataJsonString, + }); + let responseData = await res.json(); + if (responseData.status === "ok") { + window.location.href = "/panel/panel_config"; + } else { + if (responseData.hasOwnProperty("error_data")){ + bootbox.alert({ + title: responseData.error, + message: responseData.error_data + }); + }else{ + bootbox.alert(responseData.error + ); + } + } + }); + + $(".delete-user").click(function () { var file_to_del = $(this).data("file"); @@ -398,10 +491,26 @@ data['lang']) }}{% end %} label: ' {{ translate("serverBackups", "confirm", data['lang']) }}' } }, - callback: function (result) { + callback: async function (result) { console.log(result); if (result === true) { - location.href = "/panel/remove_user?id=" + userId; + var token = getCookie("_xsrf") + let res = await fetch(`/api/v2/users/${userId}`, { + method: "DELETE", + headers: { + 'X-XSRFToken': token + }, + }); + let responseData = await res.json(); + if (responseData.status === "ok") { + window.location.href = "/panel/panel_config"; + } else { + + bootbox.alert({ + title: responseData.error, + message: responseData.error + }); + } } } });
{{ translate('userConfig', 'permName', data['lang']) }}{{ permission.name }} {% if permission in data['permissions_list'] %} - + {% else %} - + {% end %}