This commit is contained in:
SimonFJ20 2025-03-06 16:36:33 +01:00
parent 2b1b536172
commit b3246c9a3e
9 changed files with 168 additions and 30 deletions

1
backend/.gitignore vendored
View File

@ -1,2 +1,3 @@
build/
database.db
Session.vim

View File

@ -38,5 +38,7 @@ void route_post_user_register(HttpCtx* ctx);
#define RESPOND_JSON(HTTP_CTX, STATUS, ...) \
RESPOND(HTTP_CTX, STATUS, "application/json", __VA_ARGS__)
#define RESPOND_BAD_REQUEST(HTTP_CTX) \
RESPOND_JSON(HTTP_CTX, 400, "{\"ok\":false}")
#define RESPOND_BAD_REQUEST(HTTP_CTX, MSG) \
RESPOND_JSON(HTTP_CTX, 400, "{\"ok\":false,\"msg\":\"%s\"}", (MSG))
#define RESPOND_SERVER_ERROR(HTTP_CTX) \
RESPOND_JSON(HTTP_CTX, 500, "{\"ok\":false,\"msg\":\"server error\"}")

View File

@ -0,0 +1,10 @@
#include "../controllers.h"
#include "../http_server.h"
#include "../models_json.h"
#include "../str_util.h"
void route_post_auth_login(HttpCtx* ctx)
{
Cx* cx = http_ctx_user_ctx(ctx);
}

View File

@ -0,0 +1,48 @@
#include "../controllers.h"
#include "../http_server.h"
#include "../models_json.h"
void route_get_index(HttpCtx* ctx)
{
Cx* cx = http_ctx_user_ctx(ctx);
RESPOND_HTML(ctx, 200,
"<!DOCTYPE html><html><head><meta "
"charset=\"utf-8\"></head><body><h1>Number = %d</h1></body></html>",
cx->number);
}
void route_post_set_number(HttpCtx* ctx)
{
Cx* cx = http_ctx_user_ctx(ctx);
const char* body_text = http_ctx_req_body(ctx);
JsonParser parser;
json_parser_construct(&parser, body_text, strlen(body_text));
JsonValue* body = json_parser_parse(&parser);
json_parser_destroy(&parser);
if (!json_object_has(body, "value")) {
RESPOND_JSON(
ctx, 200, "{\"ok\": false, \"msg\": \"no 'value' key\"}\r\n");
goto l0_return;
}
int64_t value = json_int(json_object_get(body, "value"));
cx->number = (int)value;
RESPOND_JSON(ctx, 200, "{\"ok\": true}\r\n");
l0_return:
json_free(body);
}
void route_get_not_found(HttpCtx* ctx)
{
RESPOND_HTML(ctx, 404,
"<!DOCTYPE html><html><head><meta "
"charset=\"utf-8\"></head><body><center><h1>404 Not "
"Found</h1><code style=\"font-size: 1rem;\">GET "
"%s</code></center></body></html>",
http_ctx_req_path(ctx));
}

View File

@ -0,0 +1,66 @@
#include "../controllers.h"
#include "../http_server.h"
#include "../models_json.h"
#include "../str_util.h"
#include <string.h>
void route_post_user_register(HttpCtx* ctx)
{
Cx* cx = http_ctx_user_ctx(ctx);
const char* body_str = http_ctx_req_body(ctx);
JsonValue* body_json = json_parse(body_str, strlen(body_str));
UsersRegisterReq req;
if (users_register_req_from_json(&req, body_json) != 0) {
RESPOND_BAD_REQUEST(ctx, "bad request");
json_free(body_json);
return;
}
json_free(body_json);
if (strlen(req.name) == 0 || strlen(req.email) == 0
|| strlen(req.password) > MAX_HASH_INPUT_LEN) {
RESPOND_BAD_REQUEST(ctx, "bad request");
users_register_req_free(&req);
return;
}
Ids ids;
ids_construct(&ids);
if (db_users_with_email(cx->db, &ids, req.email) != DbRes_Ok) {
RESPOND_SERVER_ERROR(ctx);
ids_destroy(&ids);
users_register_req_free(&req);
return;
}
if (ids.size > 0) {
RESPOND_BAD_REQUEST(ctx, "email in use");
ids_destroy(&ids);
users_register_req_free(&req);
return;
}
ids_destroy(&ids);
char* password_hash = str_hash(req.password);
if (db_user_insert(cx->db,
&(User) {
.id = 0,
.name = req.name,
.email = req.email,
.password_hash = password_hash,
.balance_dkk_cent = 0,
})
!= DbRes_Ok) {
RESPOND_SERVER_ERROR(ctx);
free(password_hash);
users_register_req_free(&req);
return;
}
free(password_hash);
users_register_req_free(&req);
RESPOND_JSON(ctx, 200, "{\"ok\":true}");
}

View File

