Change login payload

This commit is contained in:
amcmanu3 2024-02-19 20:18:47 -05:00
parent d168a7f8f0
commit aac35b14d9
3 changed files with 172 additions and 122 deletions

View File

@ -1,7 +1,11 @@
import logging import logging
import binascii import binascii
import base64 import base64
import urllib
import json
import nh3 import nh3
from jsonschema import validate
from jsonschema.exceptions import ValidationError
from app.classes.shared.helpers import Helpers from app.classes.shared.helpers import Helpers
from app.classes.models.users import HelperUsers from app.classes.models.users import HelperUsers
@ -47,7 +51,7 @@ class PublicHandler(BaseHandler):
} }
if self.request.query: if self.request.query:
page_data["query"] = self.request.query page_data["query"] = self.request.query_arguments.get("next")[0].decode()
# sensible defaults # sensible defaults
template = "public/404.html" template = "public/404.html"
@ -77,11 +81,7 @@ class PublicHandler(BaseHandler):
# if we have no page, let's go to login # if we have no page, let's go to login
else: else:
if self.request.query: return self.redirect("/login")
self.redirect("/login?" + self.request.query)
else:
self.redirect("/login")
return
self.render( self.render(
template, template,
@ -91,42 +91,74 @@ class PublicHandler(BaseHandler):
) )
def post(self, page=None): def post(self, page=None):
# pylint: disable=no-member login_schema = {
error = nh3.clean(self.get_argument("error", "Invalid Login!")) "type": "object",
error_msg = nh3.clean(self.get_argument("error_msg", "")) "properties": {
# pylint: enable=no-member "username": {
"type": "string",
"pattern": "^[a-z0-9_]+$",
},
"password": {"type": "string"},
},
"required": ["username", "password"],
"additionalProperties": False,
}
try:
data = json.loads(self.request.body)
except json.decoder.JSONDecodeError as e:
logger.error(
"Invalid JSON schema for API"
f" login attempt from {self.get_remote_ip()}"
)
return self.finish_json(
400, {"status": "error", "error": "INVALID_JSON", "error_data": str(e)}
)
try:
validate(data, login_schema)
except ValidationError as e:
logger.error(
"Invalid JSON schema for API"
f" login attempt from {self.get_remote_ip()}"
)
return self.finish_json(
400,
{
"status": "error",
"error": "INVALID_JSON_SCHEMA",
"error_data": str(e),
},
)
page_data = { page_data = {
"version": self.helper.get_version_string(), "version": self.helper.get_version_string(),
"error": error,
"lang": self.helper.get_setting("language"), "lang": self.helper.get_setting("language"),
"lang_page": self.helper.get_lang_page(self.helper.get_setting("language")), "lang_page": self.helper.get_lang_page(self.helper.get_setting("language")),
"query": "", "query": "",
} }
if self.request.query: if self.request.query:
page_data["query"] = self.request.query page_data["query"] = self.request.query_arguments.get("next")[0].decode()
if page == "login": if page == "login":
data = json.loads(self.request.body)
auth_log.info( auth_log.info(
f"User attempting to authenticate from {self.get_remote_ip()}" f"User attempting to authenticate from {self.get_remote_ip()}"
) )
next_page = "/login" entered_username = nh3.clean(data["username"]) # pylint: disable=no-member
if self.request.query:
next_page = "/login?" + self.request.query
entered_username = nh3.clean(
self.get_argument("username")
) # pylint: disable=no-member
try: try:
entered_password = base64.b64decode( entered_password = urllib.parse.unquote(
self.get_argument("encPassword") base64.b64decode(data["password"]).decode("utf-8")
).decode("utf-8") )
except binascii.Error: except binascii.Error:
error_msg = ( return self.finish_json(
"Hello? Hello? Anybody home?" 403,
" Go straight to jail. Do not pass go." {
"status": "error",
"error": "Hello? Hello? Anybody home?"
" Go straight to jail. Do not pass go.",
},
) )
return self.redirect(f"/login?error_msg={error_msg}")
try: try:
user_id = HelperUsers.get_user_id_by_name(entered_username.lower()) user_id = HelperUsers.get_user_id_by_name(entered_username.lower())
@ -138,18 +170,18 @@ class PublicHandler(BaseHandler):
f" Authentication failed from remote IP {self.get_remote_ip()}" f" Authentication failed from remote IP {self.get_remote_ip()}"
" Users does not exist." " Users does not exist."
) )
error_msg = "Incorrect username or password. Please try again." self.finish_json(
403,
{
"status": "error",
"error": self.helper.translation.translate(
"login", "incorrect", self.helper.get_setting("language")
),
},
)
# self.clear_cookie("user") # self.clear_cookie("user")
# self.clear_cookie("user_data") # self.clear_cookie("user_data")
self.clear_cookie("token") return self.clear_cookie("token")
if self.request.query:
self.redirect(
f"/login?err or_msg={error_msg}" f"&{self.request.query}"
)
else:
self.redirect(f"/login?error_msg={error_msg}")
return
# if we don't have a user # if we don't have a user
if not user_data: if not user_data:
auth_log.error( auth_log.error(
@ -158,15 +190,18 @@ class PublicHandler(BaseHandler):
" User does not exist." " User does not exist."
) )
self.controller.log_attempt(self.get_remote_ip(), entered_username) self.controller.log_attempt(self.get_remote_ip(), entered_username)
error_msg = "Incorrect username or password. Please try again." self.finish_json(
403,
{
"status": "error",
"error": self.helper.translation.translate(
"login", "incorrect", self.helper.get_setting("language")
),
},
)
# self.clear_cookie("user") # self.clear_cookie("user")
# self.clear_cookie("user_data") # self.clear_cookie("user_data")
self.clear_cookie("token") return self.clear_cookie("token")
if self.request.query:
self.redirect(f"/login?error_msg={error_msg}&{self.request.query}")
else:
self.redirect(f"/login?error_msg={error_msg}")
return
# if they are disabled # if they are disabled
if not user_data.enabled: if not user_data.enabled:
@ -176,18 +211,18 @@ class PublicHandler(BaseHandler):
" User account disabled" " User account disabled"
) )
self.controller.log_attempt(self.get_remote_ip(), entered_username) self.controller.log_attempt(self.get_remote_ip(), entered_username)
error_msg = ( self.finish_json(
"User account disabled. Please contact " 403,
"your system administrator for more info." {
"status": "error",
"error": self.helper.translation.translate(
"login", "disabled", self.helper.get_setting("language")
),
},
) )
# self.clear_cookie("user") # self.clear_cookie("user")
# self.clear_cookie("user_data") # self.clear_cookie("user_data")
self.clear_cookie("token") return self.clear_cookie("token")
if self.request.query:
self.redirect(f"/login?error_msg={error_msg}&{self.request.query}")
else:
self.redirect(f"/login?error_msg={error_msg}")
return
login_result = self.helper.verify_pass(entered_password, user_data.password) login_result = self.helper.verify_pass(entered_password, user_data.password)
# Valid Login # Valid Login
@ -212,12 +247,9 @@ class PublicHandler(BaseHandler):
user_data.user_id, "Logged in", 0, self.get_remote_ip() user_data.user_id, "Logged in", 0, self.get_remote_ip()
) )
if self.request.query_arguments.get("next"): return self.finish_json(
next_page = self.request.query_arguments.get("next")[0].decode() 200, {"status": "ok", "data": {"message": "login successful!"}}
else: )
next_page = "/panel/dashboard"
self.redirect(next_page)
else: else:
auth_log.error( auth_log.error(
f"User attempted to log into {entered_username}." f"User attempted to log into {entered_username}."
@ -239,12 +271,9 @@ class PublicHandler(BaseHandler):
self.controller.management.add_to_audit_log( self.controller.management.add_to_audit_log(
user_data.user_id, "Tried to log in", 0, self.get_remote_ip() user_data.user_id, "Tried to log in", 0, self.get_remote_ip()
) )
if self.request.query: return self.finish_json(
self.redirect(f"/login?error_msg={error_msg}&{self.request.query}") 403,
else: {"status": "error", "error": error_msg},
self.redirect(f"/login?error_msg={error_msg}") )
else: else:
if self.request.query: self.redirect("/login?")
self.redirect("/login?" + self.request.query)
else:
self.redirect("/login")

View File

@ -77,55 +77,49 @@
box-shadow: 0 12px 16px 0 hsla(0, 0%, 0%, 0.4); box-shadow: 0 12px 16px 0 hsla(0, 0%, 0%, 0.4);
} }
</style> </style>
{% if data['query'] %} <form id="login-form" data-query="{{ data.get('query', None) }}">
<form id="login" action="/login?{{ data['query'] }}" method="post" onsubmit="encodePass()"> {% raw xsrf_form_html() %}
{% else %} <div class="form-group">
<form id="login" action="/login" method="post" onsubmit="encodePass()"> <label class="label">{{ translate('login', 'username', data['lang']) }}</label>
{% end %} <div class="input-group">
{% raw xsrf_form_html() %} <input type="text" class="form-control login-text-input login-input"
<div class="form-group"> placeholder="{{ translate('login', 'username', data['lang']) }}" name="username" id="username"
<label class="label">{{ translate('login', 'username', data['lang']) }}</label> required="true">
<div class="input-group">
<input type="text" class="form-control login-text-input login-input"
placeholder="{{ translate('login', 'username', data['lang']) }}" name="username" id="username"
required="true">
</div>
</div> </div>
<div class="form-group"> </div>
<label class="label">{{ translate('login', 'password', data['lang']) }}</label> <div class="form-group">
<div class="input-group"> <label class="label">{{ translate('login', 'password', data['lang']) }}</label>
<input type="password" class="form-control login-text-input login-input" <div class="input-group">
placeholder="{{ translate('login', 'password', data['lang']) }}" name="password" id="password" <input type="password" class="form-control login-text-input login-input"
required="true"> placeholder="{{ translate('login', 'password', data['lang']) }}" name="password" id="password"
</div> required="true">
</div> </div>
<div class="form-group"> </div>
<button class="login-input btn btn-primary submit-btn btn-block">{{ translate('login', 'login', <div class="form-group">
data['lang']) }}</button> <button class="login-input btn btn-primary submit-btn btn-block">{{ translate('login', 'login',
</div> data['lang']) }}</button>
{% if error_msg is not None %} </div>
<fieldset style="color: red; text-align: center;"> <fieldset id="error-field" style="color: red; text-align: center;">
<span>{{error_msg}}</span> </fieldset>
</fieldset> <div class="form-group d-flex justify-content-between">
{% end %} <div class="form-check form-check-flat mt-0">
<div class="form-group d-flex justify-content-between"> &nbsp;
<div class="form-check form-check-flat mt-0">
&nbsp;
</div>
<button onclick="resetPass()" id="#resetPass" form="" class="btn btn-outline-primary btn-sm forgot-password ">{{ translate('login', 'forgotPassword',
data['lang']) }}</button>
</div> </div>
<button onclick="resetPass()" id="#resetPass" form=""
class="btn btn-outline-primary btn-sm forgot-password ">{{ translate('login', 'forgotPassword',
data['lang']) }}</button>
</div>
<div class="text-block text-center my-3"> <div class="text-block text-center my-3">
<span class="text-small font-weight-semibold"><a href="https://craftycontrol.com/">Crafty Control <span class="text-small font-weight-semibold"><a href="https://craftycontrol.com/">Crafty Control
{{data['version'] }}</a> </span> {{data['version'] }}</a> </span>
</div> </div>
<div class="text-block text-center my-3"> <div class="text-block text-center my-3">
<a href="/status" class="text-small forgot-password">{{ translate('login', 'viewStatus', <a href="/status" class="text-small forgot-password">{{ translate('login', 'viewStatus',
data['lang']) }}</a> data['lang']) }}</a>
</div> </div>
</form> </form>
</div> </div>
@ -155,13 +149,13 @@
document.getElementById('login-form-background').style.background = 'rgb(34, 36, 55, ' + (opacity / 100) + ')'; document.getElementById('login-form-background').style.background = 'rgb(34, 36, 55, ' + (opacity / 100) + ')';
//Register Service worker for mobile app //Register Service worker for mobile app
if ('serviceWorker' in navigator) { if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/static/assets/js/shared/service-worker.js', {scope: '/'}) navigator.serviceWorker.register('/static/assets/js/shared/service-worker.js', { scope: '/' })
.then(function (registration) { .then(function (registration) {
console.log('Service Worker Registered'); console.log('Service Worker Registered');
}); });
} }
}); });
async function resetPass(){ async function resetPass() {
let res = await fetch(`/api/v2/crafty/resetPass/`, { let res = await fetch(`/api/v2/crafty/resetPass/`, {
method: 'GET', method: 'GET',
}); });
@ -170,9 +164,38 @@
bootbox.alert(responseData.data) bootbox.alert(responseData.data)
} }
function encodePass(){ $("#login-form").on("submit", async function (e) {
$("#encPassword").val(btoa($("#password").val())) e.preventDefault();
} let loginForm = document.getElementById("login-form");
let formData = new FormData(loginForm);
//Create an object from the form data entries
let formDataObject = Object.fromEntries(formData.entries());
console.log(formDataObject)
let res = await fetch(`/login`, {
method: 'POST',
headers: {
'X-XSRFToken': formDataObject._xsrf,
"Content-Type": "application/json"
},
body: JSON.stringify({
"username": formDataObject.username,
"password": btoa(encodeURIComponent(formDataObject.password))
}),
});
let responseData = await res.json();
if (responseData.status === "ok") {
console.log("OK")
if ($("#login-form").data("query")) {
location.href = `${$("#login-form").data("query")}`;
} else {
location.href = `/panel/dashboard`
}
} else {
$("#error-field").html(responseData.error);
}
});
</script> </script>
<style> <style>
.modal-content { .modal-content {
@ -181,9 +204,6 @@
text-align: center; text-align: center;
} }
</style> </style>
<input form="login" type="password" class="form-control login-text-input login-input"
placeholder="{{ translate('login', 'password', data['lang']) }}" name="encPassword" id="encPassword"
style="visibility: hidden;">
</body> </body>
</html> </html>

View File

@ -221,7 +221,8 @@
"username": "Username", "username": "Username",
"viewStatus": "View Public Status Page", "viewStatus": "View Public Status Page",
"incorrect": "Incorrect username or password", "incorrect": "Incorrect username or password",
"defaultPath": "The password you entered is the default credential path, not the password. Please find the default password in that location." "defaultPath": "The password you entered is the default credential path, not the password. Please find the default password in that location.",
"disabled": "User account disabled. Please contact your system administrator for more info."
}, },
"notify": { "notify": {
"activityLog": "Activity Logs", "activityLog": "Activity Logs",
@ -670,4 +671,4 @@
"webhook_body": "Webhook Body", "webhook_body": "Webhook Body",
"webhooks": "Webhooks" "webhooks": "Webhooks"
} }
} }