diff --git a/meson.build b/meson.build index 79ed8d9..85c3ed0 100644 --- a/meson.build +++ b/meson.build @@ -23,6 +23,9 @@ executable( 'src/db_get_user_by_username.c', 'src/db_get_users_from_file.c', 'src/file.c', + 'src/db_insert_user.c', + 'src/db_update_user.c', + 'src/db_delete_user.c', include_directories: include_dirs, dependencies: deps, ) diff --git a/src/add_user.c b/src/add_user.c index ed68752..9741e2d 100644 --- a/src/add_user.c +++ b/src/add_user.c @@ -10,8 +10,47 @@ void add_user(struct mg_connection *c, struct mg_http_message *hm) { MG_ESC("invalid token")); return; } + cJSON *jsonbody = cJSON_Parse(hm->body.buf); + cJSON *username = cJSON_GetObjectItem(jsonbody, "username"); + cJSON *password = cJSON_GetObjectItem(jsonbody, "password"); + cJSON *email = cJSON_GetObjectItem(jsonbody, "email"); + cJSON *group = cJSON_GetObjectItem(jsonbody, "group"); + cJSON *is_superuser = cJSON_GetObjectItem(jsonbody, "is_superuser"); + if (username == nullptr || password == nullptr || email == nullptr || + is_superuser == nullptr || group == nullptr) { + mg_http_reply(c, 400, "", "{%m:%m}", MG_ESC("error"), + MG_ESC("must have , , , , ")); + return; + } + cJSON *payload = jwt_get_payload(requestdata->token); + bool jwt_is_superuser = + cJSON_GetObjectItem(payload, "is_superuser")->valueint; + if (!jwt_is_superuser) { + mg_http_reply(c, 403, "", "{%m:%m}", MG_ESC("error"), + MG_ESC("not authorized")); + cJSON_Delete(payload); + return; + } + const bool user_is_superuser = is_superuser->valueint ? true : false; + User *new_db_user = db_insert_user( + username->valuestring, email->valuestring, password->valuestring, + group->valuestring, + &user_is_superuser); + if (!new_db_user) { + mg_http_reply(c, 500, "", "{%m:%m}", MG_ESC("error"), + MG_ESC("failed to create user")); + cJSON_Delete(payload); + return; + } char responsedata[2048] = {0}; - snprintf(responsedata, sizeof(responsedata), "{\"success\": true}"); + snprintf(responsedata, sizeof(responsedata), + "{\"username\":\"%s\",\"email\":\"%s\",\"group\":\"%s\",\"is_" + "superuser\":%s}", + username->valuestring, email->valuestring, group->valuestring, + is_superuser->valueint ? "true" : "false"); + mg_http_reply(c, 200, "Content-Type: application/json\r\n", responsedata); + cJSON_Delete(payload); } diff --git a/src/change_password.c b/src/change_password.c index 9fbd0af..8e1aa1b 100644 --- a/src/change_password.c +++ b/src/change_password.c @@ -10,8 +10,39 @@ void change_password(struct mg_connection *c, struct mg_http_message *hm) { MG_ESC("invalid token")); return; } + cJSON *jsonbody = cJSON_Parse(hm->body.buf); + cJSON *old_password = cJSON_GetObjectItem(jsonbody, "old_password"); + cJSON *new_password = cJSON_GetObjectItem(jsonbody, "new_password"); + if (old_password == nullptr || new_password == nullptr) { + mg_http_reply(c, 400, "", "{%m:%m}", MG_ESC("error"), + MG_ESC("must have , ")); + return; + } + cJSON *payload = jwt_get_payload(requestdata->token); + char *username = cJSON_GetObjectItem(payload, "username")->valuestring; + User *old_user_data = db_get_user_by_username(username); + char *password = old_user_data->password; + + if (strcmp(password, old_password->valuestring)) { + mg_http_reply(c, 400, "", "{%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, "", "{%m:%m}", MG_ESC("error"), + MG_ESC("failed to update user")); + return; + } + char responsedata[64] = {0}; snprintf(responsedata, sizeof(responsedata), "{\"success\": true}"); mg_http_reply(c, 200, "Content-Type: application/json\r\n", responsedata); + // free(username); + free_user(old_user_data); + cJSON_Delete(payload); } diff --git a/src/change_user.c b/src/change_user.c index df91ded..f64f8d9 100644 --- a/src/change_user.c +++ b/src/change_user.c @@ -10,8 +10,58 @@ void change_user(struct mg_connection *c, struct mg_http_message *hm) { MG_ESC("invalid token")); return; } + cJSON *jsonbody = cJSON_Parse(hm->body.buf); + cJSON *username = cJSON_GetObjectItem(jsonbody, "username"); + cJSON *email = cJSON_GetObjectItem(jsonbody, "email"); + cJSON *group = cJSON_GetObjectItem(jsonbody, "group"); + cJSON *is_superuser = cJSON_GetObjectItem(jsonbody, "is_superuser"); + User *old_user_data = db_get_user_by_username(username->valuestring); + if (!old_user_data) { + mg_http_reply(c, 404, "", "{%m:%m}", MG_ESC("error"), + MG_ESC("user not found")); + return; + } + char *password = old_user_data->password; + if (!password) { + mg_http_reply(c, 400, "", "{%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, "", "{%m:%m}", MG_ESC("error"), + MG_ESC("must have , , , ")); + free_user(old_user_data); + free(password); + return; + } + cJSON *payload = jwt_get_payload(requestdata->token); + int id = cJSON_GetObjectItem(payload, "id")->valueint; + const bool user_is_superuser = is_superuser->valueint ? true : false; + User *updated_user_data = db_update_user( + id, username->valuestring, email->valuestring, password, + group->valuestring, &user_is_superuser); + + if (!updated_user_data) { + mg_http_reply(c, 500, "", "{%m:%m}", MG_ESC("error"), + MG_ESC("failed to update user")); + cJSON_Delete(payload); + free_user(old_user_data); + free(password); + return; + } + char responsedata[2048] = {0}; - snprintf(responsedata, sizeof(responsedata), "{\"success\": true}"); + snprintf(responsedata, sizeof(responsedata), + "{\"username\":\"%s\",\"email\":\"%s\",\"group\":\"%s\",\"is_" + "superuser\":%s}", + username->valuestring, email->valuestring, group->valuestring, + is_superuser->valueint ? "true" : "false"); mg_http_reply(c, 200, "Content-Type: application/json\r\n", responsedata); + free_user(updated_user_data); + free_user(old_user_data); + cJSON_Delete(payload); + free(password); } diff --git a/src/db_delete_user.c b/src/db_delete_user.c new file mode 100644 index 0000000..35513af --- /dev/null +++ b/src/db_delete_user.c @@ -0,0 +1,44 @@ +#include "database.h" +#include "sql.h" +#include "user.h" + +bool db_delete_user(const int id) { + pthread_mutex_lock(&db_mutex); + if (!db) { + fprintf(stderr, "Database not initialized\n"); + pthread_mutex_unlock(&db_mutex); + return false; + } + sqlite3_stmt *stmt; + int rc; + + rc = sqlite3_prepare_v2(db, DELETE_USER_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_int(stmt, 1, id); + + rc = sqlite3_step(stmt); + if (rc != SQLITE_DONE) { + fprintf(stderr, "Execution failed: %s\n", sqlite3_errmsg(db)); + sqlite3_finalize(stmt); + pthread_mutex_unlock(&db_mutex); + return false; + } + + int changes = sqlite3_changes(db); + sqlite3_finalize(stmt); + + if (changes > 0) { + printf("User deleted successfully. ID: %d\n", id); + } else { + printf("No user found with ID: %d\n", id); + return false; + } + + pthread_mutex_unlock(&db_mutex); + return true; +} diff --git a/src/db_get_users_from_file.c b/src/db_get_users_from_file.c index a435976..c7956e0 100644 --- a/src/db_get_users_from_file.c +++ b/src/db_get_users_from_file.c @@ -1,14 +1,11 @@ #include #include -#include "sql.h" -#include "user.h" #include "file.h" -#include "database.h" +#include "user.h" void db_get_users_from_file(const char *path) { char *users_string = read_file(path); cJSON *json_array = cJSON_Parse(users_string); - pthread_mutex_lock(&db_mutex); for (int i = 0; i < cJSON_GetArraySize(json_array); i++) { cJSON *json_object = cJSON_GetArrayItem(json_array, i); const char *username = @@ -19,32 +16,14 @@ void db_get_users_from_file(const char *path) { const char *group = cJSON_GetObjectItem(json_object, "group")->valuestring; const bool is_superuser = cJSON_GetObjectItem(json_object, "is_superuser")->valueint; - - sqlite3_stmt *stmt; - int rc = sqlite3_prepare_v2(db, INSERT_USER_SQL, -1, &stmt, nullptr); - if (rc != SQLITE_OK) { - fprintf(stderr, "Cannot prepare statement: %s\n", sqlite3_errmsg(db)); - sqlite3_close(db); - exit(1); + User *new_db_user = + db_insert_user(username, email, password, group, &is_superuser); + if (!new_db_user) { + fprintf(stderr, "Cannot insert user: %s\n", username); } - - sqlite3_bind_text(stmt, 1, username, -1, SQLITE_STATIC); - sqlite3_bind_text(stmt, 2, email, -1, SQLITE_STATIC); - sqlite3_bind_text(stmt, 3, password, -1, SQLITE_STATIC); - sqlite3_bind_text(stmt, 4, group, -1, SQLITE_STATIC); - sqlite3_bind_int(stmt, 5, is_superuser); - - rc = sqlite3_step(stmt); - if (rc != SQLITE_DONE) { - fprintf(stderr, "Cannot insert data: %s\n", sqlite3_errmsg(db)); - } else { - printf("Record inserted successfully. Row ID: %lld\n", - sqlite3_last_insert_rowid(db)); - } - sqlite3_finalize(stmt); + free_user(new_db_user); } cJSON_Delete(json_array); free(users_string); - pthread_mutex_unlock(&db_mutex); } diff --git a/src/db_insert_user.c b/src/db_insert_user.c index 409fb1e..d293129 100644 --- a/src/db_insert_user.c +++ b/src/db_insert_user.c @@ -4,14 +4,16 @@ User *db_insert_user(const char *username, const char *email, const char *password, const char *group, - bool *is_superuser) { + const bool *is_superuser) { pthread_mutex_lock(&db_mutex); if (!db) { fprintf(stderr, "Databse not initialized\n"); + pthread_mutex_unlock(&db_mutex); return nullptr; } if (!username || !email || !password) { fprintf(stderr, "username, email, password connot be empty\n"); + pthread_mutex_unlock(&db_mutex); return nullptr; } const char *sql = nullptr; @@ -27,6 +29,7 @@ User *db_insert_user(const char *username, const char *email, rc = sqlite3_prepare_v2(db, sql, -1, &stmt, 0); if (rc != SQLITE_OK) { fprintf(stderr, "Cannot prepare statement: %s\n", sqlite3_errmsg(db)); + pthread_mutex_unlock(&db_mutex); return nullptr; } @@ -38,13 +41,14 @@ User *db_insert_user(const char *username, const char *email, sqlite3_bind_text(stmt, 4, group, -1, SQLITE_STATIC); } if (is_superuser) { - sqlite3_bind_int(stmt, 5, is_superuser); + sqlite3_bind_int(stmt, 5, *is_superuser); } rc = sqlite3_step(stmt); if (rc != SQLITE_DONE) { fprintf(stderr, "Execution failed: %s\n", sqlite3_errmsg(db)); sqlite3_finalize(stmt); + pthread_mutex_unlock(&db_mutex); return nullptr; } @@ -53,4 +57,7 @@ User *db_insert_user(const char *username, const char *email, sqlite3_finalize(stmt); printf("User inserted successfully. ID: %d\n", inserted_id); pthread_mutex_unlock(&db_mutex); -} \ No newline at end of file + User *new_db_user = + new_user(inserted_id, username, email, password, group, is_superuser); + return new_db_user; +} diff --git a/src/db_update_user.c b/src/db_update_user.c new file mode 100644 index 0000000..9215d22 --- /dev/null +++ b/src/db_update_user.c @@ -0,0 +1,55 @@ +#include "database.h" +#include "sql.h" +#include "user.h" + +User *db_update_user(const int userid, const char *username, const char *email, + const char *password, const char *group, + const bool *is_superuser) { + pthread_mutex_lock(&db_mutex); + if (!db) { + fprintf(stderr, "Databse not initialized\n"); + pthread_mutex_unlock(&db_mutex); + return nullptr; + } + if (!username || !email || !password || !group || !is_superuser) { + fprintf(stderr, + "username, email, password, group, is_superuser connot be empty\n"); + pthread_mutex_unlock(&db_mutex); + return nullptr; + } + sqlite3_stmt *stmt; + int rc; + + rc = sqlite3_prepare_v2(db, UPDATE_USER_FULL_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 nullptr; + } + sqlite3_bind_text(stmt, 1, username, -1, SQLITE_STATIC); + sqlite3_bind_text(stmt, 2, email, -1, SQLITE_STATIC); + sqlite3_bind_text(stmt, 3, password, -1, SQLITE_STATIC); + sqlite3_bind_text(stmt, 4, group, -1, SQLITE_STATIC); + sqlite3_bind_int(stmt, 5, *is_superuser); + sqlite3_bind_int(stmt, 6, userid); + + rc = sqlite3_step(stmt); + if (rc != SQLITE_DONE) { + fprintf(stderr, "Execution failed: %s\n", sqlite3_errmsg(db)); + sqlite3_finalize(stmt); + pthread_mutex_unlock(&db_mutex); + return nullptr; + } + + int changes = sqlite3_changes(db); + sqlite3_finalize(stmt); + + if (changes > 0) { + printf("User updated successfully. ID: %d\n", userid); + } else { + printf("No user found with ID: %d\n", userid); + return nullptr; + } + pthread_mutex_unlock(&db_mutex); + return new_user(userid, username, email, password, group, is_superuser); +} diff --git a/src/delete_user.c b/src/delete_user.c index b35ee49..afd6e8c 100644 --- a/src/delete_user.c +++ b/src/delete_user.c @@ -10,8 +10,12 @@ void delete_user(struct mg_connection *c, struct mg_http_message *hm) { MG_ESC("invalid token")); return; } + cJSON *payload = jwt_get_payload(requestdata->token); + int id = cJSON_GetObjectItem(payload, "id")->valueint; + bool user_deleted = db_delete_user(id); char responsedata[2048] = {0}; - snprintf(responsedata, sizeof(responsedata), "{\"success\": true}"); + snprintf(responsedata, sizeof(responsedata), "{\"success\": %s}", + user_deleted ? "true" : "false"); mg_http_reply(c, 200, "Content-Type: application/json\r\n", responsedata); } diff --git a/src/main.c b/src/main.c index 0ed524b..5a21502 100644 --- a/src/main.c +++ b/src/main.c @@ -2,7 +2,6 @@ #include #include "database.h" #include "route.h" -#include "sql.h" #include "user.h" static void root_handler(struct mg_connection *c, struct mg_http_message *hm) { @@ -18,7 +17,7 @@ static const struct route routes[] = { {"POST", "/api/users/user", get_user}, {"POST", "/api/users/delete", delete_user}, {"POST", "/api/users/password", change_password}, - {"POST", "/api/users/change", change_password}, + {"POST", "/api/users/change", change_user}, {"POST", "/api/users/add", add_user}, {"POST", "/api/register", register_user}, {nullptr, nullptr, nullptr}}; diff --git a/src/register_user.c b/src/register_user.c index 6eadf46..0476f3e 100644 --- a/src/register_user.c +++ b/src/register_user.c @@ -25,9 +25,10 @@ void register_user(struct mg_connection *c, struct mg_http_message *hm) { if (is_superuser == nullptr) { is_superuser = cJSON_CreateBool(false); } - const User *new_user_data = - new_user(0, username->valuestring, password->valuestring, email->valuestring, - group->valuestring, is_superuser->valueint); + const bool is_superuser_value = is_superuser->valueint ? true : false; + User *new_user_data = db_insert_user( + username->valuestring, email->valuestring, password->valuestring, + group->valuestring, (const bool*)is_superuser_value); if (new_user_data == nullptr) { mg_http_reply(c, 500, "", "{%m:%m}", MG_ESC("error"), MG_ESC("failed to create user")); @@ -41,4 +42,5 @@ void register_user(struct mg_connection *c, struct mg_http_message *hm) { new_user_data->group, new_user_data->is_superuser); mg_http_reply(c, 200, "Content-Type: application/json\r\n", responsedata); + free_user(new_user_data); } diff --git a/src/sql.h b/src/sql.h index 88ee2fb..c77721c 100644 --- a/src/sql.h +++ b/src/sql.h @@ -24,3 +24,8 @@ #define INSERT_USER_FULL_SQL \ "insert into users (username, email, password, user_group, is_superuser) " \ "values (?, ?, ?, ?, ?)" +#define UPDATE_USER_FULL_SQL \ + "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 = ?" diff --git a/src/user.c b/src/user.c index a6bd83e..1bc96b8 100644 --- a/src/user.c +++ b/src/user.c @@ -24,8 +24,8 @@ void free_request_with_token(request_with_token *data) { free(data); } -User *new_user(int id, const char *username, const char *password, const char *email, - const char *group, bool is_superuser) { +User *new_user(int id, const char *username, const char *password, + const char *email, const char *group, bool is_superuser) { User *user = malloc(sizeof(User)); if (user == NULL) { return NULL; @@ -40,9 +40,20 @@ User *new_user(int id, const char *username, const char *password, const char *e } void free_user(User *user) { - free(user->username); - free(user->password); - free(user->email); - free(user->group); + if (!user) { + return; + } + if (user->username != nullptr) { + free(user->username); + } + if (user->password != nullptr) { + free(user->password); + } + if (user->email != nullptr) { + free(user->email); + } + if (user->group != nullptr) { + free(user->group); + } free(user); } diff --git a/src/user.h b/src/user.h index d030b45..7cf3428 100644 --- a/src/user.h +++ b/src/user.h @@ -57,4 +57,8 @@ char *db_get_all_users(); User *db_get_user_by_username(const char *username); User *db_insert_user(const char *username, const char *email, const char *password, const char *group, - bool *is_superuser); + const bool *is_superuser); +User *db_update_user(const int userid, const char *username, const char *email, + const char *password, const char *group, + const bool *is_superuser); +bool db_delete_user(const int id);