@ -15,7 +15,7 @@ typedef enum {
typedef struct Db Db;
/// `user.id` field is ignored.
DbRes db_user_insert(Db* db, User* user);
DbRes db_user_insert(Db* db, const User* user);
DbRes db_user_from_id(Db* db, User* user, int64_t id);
/// Expects `ids` to be constructed.
DbRes db_users_with_email(Db* db, Ids* ids, const char* email);

View File

@ -65,7 +65,7 @@ static inline void disconnect(sqlite3* connection)
disconnect(connection); \
}
DbRes db_user_insert(Db* db, User* user)
DbRes db_user_insert(Db* db, const User* user)
{
static_assert(sizeof(User) == 40, "model has changed");
@ -76,7 +76,7 @@ DbRes db_user_insert(Db* db, User* user)
sqlite3_stmt* stmt;
sqlite3_prepare_v2(connection,
"INSERT INTO users (name, email, password_hash, balance_dkk_cent) "
"VALUES (?, ?, ?)",
"VALUES (?, ?, ?, ?)",
-1, &stmt, NULL);
sqlite3_bind_text(stmt, 1, user->name, -1, SQLITE_STATIC);

View File

@ -72,17 +72,27 @@ char* string_copy(const String* string)
return copy;
}
static inline StrHash str_hash_with_salt(const char* str, const uint8_t* salt)
#define STR_HASH_SALT_SIZE 32
#define STR_HASH_HASH_SIZE 32
#define STR_HASH_STR_LEN 128
typedef struct {
uint8_t salt[STR_HASH_SALT_SIZE];
uint8_t hash[STR_HASH_HASH_SIZE];
} HashData;
static inline HashData hashdata_from_str_and_salt(
const char* str, const uint8_t* salt)
{
if (strlen(str) >= MAX_HASH_INPUT_LEN - 1) {
if (strlen(str) >= MAX_HASH_INPUT_LEN) {
fprintf(stderr, "error: tried to hash too long input\n");
exit(1);
}
StrHash hash;
HashData hash;
memcpy(hash.salt, salt, STR_HASH_SALT_SIZE);
uint8_t input[MAX_HASH_INPUT_LEN + STR_HASH_SALT_SIZE] = { 0 };
uint8_t input[MAX_HASH_INPUT_LEN + 1 + STR_HASH_SALT_SIZE] = { 0 };
memcpy(input, hash.salt, STR_HASH_SALT_SIZE);
memcpy(&input[STR_HASH_SALT_SIZE], str, strlen(str));
@ -90,20 +100,20 @@ static inline StrHash str_hash_with_salt(const char* str, const uint8_t* salt)
return hash;
}
StrHash str_hash(const char* str)
static inline HashData hashdata_from_str(const char* str)
{
uint8_t salt[STR_HASH_SALT_SIZE];
RAND_bytes(salt, STR_HASH_SALT_SIZE);
return str_hash_with_salt(str, salt);
return hashdata_from_str_and_salt(str, salt);
}
bool str_hash_is_equal(StrHash hash, const char* str)
static inline bool hashdata_is_equal(HashData hash, const char* str)
{
StrHash other = str_hash_with_salt(str, hash.salt);
HashData other = hashdata_from_str_and_salt(str, hash.salt);
return memcmp(hash.hash, other.hash, STR_HASH_HASH_SIZE) == 0;
}
char* str_hash_to_string(StrHash hash)
static inline char* hashdata_to_string(HashData hash)
{
char* result = calloc(STR_HASH_STR_LEN + 1, sizeof(char));
for (size_t i = 0; i < STR_HASH_SALT_SIZE; ++i) {
@ -121,7 +131,7 @@ char* str_hash_to_string(StrHash hash)
return result;
}
StrHash str_hash_from_string(const char* str)
static inline HashData hashdata_from_hash_string(const char* str)
{
uint8_t result[64] = { 0 };
size_t result_i = 0;
@ -133,7 +143,7 @@ StrHash str_hash_from_string(const char* str)
result_i += 1;
}
StrHash hash;
HashData hash;
// memcpy((uint8_t*)&hash, result, sizeof(result));
for (size_t i = 0; i < 32; ++i) {
hash.salt[i] = result[i];
@ -141,3 +151,15 @@ StrHash str_hash_from_string(const char* str)
}
return hash;
}
char* str_hash(const char* input)
{
HashData data = hashdata_from_str(input);
return hashdata_to_string(data);
}
bool str_hash_equal(const char* hash, const char* input)
{
HashData data = hashdata_from_hash_string(hash);
return hashdata_is_equal(data, input);
}

View File

@ -33,18 +33,7 @@ char* string_copy(const String* string);
DEFINE_VEC(char*, RawStrVec, rawstr_vec, 8)
#define MAX_HASH_INPUT_LEN 256
#define MAX_HASH_INPUT_LEN 256 - 1
#define STR_HASH_SALT_SIZE 32
#define STR_HASH_HASH_SIZE 32
#define STR_HASH_STR_LEN 128
typedef struct {
uint8_t salt[STR_HASH_SALT_SIZE];
uint8_t hash[STR_HASH_HASH_SIZE];
} StrHash;
StrHash str_hash(const char* str);
bool str_hash_is_equal(StrHash hash, const char* str);
char* str_hash_to_string(StrHash hash);
StrHash str_hash_from_string(const char* str);
char* str_hash(const char* input);
bool str_hash_equal(const char* hash, const char* input);