From 7caf4a01739e1c5efeb8e432d41dc3d0439a69c0 Mon Sep 17 00:00:00 2001 From: Cory McWilliams Date: Fri, 10 May 2024 22:21:59 -0400 Subject: [PATCH] Fix numerous issues around setting the first registered used as an admin. --- src/httpd.js.c | 159 ++++++++++++++++++++++++++++--------------------- 1 file changed, 92 insertions(+), 67 deletions(-) diff --git a/src/httpd.js.c b/src/httpd.js.c index ca99e240..1e61606c 100644 --- a/src/httpd.js.c +++ b/src/httpd.js.c @@ -1213,6 +1213,94 @@ static bool _verify_password(const char* password, const char* hash) return out_hash && strcmp(hash, out_hash) == 0; } +static const char* _get_code_of_conduct(tf_ssb_t* ssb) +{ + JSContext* context = tf_ssb_get_context(ssb); + const char* settings = tf_ssb_db_get_property(ssb, "core", "settings"); + JSValue settings_value = settings ? JS_ParseJSON(context, settings, strlen(settings), NULL) : JS_UNDEFINED; + 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*)settings); + return result; +} + +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); + const char* settings = tf_ssb_db_get_property(ssb, "core", "settings"); + JSValue settings_value = settings ? JS_ParseJSON(context, settings, strlen(settings), NULL) : JS_UNDEFINED; + if (JS_IsUndefined(settings_value)) + { + settings_value = JS_NewObject(context); + } + + bool have_administrator = false; + JSValue permissions = JS_GetPropertyStr(context, settings_value, "permissions"); + + JSPropertyEnum* ptab = NULL; + uint32_t plen = 0; + JS_GetOwnPropertyNames(context, &ptab, &plen, permissions, JS_GPN_STRING_MASK); + for (int i = 0; i < (int)plen; i++) + { + JSPropertyDescriptor desc = { 0 }; + if (JS_GetOwnProperty(context, &desc, permissions, ptab[i].atom) == 1) + { + int permission_length = tf_util_get_length(context, desc.value); + for (int i = 0; i < permission_length; i++) + { + JSValue entry = JS_GetPropertyUint32(context, desc.value, i); + const char* permission = JS_ToCString(context, entry); + if (permission && strcmp(permission, "administration") == 0) + { + have_administrator = true; + } + JS_FreeCString(context, permission); + JS_FreeValue(context, entry); + } + JS_FreeValue(context, desc.setter); + JS_FreeValue(context, desc.getter); + JS_FreeValue(context, desc.value); + } + } + for (uint32_t i = 0; i < plen; ++i) + { + JS_FreeAtom(context, ptab[i].atom); + } + js_free(context, ptab); + + if (!have_administrator && may_become_first_admin) + { + if (JS_IsUndefined(permissions)) + { + permissions = JS_NewObject(context); + JS_SetPropertyStr(context, settings_value, "permissions", JS_DupValue(context, permissions)); + } + JSValue user = JS_GetPropertyStr(context, permissions, account_name_copy); + if (JS_IsUndefined(user)) + { + user = JS_NewArray(context); + JS_SetPropertyStr(context, permissions, account_name_copy, JS_DupValue(context, user)); + } + JS_SetPropertyUint32(context, user, tf_util_get_length(context, user), JS_NewString(context, "administration")); + JS_FreeValue(context, user); + + JSValue settings_json = JS_JSONStringify(context, settings_value, JS_NULL, JS_NULL); + const char* settings_string = JS_ToCString(context, settings_json); + tf_ssb_db_set_property(ssb, "core", "settings", settings_string); + JS_FreeCString(context, settings_string); + JS_FreeValue(context, settings_json); + } + + JS_FreeValue(context, permissions); + JS_FreeValue(context, settings_value); + tf_free((void*)settings); + return have_administrator; +} + static void _httpd_endpoint_login(tf_http_request_t* request) { tf_task_t* task = request->user_data; @@ -1310,6 +1398,8 @@ static void _httpd_endpoint_login(tf_http_request_t* request) tf_free(post_form_data); } + bool have_administrator = _make_administrator_if_first(ssb, account_name_copy, may_become_first_admin); + if (session_is_new && _form_data_get(form_data, "return") && !login_error) { const char* return_url = _form_data_get(form_data, "return"); @@ -1334,69 +1424,8 @@ static void _httpd_endpoint_login(tf_http_request_t* request) { tf_http_request_ref(request); - const char* settings = tf_ssb_db_get_property(ssb, "core", "settings"); - JSValue settings_value = settings ? JS_ParseJSON(context, settings, strlen(settings), NULL) : JS_UNDEFINED; - 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); - - bool have_administrator = false; - JSValue permissions = JS_GetPropertyStr(context, settings_value, "permissions"); - - JSPropertyEnum* ptab = NULL; - uint32_t plen = 0; - JS_GetOwnPropertyNames(context, &ptab, &plen, permissions, JS_GPN_STRING_MASK); - for (int i = 0; i < (int)plen; i++) - { - JSPropertyDescriptor desc = { 0 }; - if (JS_GetOwnProperty(context, &desc, permissions, ptab[i].atom) == 1) - { - int permission_length = tf_util_get_length(context, desc.value); - for (int i = 0; i < permission_length; i++) - { - JSValue entry = JS_GetPropertyUint32(context, desc.value, i); - const char* permission = JS_ToCString(context, entry); - if (permission && strcmp(permission, "administration") == 0) - { - have_administrator = true; - } - JS_FreeCString(context, permission); - JS_FreeValue(context, entry); - } - JS_FreeValue(context, desc.setter); - JS_FreeValue(context, desc.getter); - JS_FreeValue(context, desc.value); - } - } - for (uint32_t i = 0; i < plen; ++i) - { - JS_FreeAtom(context, ptab[i].atom); - } - js_free(context, ptab); - - if (!have_administrator && may_become_first_admin) - { - if (JS_IsUndefined(permissions)) - { - permissions = JS_NewObject(context); - JS_SetPropertyStr(context, settings_value, "permissions", permissions); - } - JSValue user = JS_GetPropertyStr(context, permissions, account_name_copy); - if (JS_IsUndefined(user)) - { - user = JS_NewArray(context); - JS_SetPropertyStr(context, permissions, account_name_copy, user); - } - JS_SetPropertyUint32(context, user, tf_util_get_length(context, user), JS_NewString(context, "administration")); - - JSValue settings_json = JS_JSONStringify(context, settings_value, JS_NULL, JS_NULL); - const char* settings_string = JS_ToCString(context, settings_json); - tf_ssb_db_set_property(ssb, "core", "settings", settings_string); - JS_FreeCString(context, settings_string); - JS_FreeValue(context, settings_json); - } - JS_FreeValue(context, permissions); - login_request_t* login = tf_malloc(sizeof(login_request_t)); + const char* code_of_conduct = _get_code_of_conduct(ssb); *login = (login_request_t) { .request = request, .name = account_name_copy, @@ -1404,14 +1433,10 @@ static void _httpd_endpoint_login(tf_http_request_t* request) .error = login_error, .session_cookie = send_session, .session_is_new = session_is_new, - .code_of_conduct = tf_strdup(code_of_conduct), + .code_of_conduct = code_of_conduct, .have_administrator = have_administrator, }; - JS_FreeCString(context, code_of_conduct); - JS_FreeValue(context, code_of_conduct_value); - JS_FreeValue(context, settings_value); - tf_free((void*)settings); tf_file_read(request->user_data, "core/auth.html", _httpd_endpoint_login_file_read_callback, login); jwt = JS_UNDEFINED; account_name_copy = NULL;