forked from cory/tildefriends
Login without hitting the DB from the main thread.
This commit is contained in:
parent
c29378c2f8
commit
0423ed7fb4
188
src/httpd.js.c
188
src/httpd.js.c
@ -37,7 +37,7 @@
|
|||||||
|
|
||||||
const int64_t k_refresh_interval = 1ULL * 7 * 24 * 60 * 60 * 1000;
|
const int64_t k_refresh_interval = 1ULL * 7 * 24 * 60 * 60 * 1000;
|
||||||
|
|
||||||
static JSValue _authenticate_jwt(JSContext* context, const char* jwt);
|
static JSValue _authenticate_jwt(tf_ssb_t* ssb, JSContext* context, const char* jwt);
|
||||||
static JSValue _httpd_websocket_upgrade(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv);
|
static JSValue _httpd_websocket_upgrade(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv);
|
||||||
static const char* _make_session_jwt(tf_ssb_t* ssb, const char* name);
|
static const char* _make_session_jwt(tf_ssb_t* ssb, const char* name);
|
||||||
static const char* _make_set_session_cookie_header(tf_http_request_t* request, const char* session_cookie);
|
static const char* _make_set_session_cookie_header(tf_http_request_t* request, const char* session_cookie);
|
||||||
@ -330,7 +330,7 @@ static JSValue _httpd_websocket_upgrade(JSContext* context, JSValueConst this_va
|
|||||||
|
|
||||||
tf_ssb_t* ssb = tf_task_get_ssb(tf_task_get(context));
|
tf_ssb_t* ssb = tf_task_get_ssb(tf_task_get(context));
|
||||||
const char* session = tf_http_get_cookie(tf_http_request_get_header(request, "cookie"), "session");
|
const char* session = tf_http_get_cookie(tf_http_request_get_header(request, "cookie"), "session");
|
||||||
JSValue jwt = _authenticate_jwt(context, session);
|
JSValue jwt = _authenticate_jwt(ssb, context, session);
|
||||||
tf_free((void*)session);
|
tf_free((void*)session);
|
||||||
JSValue name = !JS_IsUndefined(jwt) ? JS_GetPropertyStr(context, jwt, "name") : JS_UNDEFINED;
|
JSValue name = !JS_IsUndefined(jwt) ? JS_GetPropertyStr(context, jwt, "name") : JS_UNDEFINED;
|
||||||
const char* name_string = !JS_IsUndefined(name) ? JS_ToCString(context, name) : NULL;
|
const char* name_string = !JS_IsUndefined(name) ? JS_ToCString(context, name) : NULL;
|
||||||
@ -508,7 +508,7 @@ static JSValue _httpd_auth_query(JSContext* context, JSValueConst this_val, int
|
|||||||
JSValue cookie = JS_GetPropertyStr(context, headers, "cookie");
|
JSValue cookie = JS_GetPropertyStr(context, headers, "cookie");
|
||||||
const char* cookie_string = JS_ToCString(context, cookie);
|
const char* cookie_string = JS_ToCString(context, cookie);
|
||||||
const char* session = tf_http_get_cookie(cookie_string, "session");
|
const char* session = tf_http_get_cookie(cookie_string, "session");
|
||||||
JSValue entry = _authenticate_jwt(context, session);
|
JSValue entry = _authenticate_jwt(ssb, context, session);
|
||||||
tf_free((void*)session);
|
tf_free((void*)session);
|
||||||
JS_FreeCString(context, cookie_string);
|
JS_FreeCString(context, cookie_string);
|
||||||
JS_FreeValue(context, cookie);
|
JS_FreeValue(context, cookie);
|
||||||
@ -1094,14 +1094,17 @@ const char* _form_data_get(const char** form_data, const char* key)
|
|||||||
typedef struct _login_request_t
|
typedef struct _login_request_t
|
||||||
{
|
{
|
||||||
tf_http_request_t* request;
|
tf_http_request_t* request;
|
||||||
const char* session_cookie;
|
|
||||||
JSValue jwt;
|
|
||||||
const char* name;
|
const char* name;
|
||||||
const char* error;
|
const char* error;
|
||||||
const char* settings;
|
const char* settings;
|
||||||
const char* code_of_conduct;
|
const char* code_of_conduct;
|
||||||
bool have_administrator;
|
bool have_administrator;
|
||||||
bool session_is_new;
|
bool session_is_new;
|
||||||
|
|
||||||
|
char location_header[1024];
|
||||||
|
const char* set_cookie_header;
|
||||||
|
|
||||||
|
int pending;
|
||||||
} login_request_t;
|
} login_request_t;
|
||||||
|
|
||||||
static const char* _make_set_session_cookie_header(tf_http_request_t* request, const char* session_cookie)
|
static const char* _make_set_session_cookie_header(tf_http_request_t* request, const char* session_cookie)
|
||||||
@ -1116,18 +1119,29 @@ static const char* _make_set_session_cookie_header(tf_http_request_t* request, c
|
|||||||
return cookie;
|
return cookie;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void _login_release(login_request_t* login)
|
||||||
|
{
|
||||||
|
int ref_count = --login->pending;
|
||||||
|
if (ref_count == 0)
|
||||||
|
{
|
||||||
|
tf_free((void*)login->name);
|
||||||
|
tf_free((void*)login->code_of_conduct);
|
||||||
|
tf_free((void*)login->set_cookie_header);
|
||||||
|
tf_free(login);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void _httpd_endpoint_login_file_read_callback(tf_task_t* task, const char* path, int result, const void* data, void* user_data)
|
static void _httpd_endpoint_login_file_read_callback(tf_task_t* task, const char* path, int result, const void* data, void* user_data)
|
||||||
{
|
{
|
||||||
login_request_t* login = user_data;
|
login_request_t* login = user_data;
|
||||||
tf_http_request_t* request = login->request;
|
tf_http_request_t* request = login->request;
|
||||||
if (result >= 0)
|
if (result >= 0)
|
||||||
{
|
{
|
||||||
const char* cookie = _make_set_session_cookie_header(request, login->session_cookie);
|
|
||||||
const char* headers[] = {
|
const char* headers[] = {
|
||||||
"Content-Type",
|
"Content-Type",
|
||||||
"text/html; charset=utf-8",
|
"text/html; charset=utf-8",
|
||||||
"Set-Cookie",
|
"Set-Cookie",
|
||||||
cookie ? cookie : "",
|
login->set_cookie_header ? login->set_cookie_header : "",
|
||||||
};
|
};
|
||||||
const char* replace_me = "$AUTH_DATA";
|
const char* replace_me = "$AUTH_DATA";
|
||||||
const char* auth = strstr(data, replace_me);
|
const char* auth = strstr(data, replace_me);
|
||||||
@ -1161,7 +1175,6 @@ static void _httpd_endpoint_login_file_read_callback(tf_task_t* task, const char
|
|||||||
{
|
{
|
||||||
tf_http_respond(request, 200, headers, tf_countof(headers) / 2, data, result);
|
tf_http_respond(request, 200, headers, tf_countof(headers) / 2, data, result);
|
||||||
}
|
}
|
||||||
tf_free((void*)cookie);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -1169,10 +1182,7 @@ static void _httpd_endpoint_login_file_read_callback(tf_task_t* task, const char
|
|||||||
tf_http_respond(request, 404, NULL, 0, k_payload, strlen(k_payload));
|
tf_http_respond(request, 404, NULL, 0, k_payload, strlen(k_payload));
|
||||||
}
|
}
|
||||||
tf_http_request_unref(request);
|
tf_http_request_unref(request);
|
||||||
tf_free((void*)login->name);
|
_login_release(login);
|
||||||
tf_free((void*)login->code_of_conduct);
|
|
||||||
tf_free((void*)login->session_cookie);
|
|
||||||
tf_free(login);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool _string_property_equals(JSContext* context, JSValue object, const char* name, const char* value)
|
static bool _string_property_equals(JSContext* context, JSValue object, const char* name, const char* value)
|
||||||
@ -1185,7 +1195,7 @@ static bool _string_property_equals(JSContext* context, JSValue object, const ch
|
|||||||
return equals;
|
return equals;
|
||||||
}
|
}
|
||||||
|
|
||||||
static JSValue _authenticate_jwt(JSContext* context, const char* jwt)
|
static JSValue _authenticate_jwt(tf_ssb_t* ssb, JSContext* context, const char* jwt)
|
||||||
{
|
{
|
||||||
if (!jwt)
|
if (!jwt)
|
||||||
{
|
{
|
||||||
@ -1226,8 +1236,6 @@ static JSValue _authenticate_jwt(JSContext* context, const char* jwt)
|
|||||||
return JS_UNDEFINED;
|
return JS_UNDEFINED;
|
||||||
}
|
}
|
||||||
|
|
||||||
tf_task_t* task = tf_task_get(context);
|
|
||||||
tf_ssb_t* ssb = tf_task_get_ssb(task);
|
|
||||||
char public_key_b64[k_id_base64_len] = { 0 };
|
char public_key_b64[k_id_base64_len] = { 0 };
|
||||||
tf_ssb_whoami(ssb, public_key_b64, sizeof(public_key_b64));
|
tf_ssb_whoami(ssb, public_key_b64, sizeof(public_key_b64));
|
||||||
|
|
||||||
@ -1343,32 +1351,6 @@ static bool _verify_password(const char* password, const char* hash)
|
|||||||
return out_hash && strcmp(hash, out_hash) == 0;
|
return out_hash && strcmp(hash, out_hash) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _httpd_endpoint_login_get_code_of_conduct_work(tf_ssb_t* ssb, void* user_data)
|
|
||||||
{
|
|
||||||
login_request_t* login = user_data;
|
|
||||||
login->settings = tf_ssb_db_get_property(ssb, "core", "settings");
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _httpd_endpoint_login_get_code_of_conduct_after_work(tf_ssb_t* ssb, int status, void* user_data)
|
|
||||||
{
|
|
||||||
login_request_t* login = user_data;
|
|
||||||
if (login->settings)
|
|
||||||
{
|
|
||||||
JSContext* context = tf_ssb_get_context(ssb);
|
|
||||||
JSValue settings_value = JS_ParseJSON(context, login->settings, strlen(login->settings), NULL);
|
|
||||||
JSValue code_of_conduct_value = JS_GetPropertyStr(context, settings_value, "code_of_conduct");
|
|
||||||
const char* code_of_conduct = JS_ToCString(context, code_of_conduct_value);
|
|
||||||
const char* result = tf_strdup(code_of_conduct);
|
|
||||||
JS_FreeCString(context, code_of_conduct);
|
|
||||||
JS_FreeValue(context, code_of_conduct_value);
|
|
||||||
JS_FreeValue(context, settings_value);
|
|
||||||
tf_free((void*)login->settings);
|
|
||||||
login->settings = NULL;
|
|
||||||
login->code_of_conduct = result;
|
|
||||||
}
|
|
||||||
tf_file_read(login->request->user_data, "core/auth.html", _httpd_endpoint_login_file_read_callback, login);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool _make_administrator_if_first(tf_ssb_t* ssb, const char* account_name_copy, bool may_become_first_admin)
|
static bool _make_administrator_if_first(tf_ssb_t* ssb, const char* account_name_copy, bool may_become_first_admin)
|
||||||
{
|
{
|
||||||
JSContext* context = tf_ssb_get_context(ssb);
|
JSContext* context = tf_ssb_get_context(ssb);
|
||||||
@ -1442,30 +1424,32 @@ static bool _make_administrator_if_first(tf_ssb_t* ssb, const char* account_name
|
|||||||
return have_administrator;
|
return have_administrator;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _httpd_endpoint_login(tf_http_request_t* request)
|
static void _httpd_endpoint_login_work(tf_ssb_t* ssb, void* user_data)
|
||||||
{
|
{
|
||||||
tf_task_t* task = request->user_data;
|
login_request_t* login = user_data;
|
||||||
JSContext* context = tf_task_get_context(task);
|
tf_http_request_t* request = login->request;
|
||||||
tf_ssb_t* ssb = tf_task_get_ssb(task);
|
|
||||||
|
JSMallocFunctions funcs = { 0 };
|
||||||
|
tf_get_js_malloc_functions(&funcs);
|
||||||
|
JSRuntime* runtime = JS_NewRuntime2(&funcs, NULL);
|
||||||
|
JSContext* context = JS_NewContext(runtime);
|
||||||
|
|
||||||
const char* session = tf_http_get_cookie(tf_http_request_get_header(request, "cookie"), "session");
|
const char* session = tf_http_get_cookie(tf_http_request_get_header(request, "cookie"), "session");
|
||||||
const char** form_data = _form_data_decode(request->query, request->query ? strlen(request->query) : 0);
|
const char** form_data = _form_data_decode(request->query, request->query ? strlen(request->query) : 0);
|
||||||
const char* account_name_copy = NULL;
|
const char* account_name_copy = NULL;
|
||||||
JSValue jwt = _authenticate_jwt(context, session);
|
JSValue jwt = _authenticate_jwt(ssb, context, session);
|
||||||
|
|
||||||
if (_session_is_authenticated_as_user(context, jwt))
|
if (_session_is_authenticated_as_user(context, jwt))
|
||||||
{
|
{
|
||||||
const char* return_url = _form_data_get(form_data, "return");
|
const char* return_url = _form_data_get(form_data, "return");
|
||||||
char url[1024];
|
if (return_url)
|
||||||
if (!return_url)
|
|
||||||
{
|
{
|
||||||
snprintf(url, sizeof(url), "%s%s/", request->is_tls ? "https://" : "http://", tf_http_request_get_header(request, "host"));
|
snprintf(login->location_header, sizeof(login->location_header), "%s", return_url);
|
||||||
return_url = url;
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
snprintf(login->location_header, sizeof(login->location_header), "%s%s/", request->is_tls ? "https://" : "http://", tf_http_request_get_header(request, "host"));
|
||||||
}
|
}
|
||||||
const char* headers[] = {
|
|
||||||
"Location",
|
|
||||||
return_url,
|
|
||||||
};
|
|
||||||
tf_http_respond(request, 303, headers, tf_countof(headers) / 2, NULL, 0);
|
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1544,40 +1528,46 @@ static void _httpd_endpoint_login(tf_http_request_t* request)
|
|||||||
if (session_is_new && _form_data_get(form_data, "return") && !login_error)
|
if (session_is_new && _form_data_get(form_data, "return") && !login_error)
|
||||||
{
|
{
|
||||||
const char* return_url = _form_data_get(form_data, "return");
|
const char* return_url = _form_data_get(form_data, "return");
|
||||||
char url[1024];
|
if (return_url)
|
||||||
if (!return_url)
|
|
||||||
{
|
{
|
||||||
snprintf(url, sizeof(url), "%s%s/", request->is_tls ? "https://" : "http://", tf_http_request_get_header(request, "host"));
|
snprintf(login->location_header, sizeof(login->location_header), "%s", return_url);
|
||||||
return_url = url;
|
|
||||||
}
|
}
|
||||||
const char* cookie = _make_set_session_cookie_header(request, send_session);
|
else
|
||||||
const char* headers[] = {
|
{
|
||||||
"Location",
|
snprintf(login->location_header, sizeof(login->location_header), "%s%s/", request->is_tls ? "https://" : "http://", tf_http_request_get_header(request, "host"));
|
||||||
return_url,
|
}
|
||||||
"Set-Cookie",
|
login->set_cookie_header = _make_set_session_cookie_header(request, send_session);
|
||||||
cookie ? cookie : "",
|
|
||||||
};
|
|
||||||
tf_http_respond(request, 303, headers, tf_countof(headers) / 2, NULL, 0);
|
|
||||||
tf_free((void*)cookie);
|
|
||||||
tf_free((void*)send_session);
|
tf_free((void*)send_session);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
tf_http_request_ref(request);
|
tf_http_request_ref(request);
|
||||||
|
|
||||||
login_request_t* login = tf_malloc(sizeof(login_request_t));
|
login->name = account_name_copy;
|
||||||
*login = (login_request_t) {
|
login->error = login_error;
|
||||||
.request = request,
|
login->set_cookie_header = _make_set_session_cookie_header(request, send_session);
|
||||||
.name = account_name_copy,
|
tf_free((void*)send_session);
|
||||||
.jwt = jwt,
|
login->session_is_new = session_is_new;
|
||||||
.error = login_error,
|
login->have_administrator = have_administrator;
|
||||||
.session_cookie = send_session,
|
login->settings = tf_ssb_db_get_property(ssb, "core", "settings");
|
||||||
.session_is_new = session_is_new,
|
|
||||||
.have_administrator = have_administrator,
|
if (login->settings)
|
||||||
};
|
{
|
||||||
|
JSValue settings_value = JS_ParseJSON(context, login->settings, strlen(login->settings), NULL);
|
||||||
|
JSValue code_of_conduct_value = JS_GetPropertyStr(context, settings_value, "code_of_conduct");
|
||||||
|
const char* code_of_conduct = JS_ToCString(context, code_of_conduct_value);
|
||||||
|
const char* result = tf_strdup(code_of_conduct);
|
||||||
|
JS_FreeCString(context, code_of_conduct);
|
||||||
|
JS_FreeValue(context, code_of_conduct_value);
|
||||||
|
JS_FreeValue(context, settings_value);
|
||||||
|
tf_free((void*)login->settings);
|
||||||
|
login->settings = NULL;
|
||||||
|
login->code_of_conduct = result;
|
||||||
|
}
|
||||||
|
|
||||||
|
login->pending++;
|
||||||
|
tf_file_read(login->request->user_data, "core/auth.html", _httpd_endpoint_login_file_read_callback, login);
|
||||||
|
|
||||||
tf_ssb_run_work(ssb, _httpd_endpoint_login_get_code_of_conduct_work, _httpd_endpoint_login_get_code_of_conduct_after_work, login);
|
|
||||||
jwt = JS_UNDEFINED;
|
|
||||||
account_name_copy = NULL;
|
account_name_copy = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1586,6 +1576,44 @@ done:
|
|||||||
tf_free(form_data);
|
tf_free(form_data);
|
||||||
tf_free((void*)account_name_copy);
|
tf_free((void*)account_name_copy);
|
||||||
JS_FreeValue(context, jwt);
|
JS_FreeValue(context, jwt);
|
||||||
|
|
||||||
|
JS_FreeContext(context);
|
||||||
|
JS_FreeRuntime(runtime);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _httpd_endpoint_login_after_work(tf_ssb_t* ssb, int status, void* user_data)
|
||||||
|
{
|
||||||
|
login_request_t* login = user_data;
|
||||||
|
if (login->pending == 1)
|
||||||
|
{
|
||||||
|
tf_http_request_t* request = login->request;
|
||||||
|
if (*login->location_header)
|
||||||
|
{
|
||||||
|
const char* headers[] = {
|
||||||
|
"Location",
|
||||||
|
login->location_header,
|
||||||
|
"Set-Cookie",
|
||||||
|
login->set_cookie_header ? login->set_cookie_header : "",
|
||||||
|
};
|
||||||
|
tf_http_respond(request, 303, headers, tf_countof(headers) / 2, NULL, 0);
|
||||||
|
}
|
||||||
|
tf_http_request_unref(request);
|
||||||
|
}
|
||||||
|
_login_release(login);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _httpd_endpoint_login(tf_http_request_t* request)
|
||||||
|
{
|
||||||
|
tf_task_t* task = request->user_data;
|
||||||
|
tf_http_request_ref(request);
|
||||||
|
|
||||||
|
tf_ssb_t* ssb = tf_task_get_ssb(task);
|
||||||
|
login_request_t* login = tf_malloc(sizeof(login_request_t));
|
||||||
|
*login = (login_request_t) {
|
||||||
|
.request = request,
|
||||||
|
};
|
||||||
|
login->pending++;
|
||||||
|
tf_ssb_run_work(ssb, _httpd_endpoint_login_work, _httpd_endpoint_login_after_work, login);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _httpd_endpoint_logout(tf_http_request_t* request)
|
static void _httpd_endpoint_logout(tf_http_request_t* request)
|
||||||
|
@ -42,20 +42,19 @@ try:
|
|||||||
|
|
||||||
driver.switch_to.frame(driver.find_element(By.ID, 'document'))
|
driver.switch_to.frame(driver.find_element(By.ID, 'document'))
|
||||||
wait.until(expected_conditions.presence_of_element_located((By.LINK_TEXT, 'identity'))).click()
|
wait.until(expected_conditions.presence_of_element_located((By.LINK_TEXT, 'identity'))).click()
|
||||||
driver.switch_to.default_content()
|
|
||||||
|
|
||||||
wait.until(expected_conditions.presence_of_element_located((By.ID, 'content')))
|
|
||||||
|
|
||||||
# StaleElementReferenceException
|
# StaleElementReferenceException
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
|
driver.switch_to.default_content()
|
||||||
|
wait.until(expected_conditions.presence_of_element_located((By.ID, 'content')))
|
||||||
driver.switch_to.frame(wait.until(expected_conditions.presence_of_element_located((By.ID, 'document'))))
|
driver.switch_to.frame(wait.until(expected_conditions.presence_of_element_located((By.ID, 'document'))))
|
||||||
|
wait.until(expected_conditions.presence_of_element_located((By.ID, 'create_id'))).click()
|
||||||
|
driver.switch_to.alert.accept()
|
||||||
break
|
break
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
wait.until(expected_conditions.presence_of_element_located((By.ID, 'create_id'))).click()
|
|
||||||
driver.switch_to.alert.accept()
|
|
||||||
# StaleElementReferenceException
|
# StaleElementReferenceException
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
|
Loading…
Reference in New Issue
Block a user