添加配置文件和用户删除功能的实现,包括配置读取、JWT 验证和用户存在性检查

This commit is contained in:
keqingmoe 2024-12-23 19:05:43 +08:00
parent a4aed41ff1
commit 58e93e295b
15 changed files with 573 additions and 15 deletions

3
configs/config.json Normal file
View File

@ -0,0 +1,3 @@
{
"server_port": 8081
}

25
include/db/auth.h Normal file
View File

@ -0,0 +1,25 @@
#ifndef DB_AUTH_H
#define DB_AUTH_H
#ifdef __cplusplus
extern "C"
{
#endif
int open_user_db();
void close_user_db();
int check_user_exists(const char* user_id, int* result);
int set_user_password(const char* user_id, const char* password);
int login(const char* user_id, const char* password, int* result);
int delete_user(const char* user_id);
#ifdef __cplusplus
}
#endif
#endif // DB_AUTH_H

View File

@ -1,6 +1,12 @@
#ifndef SERVER_AUTH_H
#define SERVER_AUTH_H
// Your code here
#include "server/types.h"
int login_handler(mg_connection* conn, void* cbdata);
int register_handler(mg_connection* conn, void* cbdata);
int delete_handler(mg_connection* conn, void* cbdata);
extern char* secret;
#endif // SERVER_AUTH_H

19
include/server/config.h Normal file
View File

@ -0,0 +1,19 @@
#ifndef SERVER_CONFIG_H
#define SERVER_CONFIG_H
#include <stdbool.h>
typedef struct
{
int server_port;
char* secret;
} config_t;
void config_ctor(config_t* this);
void config_dtor(config_t* this);
bool config_read(config_t* this, const char* filename);
int print_config();
#endif // SERVER_CONFIG_H

View File

@ -1,8 +0,0 @@
#ifndef KQM_DEFS_H
#define KQM_DEFS_H
#define CR "\r"
#define LF "\n"
#define CRLF "\r\n"
#endif // KQM_DEFS_H

View File

@ -1,11 +1,10 @@
#ifndef SERVER_TYPES_H
#define SERVER_TYPES_H
#include <civetweb.h>
typedef struct mg_callbacks mg_callbacks;
typedef struct mg_context mg_context;
typedef struct mg_connection mg_connection;
typedef struct mg_request_info mg_request_info;
typedef struct mg_form_data_handler mg_form_data_handler;
#endif // SERVER_TYPES_H

6
include/server/util.h Normal file
View File

@ -0,0 +1,6 @@
#ifndef SERVER_UTIL_H
#define SERVER_UTIL_H
char* kqm_strndup(const char* str, size_t n);
#endif // SERVER_UTIL_H

View File

@ -70,10 +70,10 @@ extern "C"
return 1;
}
auto logout(const char* user_id) -> int
auto delete_user(const char* user_id) -> int
{
leveldb::WriteOptions write_options;
leveldb::Status status = user_db->Delete(write_options, user_id);
auto write_options = leveldb::WriteOptions{};
auto status = user_db->Delete(write_options, user_id);
return status.ok();
}
}

View File

@ -15,7 +15,7 @@ extern "C"
.set_issuer("KeqingMoe")
.set_subject("user_id")
.set_audience("web_client")
.set_expires_at(std::chrono::system_clock::now() + 3s)
.set_expires_at(std::chrono::system_clock::now() + 1h)
.set_payload_claim("user_id", jwt::claim(std::string{user_id}))
.sign(jwt::algorithm::hs256{secret});
return strdup(token.c_str());

5
src/server/auth/auth.c Normal file
View File

@ -0,0 +1,5 @@
#include <server/auth.h>
#include <stddef.h>
char* secret = NULL;

134
src/server/auth/delete.c Normal file
View File

