added field change and get and updated tests
Prepare Build and Testing executable binary / Prepare-Build-Testing-With-Make (push) Successful in 10s
Prepare Build and Testing executable binary / Prepare-Build-Testing-With-CMake (push) Successful in 15s
Prepare Build and Testing executable binary / Prepare-Build-Testing-With-Meson (push) Successful in 12s

This commit is contained in:
wt
2025-08-30 12:08:57 +07:00
parent 27ba0ab45e
commit 56d670c9b6
10 changed files with 225 additions and 69 deletions
+2
View File
@@ -31,6 +31,8 @@ executable(
'src/config.c',
'src/static.c',
'src/cors.c',
'src/db_get_user_field_by_id.c',
'src/db_update_user_field.c',
include_directories: include_dirs,
dependencies: deps,
)
+35 -44
View File
@@ -1,13 +1,24 @@
#include "cJSON.h"
#include "jwt.h"
#include "user.h"
#include "utils.h"
void change_password(struct mg_connection *c, struct mg_http_message *hm) {
if (hm->body.len == 0) {
mg_http_reply(c, 400, HTTP_HEADERS, "{%m:%m}", MG_ESC("error"),
MG_ESC("must have token, old_password, new_password"));
return;
}
request_with_token *requestdata = check_body(hm);
if (requestdata->error != NULL) {
mg_http_reply(c, 403, HTTP_HEADERS, "{%m:%m}", MG_ESC("error"),
MG_ESC(requestdata->error));
return;
}
const bool valid = jwt_verifyJWT(requestdata->token, JWT_DEFAULT_SECRET);
if (!valid) {
mg_http_reply(c, 403, HTTP_HEADERS, "{%m:%m}",
MG_ESC("error"), MG_ESC("invalid token"));
mg_http_reply(c, 403, HTTP_HEADERS, "{%m:%m}", MG_ESC("error"),
MG_ESC("invalid token"));
return;
}
cJSON *jsonbody = cJSON_Parse(hm->body.buf);
@@ -16,37 +27,31 @@ void change_password(struct mg_connection *c, struct mg_http_message *hm) {
cJSON *userid = cJSON_GetObjectItem(jsonbody, "userid");
if ((old_password == nullptr || new_password == nullptr) &&
userid == nullptr) {
mg_http_reply(c, 400, HTTP_HEADERS, "{%m:%m}",
MG_ESC("error"),
mg_http_reply(c, 400, HTTP_HEADERS, "{%m:%m}", MG_ESC("error"),
MG_ESC("must have <old_password>, <new_password>"));
return;
}
cJSON *payload = jwt_get_payload(requestdata->token);
if (!userid) {
char *username = cJSON_GetObjectItem(payload, "username")->valuestring;
User *old_user_data = db_get_user_by_username(username);
if (!old_user_data) {
mg_http_reply(c, 500, HTTP_HEADERS, "{%m:%m}",
MG_ESC("error"),
cJSON *id = cJSON_GetObjectItem(payload, "id");
char *password = db_get_user_field_by_id(id->valueint, "password");
if (!password) {
mg_http_reply(c, 500, HTTP_HEADERS, "{%m:%m}", MG_ESC("error"),
MG_ESC("failed to update user can't get old_user_data"));
cJSON_Delete(payload);
}
char *password = old_user_data->password;
if (strcmp(password, old_password->valuestring)) {
mg_http_reply(c, 400, HTTP_HEADERS, "{%m:%m}",
MG_ESC("error"), MG_ESC("invalid old password"));
return;
}
const bool old_is_superuser = old_user_data->is_superuser;
User *new_user_data = db_update_user(
old_user_data->id, old_user_data->username, old_user_data->email,
new_password->valuestring, old_user_data->group, &old_is_superuser);
if (!new_user_data) {
mg_http_reply(c, 500, HTTP_HEADERS, "{%m:%m}",
MG_ESC("error"), MG_ESC("failed to update user can't update database"));
free_user(old_user_data);
if (strcmp(password, old_password->valuestring)) {
mg_http_reply(c, 400, HTTP_HEADERS, "{%m:%m}", MG_ESC("error"),
MG_ESC("invalid old password"));
return;
}
if (!db_update_user_field(id->valueint, "password",
new_password->valuestring)) {
mg_http_reply(c, 500, HTTP_HEADERS, "{%m:%m}", MG_ESC("error"),
MG_ESC("failed to update user can't update database"));
cJSON_Delete(payload);
return;
}
@@ -55,33 +60,21 @@ void change_password(struct mg_connection *c, struct mg_http_message *hm) {
snprintf(responsedata, sizeof(responsedata), "{\"success\": true}");
mg_http_reply(c, 200, HTTP_HEADERS, responsedata);
free_user(old_user_data);
cJSON_Delete(payload);
return;
}
bool is_superuser = cJSON_GetObjectItem(payload, "is_superuser")->valueint;
if (!is_superuser) {
mg_http_reply(c, 403, HTTP_HEADERS, "{%m:%m}",
MG_ESC("error"), MG_ESC("user is not superuser"));
mg_http_reply(c, 403, HTTP_HEADERS, "{%m:%m}", MG_ESC("error"),
MG_ESC("user is not superuser"));
cJSON_Delete(payload);
return;
}
User *old_user_data = db_get_user_by_id(userid->valueint);
if (!old_user_data) {
mg_http_reply(c, 500, HTTP_HEADERS, "{%m:%m}",
MG_ESC("error"),
MG_ESC("failed to update user can't get old_user_data"));
cJSON_Delete(payload);
return;
}
const bool old_is_superuser = old_user_data->is_superuser;
User *new_user_data = db_update_user(
old_user_data->id, old_user_data->username, old_user_data->email,
new_password->valuestring, old_user_data->group, &old_is_superuser);
if (!new_user_data) {
mg_http_reply(c, 500, HTTP_HEADERS, "{%m:%m}",
MG_ESC("error"), MG_ESC("failed to update user can't create new_user_data from root"));
free_user(old_user_data);
if (!db_update_user_field(userid->valueint, "password",
new_password->valuestring)) {
mg_http_reply(
c, 500, HTTP_HEADERS, "{%m:%m}", MG_ESC("error"),
MG_ESC("failed to update user can't create new_user_data from root"));
cJSON_Delete(payload);
return;
}
@@ -89,7 +82,5 @@ void change_password(struct mg_connection *c, struct mg_http_message *hm) {
snprintf(responsedata, sizeof(responsedata), "{\"success\": true}");
mg_http_reply(c, 200, HTTP_HEADERS, responsedata);
free_user(old_user_data);
free_user(new_user_data);
cJSON_Delete(payload);
}
+23 -13
View File
@@ -3,11 +3,21 @@
#include "utils.h"
void change_user(struct mg_connection *c, struct mg_http_message *hm) {
if (hm->body.len == 0) {
mg_http_reply(c, 400, HTTP_HEADERS, "{%m:%m}", MG_ESC("error"),
MG_ESC("must have token, new_user_data"));
return;
}
request_with_token *requestdata = check_body(hm);
if (requestdata->error != NULL) {
mg_http_reply(c, 403, HTTP_HEADERS, "{%m:%m}", MG_ESC("error"),
MG_ESC(requestdata->error));
return;
}
const bool valid = jwt_verifyJWT(requestdata->token, JWT_DEFAULT_SECRET);
if (!valid) {
mg_http_reply(c, 403, HTTP_HEADERS, "{%m:%m}",
MG_ESC("error"), MG_ESC("invalid token"));
mg_http_reply(c, 403, HTTP_HEADERS, "{%m:%m}", MG_ESC("error"),
MG_ESC("invalid token"));
return;
}
cJSON *jsonbody = cJSON_Parse(hm->body.buf);
@@ -23,20 +33,19 @@ void change_user(struct mg_connection *c, struct mg_http_message *hm) {
old_user_data = db_get_user_by_id(userid->valueint);
}
if (!old_user_data) {
mg_http_reply(c, 404, HTTP_HEADERS, "{%m:%m}",
MG_ESC("error"), MG_ESC("user not found"));
mg_http_reply(c, 404, HTTP_HEADERS, "{%m:%m}", MG_ESC("error"),
MG_ESC("user not found"));
return;
}
char *password = old_user_data->password;
if (!password) {
mg_http_reply(c, 400, HTTP_HEADERS, "{%m:%m}",
MG_ESC("error"), MG_ESC("password is required"));
mg_http_reply(c, 400, HTTP_HEADERS, "{%m:%m}", MG_ESC("error"),
MG_ESC("password is required"));
return;
}
if (username == nullptr || email == nullptr || is_superuser == nullptr ||
group == nullptr) {
mg_http_reply(c, 400, HTTP_HEADERS, "{%m:%m}",
MG_ESC("error"),
mg_http_reply(c, 400, HTTP_HEADERS, "{%m:%m}", MG_ESC("error"),
MG_ESC("must have <username>, <email>, <group, "
"optional>, <is_superuser, optional>"));
free_user(old_user_data);
@@ -52,8 +61,8 @@ void change_user(struct mg_connection *c, struct mg_http_message *hm) {
group->valuestring, &user_is_superuser);
if (!updated_user_data) {
mg_http_reply(c, 500, HTTP_HEADERS, "{%m:%m}",
MG_ESC("error"), MG_ESC("failed to update user change user request"));
mg_http_reply(c, 500, HTTP_HEADERS, "{%m:%m}", MG_ESC("error"),
MG_ESC("failed to update user change user request"));
cJSON_Delete(payload);
free_user(old_user_data);
return;
@@ -75,8 +84,8 @@ void change_user(struct mg_connection *c, struct mg_http_message *hm) {
bool req_is_superuser =
cJSON_GetObjectItem(payload, "is_superuser")->valueint;
if (!req_is_superuser) {
mg_http_reply(c, 403, HTTP_HEADERS, "{%m:%m}",
MG_ESC("error"), MG_ESC("user is not superuser"));
mg_http_reply(c, 403, HTTP_HEADERS, "{%m:%m}", MG_ESC("error"),
MG_ESC("user is not superuser"));
cJSON_Delete(payload);
free_user(old_user_data);
return;
@@ -86,7 +95,8 @@ void change_user(struct mg_connection *c, struct mg_http_message *hm) {
group->valuestring, &user_is_superuser);
if (!updated_user_data) {
mg_http_reply(c, 500, "Content-Type: application/json\r\n", "{%m:%m}",
MG_ESC("error"), MG_ESC("failed to update user can't change userdata"));
MG_ESC("error"),
MG_ESC("failed to update user can't change userdata"));
cJSON_Delete(payload);
free_user(old_user_data);
return;
+7 -7
View File
@@ -1,9 +1,9 @@
#include "user.h"
#include "database.h"
#include "sql.h"
#include "user.h"
User *db_get_user_by_id(const int id) {
User *user = nullptr;
User *user = nullptr;
pthread_mutex_lock(&db_mutex);
if (!db) {
fprintf(stderr, "Database not initialized\n");
@@ -11,12 +11,11 @@ User *db_get_user_by_id(const int id) {
return nullptr;
}
sqlite3_stmt *stmt;
int rc =
sqlite3_prepare_v2(db, SELECT_USER_BY_ID_SQL, -1, &stmt, nullptr);
int rc = sqlite3_prepare_v2(db, SELECT_USER_BY_ID_SQL, -1, &stmt, nullptr);
if (rc != SQLITE_OK) {
fprintf(stderr, "Cannot prepare statement: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
exit(1);
pthread_mutex_unlock(&db_mutex);
return nullptr;
}
sqlite3_bind_int(stmt, 1, id);
@@ -36,9 +35,10 @@ User *db_get_user_by_id(const int id) {
fprintf(stderr, "Error reading data: %s\n", sqlite3_errmsg(db));
free_user(user);
sqlite3_finalize(stmt);
pthread_mutex_unlock(&db_mutex);
return nullptr;
}
pthread_mutex_unlock(&db_mutex);
return user;
}
}
+3 -3
View File
@@ -15,8 +15,8 @@ User *db_get_user_by_username(const char *username) {
sqlite3_prepare_v2(db, SELECT_USER_BY_USERNAME_SQL, -1, &stmt, nullptr);
if (rc != SQLITE_OK) {
fprintf(stderr, "Cannot prepare statement: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
exit(1);
pthread_mutex_unlock(&db_mutex);
return nullptr;
}
sqlite3_bind_text(stmt, 1, username, -1, SQLITE_STATIC);
@@ -41,4 +41,4 @@ User *db_get_user_by_username(const char *username) {
pthread_mutex_unlock(&db_mutex);
return user;
}
}
+45
View File
@@ -0,0 +1,45 @@
#include <pthread.h>
#include "database.h"
#include "sql.h"
#include "user.h"
char *db_get_user_field_by_id(const int id, const char *field) {
char *password = nullptr;
pthread_mutex_lock(&db_mutex);
if (!db) {
fprintf(stderr, "Database not initialized\n");
pthread_mutex_unlock(&db_mutex);
return nullptr;
}
char sql[256];
snprintf(sql, sizeof(sql), SELECT_USER_FIELD_SQL, field);
sqlite3_stmt *stmt;
int rc = sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr);
if (rc != SQLITE_OK) {
fprintf(stderr, "Cannot prepare statement: %s\n", sqlite3_errmsg(db));
pthread_mutex_unlock(&db_mutex);
return nullptr;
}
sqlite3_bind_int(stmt, 1, id);
rc = sqlite3_step(stmt);
if (rc == SQLITE_ROW) {
const unsigned char *db_password = sqlite3_column_text(stmt, 0);
password = malloc(strlen((const char *) db_password) + 1);
if (password) {
strcpy(password, (const char *) db_password);
}
sqlite3_finalize(stmt);
} else if (rc == SQLITE_DONE) {
fprintf(stderr, "Can't find user");
sqlite3_finalize(stmt);
pthread_mutex_unlock(&db_mutex);
return nullptr;
} else {
fprintf(stderr, "Error executing query: %s\n", sqlite3_errmsg(db));
sqlite3_finalize(stmt);
pthread_mutex_unlock(&db_mutex);
return nullptr;
}
pthread_mutex_unlock(&db_mutex);
return password;
}
+35
View File
@@ -0,0 +1,35 @@
#include "database.h"
#include "sql.h"
#include "user.h"
bool db_update_user_field(const int id, const char *field, const char *value) {
pthread_mutex_lock(&db_mutex);
if (!db || !field || !value) {
fprintf(stderr, "invalid parameters\n");
pthread_mutex_unlock(&db_mutex);
return false;
}
char sql[256];
snprintf(sql, sizeof(sql), UPDATE_USER_FIELD_SQL, field);
sqlite3_stmt *stmt;
int rc = sqlite3_prepare_v2(db, sql, -1, &stmt, 0);
if (rc != SQLITE_OK) {
fprintf(stderr, "failed to prepare statement: %s\n", sqlite3_errmsg(db));
pthread_mutex_unlock(&db_mutex);
return false;
}
sqlite3_bind_text(stmt, 1, value, -1, SQLITE_STATIC);
sqlite3_bind_int(stmt, 2, id);
rc = sqlite3_step(stmt);
sqlite3_finalize(stmt);
if (rc != SQLITE_DONE) {
fprintf(stderr, "execution failed: %s\n", sqlite3_errmsg(db));
pthread_mutex_unlock(&db_mutex);
return false;
}
pthread_mutex_unlock(&db_mutex);
return sqlite3_changes(db) > 0;
}
+2 -1
View File
@@ -29,4 +29,5 @@
"update users set username = ?, email = ?, password = ?, user_group = ?, " \
"is_superuser = ? where id = ?"
#define DELETE_USER_SQL "delete from users where id = ?"
#define UPDATE_USER_PASSWORD_SQL "update users set password = ? where id = ?"
#define UPDATE_USER_FIELD_SQL "update users set %s = ? where id = ?"
#define SELECT_USER_FIELD_SQL "select %s from users where id = ?"
+5 -1
View File
@@ -4,7 +4,8 @@
#include <mongoose.h>
#include <sqlite3.h>
#define HTTP_HEADERS "Content-Type: application/json\r\nAccess-Control-Allow-Origin: *\r\n"
#define HTTP_HEADERS \
"Content-Type: application/json\r\nAccess-Control-Allow-Origin: *\r\n"
typedef struct User {
int id;
@@ -54,6 +55,7 @@ void change_password(struct mg_connection *c, struct mg_http_message *hm);
void register_user(struct mg_connection *c, struct mg_http_message *hm);
void get_token(struct mg_connection *c, struct mg_http_message *hm);
void verify_token(struct mg_connection *c, struct mg_http_message *hm);
void db_get_users_from_file(const char *filename);
char *db_get_all_users();
User *db_get_user_by_username(const char *username);
@@ -65,3 +67,5 @@ User *db_update_user(const int userid, const char *username, const char *email,
const bool *is_superuser);
bool db_delete_user(const int id);
User *db_get_user_by_id(const int id);
char *db_get_user_field_by_id(const int id, const char *field);
bool db_update_user_field(const int id, const char *field, const char *value);
+68
View File
@@ -436,6 +436,40 @@ else
errors=$(($errors+1))
fi
# test POST /api/users/password without post data
printf "==> ${BLUE}Testing${CYAN} POST${BWHITE} /api/users/password without post data${RESET} "
curl \
--silent \
-X POST \
-d "" \
localhost:8222/api/users/password &> api_users_password_no_data_test.log
sleep 0.1
res=$(cat api_users_password_no_data_test.log)
if [[ "$res" == *'{"error":"must have token, old_password, new_password"}'* ]]; then
printf "[$GREEN OK $RESET]\n"
success=$(($success+1))
else
printf "[$RED ERR $RESET]\n==> ${BLUE}Response: $RESET$(echo $res)\n"
errors=$(($errors+1))
fi
# test POST /api/users/password with wrong json
printf "==> ${BLUE}Testing${CYAN} POST${BWHITE} /api/users/password with wrong json${RESET} "
curl \
--silent \
-X POST \
-d "ijansd fu9q303ghq9" \
localhost:8222/api/users/password &> api_users_password_wrong_json_test.log
sleep 0.1
res=$(cat api_users_password_wrong_json_test.log)
if [[ "$res" == *'error'* ]]; then
printf "[$GREEN OK $RESET]\n"
success=$(($success+1))
else
printf "[$RED ERR $RESET]\n==> ${BLUE}Response: $RESET$(echo $res)\n"
errors=$(($errors+1))
fi
# test PUT /api/users
printf "==> ${BLUE}Testing${YELLOW} PUT${BWHITE} /api/users${RESET} "
reqtoken=$(cat api_token_test.log | sed -e "s/{//g" | sed -e "s/}//g")
@@ -459,6 +493,40 @@ else
errors=$(($errors+1))
fi
# test PUT /api/users without post data
printf "==> ${BLUE}Testing${YELLOW} PUT${BWHITE} /api/users without post data${RESET} "
curl \
--silent \
-X PUT \
-d "" \
localhost:8222/api/users &> put_api_users_no_data_test.log
sleep 0.1
res=$(cat put_api_users_no_data_test.log)
if [[ "$res" == *'{"error":"must have token, new_user_data"}'* ]]; then
printf "[$GREEN OK $RESET]\n"
success=$(($success+1))
else
printf "[$RED ERR $RESET]\n==> ${BLUE}Response: $RESET$(echo $res)\n"
errors=$(($errors+1))
fi
# test PUT /api/users with wrong json
printf "==> ${BLUE}Testing${YELLOW} PUT${BWHITE} /api/users with wrong json${RESET} "
curl \
--silent \
-X PUT \
-d "hiusdf 7qyt0 y qy40" \
localhost:8222/api/users &> put_api_users_wrong_json_test.log
sleep 0.1
res=$(cat put_api_users_wrong_json_test.log)
if [[ "$res" == *'error'* ]]; then
printf "[$GREEN OK $RESET]\n"
success=$(($success+1))
else
printf "[$RED ERR $RESET]\n==> ${BLUE}Response: $RESET$(echo $res)\n"
errors=$(($errors+1))
fi
# test POST /api/register
printf "==> ${BLUE}Testing${CYAN} POST${BWHITE} /api/register${RESET} "
curl \