mirror of
https://github.com/Mercantec-GHC/h4-projekt-gruppe-0-sm.git
synced 2025-04-27 16:24:07 +02:00
login endpoint for backend
This commit is contained in:
parent
caf97efc0c
commit
b5f8c383c5
@ -35,6 +35,12 @@ else
|
||||
OPTIMIZATION += -Og
|
||||
endif
|
||||
|
||||
|
||||
ifeq ($(RUN_TESTS),1)
|
||||
C_FLAGS += -DRUN_TESTS
|
||||
endif
|
||||
|
||||
|
||||
HEADERS = $(shell find src/ -name *.h)
|
||||
C_FILES = $(shell find src/ -name *.c)
|
||||
O_FILES = $(patsubst src/%.c,build/%.o,$(C_FILES))
|
||||
|
@ -49,7 +49,7 @@ CREATE TABLE IF NOT EXISTS cart_items (
|
||||
|
||||
|
||||
|
||||
INSERT OR REPLACE INTO users VALUES(1,'User','test@email.com','d980840fcb82970ab86656feebdccdd288be0e9b05f14e712b59529a2868fee3d980840fcb82970ab86656feebdccdd288be0e9b05f14e712b59529a2868fee3',10000);
|
||||
INSERT OR REPLACE INTO users VALUES(1,'User','test@email.com','08ce0220f6d63d85c3ac313e308f4fca35ecfb850baa8ddb924cfab98137b6b18b4a8e027067cb98802757df1337246a0f3aa25c44c2b788517a871086419dcf',10000);
|
||||
|
||||
INSERT OR REPLACE INTO products VALUES(1,'Letmælk',1195,'Mælk fra ko',NULL,NULL);
|
||||
INSERT OR REPLACE INTO products VALUES(2,'Smør',2000,'Smør fra mejeri',NULL,NULL);
|
||||
|
@ -2,11 +2,13 @@
|
||||
|
||||
#include "db.h"
|
||||
#include "http_server.h"
|
||||
#include "session.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
typedef struct {
|
||||
int number;
|
||||
SessionVec sessions;
|
||||
Db* db;
|
||||
} Cx;
|
||||
|
||||
@ -18,6 +20,8 @@ void route_get_products_all(HttpCtx* ctx);
|
||||
|
||||
void route_post_user_register(HttpCtx* ctx);
|
||||
|
||||
void route_post_auth_login(HttpCtx* ctx);
|
||||
|
||||
#define RESPOND(HTTP_CTX, STATUS, MIME_TYPE, ...) \
|
||||
{ \
|
||||
HttpCtx* _ctx = (HTTP_CTX); \
|
||||
@ -42,3 +46,9 @@ void route_post_user_register(HttpCtx* ctx);
|
||||
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\"}")
|
||||
|
||||
__attribute__((unused))
|
||||
static inline void ___include_user(void)
|
||||
{
|
||||
RESPOND((HttpCtx*)0, 200, "text/html", "")
|
||||
}
|
||||
|
@ -2,9 +2,53 @@
|
||||
#include "../http_server.h"
|
||||
#include "../models_json.h"
|
||||
#include "../str_util.h"
|
||||
#include <string.h>
|
||||
|
||||
void route_post_auth_login(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));
|
||||
|
||||
AuthLoginReq req;
|
||||
int parse_res = auth_login_req_from_json(&req, body_json);
|
||||
json_free(body_json);
|
||||
|
||||
if (parse_res != 0) {
|
||||
RESPOND_BAD_REQUEST(ctx, "bad request");
|
||||
goto l0_return;
|
||||
}
|
||||
if (strlen(req.email) == 0
|
||||
|| strlen(req.password) > MAX_HASH_INPUT_LEN) {
|
||||
|
||||
RESPOND_BAD_REQUEST(ctx, "bad request");
|
||||
goto l0_return;
|
||||
}
|
||||
|
||||
User user;
|
||||
DbRes db_res = db_user_from_email(cx->db, &user, req.email);
|
||||
if (db_res == DbRes_NotFound) {
|
||||
RESPOND_BAD_REQUEST(ctx, "user with email not found");
|
||||
goto l0_return;
|
||||
}
|
||||
else if (db_res == DbRes_Error) {
|
||||
RESPOND_SERVER_ERROR(ctx);
|
||||
goto l0_return;
|
||||
}
|
||||
|
||||
if (!str_hash_equal(user.password_hash, req.password)) {
|
||||
RESPOND_BAD_REQUEST(ctx, "wrong password");
|
||||
goto l2_return;
|
||||
}
|
||||
|
||||
session_vec_push(&cx->sessions, (Session) {.user_id = user.id});
|
||||
|
||||
RESPOND_JSON(ctx, 200, "{\"ok\":true}");
|
||||
l2_return:
|
||||
user_destroy(&user);
|
||||
l0_return:
|
||||
auth_login_req_destroy(&req);
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ void route_post_user_register(HttpCtx* ctx)
|
||||
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);
|
||||
users_register_req_destroy(&req);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -32,13 +32,13 @@ void route_post_user_register(HttpCtx* ctx)
|
||||
if (db_users_with_email(cx->db, &ids, req.email) != DbRes_Ok) {
|
||||
RESPOND_SERVER_ERROR(ctx);
|
||||
ids_destroy(&ids);
|
||||
users_register_req_free(&req);
|
||||
users_register_req_destroy(&req);
|
||||
return;
|
||||
}
|
||||
if (ids.size > 0) {
|
||||
RESPOND_BAD_REQUEST(ctx, "email in use");
|
||||
ids_destroy(&ids);
|
||||
users_register_req_free(&req);
|
||||
users_register_req_destroy(&req);
|
||||
return;
|
||||
}
|
||||
ids_destroy(&ids);
|
||||
@ -57,10 +57,10 @@ void route_post_user_register(HttpCtx* ctx)
|
||||
|
||||
RESPOND_SERVER_ERROR(ctx);
|
||||
free(password_hash);
|
||||
users_register_req_free(&req);
|
||||
users_register_req_destroy(&req);
|
||||
return;
|
||||
}
|
||||
free(password_hash);
|
||||
users_register_req_free(&req);
|
||||
users_register_req_destroy(&req);
|
||||
RESPOND_JSON(ctx, 200, "{\"ok\":true}");
|
||||
}
|
||||
|
@ -16,9 +16,16 @@ typedef struct Db Db;
|
||||
|
||||
/// `user.id` field is ignored.
|
||||
DbRes db_user_insert(Db* db, const User* user);
|
||||
/// `user` field is an out parameter.
|
||||
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);
|
||||
|
||||
|
||||
/// `user` is an out parameter.
|
||||
DbRes db_user_from_email(Db* db, User* user, const char* email);
|
||||
|
||||
/// Expects `vec` to be constructed.
|
||||
DbRes db_product_all(Db* db, ProductVec* vec);
|
||||
|
@ -110,7 +110,7 @@ DbRes db_user_from_id(Db* db, User* user, int64_t id)
|
||||
sqlite3_stmt* stmt;
|
||||
sqlite3_prepare_v2(connection,
|
||||
"SELECT id, name, email, password_hash, balance_dkk_cent"
|
||||
"FROM users WHERE id = ?",
|
||||
" FROM users WHERE id = ?",
|
||||
-1, &stmt, NULL);
|
||||
sqlite3_bind_int64(stmt, 1, id);
|
||||
|
||||
@ -171,6 +171,53 @@ l0_return:
|
||||
return res;
|
||||
}
|
||||
|
||||
DbRes db_user_from_email(Db* db, User* user, const char* email)
|
||||
{
|
||||
static_assert(sizeof(User) == 40, "model has changed");
|
||||
|
||||
sqlite3* connection;
|
||||
CONNECT;
|
||||
DbRes res;
|
||||
|
||||
sqlite3_stmt* stmt;
|
||||
int prepare_res = sqlite3_prepare_v2(connection,
|
||||
"SELECT id, name, email, password_hash, balance_dkk_cent"
|
||||
" FROM users WHERE email = ?",
|
||||
-1, &stmt, NULL);
|
||||
if (prepare_res != SQLITE_OK) {
|
||||
fprintf(stderr, "error: %s\n at %s:%d\n", sqlite3_errmsg(connection), __func__, __LINE__);
|
||||
res = DbRes_Error;
|
||||
goto l0_return;
|
||||
}
|
||||
sqlite3_bind_text(stmt, 1, email, -1, NULL);
|
||||
|
||||
int step_res = sqlite3_step(stmt);
|
||||
if (step_res == SQLITE_DONE) {
|
||||
res = DbRes_NotFound;
|
||||
goto l0_return;
|
||||
} else if (step_res != SQLITE_ROW) {
|
||||
printf("step_res = %d, email = '%s'\n", step_res, email);
|
||||
fprintf(stderr, "error: %s\n at %s:%d\n", sqlite3_errmsg(connection), __func__, __LINE__);
|
||||
res = DbRes_Error;
|
||||
goto l0_return;
|
||||
}
|
||||
*user = (User) {
|
||||
.id = GET_INT(0),
|
||||
.name = GET_STR(1),
|
||||
.email = GET_STR(2),
|
||||
.password_hash = GET_STR(3),
|
||||
.balance_dkk_cent = GET_INT(4),
|
||||
};
|
||||
|
||||
res = DbRes_Ok;
|
||||
l0_return:
|
||||
if (stmt)
|
||||
sqlite3_finalize(stmt);
|
||||
DISCONNECT;
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
DbRes db_product_all(Db* db, ProductVec* vec)
|
||||
{
|
||||
sqlite3* connection;
|
||||
|
@ -4,20 +4,30 @@
|
||||
#include "json.h"
|
||||
#include "models.h"
|
||||
#include "models_json.h"
|
||||
#include "session.h"
|
||||
#include "str_util.h"
|
||||
#include <sqlite3.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
void test(void);
|
||||
|
||||
|
||||
HttpServer* server;
|
||||
|
||||
int main(void)
|
||||
{
|
||||
#ifdef RUN_TESTS
|
||||
test();
|
||||
#endif
|
||||
|
||||
Db* db = db_sqlite_new();
|
||||
|
||||
Cx cx = {
|
||||
.number = 1,
|
||||
.db = db,
|
||||
};
|
||||
session_vec_construct(&cx.sessions);
|
||||
|
||||
server = http_server_new((HttpServerOpts) {
|
||||
.port = 8080,
|
||||
@ -32,6 +42,7 @@ int main(void)
|
||||
http_server_get(server, "/api/products/all", route_get_products_all);
|
||||
|
||||
http_server_post(server, "/api/users/register", route_post_user_register);
|
||||
http_server_post(server, "/api/auth/login", route_post_auth_login);
|
||||
|
||||
http_server_get(server, "/", route_get_index);
|
||||
http_server_post(server, "/set_number", route_post_set_number);
|
||||
@ -44,3 +55,10 @@ int main(void)
|
||||
http_server_free(server);
|
||||
db_sqlite_free(db);
|
||||
}
|
||||
|
||||
void test(void)
|
||||
{
|
||||
str_util_test();
|
||||
printf("ALL TESTS PASSED 💅\n");
|
||||
exit(0);
|
||||
}
|
||||
|
@ -6,7 +6,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
void user_free(User* m)
|
||||
void user_destroy(User* m)
|
||||
{
|
||||
static_assert(sizeof(User) == 40, "model has changed");
|
||||
|
||||
@ -15,14 +15,14 @@ void user_free(User* m)
|
||||
free(m->password_hash);
|
||||
}
|
||||
|
||||
void coord_free(Coord* m)
|
||||
void coord_destroy(Coord* m)
|
||||
{
|
||||
static_assert(sizeof(Coord) == 24, "model has changed");
|
||||
|
||||
(void)m;
|
||||
}
|
||||
|
||||
void product_free(Product* m)
|
||||
void product_destroy(Product* m)
|
||||
{
|
||||
static_assert(sizeof(Product) == 40, "model has changed");
|
||||
|
||||
@ -30,28 +30,28 @@ void product_free(Product* m)
|
||||
free(m->barcode);
|
||||
}
|
||||
|
||||
void product_price_free(ProductPrice* m)
|
||||
void product_price_destroy(ProductPrice* m)
|
||||
{
|
||||
static_assert(sizeof(ProductPrice) == 24, "model has changed");
|
||||
|
||||
(void)m;
|
||||
}
|
||||
|
||||
void cart_free(Cart* m)
|
||||
void cart_destroy(Cart* m)
|
||||
{
|
||||
static_assert(sizeof(Cart) == 16, "model has changed");
|
||||
|
||||
(void)m;
|
||||
}
|
||||
|
||||
void cart_item_free(CartItem* m)
|
||||
void cart_item_destroy(CartItem* m)
|
||||
{
|
||||
static_assert(sizeof(CartItem) == 24, "model has changed");
|
||||
|
||||
(void)m;
|
||||
}
|
||||
|
||||
void users_register_req_free(UsersRegisterReq* model)
|
||||
void users_register_req_destroy(UsersRegisterReq* model)
|
||||
{
|
||||
static_assert(sizeof(UsersRegisterReq) == 24, "model has changed");
|
||||
|
||||
@ -60,7 +60,7 @@ void users_register_req_free(UsersRegisterReq* model)
|
||||
free(model->password);
|
||||
}
|
||||
|
||||
void auth_login_req_free(AuthLoginReq* model)
|
||||
void auth_login_req_destroy(AuthLoginReq* model)
|
||||
{
|
||||
static_assert(sizeof(AuthLoginReq) == 16, "model has changed");
|
||||
|
||||
|
@ -41,12 +41,12 @@ typedef struct {
|
||||
int64_t amount;
|
||||
} CartItem;
|
||||
|
||||
void user_free(User* model);
|
||||
void coord_free(Coord* model);
|
||||
void product_free(Product* model);
|
||||
void product_price_free(ProductPrice* model);
|
||||
void cart_free(Cart* model);
|
||||
void cart_item_free(CartItem* model);
|
||||
void user_destroy(User* model);
|
||||
void coord_destroy(Coord* model);
|
||||
void product_destroy(Product* model);
|
||||
void product_price_destroy(ProductPrice* model);
|
||||
void cart_destroy(Cart* model);
|
||||
void cart_item_destroy(CartItem* model);
|
||||
|
||||
//
|
||||
|
||||
@ -61,5 +61,5 @@ typedef struct {
|
||||
char* password;
|
||||
} AuthLoginReq;
|
||||
|
||||
void users_register_req_free(UsersRegisterReq* model);
|
||||
void auth_login_req_free(AuthLoginReq* model);
|
||||
void users_register_req_destroy(UsersRegisterReq* model);
|
||||
void auth_login_req_destroy(AuthLoginReq* model);
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include "str_util.h"
|
||||
#include "panic.h"
|
||||
#include <openssl/rand.h>
|
||||
#include <openssl/sha.h>
|
||||
#include <stddef.h>
|
||||
@ -124,7 +125,7 @@ static inline char* hashdata_to_string(HashData hash)
|
||||
}
|
||||
for (size_t i = 0; i < STR_HASH_HASH_SIZE; ++i) {
|
||||
char bytestr[3] = { 0 };
|
||||
snprintf(bytestr, 3, "%02x", hash.salt[i]);
|
||||
snprintf(bytestr, 3, "%02x", hash.hash[i]);
|
||||
result[(STR_HASH_SALT_SIZE + i) * 2] = bytestr[0];
|
||||
result[(STR_HASH_SALT_SIZE + i) * 2 + 1] = bytestr[1];
|
||||
}
|
||||
@ -144,7 +145,6 @@ static inline HashData hashdata_from_hash_string(const char* str)
|
||||
}
|
||||
|
||||
HashData hash;
|
||||
// memcpy((uint8_t*)&hash, result, sizeof(result));
|
||||
for (size_t i = 0; i < 32; ++i) {
|
||||
hash.salt[i] = result[i];
|
||||
hash.hash[i] = result[32 + i];
|
||||
@ -163,3 +163,14 @@ bool str_hash_equal(const char* hash, const char* input)
|
||||
HashData data = hashdata_from_hash_string(hash);
|
||||
return hashdata_is_equal(data, input);
|
||||
}
|
||||
|
||||
void str_util_test(void) {
|
||||
char* hash = str_hash("1234");
|
||||
if (!str_hash_equal(hash, "1234")) {
|
||||
PANIC("hash should be equal");
|
||||
}
|
||||
if (str_hash_equal(hash, "4321")) {
|
||||
PANIC("hash should not be equal");
|
||||
}
|
||||
free(hash);
|
||||
}
|
||||
|
@ -37,3 +37,5 @@ DEFINE_VEC(char*, RawStrVec, rawstr_vec, 8)
|
||||
|
||||
char* str_hash(const char* input);
|
||||
bool str_hash_equal(const char* hash, const char* input);
|
||||
|
||||
void str_util_test(void);
|
||||
|
Loading…
x
Reference in New Issue
Block a user