@ -0,0 +1,134 @@
#include "server/auth.h"
#include "server/util.h"
#include "db/auth.h"
#include "jwt/jwt.h"
#include <civetweb.h>
#include <cjson/cJSON.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
typedef struct
{
char* token;
} delete_form_t;
static void delete_form_dtor(delete_form_t* form)
{
if (form->token) free(form->token);
}
static int field_found(const char* key, const char* filename, char* path, size_t pathlen, void* user_data)
{
return MG_FORM_FIELD_HANDLE_GET;
}
static int field_get(const char* key, const char* value, size_t valuelen, void* user_data)
{
delete_form_t* form = (delete_form_t*)user_data;
if (strcmp(key, "token") == 0) {
form->token = kqm_strndup(value, valuelen);
return MG_FORM_FIELD_HANDLE_ABORT;
}
return MG_FORM_FIELD_HANDLE_GET;
}
int delete_handler(mg_connection* conn, void* cbdata)
{
const mg_request_info* post_body = mg_get_request_info(conn);
if (post_body == NULL) {
mg_printf(conn,
"HTTP/1.1 400 Bad Request\r\n"
"Content-Type: application/json\r\n"
"Access-Control-Allow-Origin: *\r\n\r\n");
mg_printf(conn, "{\"error\":\"null request\"}");
return 1;
}
if (strcmp(post_body->request_method, "POST")) {
mg_printf(conn,
"HTTP/1.1 405 Method Not Allowed\r\n"
"Content-Type: application/json\r\n"
"Access-Control-Allow-Origin: *\r\n\r\n");
mg_printf(conn, "{\"error\":\"must use post request\"}");
return 1;
}
delete_form_t form = {NULL};
mg_form_data_handler delete_callback = {
.field_found = field_found,
.field_get = field_get,
.field_store = NULL,
.user_data = &form,
};
mg_handle_form_request(conn, &delete_callback);
if (!form.token) {
mg_printf(conn,
"HTTP/1.1 400 Bad Request\r\n"
"Content-Type: application/json\r\n"
"Access-Control-Allow-Origin: *\r\n\r\n"
"{\"error\":\"need token\"}");
delete_form_dtor(&form);
return 1;
}
int result = verify_token(form.token, secret);
if (!result) {
mg_printf(conn,
"HTTP/1.1 401 Unauthorized\r\n"
"Content-Type: application/json\r\n"
"Access-Control-Allow-Origin: *\r\n\r\n"
"{\"error\":\"auth failed\"}");
delete_form_dtor(&form);
return 1;
}
char* user_id = get_payload(form.token);
int flag = check_user_exists(user_id, &result);
if (!flag) {
mg_printf(conn,
"HTTP/1.1 500 Internal Server Error\r\n"
"Content-Type: application/json\r\n"
"Access-Control-Allow-Origin: *\r\n\r\n"
"{\"error\":\"failed to check user existence\"}");
delete_form_dtor(&form);
return 1;
} else if (!result) {
mg_printf(conn,
"HTTP/1.1 404 Not Found\r\n"
"Content-Type: application/json\r\n"
"Access-Control-Allow-Origin: *\r\n\r\n"
"{\"error\":\"user does not exist\"}");
delete_form_dtor(&form);
return 1;
}
flag = delete_user(user_id);
if (!flag) {
mg_printf(conn,
"HTTP/1.1 500 Internal Server Error\r\n"
"Content-Type: application/json\r\n"
"Access-Control-Allow-Origin: *\r\n\r\n"
"{\"error\":\"failed to delete account\"}");
} else {
mg_printf(conn,
"HTTP/1.1 200 OK\r\n"
"Content-Type: application/json\r\n"
"Access-Control-Allow-Origin: *\r\n\r\n"
"{\"success\":\"delete account success\"}");
}
free(user_id);
delete_form_dtor(&form);
return 1;
}

145
src/server/auth/login.c Normal file
View File

