Add an option to disable account registation, and fix use of a JSContext from the wrong thread along the way.
Some checks are pending
Build Tilde Friends / Build-All (push) Waiting to run

This commit is contained in:
Cory McWilliams 2024-08-21 20:56:21 -04:00
parent e38ff99607
commit bfb3d8b8a2
4 changed files with 92 additions and 51 deletions

View File

@ -79,6 +79,11 @@ const k_global_settings = {
default_value: false, default_value: false,
description: 'Enable discovery of, sharing of, and connecting to internet peer strangers, including announcing this instance.', description: 'Enable discovery of, sharing of, and connecting to internet peer strangers, including announcing this instance.',
}, },
account_registration: {
type: 'boolean',
default_value: true,
description: 'Allow registration of new accounts.',
},
}; };
let gGlobalSettings = { let gGlobalSettings = {

View File

@ -1484,27 +1484,39 @@ static void _httpd_endpoint_login_work(tf_ssb_t* ssb, void* user_data)
if (form_register && strcmp(form_register, "1") == 0) if (form_register && strcmp(form_register, "1") == 0)
{ {
if (!have_account && _is_name_valid(account_name) && password && confirm && strcmp(password, confirm) == 0 && bool registered = false;
tf_ssb_db_register_account(ssb, account_name, password)) if (!have_account && _is_name_valid(account_name) && password && confirm && strcmp(password, confirm) == 0)
{ {
tf_free((void*)send_session); sqlite3* db = tf_ssb_acquire_db_writer(ssb);
send_session = _make_session_jwt(ssb, account_name); registered = tf_ssb_db_register_account(tf_ssb_get_loop(ssb), db, context, account_name, password);
may_become_first_admin = true; tf_ssb_release_db_writer(ssb, db);
if (registered)
{
tf_free((void*)send_session);
send_session = _make_session_jwt(ssb, account_name);
may_become_first_admin = true;
}
} }
else if (!registered)
{ {
login_error = "Error registering account."; login_error = "Error registering account.";
} }
} }
else if (change && strcmp(change, "1") == 0) else if (change && strcmp(change, "1") == 0)
{ {
if (have_account && _is_name_valid(account_name) && new_password && confirm && strcmp(new_password, confirm) == 0 && _verify_password(password, account_passwd) && bool set = false;
tf_ssb_db_set_account_password(ssb, account_name, new_password)) if (have_account && _is_name_valid(account_name) && new_password && confirm && strcmp(new_password, confirm) == 0 && _verify_password(password, account_passwd))
{ {
tf_free((void*)send_session); sqlite3* db = tf_ssb_acquire_db_writer(ssb);
send_session = _make_session_jwt(ssb, account_name); set = tf_ssb_db_set_account_password(tf_ssb_get_loop(ssb), db, context, account_name, new_password);
tf_ssb_release_db_writer(ssb, db);
if (set)
{
tf_free((void*)send_session);
send_session = _make_session_jwt(ssb, account_name);
}
} }
else if (!set)
{ {
login_error = "Error changing password."; login_error = "Error changing password.";
} }

View File

@ -1644,14 +1644,13 @@ bool tf_ssb_db_get_account_password_hash(tf_ssb_t* ssb, const char* name, char*
return result; return result;
} }
bool tf_ssb_db_set_account_password(tf_ssb_t* ssb, const char* name, const char* password) bool tf_ssb_db_set_account_password(uv_loop_t* loop, sqlite3* db, JSContext* context, const char* name, const char* password)
{ {
JSContext* context = tf_ssb_get_context(ssb);
bool result = false; bool result = false;
static const int k_salt_length = 12; static const int k_salt_length = 12;
char buffer[16]; char buffer[16];
size_t bytes = uv_random(tf_ssb_get_loop(ssb), &(uv_random_t) { 0 }, buffer, sizeof(buffer), 0, NULL) == 0 ? sizeof(buffer) : 0; size_t bytes = uv_random(loop, &(uv_random_t) { 0 }, buffer, sizeof(buffer), 0, NULL) == 0 ? sizeof(buffer) : 0;
char output[7 + 22 + 1]; char output[7 + 22 + 1];
char* salt = crypt_gensalt_rn("$2b$", k_salt_length, buffer, bytes, output, sizeof(output)); char* salt = crypt_gensalt_rn("$2b$", k_salt_length, buffer, bytes, output, sizeof(output));
char hash_output[7 + 22 + 31 + 1]; char hash_output[7 + 22 + 31 + 1];
@ -1663,7 +1662,6 @@ bool tf_ssb_db_set_account_password(tf_ssb_t* ssb, const char* name, const char*
size_t user_length = 0; size_t user_length = 0;
const char* user_string = JS_ToCStringLen(context, &user_length, user_json); const char* user_string = JS_ToCStringLen(context, &user_length, user_json);
sqlite3* db = tf_ssb_acquire_db_writer(ssb);
sqlite3_stmt* statement = NULL; sqlite3_stmt* statement = NULL;
if (sqlite3_prepare(db, "INSERT OR REPLACE INTO properties (id, key, value) VALUES ('auth', 'user:' || ?, ?)", -1, &statement, NULL) == SQLITE_OK) if (sqlite3_prepare(db, "INSERT OR REPLACE INTO properties (id, key, value) VALUES ('auth', 'user:' || ?, ?)", -1, &statement, NULL) == SQLITE_OK)
{ {
@ -1673,7 +1671,6 @@ bool tf_ssb_db_set_account_password(tf_ssb_t* ssb, const char* name, const char*
} }
sqlite3_finalize(statement); sqlite3_finalize(statement);
} }
tf_ssb_release_db_writer(ssb, db);
JS_FreeCString(context, user_string); JS_FreeCString(context, user_string);
JS_FreeValue(context, user_json); JS_FreeValue(context, user_json);
@ -1681,47 +1678,70 @@ bool tf_ssb_db_set_account_password(tf_ssb_t* ssb, const char* name, const char*
return result; return result;
} }
bool tf_ssb_db_register_account(tf_ssb_t* ssb, const char* name, const char* password) static bool _tf_ssb_db_get_global_setting_bool(sqlite3* db, const char* name, bool default_value)
{
bool result = default_value;
sqlite3_stmt* statement;
if (sqlite3_prepare(db, "SELECT json_extract(value, '$.' || ?) FROM properties WHERE id = 'core' AND key = 'settings'", -1, &statement, NULL) == SQLITE_OK)
{
if (sqlite3_bind_text(statement, 1, name, -1, NULL) == SQLITE_OK)
{
if (sqlite3_step(statement) == SQLITE_ROW && sqlite3_column_type(statement, 0) != SQLITE_NULL)
{
result = sqlite3_column_int(statement, 0) != 0;
}
}
sqlite3_finalize(statement);
}
else
{
tf_printf("prepare failed: %s\n", sqlite3_errmsg(db));
}
return result;
}
bool tf_ssb_db_register_account(uv_loop_t* loop, sqlite3* db, JSContext* context, const char* name, const char* password)
{ {
bool result = false; bool result = false;
JSContext* context = tf_ssb_get_context(ssb);
JSValue users_array = JS_UNDEFINED; JSValue users_array = JS_UNDEFINED;
sqlite3* db = tf_ssb_acquire_db_writer(ssb); bool registration_allowed = _tf_ssb_db_get_global_setting_bool(db, "account_registration", true);
sqlite3_stmt* statement = NULL; if (registration_allowed)
if (sqlite3_prepare(db, "SELECT value FROM properties WHERE id = 'auth' AND key = 'users'", -1, &statement, NULL) == SQLITE_OK)
{ {
if (sqlite3_step(statement) == SQLITE_ROW) sqlite3_stmt* statement = NULL;
if (sqlite3_prepare(db, "SELECT value FROM properties WHERE id = 'auth' AND key = 'users'", -1, &statement, NULL) == SQLITE_OK)
{ {
users_array = JS_ParseJSON(context, (const char*)sqlite3_column_text(statement, 0), sqlite3_column_bytes(statement, 0), NULL); if (sqlite3_step(statement) == SQLITE_ROW)
{
users_array = JS_ParseJSON(context, (const char*)sqlite3_column_text(statement, 0), sqlite3_column_bytes(statement, 0), NULL);
}
sqlite3_finalize(statement);
} }
sqlite3_finalize(statement); if (JS_IsUndefined(users_array))
}
if (JS_IsUndefined(users_array))
{
users_array = JS_NewArray(context);
}
int length = tf_util_get_length(context, users_array);
JS_SetPropertyUint32(context, users_array, length, JS_NewString(context, name));
JSValue json = JS_JSONStringify(context, users_array, JS_NULL, JS_NULL);
JS_FreeValue(context, users_array);
size_t value_length = 0;
const char* value = JS_ToCStringLen(context, &value_length, json);
if (sqlite3_prepare(db, "INSERT OR REPLACE INTO properties (id, key, value) VALUES ('auth', 'users', ?)", -1, &statement, NULL) == SQLITE_OK)
{
if (sqlite3_bind_text(statement, 1, value, value_length, NULL) == SQLITE_OK)
{ {
tf_printf("added user to properties\n"); users_array = JS_NewArray(context);
result = sqlite3_step(statement) == SQLITE_DONE;
} }
sqlite3_finalize(statement); int length = tf_util_get_length(context, users_array);
} JS_SetPropertyUint32(context, users_array, length, JS_NewString(context, name));
JS_FreeCString(context, value);
JS_FreeValue(context, json);
tf_ssb_release_db_writer(ssb, db);
result = result && tf_ssb_db_set_account_password(ssb, name, password); JSValue json = JS_JSONStringify(context, users_array, JS_NULL, JS_NULL);
JS_FreeValue(context, users_array);
size_t value_length = 0;
const char* value = JS_ToCStringLen(context, &value_length, json);
if (sqlite3_prepare(db, "INSERT OR REPLACE INTO properties (id, key, value) VALUES ('auth', 'users', ?)", -1, &statement, NULL) == SQLITE_OK)
{
if (sqlite3_bind_text(statement, 1, value, value_length, NULL) == SQLITE_OK)
{
tf_printf("added user to properties\n");
result = sqlite3_step(statement) == SQLITE_DONE;
}
sqlite3_finalize(statement);
}
JS_FreeCString(context, value);
JS_FreeValue(context, json);
}
result = result && tf_ssb_db_set_account_password(loop, db, context, name, password);
return result; return result;
} }

View File

@ -360,21 +360,25 @@ bool tf_ssb_db_get_account_password_hash(tf_ssb_t* ssb, const char* name, char*
/** /**
** Insert or update a user's hashed password in the database. ** Insert or update a user's hashed password in the database.
** @param ssb The SSB instance. ** @param loop The event loop.
** @param db A DB writer.
** @param context A JS context.
** @param name The username. ** @param name The username.
** @param password The raw password. ** @param password The raw password.
** @return true if the hash of the password was successfully stored. ** @return true if the hash of the password was successfully stored.
*/ */
bool tf_ssb_db_set_account_password(tf_ssb_t* ssb, const char* name, const char* password); bool tf_ssb_db_set_account_password(uv_loop_t* loop, sqlite3* db, JSContext* context, const char* name, const char* password);
/** /**
** Add a user account to the database. ** Add a user account to the database.
** @param ssb The SSB instance. ** @param loop The event loop.
** @param db A DB writer.
** @param context A JS context.
** @param name The username to add. ** @param name The username to add.
** @param password The user's raw password. ** @param password The user's raw password.
** @return true If the user was added successfully. ** @return true If the user was added successfully.
*/ */
bool tf_ssb_db_register_account(tf_ssb_t* ssb, const char* name, const char* password); bool tf_ssb_db_register_account(uv_loop_t* loop, sqlite3* db, JSContext* context, const char* name, const char* password);
/** /**
** Get an entry from the properties table. ** Get an entry from the properties table.