mirror of
https://github.com/Mercantec-GHC/h4-projekt-gruppe-0-sm.git
synced 2025-04-28 08:44:06 +02:00
register
This commit is contained in:
parent
2b1b536172
commit
b3246c9a3e
1
backend/.gitignore
vendored
1
backend/.gitignore
vendored
@ -1,2 +1,3 @@
|
|||||||
build/
|
build/
|
||||||
database.db
|
database.db
|
||||||
|
Session.vim
|
||||||
|
@ -38,5 +38,7 @@ void route_post_user_register(HttpCtx* ctx);
|
|||||||
#define RESPOND_JSON(HTTP_CTX, STATUS, ...) \
|
#define RESPOND_JSON(HTTP_CTX, STATUS, ...) \
|
||||||
RESPOND(HTTP_CTX, STATUS, "application/json", __VA_ARGS__)
|
RESPOND(HTTP_CTX, STATUS, "application/json", __VA_ARGS__)
|
||||||
|
|
||||||
#define RESPOND_BAD_REQUEST(HTTP_CTX) \
|
#define RESPOND_BAD_REQUEST(HTTP_CTX, MSG) \
|
||||||
RESPOND_JSON(HTTP_CTX, 400, "{\"ok\":false}")
|
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\"}")
|
||||||
|
10
backend/src/controllers/auth.c
Normal file
10
backend/src/controllers/auth.c
Normal 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);
|
||||||
|
}
|
48
backend/src/controllers/general.c
Normal file
48
backend/src/controllers/general.c
Normal 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));
|
||||||
|
}
|
66
backend/src/controllers/users.c
Normal file
66
backend/src/controllers/users.c
Normal 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}");
|
||||||
|
}
|
@ -15,7 +15,7 @@ typedef enum {
|
|||||||
typedef struct Db Db;
|
typedef struct Db Db;
|
||||||
|
|
||||||
/// `user.id` field is ignored.
|
/// `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);
|
DbRes db_user_from_id(Db* db, User* user, int64_t id);
|
||||||
/// Expects `ids` to be constructed.
|
/// Expects `ids` to be constructed.
|
||||||
DbRes db_users_with_email(Db* db, Ids* ids, const char* email);
|
DbRes db_users_with_email(Db* db, Ids* ids, const char* email);
|
||||||
|
@ -65,7 +65,7 @@ static inline void disconnect(sqlite3* connection)
|
|||||||
disconnect(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");
|
static_assert(sizeof(User) == 40, "model has changed");
|
||||||
|
|
||||||
@ -76,7 +76,7 @@ DbRes db_user_insert(Db* db, User* user)
|
|||||||
sqlite3_stmt* stmt;
|
sqlite3_stmt* stmt;
|
||||||
sqlite3_prepare_v2(connection,
|
sqlite3_prepare_v2(connection,
|
||||||
"INSERT INTO users (name, email, password_hash, balance_dkk_cent) "
|
"INSERT INTO users (name, email, password_hash, balance_dkk_cent) "
|
||||||
"VALUES (?, ?, ?)",
|
"VALUES (?, ?, ?, ?)",
|
||||||
-1, &stmt, NULL);
|
-1, &stmt, NULL);
|
||||||
|
|
||||||
sqlite3_bind_text(stmt, 1, user->name, -1, SQLITE_STATIC);
|
sqlite3_bind_text(stmt, 1, user->name, -1, SQLITE_STATIC);
|
||||||
|
@ -72,17 +72,27 @@ char* string_copy(const String* string)
|
|||||||
return copy;
|
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");
|
fprintf(stderr, "error: tried to hash too long input\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
StrHash hash;
|
HashData hash;
|
||||||
memcpy(hash.salt, salt, STR_HASH_SALT_SIZE);
|
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, hash.salt, STR_HASH_SALT_SIZE);
|
||||||
memcpy(&input[STR_HASH_SALT_SIZE], str, strlen(str));
|
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;
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
StrHash str_hash(const char* str)
|
static inline HashData hashdata_from_str(const char* str)
|
||||||
{
|
{
|
||||||
uint8_t salt[STR_HASH_SALT_SIZE];
|
uint8_t salt[STR_HASH_SALT_SIZE];
|
||||||
RAND_bytes(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;
|
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));
|
char* result = calloc(STR_HASH_STR_LEN + 1, sizeof(char));
|
||||||
for (size_t i = 0; i < STR_HASH_SALT_SIZE; ++i) {
|
for (size_t i = 0; i < STR_HASH_SALT_SIZE; ++i) {
|
||||||
@ -121,7 +131,7 @@ char* str_hash_to_string(StrHash hash)
|
|||||||
return result;
|
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 };
|
uint8_t result[64] = { 0 };
|
||||||
size_t result_i = 0;
|
size_t result_i = 0;
|
||||||
@ -133,7 +143,7 @@ StrHash str_hash_from_string(const char* str)
|
|||||||
result_i += 1;
|
result_i += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
StrHash hash;
|
HashData hash;
|
||||||
// memcpy((uint8_t*)&hash, result, sizeof(result));
|
// memcpy((uint8_t*)&hash, result, sizeof(result));
|
||||||
for (size_t i = 0; i < 32; ++i) {
|
for (size_t i = 0; i < 32; ++i) {
|
||||||
hash.salt[i] = result[i];
|
hash.salt[i] = result[i];
|
||||||
@ -141,3 +151,15 @@ StrHash str_hash_from_string(const char* str)
|
|||||||
}
|
}
|
||||||
return hash;
|
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);
|
||||||
|
}
|
||||||
|
@ -33,18 +33,7 @@ char* string_copy(const String* string);
|
|||||||
|
|
||||||
DEFINE_VEC(char*, RawStrVec, rawstr_vec, 8)
|
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
|
char* str_hash(const char* input);
|
||||||
#define STR_HASH_HASH_SIZE 32
|
bool str_hash_equal(const char* hash, const char* input);
|
||||||
#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);
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user