@ -0,0 +1,145 @@
#include "server/auth.h"
#include "server/util.h"
#include "db/auth.h"
#include "jwt/jwt.h"
#include <civetweb.h>
#include <cjson/cJSON.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
typedef struct
{
char* user_id;
char* password;
} login_form_t;
static void login_form_dtor(login_form_t* form)
{
if (form->user_id) free(form->user_id);
if (form->password) free(form->password);
}
static int field_found(const char* key, const char* filename, char* path, size_t pathlen, void* user_data)
{
return MG_FORM_FIELD_HANDLE_GET;
}
static int field_get(const char* key, const char* value, size_t valuelen, void* user_data)
{
login_form_t* form = (login_form_t*)user_data;
if (strcmp(key, "user_id") == 0) {
form->user_id = kqm_strndup(value, valuelen);
} else if (strcmp(key, "password") == 0) {
form->password = kqm_strndup(value, valuelen);
}
if (form->user_id && form->password) {
return MG_FORM_FIELD_HANDLE_ABORT;
}
return MG_FORM_FIELD_HANDLE_GET;
}
int login_handler(mg_connection* conn, void* cbdata)
{
const mg_request_info* post_body = mg_get_request_info(conn);
if (post_body == NULL) {
mg_printf(conn,
"HTTP/1.1 400 Bad Request\r\n"
"Content-Type: application/json\r\n"
"Access-Control-Allow-Origin: *\r\n\r\n");
mg_printf(conn, "{\"error\":\"null request\"}");
return 1;
}
if (strcmp(post_body->request_method, "POST")) {
mg_printf(conn,
"HTTP/1.1 405 Method Not Allowed\r\n"
"Content-Type: application/json\r\n"
"Access-Control-Allow-Origin: *\r\n\r\n");
mg_printf(conn, "{\"error\":\"must use post request\"}");
return 1;
}
login_form_t form = {NULL, NULL};
mg_form_data_handler login_callback = {
.field_found = field_found,
.field_get = field_get,
.field_store = NULL,
.user_data = &form,
};
mg_handle_form_request(conn, &login_callback);
if (!form.user_id) {
mg_printf(conn,
"HTTP/1.1 400 Bad Request\r\n"
"Content-Type: application/json\r\n"
"Access-Control-Allow-Origin: *\r\n\r\n"
"{\"error\":\"need user_id\"}");
login_form_dtor(&form);
return 1;
}
if (!form.password) {
mg_printf(conn,
"HTTP/1.1 400 Bad Request\r\n"
"Content-Type: application/json\r\n"
"Access-Control-Allow-Origin: *\r\n\r\n"
"{\"error\":\"need password\"}");
login_form_dtor(&form);
return 1;
}
int result;
int flag = check_user_exists(form.user_id, &result);
if (!flag) {
mg_printf(conn,
"HTTP/1.1 500 Internal Server Error\r\n"
"Content-Type: application/json\r\n"
"Access-Control-Allow-Origin: *\r\n\r\n"
"{\"error\":\"failed to check user existence\"}");
login_form_dtor(&form);
return 1;
} else if (!result) {
mg_printf(conn,
"HTTP/1.1 404 Not Found\r\n"
"Content-Type: application/json\r\n"
"Access-Control-Allow-Origin: *\r\n\r\n"
"{\"error\":\"user does not exist\"}");
login_form_dtor(&form);
return 1;
}
flag = login(form.user_id, form.password, &result);
if (!flag) {
mg_printf(conn,
"HTTP/1.1 500 Internal Server Error\r\n"
"Content-Type: application/json\r\n"
"Access-Control-Allow-Origin: *\r\n\r\n"
"{\"error\":\"failed to login\"}");
} else if (!result) {
mg_printf(conn,
"HTTP/1.1 401 Unauthorized\r\n"
"Content-Type: application/json\r\n"
"Access-Control-Allow-Origin: *\r\n\r\n"
"{\"error\":\"incorrect password or user id\"}");
} else {
char* token = create_token(form.user_id, secret);
mg_printf(conn,
"HTTP/1.1 200 OK\r\n"
"Content-Type: application/json\r\n"
"Access-Control-Allow-Origin: *\r\n\r\n"
"{\"token\":\"%s\"}",
token);
free(token);
}
login_form_dtor(&form);
return 1;
}

134
src/server/auth/register.c Normal file
View File

