add auth to connect form

master
n0emis 5 years ago
parent bc0e61b972
commit ff5597d4be
Signed by: n0emis
GPG Key ID: 00FAF748B777CF10

@ -14,16 +14,17 @@
</div> </div>
<div class="card-body"> <div class="card-body">
<div class="tabcontent active" id="register"> <div class="tabcontent active" id="register">
<form method="post" class="form__input" id="form" action="submit"> <form method="post" class="form__input" id="register-form" action="submit">
<input type="text" name="username" id="field-username" class="field" autofocus=""> <input type="text" name="username" id="field-username" class="field" autofocus="">
<label for="field-username"> <label for="field-username">
<span><span aria-hidden="true">Please pick your </span>username</span> <span><span aria-hidden="true">Please pick your </span>username</span>
</label> </label>
<input type="button" class="button button--full-width" id="button-submit" value="Submit"> <input type="button" class="button button--full-width" id="button-register-submit" value="Submit">
</form> </form>
</div> </div>
<div class="tabcontent" id="connect"> <div class="tabcontent" id="connect">
<form method="post" id="form" action="submit"> <h4>Connect your existing Matrix-Account:</h4>
<form method="post" id="connect-form" action="submit">
<div class="form__input"> <div class="form__input">
<input type="text" name="username" id="connect-field-username" class="field" autofocus=""> <input type="text" name="username" id="connect-field-username" class="field" autofocus="">
<label for="connect-field-username"> <label for="connect-field-username">
@ -32,12 +33,12 @@
</div> </div>
<br> <br>
<div class="form__input"> <div class="form__input">
<input type="password" name="password" id="connect-field-password"> <input type="password" name="password" id="connect-field-password" class="field">
<label for="connect-field-password"> <label for="connect-field-password">
<span><span aria-hidden="true">Your old Matrix </span>password</span> <span><span aria-hidden="true">Your old Matrix </span>password</span>
</label> </label>
</div> </div>
<input type="button" class="button button--full-width" id="button-submit" value="Submit"> <input type="button" class="button button--full-width" id="button-connect-submit" value="Submit">
</form> </form>
</div> </div>
<div role=alert class="tooltip hidden" id="message"></div> <div role=alert class="tooltip hidden" id="message"></div>

