diff --git a/.gitlab/scripts/lang_sort.sh b/.gitlab/scripts/lang_sort.sh index 5710ce1b..9a1e1cf0 100644 --- a/.gitlab/scripts/lang_sort.sh +++ b/.gitlab/scripts/lang_sort.sh @@ -56,8 +56,8 @@ get_keys "${DIR}/en_EN.json" | sort > "${ref_keys}" # Iterate over each .json file in the directory for file in "${DIR}"/*.json; do - # Check if file is a regular file and not en_EN.json, and does not contain "_incomplete" in its name - if [[ -f "${file}" && "${file}" != "${DIR}/en_EN.json" && ! "${file}" =~ _incomplete ]]; then + # Check if file is a regular file and not en_EN.json, humanized index and does not contain "_incomplete" in its name + if [[ -f "${file}" && "${file}" != "${DIR}/en_EN.json" && "${file}" != "${DIR}/humanized_index.json" && ! "${file}" =~ _incomplete ]]; then # Get keys and subkeys from the current file current_keys=$(mktemp) diff --git a/CHANGELOG.md b/CHANGELOG.md index ee1e8ef5..8d2e1b61 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,15 +5,24 @@ TBD ### Bug fixes - Fix zip imports so the root dir selection is functional ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/764)) - Fix bug where full access gives minimal access ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/768)) +- Bump tornado & requests for sec advisories ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/774)) - Ensure audit.log exists or create it on Crafty startup ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/771)) +- Fix typing issue on ID comparison causing general users to not be able to delete their own API keys ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/775)) +- Fix user creation bug where it would fail when a role was selected ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/763)) +- Security improvements for general user creations on roles page ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/763)) +- Security improvements for general user creations on user page ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/763)) +- Use UTC for tokens_valid_from in user config, to resolve token invalidation on instance TZ change ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/765)) ### Tweaks - Add info note to default creds file ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/760)) - Remove navigation label from sidebar ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/766)) - Do not allow slashes in server names ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/767)) - Add a thread dump to support logs ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/769)) - Remove text from status page and use symbols ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/770)) +- Add better feedback on when errors appear on user creation ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/763)) ### Lang +- Show natural language name instead of country code in User Config Lang select list ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/773)) - Add remaining `he_IL`, `th_TH` translations for 4.4.0 Release ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/761)) +- Mark `he_IL` incomplete ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/763))