@ -0,0 +1,134 @@
#include "server/auth.h"
#include "server/util.h"
#include "db/auth.h"
#include <civetweb.h>
#include <cjson/cJSON.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
typedef struct
{
char* user_id;
char* password;
} register_form_t;
static void register_form_dtor(register_form_t* form)
{
if (form->user_id) free(form->user_id);
if (form->password) free(form->password);
}
static int field_found(const char* key, const char* filename, char* path, size_t pathlen, void* user_data)
{
return MG_FORM_FIELD_HANDLE_GET;
}
static int field_get(const char* key, const char* value, size_t valuelen, void* user_data)
{
register_form_t* form = (register_form_t*)user_data;
if (strcmp(key, "user_id") == 0) {
form->user_id = kqm_strndup(value, valuelen);
} else if (strcmp(key, "password") == 0) {
form->password = kqm_strndup(value, valuelen);
}
if (form->user_id && form->password) {
return MG_FORM_FIELD_HANDLE_ABORT;
}
return MG_FORM_FIELD_HANDLE_GET;
}
int register_handler(mg_connection* conn, void* cbdata)
{
const mg_request_info* post_body = mg_get_request_info(conn);
if (post_body == NULL) {
mg_printf(conn,
"HTTP/1.1 400 Bad Request\r\n"
"Content-Type: application/json\r\n"
"Access-Control-Allow-Origin: *\r\n\r\n");
mg_printf(conn, "{\"error\":\"null request\"}");
return 1;
}
if (strcmp(post_body->request_method, "POST")) {
mg_printf(conn,
"HTTP/1.1 405 Method Not Allowed\r\n"
"Content-Type: application/json\r\n"
"Access-Control-Allow-Origin: *\r\n\r\n");
mg_printf(conn, "{\"error\":\"must use post request\"}");
return 1;
}
register_form_t form = {NULL, NULL};
mg_form_data_handler register_callback = {
.field_found = field_found,
.field_get = field_get,
.field_store = NULL,
.user_data = &form,
};
mg_handle_form_request(conn, &register_callback);
if (!form.user_id) {
mg_printf(conn,
"HTTP/1.1 400 Bad Request\r\n"
"Content-Type: application/json\r\n"
"Access-Control-Allow-Origin: *\r\n\r\n"
"{\"error\":\"need user_id\"}");
register_form_dtor(&form);
return 1;
}
if (!form.password) {
mg_printf(conn,
"HTTP/1.1 400 Bad Request\r\n"
"Content-Type: application/json\r\n"
"Access-Control-Allow-Origin: *\r\n\r\n"
"{\"error\":\"need password\"}");
register_form_dtor(&form);
return 1;
}
int result;
int flag = check_user_exists(form.user_id, &result);
if (!flag) {
mg_printf(conn,
"HTTP/1.1 500 Internal Server Error\r\n"
"Content-Type: application/json\r\n"
"Access-Control-Allow-Origin: *\r\n\r\n"
"{\"error\":\"failed to check user existence\"}");
register_form_dtor(&form);
return 1;
} else if (result) {
mg_printf(conn,
"HTTP/1.1 409 Conflict\r\n"
"Content-Type: application/json\r\n"
"Access-Control-Allow-Origin: *\r\n\r\n"
"{\"error\":\"user already exists\"}");
register_form_dtor(&form);
return 1;
}
flag = set_user_password(form.user_id, form.password);
if (!flag) {
mg_printf(conn,
"HTTP/1.1 500 Internal Server Error\r\n"
"Content-Type: application/json\r\n"
"Access-Control-Allow-Origin: *\r\n\r\n"
"{\"error\":\"failed to register\"}");
} else {
mg_printf(conn,
"HTTP/1.1 200 OK\r\n"
"Content-Type: application/json\r\n"
"Access-Control-Allow-Origin: *\r\n\r\n"
"{\"success\":\"registration success\"}");
}
register_form_dtor(&form);
return 1;
}

77
src/server/config.c Normal file
View File

@ -0,0 +1,77 @@
#include "server/config.h"
#include <cjson/cJSON.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char* read_file(const char* filename)
{
FILE* file = fopen(filename, "rb");
if (file == NULL) {
printf("Error opening file\n");
return NULL;
}
fseek(file, 0, SEEK_END);
long file_size = ftell(file);
fseek(file, 0, SEEK_SET);
char* buffer = (char*)malloc(file_size + 1);
if (buffer == NULL) {
printf("Memory allocation failed\n");
fclose(file);
return NULL;
}
fread(buffer, 1, file_size, file);
buffer[file_size] = '\0';
fclose(file);
return buffer;
}
void config_ctor(config_t* this)
{
this->server_port = 0;
this->secret = NULL;
}
void config_dtor(config_t* this)
{
if (this->secret) free(this->secret);
}
bool config_read(config_t* this, const char* filename)
{
char* json_data = read_file(filename);
if (json_data == NULL) {
return false;
}
cJSON* root = cJSON_Parse(json_data);
if (root == NULL) {
free(json_data);
return false;
}
cJSON* server_port = cJSON_GetObjectItem(root, "server_port");
if (cJSON_IsNumber(server_port)) {
this->server_port = server_port->valueint;
} else {
this->server_port = 80;
}
cJSON* secret = cJSON_GetObjectItem(root, "secret");
if (cJSON_IsString(secret) && secret->valuestring != NULL) {
this->secret = strdup(secret->valuestring);
} else {
this->secret = strdup("me@keqing.moe");
}
cJSON_Delete(root);
free(json_data);
return true;
}

13
src/server/util.c Normal file
View File

@ -0,0 +1,13 @@
#include "server/util.h"
#include <string.h>
#include <stdlib.h>
char* kqm_strndup(const char* str, size_t n)
{
char* s = (char*)malloc(n + 1);
memcpy(s, str, n);
s[n] = '\0';
return s;
}