@ -1,60 +1,80 @@
let regiterFields = document.getElementById("field-username"); let registerField = document.getElementById("field-username");
let connectUsernameField = document.getElementById("connect-field-username");
let connectPasswordField = document.getElementById("connect-field-password");
let inputFields = document.getElementsByClassName("field"); let inputFields = document.getElementsByClassName("field");
let registerForm = document.getElementById("form"); let registerForm = document.getElementById("register-form");
let submitButton = document.getElementById("button-submit"); let registerButton = document.getElementById("button-register-submit");
let connectForm = document.getElementById("connect-form");
let connectButton = document.getElementById("button-connect-submit");
let message = document.getElementById("message"); let message = document.getElementById("message");
let tabLinkButtons = document.getElementsByClassName("tablinks"); let tabLinkButtons = document.getElementsByClassName("tablinks");
let tabContentBlocks = document.getElementsByClassName("tabcontent"); let tabContentBlocks = document.getElementsByClassName("tabcontent");
// Remove input field placeholder if the text field is not empty // Remove input field placeholder if the text field is not empty
let switchClass = function(input) { let switchClass = function(input) {
if (input.value.length > 0) { if (input.value.length > 0) {
input.classList.add('has-contents'); input.classList.add('has-contents');
} }
else { else {
input.classList.remove('has-contents'); input.classList.remove('has-contents');
} }
}; };
// Submit username and receive response // Submit username and receive response
let showMessage = function(messageText) { let showMessage = function(messageText) {
// Unhide the message text // Unhide the message text
message.classList.remove("hidden"); message.classList.remove("hidden");
message.innerHTML = messageText; message.innerHTML = messageText;
}; };
let hideMessage = function() { let hideMessage = function() {
// Hide the message text // Hide the message text
message.classList.add("hidden"); message.classList.add("hidden");
}; };
let onResponse = function(response, success) { let onRegisterResponse = function(response, success) {
// Display message // Display message
showMessage(response); showMessage(response);
if(success) { if(success) {
registerForm.submit(); registerForm.submit();
return; return;
} }
// Enable submit button and input field // Enable submit button and input field
submitButton.classList.remove('button--disabled'); registerButton.classList.remove('button--disabled');
submitButton.value = "Submit" registerButton.value = "Submit"
}; };
let onConnectResponse = function(response, success) {
// Display message
showMessage(response);
if(success) {
connectForm.submit();
return;
}
// Enable submit button and input field
connectButton.classList.remove('button--disabled');
connectButton.value = "Submit"
};
let allowedUsernameCharacters = RegExp("[^a-z0-9\\.\\_\\=\\-\\/]"); let allowedUsernameCharacters = RegExp("[^a-z0-9\\.\\_\\=\\-\\/]");
let usernameIsValid = function(username) { let usernameIsValid = function(username) {
return !allowedUsernameCharacters.test(username); return !allowedUsernameCharacters.test(username);
} }
let allowedCharactersString = "" + let allowedCharactersString = "" +
"lowercase letters, " + "lowercase letters, " +
"digits, " + "digits, " +
"<code>.</code>, " + "<code>.</code>, " +
"<code>_</code>, " + "<code>_</code>, " +
"<code>-</code>, " + "<code>-</code>, " +
"<code>/</code>, " + "<code>/</code>, " +
"<code>=</code>"; "<code>=</code>";
let buildQueryString = function(params) { let buildQueryString = function(params) {
return Object.keys(params) return Object.keys(params)
@ -62,15 +82,15 @@ let buildQueryString = function(params) {
.join('&'); .join('&');
} }
let submitUsername = function(username) { let submitRegister = function(username) {
if(username.length == 0) { if(username.length == 0) {
onResponse("Please enter a username.", false); onRegisterResponse("Please enter a username.", false);
return; return;
} }
if(!usernameIsValid(username)) { if(!usernameIsValid(username)) {
onResponse("Invalid username. Only the following characters are allowed: " + allowedCharactersString, false); onRegisterResponse("Invalid username. Only the following characters are allowed: " + allowedCharactersString, false);
return; return;
} }
let check_uri = 'check?' + buildQueryString({"username": username}); let check_uri = 'check?' + buildQueryString({"username": username});
fetch(check_uri, { fetch(check_uri, {
@ -86,27 +106,92 @@ let submitUsername = function(username) {
if(json.error) { if(json.error) {
throw json.error; throw json.error;
} else if(json.available) { } else if(json.available) {
onResponse("Success. Please wait a moment for your browser to redirect.", true); onRegisterResponse("Success. Please wait a moment for your browser to redirect.", true);
} else {
onRegisterResponse("This username is not available, please choose another.", false);
}
}).catch((err) => {
onRegisterResponse("Error checking username availability: " + err, false);
});
}
let submitConnect = function(username, password) {
if(username.length == 0) {
onConnectResponse("Please enter a username.", false);
return;
}
if(!usernameIsValid(username)) {
onConnectResponse("Invalid username. Only the following characters are allowed: " + allowedCharactersString, false);
return;
}
if(password.length == 0) {
onConnectResponse("Please enter a password.", false);
return;
}
details = {
username: username,
password: password
}
var formBody = [];
for (var property in details) {
var encodedKey = encodeURIComponent(property);
var encodedValue = encodeURIComponent(details[property]);
formBody.push(encodedKey + "=" + encodedValue);
}
formBody = formBody.join("&");
fetch('check', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: formBody
}).then((response) => {
if(!response.ok) {
// for non-200 responses, raise the body of the response as an exception
return response.text().then((text) => { throw text });
} else { } else {
onResponse("This username is not available, please choose another.", false); return response.json()
}
}).then((json) => {
if(json.error) {
throw json.error;
} else if(json.success) {
onConnectResponse("Success. Please wait a moment for your browser to redirect.", true);
} else {
onConnectResponse("The credentials you entered are not valid.", false);
} }
}).catch((err) => { }).catch((err) => {
onResponse("Error checking username availability: " + err, false); onConnectResponse("Error checking credentials: " + err, false);
}); });
} }
let clickSubmit = function() { let registerClickSubmit = function() {
if(submitButton.classList.contains('button--disabled')) { return; } if(registerButton.classList.contains('button--disabled')) { return; }
// Disable submit button and input field
registerButton.classList.add('button--disabled');
// Submit username
registerButton.value = "Checking...";
submitRegister(registerField.value);
};
let connectClickSubmit = function() {
if(connectButton.classList.contains('button--disabled')) { return; }
// Disable submit button and input field // Disable submit button and input field
submitButton.classList.add('button--disabled'); connectButton.classList.add('button--disabled');
// Submit username // Submit data
submitButton.value = "Checking..."; connectButton.value = "Checking...";
submitUsername(regiterFields.value); submitConnect(connectUsernameField.value, connectPasswordField.value);
}; };
submitButton.onclick = clickSubmit; registerButton.onclick = registerClickSubmit;
connectButton.onclick = connectClickSubmit;
// Listen for events on inputFields // Listen for events on inputFields
for (let i = 0; i < inputFields.length; i++) { for (let i = 0; i < inputFields.length; i++) {
@ -114,7 +199,11 @@ for (let i = 0; i < inputFields.length; i++) {
// Listen for Enter on input field // Listen for Enter on input field
if (event.which === 13) { if (event.which === 13) {
event.preventDefault(); event.preventDefault();
clickSubmit(); if (inputFields[i].id === "field-username") {
registerClickSubmit();
} else {
connectClickSubmit();
}
return true; return true;
} }
switchClass(inputFields[i]); switchClass(inputFields[i]);

@ -27,6 +27,8 @@ import synapse.module_api
from synapse.module_api import run_in_background from synapse.module_api import run_in_background
from synapse.module_api.errors import SynapseError from synapse.module_api.errors import SynapseError
from synapse.api.errors import Codes, LoginError
from matrix_synapse_saml_mozilla._sessions import ( from matrix_synapse_saml_mozilla._sessions import (
SESSION_COOKIE_NAME, SESSION_COOKIE_NAME,
get_mapping_session, get_mapping_session,
@ -56,7 +58,7 @@ def pick_username_resource(
base_path = pkg_resources.resource_filename("matrix_synapse_saml_mozilla", "res") base_path = pkg_resources.resource_filename("matrix_synapse_saml_mozilla", "res")
res = File(base_path) res = File(base_path)
res.putChild(b"submit", SubmitResource(module_api)) res.putChild(b"submit", SubmitResource(module_api))
res.putChild(b"check", AvailabilityCheckResource(module_api)) res.putChild(b"check", CheckResource(module_api))
return res return res
@ -155,15 +157,32 @@ class SubmitResource(AsyncResource):
_return_html_error(400, "missing username", request) _return_html_error(400, "missing username", request)
return return
localpart = request.args[b"username"][0].decode("utf-8", errors="replace") localpart = request.args[b"username"][0].decode("utf-8", errors="replace")
logger.info("Registering username %s", localpart)
try: if b"password" not in request.args:
registered_user_id = await self._module_api.register_user( logger.info("Registering username %s", localpart)
localpart=localpart, displayname=localpart try:
) registered_user_id = await self._module_api.register_user(
except SynapseError as e: localpart=localpart, displayname=localpart
logger.warning("Error during registration: %s", e) )
_return_html_error(e.code, e.msg, request) except SynapseError as e:
return logger.warning("Error during registration: %s", e)
_return_html_error(e.code, e.msg, request)
return
else:
password = request.args[b"password"][0].decode("utf-8", errors="replace")
registered_user_id = '@{}:localhost'.format(localpart)
success = False
try:
passwd_response = await self._module_api._auth_handler._check_local_password(registered_user_id, password)
if passwd_response != registered_user_id:
raise LoginError(403, "Invalid password", errcode=Codes.FORBIDDEN)
except Exception as e:
logger.warning(
"Error checking credentials of %s: %s %s" % (localpart, type(e), e)
)
_return_html_error(e.code, e.msg, request)
return
await self._module_api.record_user_external_id( await self._module_api.record_user_external_id(
"saml", session.remote_user_id, registered_user_id "saml", session.remote_user_id, registered_user_id
@ -186,7 +205,7 @@ class SubmitResource(AsyncResource):
) )
class AvailabilityCheckResource(AsyncResource): class CheckResource(AsyncResource):
def __init__(self, module_api: synapse.module_api.ModuleApi): def __init__(self, module_api: synapse.module_api.ModuleApi):
super().__init__() super().__init__()
self._module_api = module_api self._module_api = module_api
@ -224,6 +243,47 @@ class AvailabilityCheckResource(AsyncResource):
response = {"available": available} response = {"available": available}
_return_json(response, request) _return_json(response, request)
@_wrap_for_text_exceptions
async def async_render_POST(self, request: Request):
# make sure that there is a valid mapping session, to stop people dictionary-
# scanning for accounts
# session_id = request.getCookie(SESSION_COOKIE_NAME)
# if not session_id:
# _return_json({"error": "missing session_id"}, request)
# return
#
# session_id = session_id.decode("ascii", errors="replace")
# session = get_mapping_session(session_id)
# if not session:
# logger.info("Couldn't find session id %s", session_id)
# _return_json({"error": "unknown session"}, request)
# return
if b"username" not in request.args:
_return_json({"error": "missing username"}, request)
return
localpart = request.args[b"username"][0].decode("utf-8", errors="replace")
if b"password" not in request.args:
_return_json({"error": "missing password"}, request)
return
password = request.args[b"password"][0].decode("utf-8", errors="replace")
uid = '@{}:localhost'.format(localpart)
success = False
try:
passwd_response = await self._module_api._auth_handler._check_local_password(uid, password)
if passwd_response == uid:
success = True
except Exception as e:
logger.warning(
"Error checking credentials of %s: %s %s" % (localpart, type(e), e)
)
response = {"success": success}
_return_json(response, request)
def _add_login_token_to_redirect_url(url, token): def _add_login_token_to_redirect_url(url, token):
url_parts = list(urllib.parse.urlparse(url)) url_parts = list(urllib.parse.urlparse(url))

Loading…
Cancel
Save