Merge pull request 'develop to main pre_release' (#1) from develop into main
Prepare Build and Testing executable binary / Prepare-Build-Testing-With-Make (push) Successful in 11s
Prepare Build and Testing executable binary / Prepare-Build-Testing-With-CMake (push) Successful in 18s
Prepare Build and Testing executable binary / Prepare-Build-Testing-With-Meson (push) Successful in 16s

Reviewed-on: https://gitea.home.com:3000/TolaMironcenko/auth_service_c/pulls/1
This commit was merged in pull request #1.
This commit is contained in:
wt committed 2025-08-30 12:27:53 +07:00
commit 7380928faf
57 files changed
+2709 -166

No files matched your search

+48
View File
@@ -0,0 +1,48 @@
name: Prepare Build and Testing executable binary
run-name: ${{ gitea.actor }}
on: [push]
jobs:
Prepare-Build-Testing-With-Make:
runs-on: ubuntu-latest
steps:
- name: Check out repository code
uses: actions/checkout@v3
- name: Building binary with make
run: make -j$(nproc)
- name: Testing binary
run: make test
Prepare-Build-Testing-With-CMake:
runs-on: ubuntu-latest
steps:
- name: prepare-container
run: |
apt install -y cmake
- name: Check out repository code
uses: actions/checkout@v3
- name: Building binary with CMake
run: |
mkdir build && \
cd build && \
cmake .. && \
make -j$(nproc)
- name: Testing binary
run: |
cp -v build/auth_service . && ./tests.sh
Prepare-Build-Testing-With-Meson:
runs-on: ubuntu-latest
steps:
- name: prepare-container
run: |
apt install -y meson
- name: Check out repository code
uses: actions/checkout@v3
- name: Building binary with meson
run: |
meson setup build && \
ninja -C build
- name: Testing binary
run: |
cp -v build/auth_service . && ./tests.sh
+6 -2
View File
@@ -4,5 +4,9 @@
.idea
build
builddir
fmongoose
users.db
auth_service
users.db
*.swp
*.log
.proxy.sh
.no-proxy.sh
+3 -3
View File
@@ -1,5 +1,5 @@
cmake_minimum_required (VERSION 3.6)
project(fmongoose LANGUAGES C)
project(auth_service LANGUAGES C)
set(CMAKE_C_STANDARD 23)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
@@ -11,8 +11,8 @@ FILE(GLOB_RECURSE SOURCES src/*.c)
find_package(SQLite3 REQUIRED)
add_executable(
fmongoose
auth_service
${SOURCES}
)
target_link_libraries(fmongoose ssl crypto sqlite3 pthread)
target_link_libraries(auth_service ssl crypto sqlite3 pthread)
+7 -4
View File
@@ -1,11 +1,11 @@
CC = gcc
CFLAGS = -Wall -g -std=gnu2x
CFLAGS = -Wall -g -std=gnu2x -DMG_TLS=MG_TLS_OPENSSL
LDFLAGS =
LIBS = -lpthread -lcrypto -lsqlite3
LIBS = -lpthread -lcrypto -lssl -lsqlite3
INCLUDES = -I./src/lib -I./src
SRCS = $(wildcard src/*.c src/lib/*.c)
OBJS = $(SRCS:.c=.o)
TARGET = fmongoose
TARGET = auth_service
BUILD_DIR=./build
CROSS_COMPILE =
@@ -18,4 +18,7 @@ $(TARGET): $(OBJS)
$(CROSS_COMPILE)$(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@
clean:
rm -f $(OBJS) $(TARGET)
rm -f $(OBJS) $(TARGET) $(shell find . -type f -name '*.log') users.db
test: $(TARGET)
bash ./tests.sh
+10
View File
@@ -0,0 +1,10 @@
{
"database": "users.db",
"address": "localhost:8222",
"tls_address": "localhost:8223",
"default_users_file": "users.json",
"web": "./web",
"tls_enabled": true,
"tls_cert": "/home/tola/certs/localhost/localhost.crt",
"tls_key": "/home/tola/certs/localhost/localhost.key"
}
+12 -2
View File
@@ -1,8 +1,8 @@
project('fmongoose', 'c', default_options: ['c_std=gnu2x'])
project('auth_service', 'c', default_options: ['c_std=gnu2x'])
include_dirs = include_directories('src', 'src/lib')
deps = [dependency('openssl'), dependency('sqlite3')]
executable(
'fmongoose',
'auth_service',
'src/main.c',
'src/user.c',
'src/add_user.c',
@@ -23,6 +23,16 @@ 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',
'src/db_get_user_by_id.c',
'src/ConfigManager.c',
'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,
)
+172
View File
@@ -0,0 +1,172 @@
#include "ConfigManager.h"
#include <cJSON.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "file.h"
typedef struct ConfigManager {
cJSON *config;
pthread_mutex_t config_file_mutex;
char *config_file_name;
} ConfigManager;
ConfigManager *cm_new_config() {
ConfigManager *newcm = malloc(sizeof(ConfigManager));
newcm->config_file_name = nullptr;
newcm->config = nullptr;
return newcm;
}
ConfigManager *cm_new_config_from_file(const char *filename) {
ConfigManager *newcm = malloc(sizeof(ConfigManager));
newcm->config_file_name = strdup(filename);
pthread_mutex_lock(&(newcm->config_file_mutex));
char *config_file_content = read_file(newcm->config_file_name);
newcm->config = cJSON_Parse(config_file_content);
pthread_mutex_unlock(&(newcm->config_file_mutex));
return newcm;
}
ConfigManager *cm_new_config_from_object(cJSON *config) {
ConfigManager *newcm = malloc(sizeof(ConfigManager));
newcm->config = config;
newcm->config_file_name = nullptr;
return newcm;
}
void cm_set_config_from_file(ConfigManager *cm, const char *filename) {
cm->config_file_name = strdup(filename);
pthread_mutex_lock(&(cm->config_file_mutex));
char *config_file_content = read_file(cm->config_file_name);
cm->config = cJSON_Parse(config_file_content);
pthread_mutex_unlock(&(cm->config_file_mutex));
}
void cm_set_config_from_object(ConfigManager *cm, cJSON *config) {
cm->config = config;
}
void cm_delete_config(ConfigManager *cm) {
if (!cm) {
return;
}
if (cm->config) {
cJSON_Delete(cm->config);
}
if (cm->config_file_name) {
free(cm->config_file_name);
}
free(cm);
}
void cm_print_config(const ConfigManager *cm) {
printf("Config: %s\n", cJSON_Print(cm->config));
}
char *cm_get_config_filename(ConfigManager *cm) {
return strdup(cm->config_file_name);
}
cJSON *cm_get_config(ConfigManager *cm) {
return cJSON_Duplicate(cm->config, (cJSON_bool) true);
}
char *cm_get_parameter_as_string(ConfigManager *cm, const char *param_name) {
cJSON *param = cJSON_GetObjectItem(cm->config, param_name);
if (!param) return nullptr;
char *param_value = strdup(param->valuestring);
return param_value;
}
int cm_get_parameter_as_int(ConfigManager *cm, const char *param_name) {
cJSON *param = cJSON_GetObjectItem(cm->config, param_name);
return param->valueint;
}
double cm_get_parameter_as_double(ConfigManager *cm, const char *param_name) {
cJSON *param = cJSON_GetObjectItem(cm->config, param_name);
return param->valuedouble;
}
float cm_get_parameter_as_float(ConfigManager *cm, const char *param_name) {
cJSON *param = cJSON_GetObjectItem(cm->config, param_name);
return param->valuedouble;
}
bool cm_get_parameter_as_bool(ConfigManager *cm, const char *param_name) {
cJSON *param = cJSON_GetObjectItem(cm->config, param_name);
if (!param) return false;
return param->valueint;
}
void cm_set_string_parameter(ConfigManager *cm, const char *name,
const char *value) {
cJSON *param = cJSON_GetObjectItem(cm->config, name);
if (!param) {
cJSON_AddStringToObject(cm->config, name, value);
return;
}
cJSON_SetValuestring(param, value);
}
void cm_set_int_parameter(ConfigManager *cm, const char *name,
const int value) {
cJSON *param = cJSON_GetObjectItem(cm->config, name);
if (!param) {
cJSON_AddNumberToObject(cm->config, name, value);
return;
}
cJSON_SetIntValue(param, value);
}
void cm_set_double_parameter(ConfigManager *cm, const char *name,
const double value) {
cJSON *param = cJSON_GetObjectItem(cm->config, name);
if (!param) {
cJSON_AddNumberToObject(cm->config, name, value);
return;
}
cJSON_SetNumberValue(param, value);
}
void cm_set_float_parameter(ConfigManager *cm, const char *name,
const float value) {
cJSON *param = cJSON_GetObjectItem(cm->config, name);
if (!param) {
cJSON_AddNumberToObject(cm->config, name, value);
return;
}
cJSON_SetNumberValue(param, value);
}
void cm_set_bool_parameter(ConfigManager *cm, const char *name,
const bool value) {
cJSON *param = cJSON_GetObjectItem(cm->config, name);
if (!param) {
cJSON_AddBoolToObject(cm->config, name, value);
return;
}
cJSON_SetBoolValue(param, value);
}
void cm_set_null_parameter(ConfigManager *cm, const char *name) {
cJSON *param = cJSON_GetObjectItem(cm->config, name);
if (!param) {
cJSON_AddNullToObject(cm->config, name);
return;
}
cJSON_ReplaceItemInObject(cm->config, name, cJSON_CreateNull());
}
void cm_save_config(ConfigManager *cm) {
pthread_mutex_lock(&(cm->config_file_mutex));
FILE *configfile = fopen(cm->config_file_name, "w+");
char *configvalue = cJSON_Print(cm->config);
fprintf(configfile, "%s", configvalue);
fclose(configfile);
free(configvalue);
pthread_mutex_unlock(&(cm->config_file_mutex));
}
void cm_save_config_to_file(ConfigManager *cm, const char *filename) {
pthread_mutex_lock(&(cm->config_file_mutex));
FILE *configfile = fopen(filename, "w+");
char *configvalue = cJSON_Print(cm->config);
fprintf(configfile, "%s", configvalue);
fclose(configfile);
free(configvalue);
pthread_mutex_unlock(&(cm->config_file_mutex));
}
+39
View File
@@ -0,0 +1,39 @@
#pragma once
#include <cJSON.h>
typedef struct ConfigManager ConfigManager;
ConfigManager *cm_new_config();
ConfigManager *cm_new_config_from_file(const char *filename);
ConfigManager *cm_new_config_from_object(cJSON *config);
void cm_set_config_from_file(ConfigManager *cm, const char *filename);
void cm_set_config_from_object(ConfigManager *cm, cJSON *config);
void cm_set_string_parameter(ConfigManager *cm, const char *name,
const char *value);
void cm_set_int_parameter(ConfigManager *cm, const char *name, const int value);
void cm_set_double_parameter(ConfigManager *cm, const char *name,
const double value);
void cm_set_float_parameter(ConfigManager *cm, const char *name,
const float value);
void cm_set_bool_parameter(ConfigManager *cm, const char *name,
const bool value);
void cm_set_null_parameter(ConfigManager *cm, const char *name);
void cm_print_config(const ConfigManager *cm);
void cm_delete_config(ConfigManager *cm);
char *cm_get_config_filename(ConfigManager *cm);
cJSON *cm_get_config(ConfigManager *cm);
char *cm_get_parameter_as_string(ConfigManager *cm, const char *param_name);
int cm_get_parameter_as_int(ConfigManager *cm, const char *param_name);
double cm_get_parameter_as_double(ConfigManager *cm, const char *param_name);
float cm_get_parameter_as_float(ConfigManager *cm, const char *param_name);
bool cm_get_parameter_as_bool(ConfigManager *cm, const char *param_name);
void cm_save_config(ConfigManager *cm);
void cm_save_config_to_file(ConfigManager *cm, const char *filename);
+42 -3
View File
@@ -6,12 +6,51 @@ void add_user(struct mg_connection *c, struct mg_http_message *hm) {
request_with_token *requestdata = check_body(hm);
const bool valid = jwt_verifyJWT(requestdata->token, JWT_DEFAULT_SECRET);
if (!valid) {
mg_http_reply(c, 403, "", "{%m:%m}", MG_ESC("error"),
mg_http_reply(c, 403, HTTP_HEADERS, "{%m:%m}", MG_ESC("error"),
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, HTTP_HEADERS, "{%m:%m}", MG_ESC("error"),
MG_ESC("must have <username>, <password>, <email>, <group, "
"optional>, <is_superuser, optional>"));
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, HTTP_HEADERS, "{%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, HTTP_HEADERS, "{%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);
mg_http_reply(c, 200, HTTP_HEADERS, responsedata);
cJSON_Delete(payload);
}
+71 -2
View File
@@ -1,17 +1,86 @@
#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, "", "{%m:%m}", MG_ESC("error"),
mg_http_reply(c, 403, HTTP_HEADERS, "{%m:%m}", MG_ESC("error"),
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");
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_ESC("must have <old_password>, <new_password>"));
return;
}
cJSON *payload = jwt_get_payload(requestdata->token);
if (!userid) {
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);
return;
}
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;
}
char responsedata[64] = {0};
snprintf(responsedata, sizeof(responsedata), "{\"success\": true}");
mg_http_reply(c, 200, HTTP_HEADERS, responsedata);
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"));
cJSON_Delete(payload);
return;
}
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;
}
char responsedata[64] = {0};
snprintf(responsedata, sizeof(responsedata), "{\"success\": true}");
mg_http_reply(c, 200, "Content-Type: application/json\r\n", responsedata);
mg_http_reply(c, 200, HTTP_HEADERS, responsedata);
cJSON_Delete(payload);
}
+103 -4
View File
@@ -3,15 +3,114 @@
#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, "", "{%m:%m}", MG_ESC("error"),
mg_http_reply(c, 403, HTTP_HEADERS, "{%m:%m}", MG_ESC("error"),
MG_ESC("invalid token"));
return;
}
char responsedata[2048] = {0};
snprintf(responsedata, sizeof(responsedata), "{\"success\": true}");
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");
cJSON *userid = cJSON_GetObjectItem(jsonbody, "userid");
User *old_user_data = nullptr;
if (!userid) {
old_user_data = db_get_user_by_username(username->valuestring);
} else {
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"));
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"));
return;
}
if (username == nullptr || email == nullptr || is_superuser == nullptr ||
group == nullptr) {
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);
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;
if (!userid) {
User *updated_user_data =
db_update_user(id, username->valuestring, email->valuestring, password,
group->valuestring, &user_is_superuser);
mg_http_reply(c, 200, "Content-Type: application/json\r\n", responsedata);
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"));
cJSON_Delete(payload);
free_user(old_user_data);
return;
}
char responsedata[2048] = {0};
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, HTTP_HEADERS, responsedata);
free_user(updated_user_data);
free_user(old_user_data);
cJSON_Delete(payload);
return;
}
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"));
cJSON_Delete(payload);
free_user(old_user_data);
return;
}
User *updated_user_data = db_update_user(
userid->valueint, username->valuestring, email->valuestring, password,
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"));
cJSON_Delete(payload);
free_user(old_user_data);
return;
}
char responsedata[2048] = {0};
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, HTTP_HEADERS, responsedata);
free_user(updated_user_data);
free_user(old_user_data);
cJSON_Delete(payload);
}
+7
View File
@@ -0,0 +1,7 @@
#include "config.h"
ConfigManager *config = nullptr;
void init_config(const char *filename) {
config = cm_new_config_from_file(filename);
}
+7
View File
@@ -0,0 +1,7 @@
#pragma once
#include "ConfigManager.h"
extern ConfigManager *config;
void init_config(const char *filename);
+5
View File
@@ -0,0 +1,5 @@
#include "cors.h"
void set_cors_headers(struct mg_connection *c, struct mg_http_message *hm) {
mg_http_reply(c, 200, "Access-Control-Allow-Origin: *\r\nAccess-Control-Allow-Methods: GET,POST,OPTIONS,PUT,DELETE\r\nAccess-Control-Allow-Headers: Authorization,X-Custom,Content-Type", "");
}
+5
View File
@@ -0,0 +1,5 @@
#pragma once
#include <mongoose.h>
void set_cors_headers(struct mg_connection *c, struct mg_http_message *hm);
+2 -1
View File
@@ -3,12 +3,13 @@
#include <stdlib.h>
#include "sql.h"
#include <string.h>
#include "config.h"
sqlite3* db = nullptr;
pthread_mutex_t db_mutex = PTHREAD_MUTEX_INITIALIZER;
void init_db() {
if (sqlite3_open("users.db", &db) != SQLITE_OK) {
if (sqlite3_open(cm_get_parameter_as_string(config, "database"), &db) != SQLITE_OK) {
fprintf(stderr, "Cannot open database: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
exit(1);
+45
View File
@@ -0,0 +1,45 @@
#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);
pthread_mutex_unlock(&db_mutex);
return false;
}
pthread_mutex_unlock(&db_mutex);
return true;
}
+44
View File
@@ -0,0 +1,44 @@
#include "database.h"
#include "sql.h"
#include "user.h"
User *db_get_user_by_id(const int id) {
User *user = nullptr;
pthread_mutex_lock(&db_mutex);
if (!db) {
fprintf(stderr, "Database not initialized\n");
pthread_mutex_unlock(&db_mutex);
return nullptr;
}
sqlite3_stmt *stmt;
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));
pthread_mutex_unlock(&db_mutex);
return nullptr;
}
sqlite3_bind_int(stmt, 1, id);
while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
int id = sqlite3_column_int(stmt, 0);
const unsigned char *db_username = sqlite3_column_text(stmt, 1);
const unsigned char *db_email = sqlite3_column_text(stmt, 2);
const unsigned char *db_password = sqlite3_column_text(stmt, 3);
const unsigned char *db_group = sqlite3_column_text(stmt, 4);
int db_is_superuser = sqlite3_column_int(stmt, 5);
user = new_user(id, (char *) db_username, (char *) db_password,
(char *) db_email, (char *) db_group, db_is_superuser);
}
if (rc != SQLITE_DONE) {
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;
}
+6 -27
View File
@@ -1,14 +1,11 @@
#include <cJSON.h>
#include <sqlite3.h>
#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);
}
+63
View File
@@ -0,0 +1,63 @@
#include "database.h"
#include "sql.h"
#include "user.h"
User *db_insert_user(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) {
fprintf(stderr, "username, email, password connot be empty\n");
pthread_mutex_unlock(&db_mutex);
return nullptr;
}
const char *sql = nullptr;
sqlite3_stmt *stmt;
int rc;
if (!group || !is_superuser) {
sql = INSERT_USER_USERNAME_EMAIL_PASSWORD_SQL;
} else if (!group) {
sql = INSERT_USER_USERNAME_EMAIL_PASSWORD_GROUP_SQL;
} else {
sql = INSERT_USER_FULL_SQL;
}
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;
}
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);
if (group) {
sqlite3_bind_text(stmt, 4, group, -1, SQLITE_STATIC);
}
if (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;
}
int inserted_id = sqlite3_last_insert_rowid(db);
sqlite3_finalize(stmt);
printf("User inserted successfully. ID: %d\n", inserted_id);
pthread_mutex_unlock(&db_mutex);
User *new_db_user =
new_user(inserted_id, username, email, password, group, is_superuser);
return new_db_user;
}
+55
View File
@@ -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);
}
+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;
}
+44 -8
View File
@@ -3,15 +3,51 @@
#include "utils.h"
void delete_user(struct mg_connection *c, struct mg_http_message *hm) {
request_with_token *requestdata = check_body(hm);
const bool valid = jwt_verifyJWT(requestdata->token, JWT_DEFAULT_SECRET);
if (!valid) {
mg_http_reply(c, 403, "", "{%m:%m}", MG_ESC("error"),
MG_ESC("invalid token"));
if (hm->body.len == 0) {
mg_http_reply(c, 400, HTTP_HEADERS, "{%m:%m}",
MG_ESC("error"), MG_ESC("must have token"));
return;
}
char responsedata[2048] = {0};
snprintf(responsedata, sizeof(responsedata), "{\"success\": true}");
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"));
free_request_with_token(requestdata);
return;
}
cJSON *payload = jwt_get_payload(requestdata->token);
int id = cJSON_GetObjectItem(payload, "id")->valueint;
cJSON *useridfromjson = cJSON_GetObjectItem(requestdata->body, "userid");
cJSON_Print(useridfromjson);
if (useridfromjson == NULL) {
bool user_deleted = db_delete_user(id);
char responsedata[2048] = {0};
snprintf(responsedata, sizeof(responsedata), "{\"success\": %s}",
user_deleted ? "true" : "false");
mg_http_reply(c, 200, "Content-Type: application/json\r\n", responsedata);
mg_http_reply(c, 200, HTTP_HEADERS, responsedata);
free_request_with_token(requestdata);
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"));
return;
}
bool user_deleted = db_delete_user(useridfromjson->valueint);
char responsedata[2048] = {0};
snprintf(responsedata, sizeof(responsedata), "{\"success\": %s}",
user_deleted ? "true" : "false");
mg_http_reply(c, 200, HTTP_HEADERS, responsedata);
free_request_with_token(requestdata);
cJSON_Delete(payload);
}
+3 -3
View File
@@ -5,18 +5,18 @@
void get_all_users(struct mg_connection *c, struct mg_http_message *hm) {
request_with_token *data = check_body(hm);
if (data->error != NULL) {
mg_http_reply(c, 403, "", "{%m:%m}", MG_ESC("error"), MG_ESC(data->error));
mg_http_reply(c, 403, HTTP_HEADERS, "{%m:%m}", MG_ESC("error"), MG_ESC(data->error));
return;
}
if (!jwt_verifyJWT(data->token, JWT_DEFAULT_SECRET)) {
mg_http_reply(c, 403, "", "{%m:%m}", MG_ESC("error"),
mg_http_reply(c, 403, HTTP_HEADERS, "{%m:%m}", MG_ESC("error"),
MG_ESC("invalid token"));
return;
}
cJSON *payload = jwt_get_payload(data->token);
bool is_superuser = cJSON_GetObjectItem(payload, "is_superuser")->valueint;
if (!is_superuser) {
mg_http_reply(c, 403, "", "{%m:%m}", MG_ESC("error"),
mg_http_reply(c, 403, HTTP_HEADERS, "{%m:%m}", MG_ESC("error"),
MG_ESC("user is not superuser"));
return;
}
+6 -6
View File
@@ -3,7 +3,7 @@
void get_token(struct mg_connection *c, struct mg_http_message *hm) {
if (hm->body.len == 0) {
mg_http_reply(c, 400, "", "{%m:%m}", MG_ESC("error"),
mg_http_reply(c, 400, HTTP_HEADERS, "{%m:%m}", MG_ESC("error"),
MG_ESC("must have username and password"));
return;
}
@@ -13,21 +13,21 @@ void get_token(struct mg_connection *c, struct mg_http_message *hm) {
if (username == NULL || password == NULL) {
const char *error_ptr = cJSON_GetErrorPtr();
if (error_ptr != NULL) {
mg_http_reply(c, 400, "", "{%m:%m}", MG_ESC("error"), MG_ESC(error_ptr));
mg_http_reply(c, 400, HTTP_HEADERS, "{%m:%m}", MG_ESC("error"), MG_ESC(error_ptr));
return;
}
mg_http_reply(c, 400, "", "{%m:%m}", MG_ESC("error"),
mg_http_reply(c, 400, HTTP_HEADERS, "{%m:%m}", MG_ESC("error"),
MG_ESC("must have username and password"));
return;
}
User *db_user = db_get_user_by_username(username->valuestring);
if (!db_user) {
mg_http_reply(c, 400, "", "{%m:%m}", MG_ESC("error"),
mg_http_reply(c, 400, HTTP_HEADERS, "{%m:%m}", MG_ESC("error"),
MG_ESC("couldn't find user"));
return;
}
if (strcmp(db_user->password, password->valuestring)) {
mg_http_reply(c, 400, "", "{%m:%m}", MG_ESC("error"),
mg_http_reply(c, 400, HTTP_HEADERS, "{%m:%m}", MG_ESC("error"),
MG_ESC("wrong password"));
return;
}
@@ -41,7 +41,7 @@ void get_token(struct mg_connection *c, struct mg_http_message *hm) {
db_user->id, db_user->username, db_user->email, db_user->group,
db_user->is_superuser ? "true" : "false");
char *token = jwt_createJWT(header, payload, JWT_DEFAULT_SECRET);
mg_http_reply(c, 200, "", "{%m:%m}", MG_ESC("token"), MG_ESC(token));
mg_http_reply(c, 200, HTTP_HEADERS, "{%m:%m}", MG_ESC("token"), MG_ESC(token));
free_user(db_user);
free(token);
}
+12 -3
View File
@@ -3,10 +3,19 @@
#include "utils.h"
void get_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"));
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;
}
if (!jwt_verifyJWT(requestdata->token, JWT_DEFAULT_SECRET)) {
free_request_with_token(requestdata);
mg_http_reply(c, 403, "", "{%m:%m}", MG_ESC("error"),
mg_http_reply(c, 403, HTTP_HEADERS, "{%m:%m}", MG_ESC("error"),
MG_ESC("invalid token"));
return;
}
@@ -18,7 +27,7 @@ void get_user(struct mg_connection *c, struct mg_http_message *hm) {
free_request_with_token(requestdata);
cJSON_Delete(payload);
free(username);
mg_http_reply(c, 404, "", "{%m:%m}", MG_ESC("error"),
mg_http_reply(c, 404, HTTP_HEADERS, "{%m:%m}", MG_ESC("error"),
MG_ESC("user not found"));
return;
}
@@ -29,7 +38,7 @@ void get_user(struct mg_connection *c, struct mg_http_message *hm) {
user->id, user->username, user->email, user->group,
user->is_superuser ? "true" : "false");
mg_http_reply(c, 200, "Content-Type: application/json\r\n", responsedata);
mg_http_reply(c, 200, HTTP_HEADERS, responsedata);
free_request_with_token(requestdata);
free(username);
free_user(user);
+77
View File
@@ -0,0 +1,77 @@
#ifndef C_COLORS_H
#define C_COLORS_H
// reset
#define RESET "\033[0m"
// regular colrs
#define GREEN "\033[0;32m" // GREEN
#define BLUE "\033[0;34m" // BLUE
#define YELLOW "\033[1;33m" // YELLOW
#define RED "\033[0;31m" // RED
#define BLACK "\033[0;30m" // BLACK
#define PURPLE "\033[0;35m" // PURPLE
#define CYAN "\033[0;36m" // CYAN
#define WHITE "\033[0;37m" // WHITE
// bold
#define BBLACK "\033[1;30m" // BLACK
#define BRED "\033[1;31m" // RED
#define BGREEN "\033[1;32m" // GREEN
#define BYELLOW "\033[1;33m" // YELLOW
#define BBLUE "\033[1;34m" // BLUE
#define BPURPLE "\033[1;35m" // PURPLE
#define BCYAN "\033[1;36m" // CYAN
#define BWHITE "\033[1;37m" // WHITE
// Underline
#define UBLACK "\033[4;30m" // BLACK
#define URED "\033[4;31m" // RED
#define UGREEN "\033[4;32m" // GREEN
#define UYELLOW "\033[4;33m" // YELLOW
#define UBLUE "\033[4;34m" // BLUE
#define UPURPLE "\033[4;35m" // PURPLE
#define UCYAN "\033[4;36m" // CYAN
#define UWHITE "\033[4;37m" // WHITE
// Background
#define ON_BLACK "\033[40m" // BLACK
#define ON_RED "\033[41m" // RED
#define ON_GREEN "\033[42m" // GREEN
#define ON_YELLOW "\033[43m" // YELLOW
#define ON_BLUE "\033[44m" // BLUE
#define ON_PURPLE "\033[45m" // PURPLE
#define ON_CYAN "\033[46m" // CYAN
#define ON_WHITE "\033[47m" // WHITE
// High Intensity
#define IBLACK "\033[0;90m" // BLACK
#define IRED "\033[0;91m" // RED
#define IGREEN "\033[0;92m" // GREEN
#define IYELLOW "\033[0;93m" // YELLOW
#define IBLUE "\033[0;94m" // BLUE
#define IPURPLE "\033[0;95m" // PURPLE
#define ICYAN "\033[0;96m" // CYAN
#define IWHITE "\033[0;97m" // WHITE
// Bold High Intensity
#define BIBLACK "\033[1;90m" // BLACK
#define BIRED "\033[1;91m" // RED
#define BIGREEN "\033[1;92m" // GREEN
#define BIYELLOW "\033[1;93m" // YELLOW
#define BIBLUE "\033[1;94m" // BLUE
#define BIPURPLE "\033[1;95m" // PURPLE
#define BICYAN "\033[1;96m" // CYAN
#define BIWHITE "\033[1;97m" // WHITE
// High Intensity backgrounds
#define ON_IBLACK "\033[0;100m" // BLACK
#define ON_IRED "\033[0;101m" // RED
#define ON_IGREEN "\033[0;102m" // GREEN
#define ON_IYELLOW "\033[0;103m" // YELLOW
#define ON_IBLUE "\033[0;104m" // BLUE
#define ON_IPURPLE "\033[0;105m" // PURPLE
#define ON_ICYAN "\033[0;106m" // CYAN
#define ON_IWHITE "\033[0;107m" // WHITE
#endif // C_COLORS_H
+78 -11
View File
@@ -4739,7 +4739,7 @@ static size_t tx_tcp_pkt(struct mg_tcpip_if *ifp, struct pkt *pkt,
}
static struct mg_connection *accept_conn(struct mg_connection *lsn,
struct pkt *pkt) {
struct pkt *pkt, uint16_t mss) {
struct mg_connection *c = mg_alloc_conn(lsn->mgr);
struct connstate *s;
if (c == NULL) {
@@ -4747,7 +4747,7 @@ static struct mg_connection *accept_conn(struct mg_connection *lsn,
return NULL;
}
s = (struct connstate *) (c + 1);
s->dmss = 536; // assume default, RFC-9293 3.7.1
s->dmss = mss; // from options in client SYN
s->seq = mg_ntohl(pkt->tcp->ack), s->ack = mg_ntohl(pkt->tcp->seq);
memcpy(s->mac, pkt->eth->src, sizeof(s->mac));
settmout(c, MIP_TTYPE_KEEPALIVE);
@@ -4851,7 +4851,7 @@ static void read_conn(struct mg_connection *c, struct pkt *pkt) {
if (pkt->tcp->flags & TH_FIN) {
uint8_t flags = TH_ACK;
if (mg_ntohl(pkt->tcp->seq) != s->ack) {
MG_VERBOSE(("ignoring FIN, SEQ != ACK: %x %x", mg_ntohl(pkt->tcp->seq), s->ack));
MG_VERBOSE(("ignoring FIN, %x != %x", mg_ntohl(pkt->tcp->seq), s->ack));
tx_tcp(c->mgr->ifp, s->mac, rem_ip, TH_ACK, c->loc.port, c->rem.port,
mg_htonl(s->seq), mg_htonl(s->ack), "", 0);
return;
@@ -4934,6 +4934,57 @@ static void read_conn(struct mg_connection *c, struct pkt *pkt) {
}
}
// TCP backlog
struct mg_backlog {
uint16_t port, mss; // use port=0 for available entries
uint8_t age;
};
static int backlog_insert(struct mg_connection *c, uint16_t port,
uint16_t mss) {
struct mg_backlog *p = (struct mg_backlog *) c->data;
size_t i;
for (i = 0; i < sizeof(c->data) / sizeof(*p); i++) {
if (p[i].port != 0) continue;
p[i].age = 2; // remove after two calls, average 1.5 call rate
p[i].port = port, p[i].mss = mss;
return (int) i;
}
return -1;
}
static struct mg_backlog *backlog_retrieve(struct mg_connection *c,
uint16_t key, uint16_t port) {
struct mg_backlog *p = (struct mg_backlog *) c->data;
if (key >= sizeof(c->data) / sizeof(*p)) return NULL;
if (p[key].port != port) return NULL;
p += key;
return p;
}
static void backlog_remove(struct mg_connection *c, uint16_t key) {
struct mg_backlog *p = (struct mg_backlog *) c->data;
p[key].port = 0;
}
static void backlog_maintain(struct mg_connection *c) {
struct mg_backlog *p = (struct mg_backlog *) c->data;
size_t i; // dec age and remove those where it reaches 0
for (i = 0; i < sizeof(c->data) / sizeof(*p); i++) {
if (p[i].port == 0) continue;
if (p[i].age != 0) --p[i].age;
if (p[i].age == 0) p[i].port = 0;
}
}
static void backlog_poll(struct mg_mgr *mgr) {
struct mg_connection *c = NULL;
for (c = mgr->conns; c != NULL; c = c->next) {
if (!c->is_udp && c->is_listening) backlog_maintain(c);
}
}
// process options (MSS)
static void handle_opt(struct connstate *s, struct tcp *tcp) {
uint8_t *opts = (uint8_t *) (tcp + 1);
@@ -4991,15 +5042,30 @@ static void rx_tcp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
if (c->is_accepted) mg_error(c, "peer RST"); // RFC-1122 4.2.2.13
// ignore RST if not connected
} else if (pkt->tcp->flags & TH_SYN) {
// TODO(): handle_opt(s, pkt->tcp); // process options (MSS)
// At this point, s = NULL, there is no connection.
// Use peer's source port as ISN, in order to recognise the handshake
uint32_t isn = mg_htonl((uint32_t) mg_ntohs(pkt->tcp->sport));
tx_tcp_pkt(ifp, pkt, TH_SYN | TH_ACK, isn, NULL, 0);
struct connstate cs; // At this point, s = NULL, there is no connection
int key;
uint32_t isn;
if (pkt->tcp->sport != 0) {
handle_opt(&cs, pkt->tcp); // process options (MSS)
key = backlog_insert(c, pkt->tcp->sport, cs.dmss); // backlog options (MSS)
if (key < 0) return; // no room in backlog, discard SYN, client retries
// Use peer's src port and bl key as ISN, to later identify the handshake
isn = (mg_htonl(((uint32_t)key << 16) | mg_ntohs(pkt->tcp->sport)));
tx_tcp_pkt(ifp, pkt, TH_SYN | TH_ACK, isn, NULL, 0);
} // what should we do when port=0 ? Linux takes port 0 as any other port
} else if (pkt->tcp->flags & TH_FIN) {
tx_tcp_pkt(ifp, pkt, TH_FIN | TH_ACK, pkt->tcp->ack, NULL, 0);
} else if (mg_htonl(pkt->tcp->ack) == mg_htons(pkt->tcp->sport) + 1U) {
accept_conn(c, pkt);
} else if ((uint16_t) (mg_htonl(pkt->tcp->ack) - 1) ==
mg_htons(pkt->tcp->sport)) {
uint16_t key = (uint16_t) ((mg_htonl(pkt->tcp->ack) - 1) >> 16);
struct mg_backlog *b = backlog_retrieve(c, key, pkt->tcp->sport);
if (b != NULL) {
accept_conn(c, pkt, b->mss); // pass options
backlog_remove(c, key);
} else if (!c->is_accepted) { // not an actual match, reset
tx_tcp_pkt(ifp, pkt, TH_RST | TH_ACK, pkt->tcp->ack, NULL, 0);
// TODO(scaprile): revisit this and below, weird scenarios
}
} else if (!c->is_accepted) { // no peer
tx_tcp_pkt(ifp, pkt, TH_RST | TH_ACK, pkt->tcp->ack, NULL, 0);
} else {
@@ -5127,6 +5193,7 @@ static void mg_tcpip_poll(struct mg_tcpip_if *ifp, uint64_t now) {
names[ifp->state], mg_print_ip4, &ifp->ip, ifp->nrecv, ifp->nsent,
ifp->ndrop, ifp->nerr));
#endif
backlog_poll(ifp->mgr);
}
// Handle gw ARP request timeout, order is important
if (expired_1000ms && ifp->state == MG_TCPIP_STATE_IP) {
@@ -5460,7 +5527,7 @@ static void setdns4(struct mg_tcpip_if *ifp, uint32_t *ip) {
MG_DEBUG(("Set DNS URL to %s", dnsc->url));
if (ifp->mgr->use_dns6) return;
if (dnsc->c != NULL) mg_close_conn(dnsc->c);
if (!mg_dnsc_init(ifp->mgr, dnsc)) // create DNS connection
if (!mg_dnsc_init(ifp->mgr, dnsc)) // create DNS connection
MG_ERROR(("DNS connection creation failed"));
}
+36 -36
View File
@@ -20,7 +20,7 @@
#ifndef MONGOOSE_H
#define MONGOOSE_H
#define MG_VERSION "7.18"
#define MG_VERSION "7.19"
#ifdef __cplusplus
extern "C" {
@@ -3693,6 +3693,41 @@ struct mg_tcpip_driver_tms570_data {
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_XMC7) && MG_ENABLE_DRIVER_XMC7
struct mg_tcpip_driver_xmc7_data {
int mdc_cr; // Valid values: -1, 0, 1, 2, 3, 4, 5
uint8_t phy_addr;
};
#ifndef MG_TCPIP_PHY_ADDR
#define MG_TCPIP_PHY_ADDR 0
#endif
#ifndef MG_DRIVER_MDC_CR
#define MG_DRIVER_MDC_CR 3
#endif
#define MG_TCPIP_DRIVER_INIT(mgr) \
do { \
static struct mg_tcpip_driver_xmc7_data driver_data_; \
static struct mg_tcpip_if mif_; \
driver_data_.mdc_cr = MG_DRIVER_MDC_CR; \
driver_data_.phy_addr = MG_TCPIP_PHY_ADDR; \
mif_.ip = MG_TCPIP_IP; \
mif_.mask = MG_TCPIP_MASK; \
mif_.gw = MG_TCPIP_GW; \
mif_.driver = &mg_tcpip_driver_xmc7; \
mif_.driver_data = &driver_data_; \
MG_SET_MAC_ADDRESS(mif_.mac); \
mg_tcpip_init(mgr, &mif_); \
MG_INFO(("Driver: xmc7, MAC: %M", mg_print_mac, mif_.mac)); \
} while (0)
#endif
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_XMC) && MG_ENABLE_DRIVER_XMC
struct mg_tcpip_driver_xmc_data {
@@ -3739,41 +3774,6 @@ struct mg_tcpip_driver_xmc_data {
#endif
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_XMC7) && MG_ENABLE_DRIVER_XMC7
struct mg_tcpip_driver_xmc7_data {
int mdc_cr; // Valid values: -1, 0, 1, 2, 3, 4, 5
uint8_t phy_addr;
};
#ifndef MG_TCPIP_PHY_ADDR
#define MG_TCPIP_PHY_ADDR 0
#endif
#ifndef MG_DRIVER_MDC_CR
#define MG_DRIVER_MDC_CR 3
#endif
#define MG_TCPIP_DRIVER_INIT(mgr) \
do { \
static struct mg_tcpip_driver_xmc7_data driver_data_; \
static struct mg_tcpip_if mif_; \
driver_data_.mdc_cr = MG_DRIVER_MDC_CR; \
driver_data_.phy_addr = MG_TCPIP_PHY_ADDR; \
mif_.ip = MG_TCPIP_IP; \
mif_.mask = MG_TCPIP_MASK; \
mif_.gw = MG_TCPIP_GW; \
mif_.driver = &mg_tcpip_driver_xmc7; \
mif_.driver_data = &driver_data_; \
MG_SET_MAC_ADDRESS(mif_.mac); \
mg_tcpip_init(mgr, &mif_); \
MG_INFO(("Driver: xmc7, MAC: %M", mg_print_mac, mif_.mac)); \
} while (0)
#endif
#ifdef __cplusplus
}
#endif
+45 -12
View File
@@ -1,29 +1,44 @@
#include <colors.h>
#include <mongoose.h>
#include <sqlite3.h>
#include "config.h"
#include "cors.h"
#include "database.h"
#include "file.h"
#include "route.h"
#include "sql.h"
#include "static.h"
#include "user.h"
static void root_handler(struct mg_connection *c, struct mg_http_message *hm) {
struct mg_http_serve_opts opts = {.root_dir = "./web/"};
mg_http_serve_dir(c, hm, &opts);
}
static const struct route routes[] = {
{"GET", "/", root_handler},
{"GET", "/#", root_handler},
{"OPTIONS", "/#", set_cors_headers},
{"POST", "/api/token", get_token},
{"POST", "/api/token/verify", verify_token},
{"POST", "/api/users", get_all_users},
{"POST", "/api/users/user", get_user},
{"POST", "/api/users/delete", delete_user},
{"DELETE", "/api/users", delete_user},
{"POST", "/api/users/password", change_password},
{"POST", "/api/users/change", change_password},
{"PUT", "/api/users", change_user},
{"POST", "/api/users/add", add_user},
{"POST", "/api/register", register_user},
{nullptr, nullptr, nullptr}};
void ev_handler(struct mg_connection *c, int ev, void *ev_data) {
if (ev == MG_EV_ERROR) {
MG_LOG(MG_LL_ERROR, ("Ошибка соединения: %s", (char *) ev_data));
}
const bool tls_enabled = cm_get_parameter_as_bool(config, "tls_enabled");
if (tls_enabled) {
if (ev == MG_EV_ACCEPT && c->is_tls) {
struct mg_tls_opts opts;
memset(&opts, 0, sizeof(opts));
opts.cert =
mg_str(read_file(cm_get_parameter_as_string(config, "tls_cert")));
opts.key =
mg_str(read_file(cm_get_parameter_as_string(config, "tls_key")));
mg_tls_init(c, &opts);
}
}
if (ev == MG_EV_HTTP_MSG) {
const auto hm = (struct mg_http_message *) ev_data;
bool route_found = false;
@@ -31,6 +46,8 @@ void ev_handler(struct mg_connection *c, int ev, void *ev_data) {
for (int i = 0; routes[i].path != nullptr; i++) {
if (mg_match(hm->uri, mg_str(routes[i].path), nullptr) &&
mg_match(hm->method, mg_str(routes[i].method), nullptr)) {
mg_log("%s%.*s %s%.*s%s", GREEN, hm->uri.len, hm->uri.buf, BWHITE,
hm->method.len, hm->method.buf, RESET);
routes[i].handler(c, hm);
route_found = true;
break;
@@ -44,15 +61,31 @@ void ev_handler(struct mg_connection *c, int ev, void *ev_data) {
}
int main(void) {
init_config("config.json");
init_db();
migrate_db();
db_get_users_from_file("users.json");
const char *default_users_filename =
cm_get_parameter_as_string(config, "default_users_file");
if (default_users_filename) db_get_users_from_file("users.json");
struct mg_mgr mgr;
mg_log_set(MG_LL_DEBUG);
mg_mgr_init(&mgr);
mg_http_listen(&mgr, "http://0.0.0.0:8222", ev_handler, nullptr);
while (true) {
const char *address = cm_get_parameter_as_string(config, "address");
char http_address[64];
snprintf(http_address, sizeof(http_address), "http://%s", address);
address ? mg_http_listen(&mgr, http_address, ev_handler, nullptr)
: mg_http_listen(&mgr, "http://0.0.0.0:8222", ev_handler, nullptr);
if (cm_get_parameter_as_bool(config, "tls_enabled")) {
const char *tls_address = cm_get_parameter_as_string(config, "tls_address");
char https_address[64];
snprintf(https_address, sizeof(https_address), "https://%s", tls_address);
tls_address ? mg_http_listen(&mgr, https_address, ev_handler, nullptr)
: mg_http_listen(&mgr, "https://0.0.0.0:8223", ev_handler, nullptr);
}
for (;;) {
mg_mgr_poll(&mgr, 1000);
}
mg_mgr_free(&mgr);
return 0;
}
+25 -12
View File
@@ -2,9 +2,12 @@
void register_user(struct mg_connection *c, struct mg_http_message *hm) {
if (hm->body.len == 0) {
mg_http_reply(c, 400, "", "{%m:%m}", MG_ESC("error"),
MG_ESC("must have <username>, <password>, <email>, <group, "
"optional>, <is_superuser, optional>"));
mg_http_reply(
c, 400,
HTTP_HEADERS,
"{%m:%m}", MG_ESC("error"),
MG_ESC("must have <username>, <password>, <email>, <group, "
"optional>, <is_superuser, optional>"));
return;
}
cJSON *jsonbody = cJSON_Parse(hm->body.buf);
@@ -14,9 +17,12 @@ void register_user(struct mg_connection *c, struct mg_http_message *hm) {
cJSON *group = cJSON_GetObjectItem(jsonbody, "group");
cJSON *is_superuser = cJSON_GetObjectItem(jsonbody, "is_superuser");
if (username == nullptr || password == nullptr || email == nullptr) {
mg_http_reply(c, 400, "", "{%m:%m}", MG_ESC("error"),
MG_ESC("must have <username>, <password>, <email>, <group, "
"optional>, <is_superuser, optional>"));
mg_http_reply(
c, 400,
HTTP_HEADERS,
"{%m:%m}", MG_ESC("error"),
MG_ESC("must have <username>, <password>, <email>, <group, "
"optional>, <is_superuser, optional>"));
return;
}
if (group == nullptr) {
@@ -25,12 +31,15 @@ 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"));
mg_http_reply(
c, 500,
HTTP_HEADERS,
"{%m:%m}", MG_ESC("error"), MG_ESC("failed to create user"));
return;
}
char responsedata[2048] = {0};
@@ -40,5 +49,9 @@ void register_user(struct mg_connection *c, struct mg_http_message *hm) {
new_user_data->id, new_user_data->username, new_user_data->email,
new_user_data->group, new_user_data->is_superuser);
mg_http_reply(c, 200, "Content-Type: application/json\r\n", responsedata);
mg_http_reply(
c, 200,
HTTP_HEADERS,
responsedata);
free_user(new_user_data);
}
+23 -9
View File
@@ -1,13 +1,13 @@
#pragma once
#define CREATE_TABLE_USERS \
"create table if not exists users(" \
"id integer primary key autoincrement," \
"username text unique," \
"email char(50) unique," \
"password char(150) not null," \
"user_group char(50) default 'users'," \
"is_superuser boolean default false" \
#define CREATE_TABLE_USERS \
"create table if not exists users(" \
"id integer primary key autoincrement," \
"username text unique," \
"email char(50) unique," \
"password char(150) not null," \
"user_group char(50) default 'users'," \
"is_superuser boolean default false" \
");"
#define INSERT_USER_SQL \
@@ -16,4 +16,18 @@
#define SELECT_ALL_USERS_SQL "select * from users"
#define SELECT_USER_BY_USERNAME_SQL "select * from users where username = ?"
#define SELECT_USER_BY_ID_SQL "select * from users where id = ?"
#define INSERT_USER_USERNAME_EMAIL_PASSWORD_SQL \
"insert into users (username, email, password) values (?, ?, ?)"
#define INSERT_USER_USERNAME_EMAIL_PASSWORD_GROUP_SQL \
"insert into users (username, email, password, user_group) values (?, ?, " \
"?, ?)"
#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_FIELD_SQL "update users set %s = ? where id = ?"
#define SELECT_USER_FIELD_SQL "select %s from users where id = ?"
+7
View File
@@ -0,0 +1,7 @@
#include "static.h"
#include "config.h"
void root_handler(struct mg_connection *c, struct mg_http_message *hm) {
struct mg_http_serve_opts opts = {.root_dir = cm_get_parameter_as_string(config, "web")};
mg_http_serve_dir(c, hm, &opts);
}
+5
View File
@@ -0,0 +1,5 @@
#pragma once
#include <mongoose.h>
void root_handler(struct mg_connection *c, struct mg_http_message *hm);
+20 -6
View File
@@ -12,6 +12,9 @@ request_with_token *new_request_with_token(char *token, cJSON *body,
}
void free_request_with_token(request_with_token *data) {
if (!data) {
return;
}
if (data->body != nullptr) {
cJSON_Delete(data->body);
}
@@ -24,8 +27,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 +43,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);
}
+16 -2
View File
@@ -4,6 +4,9 @@
#include <mongoose.h>
#include <sqlite3.h>
#define HTTP_HEADERS \
"Content-Type: application/json\r\nAccess-Control-Allow-Origin: *\r\n"
typedef struct User {
int id;
char *username;
@@ -13,8 +16,8 @@ typedef struct User {
bool is_superuser;
} User;
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);
void free_user(User *user);
@@ -52,6 +55,17 @@ 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);
User *db_insert_user(const char *username, const char *email,
const char *password, const char *group,
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);
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);
+3 -3
View File
@@ -5,14 +5,14 @@
void verify_token(struct mg_connection *c, struct mg_http_message *hm) {
const request_with_token *data = check_body(hm);
if (data->error != nullptr) {
mg_http_reply(c, 403, "", "{%m:%m}", MG_ESC("error"), MG_ESC(data->error));
mg_http_reply(c, 403, HTTP_HEADERS, "{%m:%m}", MG_ESC("error"), MG_ESC(data->error));
return;
}
const bool valid = jwt_verifyJWT(data->token, JWT_DEFAULT_SECRET);
if (!valid) {
mg_http_reply(c, 403, "", "{%m:%m}", MG_ESC("error"),
mg_http_reply(c, 403, HTTP_HEADERS, "{%m:%m}", MG_ESC("error"),
MG_ESC("invalid token"));
return;
}
mg_http_reply(c, 200, "", "{%m:%m}", MG_ESC("valid"), MG_ESC("true"));
mg_http_reply(c, 200, HTTP_HEADERS, "{%m:true}", MG_ESC("valid"));
}
Executable
+723
View File
@@ -0,0 +1,723 @@
#!/bin/bash
RESET="\033[0m"
BWHITE="\033[1;37m"
BLUE="\033[0;34m"
GREEN="\033[0;32m"
RED="\033[0;31m"
YELLOW="\033[1;33m"
CYAN="\033[0;36m"
./auth_service &> auth_service.log &
sleep 0.1
success=0
errors=0
# test POST /api/token
printf "==> ${BLUE}Testing${CYAN} POST${BWHITE} /api/token${RESET} "
curl \
--silent \
-X POST \
-d '{"username":"tola","password":"e0c7bfa5b8e4ae0cecfc1f14093a71e05c27abd93a3850fc35f008c3baf866b4"}' \
localhost:8222/api/token &> api_token_test.log
sleep 0.1
res=$(cat api_token_test.log)
if [[ "$res" == *"token"* ]]; 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/token with wrong password
printf "==> ${BLUE}Testing${CYAN} POST${BWHITE} /api/token with wrong password${RESET} "
curl \
--silent \
-X POST \
-d '{"username":"tola","password":"288"}' \
localhost:8222/api/token &> api_token_wrong_password_test.log
sleep 0.1
res=$(cat api_token_wrong_password_test.log)
if [[ "$res" == *"{\"error\":\"wrong 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/token with wrong username
printf "==> ${BLUE}Testing${CYAN} POST${BWHITE} /api/token with wrong username${RESET} "
curl \
--silent \
-X POST \
-d '{"username":"toa","password":"2808"}' \
localhost:8222/api/token &> api_token_wrong_username_test.log
sleep 0.1
res=$(cat api_token_wrong_username_test.log)
if [[ "$res" == *"{\"error\":\"couldn't find user\"}"* ]]; 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/token without post data
printf "==> ${BLUE}Testing${CYAN} POST${BWHITE} /api/token without post data${RESET} "
curl \
--silent \
-X POST \
-d '' \
localhost:8222/api/token &> api_token_no_data_test.log
sleep 0.1
res=$(cat api_token_no_data_test.log)
if [[ "$res" == *"{\"error\":\"must have username and 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/token with not valid json
printf "==> ${BLUE}Testing${CYAN} POST${BWHITE} /api/token with not valid json${RESET} "
curl \
--silent \
-X POST \
-d '{{asdf}}}asdfasdfasdf""asdf' \
localhost:8222/api/token &> api_token_not_valid_json_test.log
sleep 0.1
res=$(cat api_token_not_valid_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/token/verify
printf "==> ${BLUE}Testing${CYAN} POST${BWHITE} /api/token/verify${RESET} "
curl \
--silent \
-X POST \
-d $(cat api_token_test.log) \
localhost:8222/api/token/verify &> api_token_verify_test.log
sleep 0.1
res=$(cat api_token_verify_test.log)
if [[ "$res" == *'{"valid":true}'* ]]; 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/token/verify without post data
printf "==> ${BLUE}Testing${CYAN} POST${BWHITE} /api/token/verify without post data${RESET} "
curl \
--silent \
-X POST \
-d '' \
localhost:8222/api/token/verify &> api_token_verify_no_data_test.log
sleep 0.1
res=$(cat api_token_verify_no_data_test.log)
if [[ "$res" == *'{"error":"must have token"}'* ]]; 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/token/verify with wrong token
printf "==> ${BLUE}Testing${CYAN} POST${BWHITE} /api/token/verify with wrong token${RESET} "
curl \
--silent \
-X POST \
-d '{"token":"asdf"}' \
localhost:8222/api/token/verify &> api_token_verify_wrong_token_test.log
sleep 0.1
res=$(cat api_token_verify_wrong_token_test.log)
if [[ "$res" == *'{"error":"invalid token"}'* ]]; 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/token/verify with wrong json
printf "==> ${BLUE}Testing${CYAN} POST${BWHITE} /api/token/verify with wrong json${RESET} "
curl \
--silent \
-X POST \
-d 'asdf[apsdfqopwjf[9qfm93qq89n]]' \
localhost:8222/api/token/verify &> api_token_verify_wrong_json_test.log
sleep 0.1
res=$(cat api_token_verify_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/users
printf "==> ${BLUE}Testing${CYAN} POST${BWHITE} /api/users${RESET} "
curl \
--silent \
-X POST \
-d $(cat api_token_test.log) \
localhost:8222/api/users &> api_users_test.log
sleep 0.1
res=$(cat api_users_test.log)
if [[ "$res" == *'username'* ]]; 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 with wrong token
printf "==> ${BLUE}Testing${CYAN} POST${BWHITE} /api/users with wrong token${RESET} "
curl \
--silent \
-X POST \
-d '{"token":"asdf"}' \
localhost:8222/api/users &> api_users_wrong_token_test.log
sleep 0.1
res=$(cat api_users_wrong_token_test.log)
if [[ "$res" == *'{"error":"invalid token"}'* ]]; 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 without post data
printf "==> ${BLUE}Testing${CYAN} POST${BWHITE} /api/users without post data${RESET} "
curl \
--silent \
-X POST \
-d '' \
localhost:8222/api/users &> api_users_no_data_test.log
sleep 0.1
res=$(cat api_users_no_data_test.log)
if [[ "$res" == *'{"error":"must have token"}'* ]]; 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 with wrong json
printf "==> ${BLUE}Testing${CYAN} POST${BWHITE} /api/users with wrong json${RESET} "
curl \
--silent \
-X POST \
-d 'ahsdlfhjalighafina[a[][apofj OFFW[Efflja]]]' \
localhost:8222/api/users &> api_users_wrong_json_test.log
sleep 0.1
res=$(cat 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/users/user
printf "==> ${BLUE}Testing${CYAN} POST${BWHITE} /api/users/user${RESET} "
curl \
--silent \
-X POST \
-d $(cat api_token_test.log) \
localhost:8222/api/users/user &> api_users_user_test.log
sleep 0.1
res=$(cat api_users_user_test.log)
if [[ "$res" == *'username'* ]]; 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/user with wrong token
printf "==> ${BLUE}Testing${CYAN} POST${BWHITE} /api/users/user with wrong token${RESET} "
curl \
--silent \
-X POST \
-d '{"token":"asdf"}' \
localhost:8222/api/users/user &> api_users_user_wrong_token_test.log
sleep 0.1
res=$(cat api_users_user_wrong_token_test.log)
if [[ "$res" == *'{"error":"invalid token"}'* ]]; 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/user without post data
printf "==> ${BLUE}Testing${CYAN} POST${BWHITE} /api/users/user without post data${RESET} "
curl \
--silent \
-X POST \
-d '' \
localhost:8222/api/users/user &> api_users_user_no_data_test.log
sleep 0.1
res=$(cat api_users_user_no_data_test.log)
if [[ "$res" == *'{"error":"must have token"}'* ]]; 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/user with wrong json
printf "==> ${BLUE}Testing${CYAN} POST${BWHITE} /api/users/user with wrong json${RESET} "
curl \
--silent \
-X POST \
-d 'alskodjfqjvoq3vn[q90\audv0q j89fvn 2vp]' \
localhost:8222/api/users/user &> api_users_user_wrong_json_test.log
sleep 0.1
res=$(cat api_users_user_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/users/add
printf "==> ${BLUE}Testing${CYAN} POST${BWHITE} /api/users/add${RESET} "
curl \
--silent \
-X POST \
-d '{
"username":"asdff",
"email":"asdf@asdff.com",
"password":"asdf",
"group":"users",
"is_superuser":false,
"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IDEsInVzZXJuYW1lIjogInRvbGEiLCJlbWFpbCI6ICJtYWlsQGV4YW1wbGUuY29tIiwiZ3JvdXAiOiAicm9vdCIsImlzX3N1cGVydXNlciI6IHRydWV9.QGUnCWPmp5hWAtGFTGDMw2SugZqLsYfMboeFU4UQLXQ"
}' \
localhost:8222/api/users/add &> api_users_add_test.log
sleep 0.1
res=$(cat api_users_add_test.log)
if [[ "$res" == *'username'* ]]; 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 DELETE /api/users
printf "==> ${BLUE}Testing${RED} DELETE${BWHITE} /api/users${RESET} "
reqdata=$(curl \
--silent \
-X POST \
-d '{"username":"asdff","password":"asdf"}' \
localhost:8222/api/token)
sleep 0.1
curl \
--silent \
-X DELETE \
-d $reqdata \
localhost:8222/api/users &> delete_api_users_test.log
sleep 0.1
res=$(cat delete_api_users_test.log)
if [[ "$res" == *'{"success": true}'* ]]; then
printf "[$GREEN OK $RESET]\n"
success=$(($success+1))
else
printf "[$RED ERR $RESET]\n==> ${BLUE}Response: $RESET$(echo $res)\n"
errors=$(($errors+1))
cat auth_service.log
fi
# test DELETE /api/users second delete
printf "==> ${BLUE}Testing${RED} DELETE${BWHITE} /api/users second delete${RESET} "
curl \
--silent \
-X DELETE \
-d $reqdata \
localhost:8222/api/users &> delete_api_users_second_delete_test.log
sleep 0.1
res=$(cat delete_api_users_second_delete_test.log)
if [[ "$res" == *'{"success": false}'* ]]; 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 DELETE /api/users with wrong json
printf "==> ${BLUE}Testing${RED} DELETE${BWHITE} /api/users with wrong json${RESET} "
curl \
--silent \
-X DELETE \
-d '$reqdata' \
localhost:8222/api/users &> delete_api_users_wrong_json_test.log
sleep 0.1
res=$(cat delete_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 DELETE /api/users with wrong token
printf "==> ${BLUE}Testing${RED} DELETE${BWHITE} /api/users with wrong token${RESET} "
curl \
--silent \
-X DELETE \
-d '{"token":"asdf"}' \
localhost:8222/api/users &> delete_api_users_wrong_token_test.log
sleep 0.1
res=$(cat delete_api_users_wrong_token_test.log)
if [[ "$res" == *'{"error":"invalid token"}'* ]]; 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 DELETE /api/users without post data
printf "==> ${BLUE}Testing${RED} DELETE${BWHITE} /api/users without post data${RESET} "
curl \
--silent \
-X DELETE \
-d '' \
localhost:8222/api/users &> delete_api_users_no_data_test.log
sleep 0.1
res=$(cat delete_api_users_no_data_test.log)
if [[ "$res" == *'{"error":"must have token"}'* ]]; 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
printf "==> ${BLUE}Testing${CYAN} POST${BWHITE} /api/users/password${RESET} "
reqtoken=$(cat api_token_test.log | sed -e "s/{//g" | sed -e "s/}//g")
curl \
--silent \
-X POST \
-d "{\"old_password\":\"e0c7bfa5b8e4ae0cecfc1f14093a71e05c27abd93a3850fc35f008c3baf866b4\",\"new_password\":\"1234\",$reqtoken}" \
localhost:8222/api/users/password &> api_users_password_test.log
sleep 0.1
res=$(cat api_users_password_test.log)
if [[ "$res" == *'{"success": true}'* ]]; 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 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")
curl \
--silent \
-X PUT \
-d "{
\"username\":\"tola\",
\"email\":\"tola@tola.tola\",
\"group\":\"admin\",
\"is_superuser\":true,
$reqtoken}" \
localhost:8222/api/users &> put_api_users_test.log
sleep 0.1
res=$(cat put_api_users_test.log)
if [[ "$res" == *'username'* ]]; 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 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 \
--silent \
-X POST \
-d '{
"username":"asdf",
"email":"asdf@asdf.com",
"password":"asdf",
"group":"users",
"is_superuser":false
}' \
localhost:8222/api/register &> api_register_test.log
sleep 0.1
res=$(cat api_register_test.log)
if [[ "$res" == *'username'* ]]; 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 without username
printf "==> ${BLUE}Testing${CYAN} POST${BWHITE} /api/register without username${RESET} "
curl \
--silent \
-X POST \
-d '{
"email":"asdf@asdf.com",
"password":"asdf",
"group":"users",
"is_superuser":false
}' \
localhost:8222/api/register &> api_register_without_username_test.log
sleep 0.1
res=$(cat api_register_without_username_test.log)
if [[ "$res" == *'{"error":"must have <username>, <password>, <email>, <group, optional>, <is_superuser, optional>"}'* ]]; 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 without email
printf "==> ${BLUE}Testing${CYAN} POST${BWHITE} /api/register without email${RESET} "
curl \
--silent \
-X POST \
-d '{
"username":"asdf",
"password":"asdf",
"group":"users",
"is_superuser":false
}' \
localhost:8222/api/register &> api_register_without_email_test.log
sleep 0.1
res=$(cat api_register_without_email_test.log)
if [[ "$res" == *'{"error":"must have <username>, <password>, <email>, <group, optional>, <is_superuser, optional>"}'* ]]; 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 without password
printf "==> ${BLUE}Testing${CYAN} POST${BWHITE} /api/register without password${RESET} "
curl \
--silent \
-X POST \
-d '{
"username":"asdf",
"email":"asdf@asdf.com",
"group":"users",
"is_superuser":false
}' \
localhost:8222/api/register &> api_register_without_password_test.log
sleep 0.1
res=$(cat api_register_without_password_test.log)
if [[ "$res" == *'{"error":"must have <username>, <password>, <email>, <group, optional>, <is_superuser, optional>"}'* ]]; 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 without group
printf "==> ${BLUE}Testing${CYAN} POST${BWHITE} /api/register without group${RESET} "
curl \
--silent \
-X POST \
-d '{
"username":"qwer",
"email":"qwer@qwer.com",
"password":"qwer",
"is_superuser":false
}' \
localhost:8222/api/register &> api_register_without_group_test.log
sleep 0.1
res=$(cat api_register_without_group_test.log)
if [[ "$res" == *'username'* ]]; 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 without is_superuser
printf "==> ${BLUE}Testing${CYAN} POST${BWHITE} /api/register without is_superuser${RESET} "
curl \
--silent \
-X POST \
-d '{
"username":"zxcv",
"email":"zxcv@zxcv.com",
"password":"zxcv",
"group":"users"
}' \
localhost:8222/api/register &> api_register_without_is_superuser_test.log
sleep 0.1
res=$(cat api_register_without_is_superuser_test.log)
if [[ "$res" == *'username'* ]]; 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 second register
printf "==> ${BLUE}Testing${CYAN} POST${BWHITE} /api/register second register${RESET} "
curl \
--silent \
-X POST \
-d '{
"username":"zxcv",
"email":"zxcv@zxcv.com",
"password":"zxcv",
"group":"users"
}' \
localhost:8222/api/register &> api_register_second_register_test.log
sleep 0.1
res=$(cat api_register_second_register_test.log)
if [[ "$res" == *'{"error":"failed to create user"}'* ]]; 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 with wrong json
printf "==> ${BLUE}Testing${CYAN} POST${BWHITE} /api/register with wrong json${RESET} "
curl \
--silent \
-X POST \
-d 'najsnvpnvq234890vn9qq234vqui3v' \
localhost:8222/api/register &> api_register_wrong_json_test.log
sleep 0.1
res=$(cat api_register_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 without post data
printf "==> ${BLUE}Testing${CYAN} POST${BWHITE} /api/register without post data${RESET} "
curl \
--silent \
-X POST \
-d '' \
localhost:8222/api/register &> api_register_no_data_test.log
sleep 0.1
res=$(cat api_register_no_data_test.log)
if [[ "$res" == *'{"error":"must have <username>, <password>, <email>, <group, optional>, <is_superuser, optional>"}'* ]]; 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
killall auth_service
printf "==> ${GREEN}Success: ${RESET}${success}\n==>${RED} Errors: ${RESET}${errors}\n"
if [ ! $errors -eq 0 ]; then
exit 1
fi
+2 -2
View File
@@ -2,14 +2,14 @@
{
"username": "tola",
"email": "mail@example.com",
"password": "2808",
"password": "e0c7bfa5b8e4ae0cecfc1f14093a71e05c27abd93a3850fc35f008c3baf866b4",
"group": "root",
"is_superuser": true
},
{
"username": "lisa",
"email": "lisa@example.com",
"password": "asdf",
"password": "f0e4c2f76c58916ec258f246851bea091d14d4247a2fc3e18694461b1816e13b",
"group": "users",
"is_superuser": false
}
+6
View File
@@ -0,0 +1,6 @@
@charset "UTF-8";/*!
* Bootstrap v5.3.5 (https://getbootstrap.com/)
* Copyright 2011-2025 The Bootstrap Authors
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/:root,[data-bs-theme=light]{--bs-blue:#0d6efd;--bs-indigo:#6610f2;--bs-purple:#6f42c1;--bs-pink:#d63384;--bs-red:#dc3545;--bs-orange:#fd7e14;--bs-yellow:#ffc107;--bs-green:#198754;--bs-teal:#20c997;--bs-cyan:#0dcaf0;--bs-black:#000;--bs-white:#fff;--bs-gray:#6c757d;--bs-gray-dark:#343a40;--bs-gray-100:#f8f9fa;--bs-gray-200:#e9ecef;--bs-gray-300:#dee2e6;--bs-gray-400:#ced4da;--bs-gray-500:#adb5bd;--bs-gray-600:#6c757d;--bs-gray-700:#495057;--bs-gray-800:#343a40;--bs-gray-900:#212529;--bs-primary:#0d6efd;--bs-secondary:#6c757d;--bs-success:#198754;--bs-info:#0dcaf0;--bs-warning:#ffc107;--bs-danger:#dc3545;--bs-light:#f8f9fa;--bs-dark:#212529;--bs-primary-rgb:13,110,253;--bs-secondary-rgb:108,117,125;--bs-success-rgb:25,135,84;--bs-info-rgb:13,202,240;--bs-warning-rgb:255,193,7;--bs-danger-rgb:220,53,69;--bs-light-rgb:248,249,250;--bs-dark-rgb:33,37,41;--bs-primary-text-emphasis:#052c65;--bs-secondary-text-emphasis:#2b2f32;--bs-success-text-emphasis:#0a3622;--bs-info-text-emphasis:#055160;--bs-warning-text-emphasis:#664d03;--bs-danger-text-emphasis:#58151c;--bs-light-text-emphasis:#495057;--bs-dark-text-emphasis:#495057;--bs-primary-bg-subtle:#cfe2ff;--bs-secondary-bg-subtle:#e2e3e5;--bs-success-bg-subtle:#d1e7dd;--bs-info-bg-subtle:#cff4fc;--bs-warning-bg-subtle:#fff3cd;--bs-danger-bg-subtle:#f8d7da;--bs-light-bg-subtle:#fcfcfd;--bs-dark-bg-subtle:#ced4da;--bs-primary-border-subtle:#9ec5fe;--bs-secondary-border-subtle:#c4c8cb;--bs-success-border-subtle:#a3cfbb;--bs-info-border-subtle:#9eeaf9;--bs-warning-border-subtle:#ffe69c;--bs-danger-border-subtle:#f1aeb5;--bs-light-border-subtle:#e9ecef;--bs-dark-border-subtle:#adb5bd;--bs-white-rgb:255,255,255;--bs-black-rgb:0,0,0;--bs-font-sans-serif:system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue","Noto Sans","Liberation Sans",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--bs-font-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--bs-gradient:linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));--bs-body-font-family:var(--bs-font-sans-serif);--bs-body-font-size:1rem;--bs-body-font-weight:400;--bs-body-line-height:1.5;--bs-body-color:#212529;--bs-body-color-rgb:33,37,41;--bs-body-bg:#fff;--bs-body-bg-rgb:255,255,255;--bs-emphasis-color:#000;--bs-emphasis-color-rgb:0,0,0;--bs-secondary-color:rgba(33, 37, 41, 0.75);--bs-secondary-color-rgb:33,37,41;--bs-secondary-bg:#e9ecef;--bs-secondary-bg-rgb:233,236,239;--bs-tertiary-color:rgba(33, 37, 41, 0.5);--bs-tertiary-color-rgb:33,37,41;--bs-tertiary-bg:#f8f9fa;--bs-tertiary-bg-rgb:248,249,250;--bs-heading-color:inherit;--bs-link-color:#0d6efd;--bs-link-color-rgb:13,110,253;--bs-link-decoration:underline;--bs-link-hover-color:#0a58ca;--bs-link-hover-color-rgb:10,88,202;--bs-code-color:#d63384;--bs-highlight-color:#212529;--bs-highlight-bg:#fff3cd;--bs-border-width:1px;--bs-border-style:solid;--bs-border-color:#dee2e6;--bs-border-color-translucent:rgba(0, 0, 0, 0.175);--bs-border-radius:0.375rem;--bs-border-radius-sm:0.25rem;--bs-border-radius-lg:0.5rem;--bs-border-radius-xl:1rem;--bs-border-radius-xxl:2rem;--bs-border-radius-2xl:var(--bs-border-radius-xxl);--bs-border-radius-pill:50rem;--bs-box-shadow:0 0.5rem 1rem rgba(0, 0, 0, 0.15);--bs-box-shadow-sm:0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);--bs-box-shadow-lg:0 1rem 3rem rgba(0, 0, 0, 0.175);--bs-box-shadow-inset:inset 0 1px 2px rgba(0, 0, 0, 0.075);--bs-focus-ring-width:0.25rem;--bs-focus-ring-opacity:0.25;--bs-focus-ring-color:rgba(13, 110, 253, 0.25);--bs-form-valid-color:#198754;--bs-form-valid-border-color:#198754;--bs-form-invalid-color:#dc3545;--bs-form-invalid-border-color:#dc3545}[data-bs-theme=dark]{color-scheme:dark;--bs-body-color:#dee2e6;--bs-body-color-rgb:222,226,230;--bs-body-bg:#212529;--bs-body-bg-rgb:33,37,41;--bs-emphasis-color:#fff;--bs-emphasis-color-rgb:255,255,255;--bs-secondary-color:rgba(222, 226, 230, 0.75);--bs-secondary-color-rgb:222,226,230;--bs-secondary-bg:#343a40;--bs-secondary-bg-rgb:52,58,64;--bs-tertiary-color:rgba(222, 226, 230, 0.5);--bs-tertiary-color-rgb:222,226,230;--bs-tertiary-bg:#2b3035;--bs-tertiary-bg-rgb:43,48,53;--bs-primary-text-emphasis:#6ea8fe;--bs-secondary-text-emphasis:#a7acb1;--bs-success-text-emphasis:#75b798;--bs-info-text-emphasis:#6edff6;--bs-warning-text-emphasis:#ffda6a;--bs-danger-text-emphasis:#ea868f;--bs-light-text-emphasis:#f8f9fa;--bs-dark-text-emphasis:#dee2e6;--bs-primary-bg-subtle:#031633;--bs-secondary-bg-subtle:#161719;--bs-success-bg-subtle:#051b11;--bs-info-bg-subtle:#032830;--bs-warning-bg-subtle:#332701;--bs-danger-bg-subtle:#2c0b0e;--bs-light-bg-subtle:#343a40;--bs-dark-bg-subtle:#1a1d20;--bs-primary-border-subtle:#084298;--bs-secondary-border-subtle:#41464b;--bs-success-border-subtle:#0f5132;--bs-info-border-subtle:#087990;--bs-warning-border-subtle:#997404;--bs-danger-border-subtle:#842029;--bs-light-border-subtle:#495057;--bs-dark-border-subtle:#343a40;--bs-heading-color:inherLine truncated
/*# sourceMappingURL=bootstrap.min.css.map */
+72
View File
@@ -0,0 +1,72 @@
.form {
position: absolute;
width: 100%;
height: 0;
top: 0;
left: 0;
display: grid;
justify-items: center;
align-items: center;
overflow: hidden;
pointer-events: none;
transition: 0.3s;
background-color: black;
z-index: 10;
}
.form.confirmform {
background-color: transparent;
}
.form.active {
height: 100%;
pointer-events: all;
}
.formbody {
padding: 10px;
background-color: gray;
backdrop-filter: blur(5px);
border-radius: 10px;
}
.loginformheader {
text-align: center;
}
.loginforminputbody {
padding: 10px;
}
.loginforminput {
padding: 10px;
width: 100%;
border-radius: 10px;
border: none;
transition: 0.3s;
}
.loginforminputbodyflex {
display: flex;
}
.loginforminputbodyflex > button {
margin: 10px;
}
.loginformbutton {
padding: 10px;
border: none;
border-radius: 10px;
position: relative;
width: 100%;
cursor: pointer;
background-color: rgb(59, 59, 197);
color: white;
transition: 0.3s;
margin: 5px 0;
}
.loginformbutton:hover {
background-color: rgb(151, 151, 241);
}
+55
View File
@@ -0,0 +1,55 @@
* {
transition: 0.3s;
}
.notificationsection {
width: max-content;
height: 100vh;
position: fixed;
top: 10px;
right: 0;
pointer-events: none;
z-index: 16;
}
.notification {
width: max-content;
height: 0;
overflow: hidden;
pointer-events: none;
z-index: 10;
border-radius: 10px;
top: 0;
right: 0;
cursor: pointer;
opacity: 0.8;
margin: 10px;
padding: 0 10px;
}
.notificationtextbox {
width: 100%;
height: 100%;
display: grid;
justify-items: center;
align-items: center;
pointer-events: none;
}
.notificationtext {
width: 100%;
text-align: center;
pointer-events: none;
}
.notification.active.error {
pointer-events: all;
height: 10%;
background-color: rgb(233, 70, 70);
}
.notification.active.success {
pointer-events: all;
height: 10%;
background-color: rgb(76, 209, 76);
}
+222 -2
View File
@@ -3,9 +3,229 @@
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>fmongoose</title>
<link rel="stylesheet" href="/css/loginform.css" />
<link rel="stylesheet" href="/css/message.css" />
<link rel="stylesheet" href="/css/bootstrap.min.css" />
<title>admin</title>
</head>
<body>
<h1>it's work</h1>
<div class="navbar navbar-expand-lg bg-body-tertiary gap-3 p-2">
<button class="btn btn-danger" id="exitbutton">Exit</button>
<button class="btn btn-warning" id="adduserbutton">Adduser</button>
<h1 class="usernameheader"></h1>
<div class="form-check">
<input
type="checkbox"
id="themecheckbox"
class="form-check-input"
/>
<label for="themecheckbox" class="form-check-label"
>Dark theme</label
>
</div>
</div>
<div class="form loginform" id="loginform">
<form class="formbody loginformbody" id="loginformbody">
<h1 class="loginformheader">Login</h1>
<div class="form-floating mb-3">
<input
type="text"
required
id="loginforminputusername"
class="form-control"
placeholder="John"
/>
<label for="loginforminputusername">Username</label>
</div>
<div class="form-floating mb-3">
<input
type="password"
required
id="loginforminputpassword"
class="form-control"
placeholder="***"
/>
<label for="loginforminputpassword">Password</label>
</div>
<div class="d-grid gap-2">
<button class="btn btn-primary">login</button>
</div>
</form>
</div>
<div class="form adduserform">
<form class="formbody adduserformbody">
<h1 class="loginformheader">Add User</h1>
<div class="form-floating mb-3">
<input
type="text"
required
id="adduserformusername"
class="form-control"
/>
<label for="adduserformusername">Username</label>
</div>
<div class="form-floating mb-3">
<input
type="text"
required
id="adduserformemail"
class="form-control"
/>
<label for="adduserformemail">Email</label>
</div>
<div class="form-floating mb-3">
<input
type="password"
required
id="adduserformpassword"
class="form-control"
/>
<label for="adduserformpassword">Password</label>
</div>
<div class="form-floating mb-3">
<input
type="text"
required
id="adduserformgroup"
class="form-control"
/>
<label for="adduserformgroup">Group</label>
</div>
<div class="form-check">
<input
type="checkbox"
id="adduserformissuperuser"
class="form-check-input"
/>
<label for="adduserformissuperuser" class="form-check-label"
>Is Superuser</label
>
</div>
<div class="d-grid gap-2">
<button class="btn btn-primary">Add</button>
<button id="canceladduser" class="btn btn-primary">
Cancel
</button>
</div>
</form>
</div>
<div class="form changeuserform">
<form class="formbody changeuserformbody">
<h1 class="loginformheader">Change User Data</h1>
<div class="form-floating mb-3">
<input
type="text"
required
id="changeuserformusername"
class="form-control"
placeholder="Username"
/>
<label for="changeuserformusername">Username</label>
</div>
<div class="form-floating mb-3">
<input
type="text"
id="changeuserformemail"
class="form-control"
placeholder="***"
/>
<label for="changeuserformemail">email</label>
</div>
<div class="form-floating mb-3">
<input
type="text"
required
id="changeuserformgroup"
class="form-control"
/>
<label for="changeuserformgroup">Group</label>
</div>
<div class="form-check">
<input
type="checkbox"
id="changeuserformissuperuser"
class="form-check-input"
/>
<label
for="changeuserformissuperuser"
class="form-check-label"
>Is Superuser</label
>
</div>
<div class="d-grid gap-2">
<button class="btn btn-primary">Change</button>
<button id="cancelchangeuser" class="btn btn-primary">
Cancel
</button>
</div>
</form>
</div>
<div class="form confirmform">
<div class="formbody confirmformbody">
<h1>Are you sure?</h1>
<div class="d-grid gap-2">
<button id="yes" class="btn btn-primary">Confirm</button>
<button id="no" class="btn btn-primary">Cancel</button>
</div>
</div>
</div>
<div class="form changepasswordform">
<form class="formbody changepasswordformbody">
<h1 class="loginformheader">Change Password</h1>
<div class="form-floating mb-3">
<input
type="text"
required
id="changepasswordnewpassword"
class="form-control"
placeholder="***"
/>
<label for="changepasswordnewpassword">New password</label>
</div>
<div class="d-grid gap-2">
<button class="btn btn-primary">Change</button>
<button class="btn btn-primary" id="cancelchangepassword">
Cancel
</button>
</div>
</form>
</div>
<div class="notificationsection"></div>
<div class="table">
<table class="table">
<thead class="alluserstablehead">
<tr>
<th>id</th>
<th>username</th>
<th>email</th>
<th>group</th>
<th>is_superuser</th>
<th>delete</th>
<th>change password</th>
<th>change user data</th>
</tr>
</thead>
<tbody id="alluserstablebody"></tbody>
</table>
</div>
<script src="/js/security.js"></script>
<script src="/js/message.js"></script>
<script src="/js/routes.js"></script>
<script src="/js/window.js"></script>
<script src="/js/theme.js"></script>
<script src="/js/login.js"></script>
<script src="/js/get_all_users.js"></script>
<script src="/js/add_user.js"></script>
<script src="/js/del_user.js"></script>
<script src="/js/change_password.js"></script>
<script src="/js/change_user.js"></script>
<script src="/js/bootstrap.min.js"></script>
</body>
</html>
+46
View File
@@ -0,0 +1,46 @@
const adduserbutton = document.querySelector('#adduserbutton')
const adduserform = document.querySelector('.adduserform')
const canceladduserbutton = document.querySelector('#canceladduser')
const adduserformbody = document.querySelector('.adduserformbody')
const usernameinput = document.querySelector('#adduserformusername')
const passwordinput = document.querySelector('#adduserformpassword')
const groupinput = document.querySelector('#adduserformgroup')
const is_superuserinput = document.querySelector('#adduserformissuperuser')
const emailinput = document.querySelector('#adduserformemail')
adduserbutton.addEventListener('click', () => {
adduserform.classList.add('active')
})
canceladduserbutton.addEventListener('click', (e) => {
e.preventDefault()
adduserform.classList.remove('active')
})
adduserformbody.addEventListener('submit', async (e) => {
e.preventDefault()
const hashedpassword = await hashPassword(passwordinput.value)
const new_user = {
username: usernameinput.value,
email: emailinput.value,
password: hashedpassword,
group: groupinput.value,
is_superuser: is_superuserinput.checked
}
const status = await fetch(routes.adduser(), {
method: 'POST',
body: `{"token":"${localStorage.getItem('token')}","username":"${new_user.username}","email":"${new_user.email}","password":"${new_user.password}","group":"${new_user.group}","is_superuser":"${new_user.is_superuser}"}`
}).then(data => data.json()).then(jsondata => jsondata).catch((error) => {
notification(`Ошибка на сервере: ${error}`, "error")
})
if (!status.error) {
adduserform.classList.remove('active')
await get_all_users()
notification("Пользователь успешно добавлен", "success")
usernameinput.value = ""
passwordinput.value = ""
groupinput.value = ""
} else {
notification(status.error, "error")
}
})
+7
View File
@@ -0,0 +1,7 @@
/*!
* Bootstrap v5.3.5 (https://getbootstrap.com/)
* Copyright 2011-2025 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e(require("@popperjs/core")):"function"==typeof define&&define.amd?define(["@popperjs/core"],e):(t="undefined"!=typeof globalThis?globalThis:t||self).bootstrap=e(t.Popper)}(this,(function(t){"use strict";function e(t){const e=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(t)for(const i in t)if("default"!==i){const s=Object.getOwnPropertyDescriptor(t,i);Object.defineProperty(e,i,s.get?s:{enumerable:!0,get:()=>t[i]})}return e.default=t,Object.freeze(e)}const i=e(t),s=new Map,n={set(t,e,i){s.has(t)||s.set(t,new Map);const n=s.get(t);n.has(e)||0===n.size?n.set(e,i):console.error(`Bootstrap doesn't allow more than one instance per element. Bound instance: ${Array.from(n.keys())[0]}.`)},get:(t,e)=>s.has(t)&&s.get(t).get(e)||null,remove(t,e){if(!s.has(t))return;const i=s.get(t);i.delete(e),0===i.size&&s.delete(t)}},o="transitionend",r=t=>(t&&window.CSS&&window.CSS.escape&&(t=t.replace(/#([^\s"#']+)/g,((t,e)=>`#${CSS.escape(e)}`))),t),a=t=>{t.dispatchEvent(new Event(o))},l=t=>!(!t||"object"!=typeof t)&&(void 0!==t.jquery&&(t=t[0]),void 0!==t.nodeType),c=t=>l(t)?t.jquery?t[0]:t:"string"==typeof t&&t.length>0?document.querySelector(r(t)):null,h=t=>{if(!l(t)||0===t.getClientRects().length)return!1;const e="visible"===getComputedStyle(t).getPropertyValue("visibility"),i=t.closest("details:not([open])");if(!i)return e;if(i!==t){const e=t.closest("summary");if(e&&e.parentNode!==i)return!1;if(null===e)return!1}return e},d=t=>!t||t.nodeType!==Node.ELEMENT_NODE||!!t.classList.contains("disabled")||(void 0!==t.disabled?t.disabled:t.hasAttribute("disabled")&&"false"!==t.getAttribute("disabled")),u=t=>{if(!document.documentElement.attachShadow)return null;if("function"==typeof t.getRootNode){const e=t.getRootNode();return e instanceof ShadowRoot?e:null}return t instanceof ShadowRoot?t:t.parentNode?u(t.parentNode):null},_=()=>{},g=t=>{t.offsetHeight},f=()=>window.jQuery&&!document.body.hasAttribute("data-bs-no-jquery")?window.jQuery:null,m=[],p=()=>"rtl"===document.documentElement.dir,b=t=>{var e;e=()=>{const e=f();if(e){const i=t.NAME,s=e.fn[i];e.fn[i]=t.jQueryInterface,e.fn[i].Constructor=t,e.fn[i].noConflict=()=>(e.fn[i]=s,t.jQueryInterface)}},"loading"===document.readyState?(m.length||document.addEventListener("DOMContentLoaded",(()=>{for(const t of m)t()})),m.push(e)):e()},v=(t,e=[],i=t)=>"function"==typeof t?t.call(...e):i,y=(t,e,i=!0)=>{if(!i)return void v(t);const s=(t=>{if(!t)return 0;let{transitionDuration:e,transitionDelay:i}=window.getComputedStyle(t);const s=Number.parseFloat(e),n=Number.parseFloat(i);return s||n?(e=e.split(",")[0],i=i.split(",")[0],1e3*(Number.parseFloat(e)+Number.parseFloat(i))):0})(e)+5;let n=!1;const r=({target:i})=>{i===e&&(n=!0,e.removeEventListener(o,r),v(t))};e.addEventListener(o,r),setTimeout((()=>{n||a(e)}),s)},w=(t,e,i,s)=>{const n=t.length;let o=t.indexOf(e);return-1===o?!i&&s?t[n-1]:t[0]:(o+=i?1:-1,s&&(o=(o+n)%n),t[Math.max(0,Math.min(o,n-1))])},A=/[^.]*(?=\..*)\.|.*/,E=/\..*/,C=/::\d+$/,T={};let k=1;const $={mouseenter:"mouseover",mouseleave:"mouseout"},S=new Set(["click","dblclick","mouseup","mousedown","contextmenu","mousewheel","DOMMouseScroll","mouseover","mouseout","mousemove","selectstart","selectend","keydown","keypress","keyup","orientationchange","touchstart","touchmove","touchend","touchcancel","pointerdown","pointermove","pointerup","pointerleave","pointercancel","gesturestart","gesturechange","gestureend","focus","blur","change","reset","select","submit","focusin","focusout","load","unload","beforeunload","resize","move","DOMContentLoaded","readystatechange","error","abort","scroll"]);function L(t,e){return e&&`${e}::${k++}`||t.uidEvent||k++}function O(t){const e=L(t);return t.uidEvent=e,T[e]=T[e]||{},T[e]}function I(t,e,i=null){return Object.values(t).find((t=>t.callable===e&&t.delegationSelector===i))}function D(t,e,i){const s="string"==typeof e,n=s?i:e||i;let o=M(t);return S.has(o)||(o=t),[s,n,o]}function N(t,e,i,s,n){if("string"!=typeof e||!t)return;let[o,r,a]=D(e,i,s);if(e in $){const t=t=>function(e){if(!e.relatedTarget||e.relatedTarget!==e.delegateTarget&&!e.delegateTarget.contains(e.relatedTarget))return t.call(this,e)};r=t(r)}const l=O(t),c=l[a]||(l[a]={}),h=I(c,r,o?i:null);if(h)return void(h.oneOff=h.oneOff&&n);const d=L(r,e.replace(A,"")),u=o?function(t,e,i){return function s(n){const o=t.querySelectorAll(e);for(let{target:r}=n;r&&r!==this;r=r.parentNode)for(const a of o)if(a===r)return F(n,{delegateTarget:r}),s.oneOff&&j.off(t,n.type,e,i),i.apply(r,[n])}}(t,i,r):function(t,e){return function i(s){return F(s,{delegateTarget:t}),i.oneOff&&j.off(t,s.type,e),e.apply(t,[s])}}(t,r);u.delegationSelector=o?i:null,u.callable=r,u.oneOff=n,u.uidEvent=d,c[d]=u,t.addEventListener(a,u,o)}function P(t,e,i,s,n){const o=I(e[i],s,n);o&&(t.removeEventListener(i,o,Boolean(n)),delete e[i][o.uidEvent])}function x(t,e,i,s){const n=e[i]||{};for(const[o,r]of Object.entries(n))o.includes(Line truncated
//# sourceMappingURL=bootstrap.min.js.map
+34
View File
@@ -0,0 +1,34 @@
const changepasswordform = document.querySelector('.changepasswordform')
const cancelchangepassword = document.querySelector('#cancelchangepassword')
const changepasswordformbody = document.querySelector('.changepasswordformbody')
const changepasswordnewpassword = document.querySelector('#changepasswordnewpassword')
let changepassuserid = "";
const change_password = (userid) => {
changepasswordform.classList.add('active')
changepassuserid = userid
}
cancelchangepassword.addEventListener('click', e => {
e.preventDefault()
changepasswordform.classList.remove('active')
})
changepasswordformbody.addEventListener('submit', async e => {
e.preventDefault()
const hashedpassword = await hashPassword(changepasswordnewpassword.value)
const status = await fetch(routes.change_password_url(), {
method: 'POST',
body: `{"token":"${localStorage.getItem('token')}","userid":${changepassuserid},"new_password":"${hashedpassword}"}`
}).then(data => data.json()).then(jsondata => jsondata).catch((error) => {
notification(`Ошибка на сервере: ${error}`, "error")
})
if (!status.error) {
notification("Пароль успешно изменен", "success")
changepasswordform.classList.remove('active')
changepasswordnewpassword.value = ""
} else {
notification(status.error, "error")
}
})
+56
View File
@@ -0,0 +1,56 @@
const changeuserform = document.querySelector('.changeuserform')
const changeuserformbody = document.querySelector('.changeuserformbody')
const changeuserformusername = document.querySelector('#changeuserformusername')
const changeuserformemail = document.querySelector('#changeuserformemail')
const changeuserformgroup = document.querySelector('#changeuserformgroup')
const changeuserformissuperuser = document.querySelector('#changeuserformissuperuser')
const cancelchangeuser = document.querySelector("#cancelchangeuser")
let changedatauserid = "";
let userdata = "";
const change_user_data = async (userid) => {
changeuserform.classList.add('active')
changedatauserid = userid
const all_users = await fetch(routes.all_users(), {
method: 'POST',
body: `{"token":"${localStorage.getItem('token')}"}`
}).then(data => data.json()).then(jsondata => jsondata).catch((error) => {
notification(`Ошибка на сервере: ${error}`, "error")
})
userdata = all_users.find(usr => usr.id === userid)
changeuserformusername.value = userdata.username
changeuserformgroup.value = userdata.group
changeuserformissuperuser.checked = userdata.is_superuser === "1"
changeuserformemail.value = userdata.email
}
cancelchangeuser.addEventListener('click', (e) => {
e.preventDefault()
changeuserform.classList.remove('active')
})
changeuserformbody.addEventListener('submit', async (e) => {
e.preventDefault()
let request = {
token: localStorage.getItem('token'),
userid: changedatauserid,
username: changeuserformusername.value,
email: changeuserformemail.value,
is_superuser: changeuserformissuperuser.checked,
group: changeuserformgroup.value
};
const status = await fetch(routes.change_user(), {
method: 'PUT',
body: JSON.stringify(request)
}).then(data => data.json()).then(jsondata => jsondata).catch((error) => {
notification(`Ошибка на сервере: ${error}`, "error")
})
if (!status.error) {
notification("Данные пользователя успешно изменены", "success")
changeuserform.classList.remove('active')
await get_all_users()
} else {
notification(status.error, "error")
}
})
+41
View File
@@ -0,0 +1,41 @@
const userstablebody = document.querySelector('#alluserstablebody')
const confirmform = document.querySelector('.confirmform')
const confirmformcancel = document.querySelector('#no')
const confirmformok = document.querySelector('#yes')
let useridtodelete = "";
userstablebody.addEventListener('click', e => {
if (e.target.classList.contains('deleteuser')) {
useridtodelete = e.target.id
confirmform.classList.add("active")
}
if (e.target.classList.contains('changepassword')) {
change_password(e.target.id)
}
if (e.target.classList.contains('changeuser')) {
change_user_data(parseInt(e.target.id))
}
})
confirmformcancel.addEventListener('click', () => {
useridtodelete = ""
confirmform.classList.remove('active')
})
confirmformok.addEventListener('click', async () => {
const status = await fetch(routes.deluser(), {
method: 'DELETE',
body: `{"token":"${localStorage.getItem('token')}","userid":${useridtodelete}}`
}).then(data => data.json()).then(jsondata => jsondata).catch((error) => {
notification(`Ошибка на сервере: ${error}`, "error")
})
if (status.success) {
confirmform.classList.remove('active')
await get_all_users()
notification("Пользователь успешно удален", "success")
} else {
notification("Can't delete user", "error")
confirmform.classList.remove('active')
}
})
+24
View File
@@ -0,0 +1,24 @@
const get_all_users = async () => {
const response = await fetch(routes.all_users(), {
method: "POST",
body: `{"token":"${localStorage.getItem("token")}"}`
}).then(data => data.json()).then(jsondata => jsondata).catch((error) => {
notification(`Ошибка на сервере: ${error}`, "error")
})
const alluserstablebody = document.querySelector('#alluserstablebody')
alluserstablebody.innerHTML = ""
for (const user of response) {
alluserstablebody.innerHTML += `
<tr>
<th><p>${user.id}</p></th>
<th>${user.username}</th>
<th>${user.email}</th>
<th>${user.group}</th>
<th>${user.is_superuser}</th>
<th><button class="deleteuser btn btn-danger" id="${user.id}">Delete</button></th>
<th><button class="changepassword btn btn-warning" id="${user.id}">Change password</button></th>
<th><button class="changeuser btn btn-warning" id="${user.id}">Change user data</button></th>
</tr>
`
}
}
+93
View File
@@ -0,0 +1,93 @@
const loginform = document.querySelector("#loginform");
const loginformbody = document.querySelector("#loginformbody");
const loginforminputusername = document.querySelector(
"#loginforminputusername",
);
const loginforminputpassword = document.querySelector(
"#loginforminputpassword",
);
const exitbutton = document.querySelector("#exitbutton");
const usernameheader = document.querySelector(".usernameheader");
if (localStorage.getItem("token") === null) {
loginform.classList.add("active");
}
loginformbody.addEventListener("submit", (e) => {
e.preventDefault();
hashPassword(loginforminputpassword.value).then((hashedpassword) => {
login(loginforminputusername.value, hashedpassword);
});
});
const login = async (username, password) => {
const response = await fetch(routes.login(), {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
body: `{"username":"${username}","password":"${password}"}`,
})
.then((data) => data.json())
.then((jsondata) => jsondata)
.catch((error) => {
if (error.message === "Unexpected end of JSON input") {
notification(
`Ошибка на сервере: Создайте файл users.json и запишите в него первого пользователя`,
"error",
);
} else {
notification(`Ошибка на сервере: ${error}`, "error");
}
});
if (response.error) {
loginforminputusername.classList.add("is-invalid");
loginforminputpassword.classList.add("is-invalid");
notification("Неправильный логин или пароль", "error");
}
if (response.token) {
notification("Успешно", "success");
loginform.classList.remove("active");
localStorage.setItem("token", response.token);
await get_user_data();
loginforminputusername.value = "";
loginforminputpassword.value = "";
loginforminputusername.classList.remove("is-invalid");
loginforminputpassword.classList.remove("is-invalid");
}
};
const get_user_data = async () => {
const response = await fetch(routes.user(), {
method: "POST",
body: `{"token":"${localStorage.getItem("token")}"}`,
})
.then((data) => data.json())
.then((jsondata) => jsondata)
.catch((error) => {
notification(`Ошибка на сервере: ${error}`, "error");
});
if (response.is_superuser) {
await get_all_users();
usernameheader.innerHTML = response.username;
} else {
document.querySelector("#alluserstablebody").innerHTML = "";
loginform.classList.add("active");
notification(
"Вы не можете войти потому что не являетесь администратором",
"error",
);
localStorage.clear();
}
if (response.id) {
localStorage.setItem("userdata", JSON.stringify(response));
}
};
exitbutton.addEventListener("click", () => {
localStorage.removeItem("token");
localStorage.removeItem("userdata");
loginform.classList.add("active");
notification("Вы вышли", "success");
});
+34
View File
@@ -0,0 +1,34 @@
const notificationsection = document.querySelector(".notificationsection");
const notification = (text, type) => {
const notificationbox = document.createElement("div");
const notificationtextbox = document.createElement("div");
const notificationtext = document.createElement("h3");
notificationtext.classList.add("notificationtext");
notificationtextbox.append(notificationtext);
notificationtextbox.classList.add("notificationtextbox");
notificationtextbox.classList.add("card-header");
notificationbox.append(notificationtextbox);
notificationbox.classList.add("notification");
notificationbox.addEventListener("click", () => {
notificationbox.classList.remove("active");
});
notificationsection.append(notificationbox);
if (type === "error") {
notificationbox.classList.remove("success");
notificationbox.classList.add("error");
} else {
notificationbox.classList.remove("error");
notificationbox.classList.add("success");
}
notificationtext.innerHTML = text;
setTimeout(() => {
notificationbox.classList.add("active");
}, 300);
setTimeout(() => {
notificationbox.classList.remove("active");
setTimeout(() => {
notificationsection.removeChild(notificationbox);
}, 200);
}, 3000);
};
+13
View File
@@ -0,0 +1,13 @@
const host = window.location.origin
const prefix = 'api'
const routes = {
login: () => [host, prefix, 'token'].join('/'),
user: () => [host, prefix, 'users', 'user'].join('/'),
all_users: () => [host, prefix, 'users'].join('/'),
access: () => [host, prefix, 'token', 'verify'].join('/'),
adduser: () => [host, prefix, 'users', 'add'].join('/'),
deluser: () => [host, prefix, 'users'].join('/'),
change_password_url: () => [host, prefix, 'users', 'password'].join('/'),
change_user: () => [host, prefix, 'users'].join('/')
}
+8
View File
@@ -0,0 +1,8 @@
const hashPassword = async (password) => {
const encoder = new TextEncoder()
const data = encoder.encode(password)
const hashBuffer = await crypto.subtle.digest('SHA-256', data)
const hashArray = Array.from(new Uint8Array(hashBuffer))
const hashHex = hashArray.map(b=>('00' + b.toString(16)).slice(-2)).join('')
return hashHex
}
+11
View File
@@ -0,0 +1,11 @@
const themecheckbox = document.querySelector('#themecheckbox')
themecheckbox.addEventListener('click', (e) => {
if (themecheckbox.checked) {
document.documentElement.setAttribute('data-bs-theme', 'dark')
localStorage.setItem('bs-theme', 'dark')
} else {
document.documentElement.setAttribute('data-bs-theme', 'light')
localStorage.setItem('bs-theme', 'light')
}
})
+27
View File
@@ -0,0 +1,27 @@
document.addEventListener("DOMContentLoaded", () => {
var theme = localStorage.getItem('bs-theme')
if (theme) {
document.documentElement.setAttribute('data-bs-theme', theme)
if (theme === 'dark') {
themecheckbox.checked = true
}
}
if (localStorage.getItem('token')) {
fetch(routes.access(), {
method: 'POST',
body: `{"token": "${localStorage.getItem('token')}"}`
}).then(data => data.json()).then(jsondata => {
if (jsondata.valid) {
document.querySelector('.loginform').classList.remove('active')
get_all_users()
} else {
document.querySelector('.loginform').classList.add('active')
}
}).catch((error) => {
notification(`Ошибка на сервере: ${error}`, "error")
})
usernameheader.innerHTML = JSON.parse(localStorage.getItem('userdata')).username
} else {
document.querySelector('.loginform').classList.add('active')
}
})