## --- [4.4.0] - 2024/05/11 diff --git a/app/classes/controllers/users_controller.py b/app/classes/controllers/users_controller.py index 5425fbf8..d45797bd 100644 --- a/app/classes/controllers/users_controller.py +++ b/app/classes/controllers/users_controller.py @@ -55,6 +55,7 @@ class UsersController: "minLength": self.helper.minimum_password_length, "examples": ["crafty"], "title": "Password", + "error": "passLength", }, "email": { "type": "string", diff --git a/app/classes/models/users.py b/app/classes/models/users.py index 3f96e651..6f6a6bde 100644 --- a/app/classes/models/users.py +++ b/app/classes/models/users.py @@ -38,7 +38,7 @@ class Users(BaseModel): superuser = BooleanField(default=False) lang = CharField(default="en_EN") support_logs = CharField(default="") - valid_tokens_from = DateTimeField(default=datetime.datetime.now) + valid_tokens_from = DateTimeField(default=Helpers.get_utc_now) server_order = CharField(default="") preparing = BooleanField(default=False) hints = BooleanField(default=True) @@ -119,7 +119,6 @@ class HelperUsers: @staticmethod def get_user_total(): count = Users.select().where(Users.username != "system").count() - print(count) return count @staticmethod diff --git a/app/classes/shared/authentication.py b/app/classes/shared/authentication.py index fad8b730..94db5532 100644 --- a/app/classes/shared/authentication.py +++ b/app/classes/shared/authentication.py @@ -1,5 +1,6 @@ import logging import time +from datetime import datetime from typing import Optional, Dict, Any, Tuple import jwt from jwt import PyJWTError @@ -62,7 +63,17 @@ class Authentication: user = HelperUsers.get_user(user_id) # TODO: Have a cache or something so we don't constantly # have to query the database - if int(user.get("valid_tokens_from").timestamp()) < iat: + valid_tokens_from_str = user.get("valid_tokens_from") + # It's possible this will be a string or a dt coming from the DB + # We need to account for that + try: + valid_tokens_from_dt = datetime.strptime( + valid_tokens_from_str, "%Y-%m-%d %H:%M:%S.%f%z" + ) + except TypeError: + valid_tokens_from_dt = valid_tokens_from_str + # Convert the string to a datetime object + if int(valid_tokens_from_dt.timestamp()) < iat: # Success! return key, data, user return None diff --git a/app/classes/shared/helpers.py b/app/classes/shared/helpers.py index 55a588fc..64d4e1d1 100644 --- a/app/classes/shared/helpers.py +++ b/app/classes/shared/helpers.py @@ -19,7 +19,7 @@ import shutil import shlex import subprocess import itertools -from datetime import datetime +from datetime import datetime, timezone from socket import gethostname from contextlib import redirect_stderr, suppress import libgravatar @@ -640,6 +640,10 @@ class Helpers: version = f"{major}.{minor}.{sub}" return str(version) + @staticmethod + def get_utc_now() -> datetime: + return datetime.fromtimestamp(time.time(), tz=timezone.utc) + def encode_pass(self, password): return self.passhasher.hash(password) diff --git a/app/classes/shared/translation.py b/app/classes/shared/translation.py index 0e441808..538856a8 100644 --- a/app/classes/shared/translation.py +++ b/app/classes/shared/translation.py @@ -20,7 +20,7 @@ class Translation: def get_language_file(self, language: str): return os.path.join(self.translations_path, str(language) + ".json") - def translate(self, page, word, language): + def translate(self, page, word, language, error=True): fallback_language = "en_EN" translated_word = self.translate_inner(page, word, language) @@ -37,7 +37,9 @@ class Translation: if hasattr(translated_word, "__iter__"): # Multiline strings return "\n".join(translated_word) - return "Error while getting translation" + if error: + return "Error while getting translation" + return word def translate_inner(self, page, word, language) -> t.Union[t.Any, None]: language_file = self.get_language_file(language) diff --git a/app/classes/web/panel_handler.py b/app/classes/web/panel_handler.py index bbbc9d9e..6e122b2d 100644 --- a/app/classes/web/panel_handler.py +++ b/app/classes/web/panel_handler.py @@ -892,6 +892,8 @@ class PanelHandler(BaseHandler): os.path.join(self.helper.root_dir, "app", "translations") ) ): + if file == "humanized_index.json": + continue if file.endswith(".json"): if file.split(".")[0] not in self.helper.get_setting( "disabled_language_files" @@ -1307,6 +1309,8 @@ class PanelHandler(BaseHandler): for file in sorted( os.listdir(os.path.join(self.helper.root_dir, "app", "translations")) ): + if file == "humanized_index.json": + continue if file.endswith(".json"): if file.split(".")[0] not in self.helper.get_setting( "disabled_language_files" diff --git a/app/classes/web/routes/api/auth/invalidate_tokens.py b/app/classes/web/routes/api/auth/invalidate_tokens.py index f15bf60d..9e38670a 100644 --- a/app/classes/web/routes/api/auth/invalidate_tokens.py +++ b/app/classes/web/routes/api/auth/invalidate_tokens.py @@ -1,6 +1,6 @@ -import datetime import logging from app.classes.web.base_api_handler import BaseApiHandler +from app.classes.shared.helpers import Helpers logger = logging.getLogger(__name__) @@ -13,7 +13,7 @@ class ApiAuthInvalidateTokensHandler(BaseApiHandler): 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()} + auth_data[4]["user_id"], {"valid_tokens_from": Helpers.get_utc_now()} ) self.finish_json(200, {"status": "ok"}) diff --git a/app/classes/web/routes/api/roles/index.py b/app/classes/web/routes/api/roles/index.py index a8612c75..17d38123 100644 --- a/app/classes/web/routes/api/roles/index.py +++ b/app/classes/web/routes/api/roles/index.py @@ -2,6 +2,7 @@ import typing as t from jsonschema import ValidationError, validate import orjson from playhouse.shortcuts import model_to_dict +from app.classes.models.crafty_permissions import EnumPermissionsCrafty from app.classes.web.base_api_handler import BaseApiHandler create_role_schema = { @@ -71,7 +72,7 @@ class ApiRolesIndexHandler(BaseApiHandler): return ( _, - _, + exec_user_permissions_crafty, _, superuser, _, @@ -81,7 +82,10 @@ class ApiRolesIndexHandler(BaseApiHandler): # GET /api/v2/roles?ids=true get_only_ids = self.get_query_argument("ids", None) == "true" - if not superuser: + if ( + not superuser + and EnumPermissionsCrafty.ROLES_CONFIG not in exec_user_permissions_crafty + ): return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"}) self.finish_json( @@ -104,14 +108,17 @@ class ApiRolesIndexHandler(BaseApiHandler): return ( _, - _, + exec_user_permissions_crafty, _, superuser, user, _, ) = auth_data - if not superuser: + if ( + not superuser + and EnumPermissionsCrafty.ROLES_CONFIG not in exec_user_permissions_crafty + ): return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"}) try: @@ -138,6 +145,8 @@ class ApiRolesIndexHandler(BaseApiHandler): role_name = data["name"] manager = data.get("manager", None) + if not superuser and not manager: + manager = auth_data[4]["user_id"] if manager == self.controller.users.get_id_by_name("SYSTEM") or manager == 0: manager = None diff --git a/app/classes/web/routes/api/roles/role/index.py b/app/classes/web/routes/api/roles/role/index.py index 73fd9ff3..5cd46918 100644 --- a/app/classes/web/routes/api/roles/role/index.py +++ b/app/classes/web/routes/api/roles/role/index.py @@ -1,6 +1,7 @@ from jsonschema import ValidationError, validate import orjson -from peewee import DoesNotExist +from peewee import DoesNotExist, IntegrityError +from app.classes.models.crafty_permissions import EnumPermissionsCrafty from app.classes.web.base_api_handler import BaseApiHandler modify_role_schema = { @@ -70,14 +71,17 @@ class ApiRolesRoleIndexHandler(BaseApiHandler): return ( _, - _, + exec_user_permissions_crafty, _, superuser, _, _, ) = auth_data - if not superuser: + if ( + not superuser + and EnumPermissionsCrafty.ROLES_CONFIG not in exec_user_permissions_crafty + ): return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"}) try: @@ -100,8 +104,11 @@ class ApiRolesRoleIndexHandler(BaseApiHandler): user, _, ) = auth_data - - if not superuser: + role = self.controller.roles.get_role(role_id) + if ( + str(role.get("manager", "no manager found")) != str(auth_data[4]["user_id"]) + and not superuser + ): return self.finish_json(400, {"status": "error", "error": "NOT_AUTHORIZED"}) self.controller.roles.remove_role(role_id) @@ -124,7 +131,7 @@ class ApiRolesRoleIndexHandler(BaseApiHandler): return ( _, - _, + exec_user_permissions_crafty, _, superuser, user, @@ -132,7 +139,10 @@ class ApiRolesRoleIndexHandler(BaseApiHandler): ) = auth_data role = self.controller.roles.get_role(role_id) - if not superuser and user["user_id"] != role["manager"]: + if not superuser and ( + user["user_id"] != role["manager"] + or EnumPermissionsCrafty.ROLES_CONFIG not in exec_user_permissions_crafty + ): return self.finish_json( 400, { @@ -179,7 +189,10 @@ class ApiRolesRoleIndexHandler(BaseApiHandler): ) except DoesNotExist: return self.finish_json(404, {"status": "error", "error": "ROLE_NOT_FOUND"}) - + except IntegrityError: + return self.finish_json( + 404, {"status": "error", "error": "ROLE_NAME_EXISTS"} + ) self.controller.management.add_to_audit_log( user["user_id"], f"modified role with ID {role_id}", diff --git a/app/classes/web/routes/api/users/index.py b/app/classes/web/routes/api/users/index.py index dbdb1ac0..32ebd283 100644 --- a/app/classes/web/routes/api/users/index.py +++ b/app/classes/web/routes/api/users/index.py @@ -2,6 +2,7 @@ import logging import json from jsonschema import validate from jsonschema.exceptions import ValidationError +from app.classes.shared.translation import Translation from app.classes.models.crafty_permissions import EnumPermissionsCrafty from app.classes.models.roles import Roles, HelperRoles from app.classes.models.users import PUBLIC_USER_ATTRS @@ -54,6 +55,7 @@ class ApiUsersIndexHandler(BaseApiHandler): ) def post(self): + self.translator = Translation(self.helper) new_user_schema = { "type": "object", "properties": { @@ -87,12 +89,17 @@ class ApiUsersIndexHandler(BaseApiHandler): try: validate(data, new_user_schema) except ValidationError as e: + err = self.translator.translate( + "validators", + e.schema["error"], + self.controller.users.get_user_lang_by_id(auth_data[4]["user_id"]), + ) return self.finish_json( 400, { "status": "error", "error": "INVALID_JSON_SCHEMA", - "error_data": str(e), + "error_data": f"{str(err)}", }, ) username = data["username"] @@ -153,7 +160,11 @@ class ApiUsersIndexHandler(BaseApiHandler): for role in roles: role = self.controller.roles.get_role(role) - if int(role["manager"]) != int(auth_data[4]["user_id"]) and not superuser: + if ( + str(role.get("manager", "no manager found")) + != str(auth_data[4]["user_id"]) + and not superuser + ): return self.finish_json( 400, {"status": "error", "error": "INVALID_ROLES_CREATE"} ) diff --git a/app/classes/web/routes/api/users/user/api.py b/app/classes/web/routes/api/users/user/api.py index 3891ef83..4baac898 100644 --- a/app/classes/web/routes/api/users/user/api.py +++ b/app/classes/web/routes/api/users/user/api.py @@ -217,7 +217,7 @@ class ApiUsersUserKeyHandler(BaseApiHandler): ) if ( - target_key.user_id != auth_data[4]["user_id"] + str(target_key.user_id) != str(auth_data[4]["user_id"]) and not auth_data[4]["superuser"] ): return self.finish_json( diff --git a/app/classes/web/routes/api/users/user/index.py b/app/classes/web/routes/api/users/user/index.py index 9fa46200..b05e4ac3 100644 --- a/app/classes/web/routes/api/users/user/index.py +++ b/app/classes/web/routes/api/users/user/index.py @@ -132,7 +132,6 @@ class ApiUsersUserIndexHandler(BaseApiHandler): return self.finish_json( 400, {"status": "error", "error": "INVALID_JSON", "error_data": str(e)} ) - try: validate(data, user_patch_schema) except ValidationError as e: @@ -144,10 +143,8 @@ class ApiUsersUserIndexHandler(BaseApiHandler): "error_data": str(e), }, ) - if user_id == "@me": user_id = user["user_id"] - if ( EnumPermissionsCrafty.USER_CONFIG not in exec_user_crafty_permissions and str(user["user_id"]) != str(user_id) @@ -215,6 +212,25 @@ class ApiUsersUserIndexHandler(BaseApiHandler): return self.finish_json( 400, {"status": "error", "error": "INVALID_ROLES_MODIFY"} ) + user_modify = self.controller.users.get_user_roles_id(user_id) + + for role in data["roles"]: + # Check if user is not a super user and that the exec user is the role + # manager or that the role already exists in the user's list + if not superuser and ( + str( + self.controller.roles.get_role(role).get( + "manager", "no manager found" + ) + ) + != str(auth_data[4]["user_id"]) + and role not in user_modify + ): + for item in user_modify: + print(type(role), type(item)) + return self.finish_json( + 400, {"status": "error", "error": "INVALID_ROLES_MODIFY"} + ) user_obj = HelperUsers.get_user_model(user_id) if "password" in data and str(user["user_id"]) != str(user_id): diff --git a/app/frontend/templates/panel/panel_edit_role.html b/app/frontend/templates/panel/panel_edit_role.html index df065bf9..b72d3a2a 100644 --- a/app/frontend/templates/panel/panel_edit_role.html +++ b/app/frontend/templates/panel/panel_edit_role.html @@ -428,10 +428,13 @@ if (responseData.status === "ok") { window.location.href = "/panel/panel_config"; } else { - + let errordata = responseData.error; + if (responseData.error_data){ + errordata = responseData.error + } bootbox.alert({ title: responseData.error, - message: responseData.error_data + message: errordata }); } }); diff --git a/app/frontend/templates/panel/panel_edit_user.html b/app/frontend/templates/panel/panel_edit_user.html index 87631219..fdb5afd8 100644 --- a/app/frontend/templates/panel/panel_edit_user.html +++ b/app/frontend/templates/panel/panel_edit_user.html @@ -122,7 +122,7 @@ data['lang']) }}{% end %} name="lang" form="user_form"> {% for lang in data['languages'] %} {% if not 'incomplete' in lang %} - + {% else %} {% end %} @@ -393,6 +393,7 @@ data['lang']) }}{% end %} } function replacer(key, value) { if (typeof value == "boolean" || key === "email" || key === "permissions" || key === "roles") { + console.log(key) return value } else { console.log(key, value) @@ -433,6 +434,7 @@ data['lang']) }}{% end %} let disabled_flag = false; let roles = null; if (superuser || userId != edit_id){ + console.log("ROLES") roles = $('.role_check').map(function() { if ($(this).attr("disabled")){ disabled_flag = true; @@ -457,9 +459,7 @@ data['lang']) }}{% end %} delete formDataObject.username } if (superuser || userId != edit_id){ - if (!disabled_flag){ formDataObject.roles = roles; - } if ($("#permissions").length){ formDataObject.permissions = permissions; } diff --git a/app/migrations/20211120221511_api_keys.py b/app/migrations/20211120221511_api_keys.py index bede2c92..f5dd1e46 100644 --- a/app/migrations/20211120221511_api_keys.py +++ b/app/migrations/20211120221511_api_keys.py @@ -1,10 +1,11 @@ import peewee import datetime +from app.classes.shared.helpers import Helpers def migrate(migrator, database, **kwargs): migrator.add_columns( - "users", valid_tokens_from=peewee.DateTimeField(default=datetime.datetime.now) + "users", valid_tokens_from=peewee.DateTimeField(default=Helpers.get_utc_now) ) migrator.drop_columns("users", ["api_token"]) diff --git a/app/translations/cs_CS.json b/app/translations/cs_CS.json index 839eabb9..142593df 100644 --- a/app/translations/cs_CS.json +++ b/app/translations/cs_CS.json @@ -672,6 +672,9 @@ "userTheme": "Motiv UI", "uses": "Počet povolených použití (-1==bez omezení)" }, + "validators": { + "passLength": "Heslo je příliš krátké. Minimální délka je 8 znaků" + }, "webhooks": { "areYouSureDel": "Seš si jistý že chceš smazat tento webhook?", "areYouSureRun": "Seš si jistý že chceš otestovat tento webhook?", diff --git a/app/translations/de_DE.json b/app/translations/de_DE.json index 937db853..41242532 100644 --- a/app/translations/de_DE.json +++ b/app/translations/de_DE.json @@ -653,6 +653,9 @@ "userTheme": "Design für die Benutzeroberfläche", "uses": "Anzahl der erlaubten Verwendungen (-1==Keine Begrenzung)" }, + "validators": { + "passLength": "Passwort zu kurz. Mindestlänge: 8" + }, "webhooks": { "areYouSureDel": "Sind Sie sicher, dass Sie diesen Webhook löschen möchten?", "areYouSureRun": "Sind Sie sicher, dass Sie diesen Webhook testen möchten?", diff --git a/app/translations/en_EN.json b/app/translations/en_EN.json index af6b1d36..5a82408a 100644 --- a/app/translations/en_EN.json +++ b/app/translations/en_EN.json @@ -649,6 +649,9 @@ "userTheme": "UI Theme", "uses": "Number of uses allowed (-1==No Limit)" }, + "validators": { + "passLength": "Password Too Short. Minimum Length: 8" + }, "webhooks": { "areYouSureDel": "Are you sure you want to delete this webhook?", "areYouSureRun": "Are you sure you want to test this webhook?", diff --git a/app/translations/es_ES.json b/app/translations/es_ES.json index a0a079c5..c098dff1 100644 --- a/app/translations/es_ES.json +++ b/app/translations/es_ES.json @@ -653,6 +653,9 @@ "userTheme": "Tema de Interfaz", "uses": "Número de usos permitidos. (Sin límite: -1)" }, + "validators": { + "passLength": "Contraseña demasiado corta. Longitud mínima: 8" + }, "webhooks": { "areYouSureDel": "¿Estás seguro de que quieres eliminar este webhook?", "areYouSureRun": "¿Estás seguro de que quieres probar este webhook?", diff --git a/app/translations/fr_FR.json b/app/translations/fr_FR.json index 092536ee..07074ea3 100644 --- a/app/translations/fr_FR.json +++ b/app/translations/fr_FR.json @@ -653,6 +653,9 @@ "userTheme": "Theme d'Interface Utilisateur", "uses": "Nombre d'utilisation Authorisé (-1 == Illimité)" }, + "validators": { + "passLength": "Mot de passe trop court. Longueur minimum : 8" + }, "webhooks": { "areYouSureDel": "Es-tu sûr de vouloir supprimer ce webhook ?", "areYouSureRun": "Es-tu sûr de vouloir tester ce webhook ?", diff --git a/app/translations/he_IL.json b/app/translations/he_IL_incomplete.json similarity index 100% rename from app/translations/he_IL.json rename to app/translations/he_IL_incomplete.json diff --git a/app/translations/humanized_index.json b/app/translations/humanized_index.json new file mode 100644 index 00000000..6f2ea8c7 --- /dev/null +++ b/app/translations/humanized_index.json @@ -0,0 +1,19 @@ +{ + "language": { + "cs_CS": "Čeština", + "de_DE": "Deutsch", + "en_EN": "English (US)", + "es_ES": "Español", + "fr_FR": "Français (France)", + "he_IL": "he_IL", + "it_IT": "Italiano", + "lol_EN": "Lolcatz", + "lv_LV": "Latviešu", + "nl_BE": "nl_BE", + "pl_PL": "Polski", + "th_TH": "ไทย", + "tr_TR": "Türkçe", + "uk_UA": "Українська", + "zh_CN": "中文(中国)" + } +} diff --git a/app/translations/it_IT.json b/app/translations/it_IT.json index 7aec26b3..b59971b5 100644 --- a/app/translations/it_IT.json +++ b/app/translations/it_IT.json @@ -653,6 +653,9 @@ "userTheme": "Tema IU", "uses": "Numero di usi permessi (-1==Nessun limite)" }, + "validators": { + "passLength": "La password è troppo corta. Lunghezza minima: 8" + }, "webhooks": { "areYouSureDel": "Sei sicuro di voler eliminare questo webhook?", "areYouSureRun": "Sei sicuro di voler testare questo webhook?", diff --git a/app/translations/lol_EN.json b/app/translations/lol_EN.json index 282ffb36..d6075623 100644 --- a/app/translations/lol_EN.json +++ b/app/translations/lol_EN.json @@ -653,6 +653,9 @@ "userTheme": "THEMEZ", "uses": "NUMBER OV USES ALLOWED (-1==NO LIMIT)" }, + "validators": { + "passLength": "PASSWRD TOO SMOL. NEEDZ 8 CATZ PLZ" + }, "webhooks": { "areYouSureDel": "U SURE U WANTZ TO EATZ DIS WEBHOOK?", "areYouSureRun": "U SURE U WANTZ TO TESTZ DIS WEBHOOK?", diff --git a/app/translations/lv_LV.json b/app/translations/lv_LV.json index 6e1f1397..0ac11432 100644 --- a/app/translations/lv_LV.json +++ b/app/translations/lv_LV.json @@ -654,6 +654,9 @@ "userTheme": "UI Tēma", "uses": "Dauzums, cik reizes lietot (-1==Bez Limita)" }, + "validators": { + "passLength": "Parole pārāk īsa. Minimālais Garums: 8" + }, "webhooks": { "areYouSureDel": "Vai tiešām vēlies noņemt šo webhook?", "areYouSureRun": "Vai tiešām vēlies testēt šo webhook?", diff --git a/app/translations/nl_BE.json b/app/translations/nl_BE.json index 0abef0b4..4d3644b8 100644 --- a/app/translations/nl_BE.json +++ b/app/translations/nl_BE.json @@ -653,6 +653,9 @@ "userTheme": "UI-thema", "uses": "Aantal toegestane gebruiken (-1==Geen Limiet)" }, + "validators": { + "passLength": "Wachtwoord te kort. Minimumlengte: 8 tekens" + }, "webhooks": { "areYouSureDel": "Weet u zeker dat u deze webhook wilt verwijderen?", "areYouSureRun": "Weet u zeker dat u deze webhook wilt testen?", diff --git a/app/translations/pl_PL.json b/app/translations/pl_PL.json index 7385db3f..7115b869 100644 --- a/app/translations/pl_PL.json +++ b/app/translations/pl_PL.json @@ -652,6 +652,9 @@ "userTheme": "Wygląd interfejsu", "uses": "Ilość użyć (-1==Bez limitu)" }, + "validators": { + "passLength": "Hasło jest zbyt krótkie. Hasło musi posiadać minimum 8 znaków." + }, "webhooks": { "areYouSureDel": "Usunąć ten webhook?", "areYouSureRun": "Przetestować ten webhook?", diff --git a/app/translations/th_TH.json b/app/translations/th_TH.json index 12c24fa7..88b97435 100644 --- a/app/translations/th_TH.json +++ b/app/translations/th_TH.json @@ -652,6 +652,9 @@ "userTheme": "ธีม UI", "uses": "จำนวนการใช้งานที่อนุญาต (-1==ไม่มีขีดจำกัด)" }, + "validators": { + "passLength": "รหัสผ่านสั้นเกินไป จำนวนตัวอักขระขั้นต่ำ: 8" + }, "webhooks": { "areYouSureDel": "คุณแน่ใจหรือไม่ว่าต้องการลบ Webhook นี้?", "areYouSureRun": "คุณแน่ใจหรือไม่ว่าต้องการทดสอบ Webhook นี้?", diff --git a/app/translations/tr_TR.json b/app/translations/tr_TR.json index 709f9e43..ccdf7414 100644 --- a/app/translations/tr_TR.json +++ b/app/translations/tr_TR.json @@ -652,6 +652,9 @@ "userTheme": "UI Teması", "uses": "İzin verilen kullanım sayısı (-1==Sınır Yok)" }, + "validators": { + "passLength": "Şifre çok kısa. Şifre en az 8 karakter olmalı." + }, "webhooks": { "areYouSureDel": "Bu webhooku silmek istediğinizden emin misiniz?", "areYouSureRun": "Bu webhooku test etmek istediğinizden emin misiniz?", diff --git a/app/translations/uk_UA.json b/app/translations/uk_UA.json index 792b6f56..a12e2e40 100644 --- a/app/translations/uk_UA.json +++ b/app/translations/uk_UA.json @@ -652,6 +652,9 @@ "userTheme": "Тема інтерфейсу", "uses": "Дозволена кількість використань(-1==Без ліміту)" }, + "validators": { + "passLength": "Пароль, надто короткий. Мінімальна довжина: 8 символів" + }, "webhooks": { "areYouSureDel": "Ви впевнені, що хочете видалити цей Вебхук?", "areYouSureRun": "Ви впевнені, що хочете перевірити цей Вебхук?", diff --git a/app/translations/zh_CN.json b/app/translations/zh_CN.json index 95e71d70..e30ecef6 100644 --- a/app/translations/zh_CN.json +++ b/app/translations/zh_CN.json @@ -653,6 +653,9 @@ "userTheme": "UI 主题", "uses": "使用次数限制(-1==无限制)" }, + "validators": { + "passLength": "密码过短。最短长度:8" + }, "webhooks": { "areYouSureDel": "您确定要删除此 webhook 吗?", "areYouSureRun": "您确定要测试此 webhook 吗?", diff --git a/requirements.txt b/requirements.txt index ed0f7698..2ca0ff8b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -13,9 +13,9 @@ psutil==5.9.5 pyOpenSSL==24.0.0 pyjwt==2.8.0 PyYAML==6.0.1 -requests==2.31.0 +requests==2.32.0 termcolor==1.1 -tornado==6.3.3 +tornado==6.4.1 tzlocal==5.1 jsonschema==4.19.1 orjson==3.9.15