Implement authorization

Read username from user config, check existence of user and then check password.
This commit is contained in:
2026-06-17 17:34:07 +02:00
parent 87b7015f17
commit 6ca91a33be
5 changed files with 187 additions and 125 deletions

View File

@@ -5,25 +5,82 @@
#define NOH_IMPLEMENTATION
#include "noh.h"
#define NO_AUTH
Noh_Arena temp;
#include "path.c"
#include "users.c"
#define SV Noh_String_View
#define sv noh_sv_from_cstr
/* To disable Basic auth for testing:
#define NO_AUTH
This will ignore any auth headers and always log in with username demo.
*/
#define SV Noh_String_View
#define sv noh_sv_from_cstr
Noh_Arena temp;
#include "id_store.c"
#include "library.c"
typedef struct {
SV base_path;
SV library_name;
} Settings;
// Authorizes the username from the Basic authorization info in the provided connection.
// The username data is guaranteed to live as long as the connection lives.
static bool authorize_user(struct MHD_Connection *connection, SV *username, Settings *settings) {
#ifdef NO_AUTH
(void)connection;
*username = sv("demo");
return false;
#else
bool result = true;
struct MHD_BasicAuthInfo *auth = MHD_basic_auth_get_username_password3(connection);
if (!auth) return false;
SV provided_username = { .elems = auth->username, .count = auth->username_len };
SV provided_password = { .elems = auth->password, .count = auth->password_len };
// Read the users file.
noh_arena_save(&temp);
Noh_String_View file_name = noh_sv_from_cstr("users.conf");
Noh_String users_file_path = build_path(false, &settings->base_path, &file_name);
noh_string_append_null(&users_file_path);
Noh_String hashed = {0};
Users users = {0};
Noh_String users_file = {0};
if (!read_users_file(users_file_path.elems, &users_file, &users)) noh_return_defer(false);
// Find the user in the users list.
bool found_user = false;
User *user = NULL;
for (size_t i = 0; i < users.count && !found_user; i++) {
user = &users.elems[i];
if (noh_sv_eq(provided_username, user->username)) found_user = true;
}
if (!found_user) noh_return_defer(false);
// Hash the provided password.
hashed = hash_password(provided_username, provided_password);
// Check the hash.
if (!noh_sv_eq(noh_sv_from_string(hashed), user->hash)) noh_return_defer(false);
*username = provided_username;
defer:
noh_string_free(&hashed);
noh_da_free(&users);
noh_string_free(&users_file_path);
noh_string_free(&users_file);
noh_arena_reset(&temp);
return result;
#endif
}
static enum MHD_Result send_response(struct MHD_Connection *connection, int status, SV message, bool xml) {
struct MHD_Response *response = MHD_create_response_from_buffer(
message.count, (void*)message.elems, MHD_RESPMEM_MUST_COPY);
@@ -47,6 +104,8 @@ static enum MHD_Result send_epub_file(struct MHD_Connection *connection, int fil
return ret;
}
#include "id_store.c"
#include "library.c"
#include "opds_handler.c"
#include "get_handler.c"
@@ -65,20 +124,7 @@ static enum MHD_Result request_handler(
noh_log(NOH_INFO, "Request: %s %s %s", version, method, _url);
SV username;
#ifdef NO_AUTH
username = sv("demo");
#else
struct MHD_BasicAuthInfo *auth = MHD_basic_auth_get_username_password3(connection);
if (!auth) return send_response(connection, 401, sv(""), false);
username = (SV){ .elems = auth->username, .count = auth->username_len };
SV password = { .elems = auth->password, .count = auth->password_len };
// TODO: Read username from user config.
if (!noh_sv_eq(password, sv("jaja"))) return send_response(connection, 401, sv(""), false);
#endif
noh_log(NOH_INFO, "Authenticated as "Nsv_Fmt, Nsv_Arg(username));
if (!authorize_user(connection, &username, &settings)) return send_response(connection, 401, sv(""), false);
if (strcmp(method, "GET") != 0) return send_response(connection, 405, sv(""), false);