mirror of
https://github.com/eddyem/onionserver.git
synced 2025-12-06 02:35:11 +03:00
initial commit
This commit is contained in:
parent
7623cb3234
commit
6cccee95ca
23
.gitignore
vendored
Normal file
23
.gitignore
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
# Prerequisites
|
||||
*.d
|
||||
|
||||
# Object files
|
||||
*.o
|
||||
|
||||
# Libraries
|
||||
*.lib
|
||||
*.a
|
||||
*.la
|
||||
*.lo
|
||||
|
||||
# Shared objects (inc. Windows DLLs)
|
||||
*.so
|
||||
*.so.*
|
||||
|
||||
# qt-creator
|
||||
*.config
|
||||
*.cflags
|
||||
*.cxxflags
|
||||
*.creator*
|
||||
*.files
|
||||
*.includes
|
||||
68
CMakeLists.txt
Normal file
68
CMakeLists.txt
Normal file
@ -0,0 +1,68 @@
|
||||
cmake_minimum_required(VERSION 3.0)
|
||||
set(PROJ Onion_test)
|
||||
set(MINOR_VERSION "1")
|
||||
set(MID_VERSION "0")
|
||||
set(MAJOR_VERSION "0")
|
||||
set(VERSION "${MAJOR_VERSION}.${MID_VERSION}.${MINOR_VERSION}")
|
||||
|
||||
project(${PROJ} VERSION ${PROJ_VERSION} LANGUAGES C)
|
||||
|
||||
message("VER: ${VERSION}")
|
||||
|
||||
# default flags
|
||||
set(CMAKE_C_FLAGS_RELEASE "")
|
||||
set(CMAKE_C_FLAGS_DEBUG "")
|
||||
set(CMAKE_C_FLAGS "-O2 -std=gnu99")
|
||||
|
||||
set(CMAKE_COLOR_MAKEFILE ON)
|
||||
|
||||
# here is one of two variants: all .c in directory or .c files in list
|
||||
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR} SOURCES)
|
||||
|
||||
# cmake -DDEBUG=1 -> debugging
|
||||
if(DEFINED EBUG)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wextra -Wall -Werror -W")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wextra -Wall -Werror -W")
|
||||
set(CMAKE_BUILD_TYPE DEBUG)
|
||||
set(CMAKE_VERBOSE_MAKEFILE "ON")
|
||||
add_definitions(-DEBUG)
|
||||
else()
|
||||
set(CMAKE_BUILD_TYPE RELEASE)
|
||||
endif()
|
||||
|
||||
###### pkgconfig ######
|
||||
# pkg-config modules (for pkg-check-modules)
|
||||
set(MODULES usefull_macros sqlite3)
|
||||
|
||||
# find packages:
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(${PROJ} REQUIRED ${MODULES})
|
||||
|
||||
###### additional flags ######
|
||||
list(APPEND ${PROJ}_LIBRARIES "-lonion -lcrypt")
|
||||
|
||||
# change wrong behaviour with install prefix
|
||||
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT AND CMAKE_INSTALL_PREFIX MATCHES "/usr/local")
|
||||
else()
|
||||
message("Change default install path to /usr/local")
|
||||
set(CMAKE_INSTALL_PREFIX "/usr/local")
|
||||
endif()
|
||||
message("Install dir prefix: ${CMAKE_INSTALL_PREFIX}")
|
||||
|
||||
# exe file
|
||||
add_executable(${PROJ} ${SOURCES})
|
||||
# -I
|
||||
include_directories(${${PROJ}_INCLUDE_DIRS})
|
||||
# -L
|
||||
link_directories(${${PROJ}_LIBRARY_DIRS})
|
||||
# -D
|
||||
add_definitions(${CFLAGS} -DLOCALEDIR=\"${LOCALEDIR}\"
|
||||
-DPACKAGE_VERSION=\"${VERSION}\" -DGETTEXT_PACKAGE=\"${PROJ}\"
|
||||
-DMINOR_VERSION=\"${MINOR_VERSION}\" -DMID_VERSION=\"${MID_VERSION}\"
|
||||
-DMAJOR_VERSION=\"${MAJOR_VESION}\")
|
||||
|
||||
# -l
|
||||
target_link_libraries(${PROJ} ${${PROJ}_LIBRARIES} -lm)
|
||||
|
||||
# Installation of the program
|
||||
INSTALL(TARGETS ${PROJ} DESTINATION "bin")
|
||||
5
HOWTO.cert
Normal file
5
HOWTO.cert
Normal file
@ -0,0 +1,5 @@
|
||||
create:
|
||||
openssl req -newkey rsa:2048 -nodes -keyout cert.key -x509 -days 365 -out cert.pem
|
||||
|
||||
review:
|
||||
openssl x509 -text -noout -in cert.pem
|
||||
682
auth.c
Normal file
682
auth.c
Normal file
@ -0,0 +1,682 @@
|
||||
/*
|
||||
* This file is part of the Onion_test project.
|
||||
* Copyright 2020 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <crypt.h>
|
||||
#include <inttypes.h>
|
||||
#include <onion/dict.h>
|
||||
#include <onion/log.h>
|
||||
#include <onion/types_internal.h>
|
||||
#include <pthread.h>
|
||||
#include <sqlite3.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <usefull_macros.h>
|
||||
|
||||
#include "auth.h"
|
||||
|
||||
extern char *onion_sessions_generate_id();
|
||||
extern void onion_random_init();
|
||||
|
||||
typedef struct{
|
||||
sqlite3 *db; // session database
|
||||
char *name; // database filename
|
||||
sqlite3_stmt *add; // template to add/modify session
|
||||
sqlite3_stmt *getsid; // get data by session ID
|
||||
sqlite3_stmt *getsockid;// get data by socked ID
|
||||
sqlite3_stmt *delold; // delete all old sessions (with atime < XX)
|
||||
sqlite3_stmt *del; // delete session
|
||||
pthread_mutex_t mutex; // locking mutex
|
||||
} _sessionDB;
|
||||
|
||||
typedef struct{
|
||||
sqlite3 *db; // auth database
|
||||
char *name; // database filename
|
||||
sqlite3_stmt *add; // add user data
|
||||
sqlite3_stmt *get; // get user data by username
|
||||
sqlite3_stmt *del; // delete user data
|
||||
pthread_mutex_t mutex; // locking mutex
|
||||
} _authDB;
|
||||
|
||||
static _sessionDB *sessionDB = NULL;
|
||||
static _authDB *authDB = NULL;
|
||||
|
||||
/**
|
||||
* @brief getQdata - find data in POST or GET parts
|
||||
* @param req - request
|
||||
* @param key - searching key
|
||||
* @return value or NULL
|
||||
*/
|
||||
const char *getQdata(onion_request *req, const char *key){
|
||||
if(!req || !key) return NULL;
|
||||
printf("key %s, ", key);
|
||||
const char *data = onion_request_get_query(req, key);
|
||||
printf("GET: '%s' ", data);
|
||||
if(!data){
|
||||
data = onion_request_get_post(req, key);
|
||||
if(data && *data == 0) data = NULL;
|
||||
printf("POST: '%s'", data);
|
||||
}
|
||||
printf("\n");
|
||||
fflush(stdout);
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief sqprep - prepare SQLite statement
|
||||
* @param db - database
|
||||
* @param str - text string for statement
|
||||
* @param stmt - the statement
|
||||
* @return 0 if all OK
|
||||
*/
|
||||
static int sqprep(sqlite3 *db, const char *str, sqlite3_stmt **stmt){
|
||||
int rc = sqlite3_prepare_v2(db, str, (int)strlen(str), stmt, NULL);
|
||||
if(rc != SQLITE_OK){
|
||||
WARNX("Can't prepare statement to save (%d)", rc);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief addtext - bind text phrase to statement
|
||||
* @param stmt - the statement
|
||||
* @param narg - argument number for binding
|
||||
* @param str - the text itself
|
||||
* @return 0 if all OK
|
||||
*/
|
||||
static int addtext(sqlite3_stmt *stmt, int narg, const char *str){
|
||||
if(str == NULL) str = "";
|
||||
int rc = sqlite3_bind_text(stmt, narg, str, -1, SQLITE_TRANSIENT);
|
||||
if(rc != SQLITE_OK){
|
||||
WARNX("Can't bind %s: %d", str, rc);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief addWSkey - add new websocket ID to session & send this ID to user
|
||||
* @param res - web response
|
||||
* @param session - session information to modify
|
||||
*/
|
||||
void addWSkey(onion_response *res, sessinfo *session){
|
||||
if(!res || !session) return;
|
||||
do{
|
||||
FREE(session->sockID);
|
||||
session->sockID = onion_sessions_generate_id();
|
||||
DBG("Try to modify session %s", session->sessID);
|
||||
if(!addSession(session, 1)){
|
||||
DBG("Modify session: ID=%s, sockid=%s, atime=%" PRId64 ", user=%s, data=%s\n",
|
||||
session->sessID, session->sockID, session->atime, session->username, session->data);
|
||||
break;
|
||||
}
|
||||
}while(1);
|
||||
onion_response_write0(res, session->sockID);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief qookieSession - find session ID from SESSION_COOKIE_NAME and search session in DB
|
||||
* @param req - web request
|
||||
* @return session data (allocated here) or NULL if session not found
|
||||
*/
|
||||
sessinfo *qookieSession(onion_request *req){
|
||||
if(!req) return NULL;
|
||||
/*const char *path = onion_request_get_path(req);
|
||||
printf("Ask path %s\n", path);*/
|
||||
onion_dict *cookies = onion_request_get_cookies_dict(req);
|
||||
const char *skey = onion_dict_get(cookies, SESSION_COOKIE_NAME);
|
||||
if(!skey){
|
||||
WARNX("No session cookie found\n");
|
||||
return NULL;
|
||||
}
|
||||
return getSession(skey);
|
||||
}
|
||||
|
||||
onion_connection_status auth(_U_ onion_handler *h, onion_request *req, onion_response *res){
|
||||
if(!req || !res) return OCS_CLOSE_CONNECTION;
|
||||
if(onion_request_get_flags(req) & OR_HEAD) {
|
||||
onion_response_write_headers(res);
|
||||
return OCS_PROCESSED;
|
||||
}
|
||||
const char *host = onion_request_get_client_description(req);
|
||||
char json[2048]; // json buffer for UA & IP
|
||||
const char *UA = onion_request_get_header(req, "User-Agent");
|
||||
snprintf(json, 2048, "{\"User-Agent\": \"%s\", \"User-IP\": \"%s\"}", UA, host);
|
||||
DBG("Client: %s, UA: %s\n", host, UA);
|
||||
const char *logout = getQdata(req, "LogOut");
|
||||
DBG("logout=%s\n", logout);
|
||||
sessinfo *session = qookieSession(req);
|
||||
if(!session) DBG("No cookie, need to create\n");
|
||||
else if(!logout){
|
||||
onion_response_write0(res, "AuthOK");
|
||||
goto closeconn;
|
||||
}
|
||||
const char *username = NULL, *passwd = NULL;
|
||||
if(logout){
|
||||
DBG("User logged out\n");
|
||||
if(session){
|
||||
if(deleteSession(session->sessID))
|
||||
WARNX("Can't delete session with ID=%s from database", session->sessID);
|
||||
}
|
||||
onion_response_write0(res, "LogOut");
|
||||
onion_response_add_cookie(res, SESSION_COOKIE_NAME, "clear", 0, "/", NULL, OC_HTTP_ONLY|OC_SECURE);
|
||||
goto closeconn;
|
||||
}else{ // log in
|
||||
freeSessInfo(&session);
|
||||
username = getQdata(req, "login");
|
||||
if(!username){
|
||||
ONION_WARNING("no login field -> need auth");
|
||||
onion_response_write0(res, "NeedAuth");
|
||||
return OCS_CLOSE_CONNECTION;
|
||||
}
|
||||
passwd = getQdata(req, "passwd");
|
||||
if(!passwd){
|
||||
ONION_WARNING("Trying to enter authenticated area without password");
|
||||
onion_response_write0(res, "No password");
|
||||
return OCS_FORBIDDEN;
|
||||
}
|
||||
}
|
||||
userinfo *U = getUserData(username);
|
||||
if(!U){
|
||||
WARNX("User %s not found", username);
|
||||
return OCS_FORBIDDEN;
|
||||
}
|
||||
char *pass = strdup(crypt(passwd, "$6$"));
|
||||
if(!pass){
|
||||
WARN("Error in ctypt or strdup");
|
||||
freeUserInfo(&U);
|
||||
return OCS_FORBIDDEN;
|
||||
}
|
||||
int comp = strcmp(pass, U->password);
|
||||
freeUserInfo(&U);
|
||||
FREE(pass);
|
||||
if(comp){
|
||||
WARNX("User %s give wrong password", username);
|
||||
return OCS_FORBIDDEN;
|
||||
}
|
||||
session = MALLOC(sessinfo, 1);
|
||||
session->atime = time(NULL);
|
||||
session->username = strdup(username);
|
||||
/*
|
||||
onion_dict *data = onion_dict_new();
|
||||
onion_dict_add(data, "UA", UA, 0);
|
||||
onion_dict_add(data, "IP", host, 0);
|
||||
onion_block *bl = onion_dict_to_json(data);
|
||||
if(bl){
|
||||
const char *json = onion_block_data(bl);
|
||||
if(json) session->data = strdup(json);
|
||||
}*/
|
||||
session->data = strdup(json);
|
||||
do{
|
||||
FREE(session->sessID);
|
||||
session->sessID = onion_sessions_generate_id();
|
||||
DBG("Try to add session %s", session->sessID);
|
||||
if(!addSession(session, 0)){
|
||||
DBG("New session: ID=%s, atime=%" PRId64 ", user=%s, data=%s\n",
|
||||
session->sessID, session->atime, session->username, session->data);
|
||||
break;
|
||||
}
|
||||
sleep(2);
|
||||
}while(1);
|
||||
onion_response_add_cookie(res, SESSION_COOKIE_NAME, session->sessID, 366*86400, "/", NULL, OC_HTTP_ONLY|OC_SECURE);
|
||||
onion_response_write0(res, "AuthOK");
|
||||
closeconn:
|
||||
freeSessInfo(&session);
|
||||
return OCS_CLOSE_CONNECTION;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief opendb - open SQLite database
|
||||
* @param name - database filename
|
||||
* @param db - database itself
|
||||
* @return 0 if all OK
|
||||
*/
|
||||
static int opendb(const char *name, sqlite3 **db){
|
||||
if(!name || !db) return 2;
|
||||
if(SQLITE_OK != sqlite3_open(name, db)){
|
||||
ONION_ERROR("Can't open database: %s", sqlite3_errmsg(*db));
|
||||
sqlite3_close(*db);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief close_sessDB - close session database & free memory
|
||||
*/
|
||||
static void close_sessDB(){
|
||||
if(!sessionDB) return;
|
||||
pthread_mutex_lock(&sessionDB->mutex);
|
||||
sqlite3_finalize(sessionDB->add);
|
||||
sqlite3_finalize(sessionDB->del);
|
||||
sqlite3_finalize(sessionDB->getsid);
|
||||
sqlite3_finalize(sessionDB->getsockid);
|
||||
sqlite3_close(sessionDB->db);
|
||||
pthread_mutex_unlock(&sessionDB->mutex);
|
||||
pthread_mutex_destroy(&sessionDB->mutex);
|
||||
FREE(sessionDB->name);
|
||||
FREE(sessionDB);
|
||||
}
|
||||
/**
|
||||
* @brief close_authDB - close user database & free memory
|
||||
*/
|
||||
static void close_authDB(){
|
||||
if(!authDB) return;
|
||||
pthread_mutex_lock(&authDB->mutex);
|
||||
sqlite3_finalize(authDB->add);
|
||||
sqlite3_finalize(authDB->del);
|
||||
sqlite3_finalize(authDB->get);
|
||||
sqlite3_close(authDB->db);
|
||||
pthread_mutex_unlock(&authDB->mutex);
|
||||
pthread_mutex_destroy(&authDB->mutex);
|
||||
FREE(authDB->name);
|
||||
FREE(authDB);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief closeSQLite - close all databases
|
||||
*/
|
||||
void closeSQLite(){
|
||||
close_authDB();
|
||||
close_sessDB();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief initSQLite - init SQL databases
|
||||
* @param auth_filename - database with auth data (username, password hash, access level)
|
||||
* @param sess_filename - session database (session ID, websosket ID, access time, data {UA, IP, username, access level - JSON})
|
||||
* @return error code or 0 if OK
|
||||
*/
|
||||
int initSQLite(const char *auth_filename, const char *sess_filename){
|
||||
if(!auth_filename || !sess_filename) return 5;
|
||||
int rc;
|
||||
char *zErrMsg = NULL;
|
||||
onion_random_init();
|
||||
closeSQLite();
|
||||
/******************************* users database *******************************/
|
||||
authDB = MALLOC(_authDB, 1);
|
||||
if(opendb(auth_filename, &authDB->db)) return 2;
|
||||
authDB->name = strdup(auth_filename);
|
||||
rc = sqlite3_exec(authDB->db, "CREATE TABLE passwd (user TEXT PRIMARY KEY NOT NULL, passhash TEXT, level INT, comment TEXT)", NULL, NULL, &zErrMsg);
|
||||
if(rc != SQLITE_OK){
|
||||
if(strcmp(zErrMsg, "table passwd already exists")){
|
||||
WARNX("SQL CREATE error %d: %s", rc, zErrMsg);
|
||||
goto ret;
|
||||
}
|
||||
}else{
|
||||
fprintf(stderr, "Add default user\n");
|
||||
// default admin: toor, password: p@ssw0rd
|
||||
rc = sqlite3_exec(authDB->db, "INSERT INTO passwd VALUES (\"toor\", \"$6$$F55h0JlnNG19zS6YHUDX6h4zksblEBCFRqzCoCt6Y3VqE7zBeM/ifdGbUNMK.dLIwu9jq3fCmLBYEpzEpNCej0\", 0, \"\")", NULL, NULL, &zErrMsg);
|
||||
if(rc != SQLITE_OK){
|
||||
fprintf(stderr, "SQL INSERT error %d: %s\n", rc, zErrMsg);
|
||||
sqlite3_free(zErrMsg);
|
||||
goto ret;
|
||||
}
|
||||
}
|
||||
// get user information
|
||||
if(sqprep(authDB->db, "SELECT passhash, level, comment FROM passwd WHERE user=?",
|
||||
&authDB->get)) goto ret;
|
||||
// add or modify user
|
||||
if(sqprep(authDB->db, "INSERT OR REPLACE INTO passwd (user, passhash, level, comment) VALUES (?, ?, ?, ?)",
|
||||
&authDB->add)) goto ret;
|
||||
// delete user
|
||||
if(sqprep(authDB->db, "DELETE FROM passwd WHERE user=?", &authDB->del)) goto ret;
|
||||
if(pthread_mutex_init(&authDB->mutex, NULL)){
|
||||
WARN("Can't init authDB mutex");
|
||||
goto ret;
|
||||
}
|
||||
/****************************** session database ******************************/
|
||||
sessionDB = MALLOC(_sessionDB, 1);
|
||||
if(opendb(sess_filename, &sessionDB->db)) goto ret;
|
||||
sessionDB->name = strdup(sess_filename);
|
||||
rc = sqlite3_exec(sessionDB->db, "CREATE TABLE sessions (sessID TEXT PRIMARY KEY NOT NULL, sockID TEXT, atime INT, username TEXT, data TEXT)", NULL, NULL, &zErrMsg);
|
||||
if(rc != SQLITE_OK){
|
||||
if(strcmp(zErrMsg, "table sessions already exists")){
|
||||
WARNX("SQL CREATE error %d: %s", rc, zErrMsg);
|
||||
sqlite3_free(zErrMsg);
|
||||
goto ret;
|
||||
}
|
||||
}
|
||||
// get session information
|
||||
if(sqprep(sessionDB->db, "SELECT sockID, atime, username, data FROM sessions WHERE sessID=?",
|
||||
&sessionDB->getsid)) goto ret;
|
||||
if(sqprep(sessionDB->db, "SELECT sessID, atime, username, data FROM sessions WHERE sockID=?",
|
||||
&sessionDB->getsockid)) goto ret;
|
||||
// add or modify session
|
||||
if(sqprep(sessionDB->db, "INSERT OR REPLACE INTO sessions (sessID, sockID, atime, username, data) VALUES (?, ?, ?, ?, ?)",
|
||||
&sessionDB->add)) goto ret;
|
||||
// delete session
|
||||
if(sqprep(sessionDB->db, "DELETE FROM sessions WHERE sessID=?",
|
||||
&sessionDB->del)) goto ret;
|
||||
if(sqprep(sessionDB->db, "DELETE FROM sessions WHERE atime<?",
|
||||
&sessionDB->delold)) goto ret;
|
||||
return 0;
|
||||
ret:
|
||||
closeSQLite();
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief getUserData - find all records in authDB for given username
|
||||
* @param username - user name
|
||||
* @return userinfo structure allocated here or NULL if user not found
|
||||
*/
|
||||
userinfo *getUserData(const char *username){
|
||||
if(!username) return NULL;
|
||||
userinfo *U = NULL;
|
||||
if(!authDB){
|
||||
WARNX("User database not initialized");
|
||||
return NULL;
|
||||
}
|
||||
pthread_mutex_lock(&authDB->mutex);
|
||||
sqlite3_reset(authDB->get);
|
||||
if(addtext(authDB->get, 1, username)) return NULL;
|
||||
if(SQLITE_ROW != sqlite3_step(authDB->get)){
|
||||
WARNX("User %s not found", username);
|
||||
goto ret;
|
||||
}
|
||||
const char *pass = (const char *) sqlite3_column_text(authDB->get, 0);
|
||||
int levl = sqlite3_column_int(authDB->get, 1);
|
||||
const char *comment = (const char *) sqlite3_column_text(authDB->get, 2);
|
||||
U = (userinfo *) malloc(sizeof(userinfo));
|
||||
if(!U) goto ret;
|
||||
U->username = strdup(username);
|
||||
U->password = strdup(pass);
|
||||
U->level = levl;
|
||||
U->comment = strdup(comment);
|
||||
ret:
|
||||
pthread_mutex_unlock(&authDB->mutex);
|
||||
return U;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief freeUserInfo - free memory for struct userinfo
|
||||
* @param x - pointer to userinfo pointer
|
||||
*/
|
||||
void freeUserInfo(userinfo **x){
|
||||
if(!x || !*x) return;
|
||||
userinfo *U = *x;
|
||||
free(U->username);
|
||||
free(U->password);
|
||||
free(U->comment);
|
||||
free(U);
|
||||
*x = NULL;
|
||||
}
|
||||
|
||||
// callback for showAllUsers()
|
||||
static int selallcb(_U_ void *notused, int argc, char **argv, _U_ char **no){
|
||||
if(argc != 4){
|
||||
fprintf(stderr, "Wrong argc: %d\n", argc);
|
||||
return 1;
|
||||
}
|
||||
printf("%s\t%s\t%s\t%s\n", argv[0], argv[2], argv[1], argv[3]);
|
||||
return 0;
|
||||
}
|
||||
/**
|
||||
* @brief showAllUsers - printout user database
|
||||
*/
|
||||
void showAllUsers(){
|
||||
char *zErrMsg = NULL;
|
||||
green("\nUSER\tLevel\tPassHash\t\t\tComment\n");
|
||||
pthread_mutex_lock(&authDB->mutex);
|
||||
int rc = sqlite3_exec(authDB->db, "SELECT * FROM passwd GROUP BY user", selallcb, NULL, &zErrMsg);
|
||||
if(rc != SQLITE_OK){
|
||||
WARNX("SQL CREATE error %d: %s", rc, zErrMsg);
|
||||
sqlite3_free(zErrMsg);
|
||||
}
|
||||
pthread_mutex_unlock(&authDB->mutex);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief deleteUser - delete user record from database
|
||||
* @param username - user name
|
||||
* @return 0 if all OK
|
||||
*/
|
||||
int deleteUser(const char *username){
|
||||
if(!username) return 1;
|
||||
userinfo *u = getUserData(username);
|
||||
if(!u) return 1;
|
||||
FREE(u);
|
||||
pthread_mutex_lock(&authDB->mutex);
|
||||
sqlite3_reset(authDB->del);
|
||||
if(addtext(authDB->del, 1, username)) goto reterr;
|
||||
if(SQLITE_DONE != sqlite3_step(authDB->del)) goto reterr;
|
||||
else{
|
||||
printf("User %s deleted\n", username);
|
||||
pthread_mutex_unlock(&authDB->mutex);
|
||||
return 0;
|
||||
}
|
||||
reterr:
|
||||
pthread_mutex_unlock(&authDB->mutex);
|
||||
WARNX("Can't delete user %s", username);
|
||||
return 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief addUser - add user to database
|
||||
* @param User - struct with user data
|
||||
* @return 0 if OK
|
||||
*/
|
||||
int addUser(userinfo *User){
|
||||
if(!User) return 1;
|
||||
if(strlen(User->username) < 1){
|
||||
WARNX("No username");
|
||||
return 1;
|
||||
}
|
||||
pthread_mutex_lock(&authDB->mutex);
|
||||
sqlite3_reset(authDB->add);
|
||||
if(addtext(authDB->add, 1, User->username)) goto reterr;
|
||||
if(addtext(authDB->add, 2, User->password)) goto reterr;
|
||||
if(SQLITE_OK != sqlite3_bind_int(authDB->add, 3, User->level)) goto reterr;
|
||||
addtext(authDB->add, 4, User->comment);
|
||||
if(SQLITE_DONE == sqlite3_step(authDB->add)){
|
||||
green("Add to database:\n");
|
||||
printf("\tuser %s, passHash %s, level %d, comment '%s'\n",
|
||||
User->username, User->password, User->level, User->comment);
|
||||
pthread_mutex_unlock(&authDB->mutex);
|
||||
return 0;
|
||||
}
|
||||
reterr:
|
||||
pthread_mutex_unlock(&authDB->mutex);
|
||||
WARNX("Can't insert user %s", User->username);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief getSession - find session by session ID or socket ID
|
||||
* @param ID - session or socket ID
|
||||
* @return NULL if not found or session structure (allocated here)
|
||||
*/
|
||||
sessinfo *getSession(const char *ID){
|
||||
if(!ID) return NULL;
|
||||
sqlite3_stmt *stmt = sessionDB->getsid;
|
||||
sessinfo *s = NULL;
|
||||
pthread_mutex_lock(&sessionDB->mutex);
|
||||
sqlite3_reset(stmt);
|
||||
if(addtext(stmt, 1, ID)) goto ret;
|
||||
const char *sessID = NULL, *sockID = NULL;
|
||||
if(SQLITE_ROW != sqlite3_step(stmt)){
|
||||
stmt = sessionDB->getsockid;
|
||||
sqlite3_reset(stmt);
|
||||
if(addtext(stmt, 1, ID)) goto ret;
|
||||
if(SQLITE_ROW != sqlite3_step(stmt)){
|
||||
WARNX("Session %s not found\n", ID);
|
||||
goto ret;
|
||||
}else{
|
||||
sockID = ID;
|
||||
sessID = (const char *) sqlite3_column_text(stmt, 0);
|
||||
}
|
||||
}else{
|
||||
sessID = ID;
|
||||
sockID = (const char *) sqlite3_column_text(stmt, 0);
|
||||
}
|
||||
// 0-ID, 1-atime, 2-username, 3-data
|
||||
int64_t atime = sqlite3_column_int64(stmt, 1);
|
||||
const char *username = (const char *)sqlite3_column_text(stmt, 2);
|
||||
const char *data = (const char *)sqlite3_column_text(stmt, 3);
|
||||
s = (sessinfo*) malloc(sizeof(sessinfo));
|
||||
if(!s) goto ret;
|
||||
s->sessID = strdup(sessID);
|
||||
s->sockID = strdup(sockID);
|
||||
s->atime = atime;
|
||||
s->username = strdup(username);
|
||||
s->data = strdup(data);
|
||||
ret:
|
||||
pthread_mutex_unlock(&sessionDB->mutex);
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief addSession - add new session or modify old
|
||||
* @param s - structure with session information
|
||||
* @param modify != 0 to modify existant session
|
||||
* @return 0 if all OK
|
||||
*/
|
||||
int addSession(sessinfo *s, int modify){
|
||||
if(!s) return 1;
|
||||
sessinfo *byID = getSession(s->sessID);
|
||||
sessinfo *bysID = getSession(s->sockID);
|
||||
if(byID || bysID){ // found session with same IDs
|
||||
if(modify){
|
||||
if(bysID && strcmp(bysID->sessID, s->sessID)){
|
||||
freeSessInfo(&byID);
|
||||
freeSessInfo(&bysID);
|
||||
WARNX("Found another session with the same sockID");
|
||||
return 2;
|
||||
}
|
||||
}else{
|
||||
if(byID) WARNX("Found another session with the same sessID");
|
||||
if(bysID) WARNX("Found another session with the same sockID");
|
||||
freeSessInfo(&byID);
|
||||
freeSessInfo(&bysID);
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
pthread_mutex_lock(&sessionDB->mutex);
|
||||
// 1-sessID, 2-sockID, 3-atime, 4-username, 5-data
|
||||
sqlite3_reset(sessionDB->add);
|
||||
if(addtext(sessionDB->add, 1, s->sessID)) goto reterr;
|
||||
if(addtext(sessionDB->add, 2, s->sockID)) goto reterr;
|
||||
if(SQLITE_OK != sqlite3_bind_int64(sessionDB->add, 3, s->atime)) goto reterr;
|
||||
if(addtext(sessionDB->add, 4, s->username)) goto reterr;
|
||||
if(addtext(sessionDB->add, 5, s->data)) goto reterr;
|
||||
if(SQLITE_DONE == sqlite3_step(sessionDB->add)){
|
||||
pthread_mutex_unlock(&sessionDB->mutex);
|
||||
return 0;
|
||||
}
|
||||
reterr:
|
||||
pthread_mutex_unlock(&sessionDB->mutex);
|
||||
WARNX("Can't insert session %s\n", s->sessID);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief deleteSession - delete session by SessID
|
||||
* @param sessID - session ID
|
||||
* @return 0 if all OK
|
||||
*/
|
||||
int deleteSession(const char *sessID){
|
||||
if(!sessID) return 1;
|
||||
pthread_mutex_lock(&sessionDB->mutex);
|
||||
sqlite3_reset(sessionDB->del);
|
||||
if(addtext(sessionDB->del, 1, sessID)) goto reterr;
|
||||
if(SQLITE_DONE != sqlite3_step(sessionDB->del)) goto reterr;
|
||||
else{
|
||||
printf("Session with id %s deleted\n", sessID);
|
||||
pthread_mutex_unlock(&sessionDB->mutex);
|
||||
return 0;
|
||||
}
|
||||
reterr:
|
||||
pthread_mutex_unlock(&sessionDB->mutex);
|
||||
WARNX("Can't delete session with ID %s\n", sessID);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief deleteSession - delete session by SessID
|
||||
* @param minatime - minimal access time (all sessions with atime<minatime will be deleted)
|
||||
* @return 0 if all OK
|
||||
*/
|
||||
int deleteOldSessions(int64_t minatime){
|
||||
pthread_mutex_lock(&sessionDB->mutex);
|
||||
sqlite3_reset(sessionDB->delold);
|
||||
if(SQLITE_OK != sqlite3_bind_int64(sessionDB->delold, 1, minatime)) goto reterr;
|
||||
if(SQLITE_DONE != sqlite3_step(sessionDB->delold)) goto reterr;
|
||||
else{
|
||||
pthread_mutex_unlock(&sessionDB->mutex);
|
||||
return 0;
|
||||
}
|
||||
reterr:
|
||||
pthread_mutex_unlock(&sessionDB->mutex);
|
||||
WARNX("Can't delete sessions with atime < %" PRId64 " \n", minatime);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// callback for showAllSessions
|
||||
static int selallscb(void _U_ *notused, int argc, char **argv, char _U_ **no){
|
||||
if(argc != 5){
|
||||
fprintf(stderr, "Wrong argc: %d\n", argc);
|
||||
return 1;
|
||||
}
|
||||
printf("%s\t%s\t%s\t%s\t%s\n", argv[3], argv[2], argv[0], argv[1], argv[4]);
|
||||
return 0;
|
||||
}
|
||||
void showAllSessions(){
|
||||
char *zErrMsg = NULL;
|
||||
green("Username\tAtime\tSession ID\t\tSocket ID\t\tData\n");
|
||||
int rc = sqlite3_exec(sessionDB->db, "SELECT * FROM sessions ORDER BY username", selallscb, NULL, &zErrMsg);
|
||||
if(rc != SQLITE_OK){
|
||||
WARNX("SQL CREATE error %d: %s\n", rc, zErrMsg);
|
||||
sqlite3_free(zErrMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief freeSessInfo - free session info structure
|
||||
* @param si - address of pointer to SI
|
||||
*/
|
||||
void freeSessInfo(sessinfo **si){
|
||||
if(!si || !*si) return;
|
||||
sessinfo *s = *si;
|
||||
free(s->data);
|
||||
free(s->sessID);
|
||||
free(s->sockID);
|
||||
free(s->username);
|
||||
free(*si);
|
||||
*si = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief vacuum - rebuild databases to free all deleted records
|
||||
*/
|
||||
void vacuumSQLite(){
|
||||
char *zErrMsg = NULL;
|
||||
int rc = sqlite3_exec(authDB->db, "vacuum", NULL, NULL, &zErrMsg);
|
||||
if(rc != SQLITE_OK){
|
||||
WARNX("Error in vacuum %s (%d): %s\n", authDB->name, rc, zErrMsg);
|
||||
sqlite3_free(zErrMsg);
|
||||
}
|
||||
rc = sqlite3_exec(sessionDB->db, "vacuum", NULL, NULL, &zErrMsg);
|
||||
if(rc != SQLITE_OK){
|
||||
WARNX("Error in vacuum %s (%d): %s\n", sessionDB->name, rc, zErrMsg);
|
||||
sqlite3_free(zErrMsg);
|
||||
}
|
||||
green("Both databases are rebuilt\n");
|
||||
}
|
||||
|
||||
64
auth.h
Normal file
64
auth.h
Normal file
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* This file is part of the Onion_test project.
|
||||
* Copyright 2020 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#ifndef AUTH_H__
|
||||
#define AUTH_H__
|
||||
|
||||
#include <onion/onion.h>
|
||||
|
||||
#define SESSION_COOKIE_NAME "Acookie"
|
||||
|
||||
typedef struct{
|
||||
char *username; // user name
|
||||
char *password; // password hash (SHA512)
|
||||
int level; // user access level
|
||||
char *comment; // optional comment
|
||||
} userinfo;
|
||||
|
||||
typedef struct{
|
||||
char *sessID; // session ID
|
||||
char *sockID; // websoket ID (cleared when disconnected or after 1 minute after `atime`)
|
||||
int64_t atime; // last access time (UNIX time) - all ID data cleared after 1yr of inactivity
|
||||
char *username; // username
|
||||
char *data; // JSON data with UA, IP etc
|
||||
} sessinfo;
|
||||
|
||||
const char *getQdata(onion_request *req, const char *key);
|
||||
onion_connection_status auth(onion_handler *h, onion_request *req, onion_response *res);
|
||||
void addWSkey(onion_response *res, sessinfo *session);
|
||||
|
||||
int initSQLite(const char *auth_filename, const char *sess_filename);
|
||||
void closeSQLite();
|
||||
void vacuumSQLite();
|
||||
|
||||
userinfo *getUserData(const char *username);
|
||||
void freeUserInfo(userinfo **x);
|
||||
void showAllUsers();
|
||||
int deleteUser(const char *username);
|
||||
int addUser(userinfo *User);
|
||||
|
||||
sessinfo *getSession(const char *ID);
|
||||
sessinfo *qookieSession(onion_request *req);
|
||||
int addSession(sessinfo *s, int modify);
|
||||
int deleteSession(const char *sessID);
|
||||
int deleteOldSessions(int64_t minatime);
|
||||
void showAllSessions();
|
||||
void freeSessInfo(sessinfo **si);
|
||||
|
||||
#endif // AUTH_H__
|
||||
28
cert.key
Normal file
28
cert.key
Normal file
@ -0,0 +1,28 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC68f94gnFpcepC
|
||||
+kpD4bxY7sQAXYbyp5ahAWun4scbLJPjJe56/CE47yvTpdU8KrVfHLYmP8KcF4pM
|
||||
w0ZUNMCEenkQkb+eWjIviw3wzgesqLSssM+GZzlg92V2xkYlS/hEolUh0Z+Gl1Se
|
||||
zl9f1S/EhL6RTfQx5ZuGlppHknqtbP0g4QNkxPXGj/qXqmaWLOxb8fX30eNf/6SF
|
||||
vl1o45xwFE1SSGQnyh8KKZUWAPH2aZWpwwgB8gv4XwaVWo7DirbqOlUhhkylhhl0
|
||||
cA7dHFgODTLluRstYr9M5u3WdoJzTGZTvg2ZV1vmfpQMVbat49TVJYZI3iLKoBHQ
|
||||
IzeSU/lvAgMBAAECggEAUlc40RmTXoBgUHPxtgh9byZrikWnpMWQIQaBJodKb3uo
|
||||
/8m7Ssw2zd76jNRkIYYmMOhyilJXI21y6vCvz3MUwMU5AcVQgyzzIeG7mC8HTlNY
|
||||
kR+nqGla6ozNUg1u5AqcJY7itGyiOSP6j6ASfiFmUsatMU8GmduqLxOyjIfGJRA1
|
||||
hnNAqYsVv2LbhbUQJTBozYMz2XYfiq/GbzL2NC1tv/xWoSL4zELjDuiGyZcT8oyV
|
||||
u+dOf0x/VXOLvHqDrA8HuFbUrBjK5z0QW9iYReti/oxuhGrlp2rJLuzivKr4l+v8
|
||||
qA0momNxhFFgyyx2Mb5zxp9TwA2x7q5Cg3vOCje3MQKBgQDe6MTywxuO4MVpwDSl
|
||||
yot8vzUlI7Bv8BtdW18JUc5y1SIyGHlW1cuaQnZW6hbor6UxAoWi7oIjaFX+JQVD
|
||||
3NLEnjuUF64/Y+k8wtvK0fFeZ2nvx2UNHF50JV0tQZtHo8znwKI/L8ZBSGJcBwh2
|
||||
BGXtJtK9AY6C5xv5U03xRukw/QKBgQDWsn0qd6mV6iGqLth6sjDOJt/RFrvcikXb
|
||||
lMhkCyGPuR2lQsUDbgWMcb/td3CqR5iZAksDbXrPqQBZVm9LO7DfDsL9GxtfgMrt
|
||||
0dWk0Baod/n4NQBn8A6qGpbheFZwOhNGayYI17IqtRJmfLJuR7gRvm/IfGKso24X
|
||||
TTA3kpSl2wKBgEBv52cJ8bB213p/fniiuXnhSDqpO3rQXQi6vhlSlaxqYk069/Cb
|
||||
MxUvu0faua6f/8/QG9OCwQn9QkaKayA3+JGv8CcaRVu7xRO0fJb/45dXq68N4+9L
|
||||
UR6gInRPr9SgzD3+WKiNZfE/PHe/7Lk5AkHw5CCRD6JVrqd/ZlumFQj9AoGBAKae
|
||||
fM7xcRYcTyYRFwYZthC3UKmnOAJO+SoRTHd/v/sXUe+IYvdncjztpmK3eCNeTwoo
|
||||
Imk1lMMGSHQMxXCgkYJ6pU7is5qpjFOGroQqzfrOqZs8HuWLAwZ2fjPbPVH5cC4N
|
||||
R8ZDB01nmzEYgy1c0XhLz9rK1ZVffDfvOoVWZ7BTAoGAdQUkTWTTHU+Xwc9HshJy
|
||||
qDFAFu5SvvO2dn3OvbGVUVc+I3M1P+HMeKS3HnomFLtRRDUGEbqZtPfrxBRnduW9
|
||||
cEtgO+smyq86vs1p/ohmUESB2ablkvOgwXTXus+NsmUt3uVDq+0lFTfog/DX49+O
|
||||
EiTvSXB1U92Opv/kh8ZhQak=
|
||||
-----END PRIVATE KEY-----
|
||||
21
cert.pem
Normal file
21
cert.pem
Normal file
@ -0,0 +1,21 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDazCCAlOgAwIBAgIUU3mUrQDJVG5A00d+WvVFfqScrqIwDQYJKoZIhvcNAQEL
|
||||
BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
|
||||
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMDA3MTMxNjE0MTlaFw0yMTA3
|
||||
MTMxNjE0MTlaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw
|
||||
HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB
|
||||
AQUAA4IBDwAwggEKAoIBAQC68f94gnFpcepC+kpD4bxY7sQAXYbyp5ahAWun4scb
|
||||
LJPjJe56/CE47yvTpdU8KrVfHLYmP8KcF4pMw0ZUNMCEenkQkb+eWjIviw3wzges
|
||||
qLSssM+GZzlg92V2xkYlS/hEolUh0Z+Gl1Sezl9f1S/EhL6RTfQx5ZuGlppHknqt
|
||||
bP0g4QNkxPXGj/qXqmaWLOxb8fX30eNf/6SFvl1o45xwFE1SSGQnyh8KKZUWAPH2
|
||||
aZWpwwgB8gv4XwaVWo7DirbqOlUhhkylhhl0cA7dHFgODTLluRstYr9M5u3WdoJz
|
||||
TGZTvg2ZV1vmfpQMVbat49TVJYZI3iLKoBHQIzeSU/lvAgMBAAGjUzBRMB0GA1Ud
|
||||
DgQWBBQiHARml0EswF90cHiLYLKxpgj3DjAfBgNVHSMEGDAWgBQiHARml0EswF90
|
||||
cHiLYLKxpgj3DjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQB4
|
||||
mMeSGGA4GAfJnxIwRJWGhj2KoweOh9UOVUtvrEJm3LoURbElo1pV3Q3vzt79wLn5
|
||||
JPdXyN/MexYhtq7cwKQps3E4FElmIGUbR4peO/M1z3wJLaXSbaZqeo7ogSW6vzZL
|
||||
xIHuxLtchNOi4rkSmOhzJqJ23cLULIF7Z94nwxXRxqKaIW8hXB7mlV9cDztpn2sz
|
||||
K58M9VYmlfZetoHDzmsymM2DTmmoqR28Ykz+wK+G4U6W1+s7sY3Q+mG91lwvpzTf
|
||||
yvz6dEtqi8ktBdu86E0yHfX04oDyKtl40d0LDzQT5JWhKQFiC25W5tE2p8QLbtHz
|
||||
D5vg23HjTgRHZ80+3e4J
|
||||
-----END CERTIFICATE-----
|
||||
147
cmdlnopts.c
Normal file
147
cmdlnopts.c
Normal file
@ -0,0 +1,147 @@
|
||||
/*
|
||||
* This file is part of the Onion_test project.
|
||||
* Copyright 2020 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <usefull_macros.h>
|
||||
|
||||
|
||||
#include "cmdlnopts.h"
|
||||
|
||||
/*
|
||||
* here are global parameters initialisation
|
||||
*/
|
||||
static int help;
|
||||
// global parameters (init with default):
|
||||
glob_pars G = {
|
||||
.port = "8080",
|
||||
.certfile = "cert.pem",
|
||||
.keyfile = "cert.key",
|
||||
};
|
||||
|
||||
/*
|
||||
* Define command line options by filling structure:
|
||||
* name has_arg flag val type argptr help
|
||||
*/
|
||||
static myoption cmdlnopts[] = {
|
||||
// common options
|
||||
{"help", NO_ARGS, NULL, 'h', arg_int, APTR(&help), _("show this help")},
|
||||
{"vacuum", NO_ARGS, NULL, 'v', arg_int, APTR(&G.vacuum), _("make \"vacuum\" operation with databases")},
|
||||
{"dumpusers", NO_ARGS, NULL, 'U', arg_int, APTR(&G.dumpUserDB),_("dump users database")},
|
||||
{"dumpsess", NO_ARGS, NULL, 'S', arg_int, APTR(&G.dumpSessDB),_("dump session database")},
|
||||
{"server", NO_ARGS, NULL, 'r', arg_int, APTR(&G.runServer), _("run server process")},
|
||||
{"port", NEED_ARG, NULL, 'p', arg_string, APTR(&G.port), _("port to listen")},
|
||||
{"certfile",NEED_ARG, NULL, 'c', arg_string, APTR(&G.certfile), _("file with SSL certificate")},
|
||||
{"keyfile", NEED_ARG, NULL, 'k', arg_string, APTR(&G.keyfile), _("file with SSL key")},
|
||||
{"usersdb", NEED_ARG, NULL, 'u', arg_string, APTR(&G.usersdb), _("users database filename")},
|
||||
{"sessiondb",NEED_ARG, NULL, 's', arg_string, APTR(&G.sessiondb), _("session database filename")},
|
||||
{"userdel", MULT_PAR, NULL, 'd', arg_string, APTR(&G.userdel), _("user to delete (maybe several)")},
|
||||
{"useradd", NO_ARGS, NULL, 'a', arg_int, APTR(&G.useradd), _("add user[s] interactively")},
|
||||
{"sdatime", NEED_ARG, NULL, 'A', arg_longlong,APTR(&G.delatime), _("minimal atime to delete sessions from DB (-1 for >year)")},
|
||||
{"sessdel", NEED_ARG, NULL, 'l', arg_string, APTR(&G.delsession),_("delete session by sessID or sockID")},
|
||||
end_option
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse command line options and return dynamically allocated structure
|
||||
* to global parameters
|
||||
* @param argc - copy of argc from main
|
||||
* @param argv - copy of argv from main
|
||||
* @return allocated structure with global parameters
|
||||
*/
|
||||
glob_pars *parse_args(int argc, char **argv){
|
||||
int i;
|
||||
size_t hlen = 1024;
|
||||
char helpstring[1024], *hptr = helpstring;
|
||||
snprintf(hptr, hlen, "Usage: %%s [args]\n\n\tWhere args are:\n");
|
||||
// format of help: "Usage: progname [args]\n"
|
||||
change_helpstring(helpstring);
|
||||
// parse arguments
|
||||
parseargs(&argc, &argv, cmdlnopts);
|
||||
if(help) showhelp(-1, cmdlnopts);
|
||||
if(argc > 0){
|
||||
G.rest_pars_num = argc;
|
||||
G.rest_pars = MALLOC(char *, argc);
|
||||
for (i = 0; i < argc; i++)
|
||||
G.rest_pars[i] = strdup(argv[i]);
|
||||
}
|
||||
return &G;
|
||||
}
|
||||
|
||||
// array with all opened logs - for error/warning messages
|
||||
static Cl_log *errlogs = NULL;
|
||||
static int errlogsnum = 0;
|
||||
|
||||
/**
|
||||
* @brief Cl_createlog - create log file: init mutex, test file open ability
|
||||
* @param log - log structure
|
||||
* @return 0 if all OK
|
||||
*/
|
||||
int Cl_createlog(Cl_log *log){
|
||||
if(!log || !log->logpath || log->loglevel >= LOGLEVEL_NONE) return 1;
|
||||
FILE *logfd = fopen(log->logpath, "a");
|
||||
if(!logfd){
|
||||
WARN("Can't open log file");
|
||||
return 2;
|
||||
}
|
||||
fclose(logfd);
|
||||
if(pthread_mutex_init(&log->mutex, NULL)){
|
||||
WARN("Can't init log mutes");
|
||||
return 3;
|
||||
}
|
||||
errlogs = realloc(errlogs, (++errlogsnum) *sizeof(Cl_log));
|
||||
if(!errlogs) errlogsnum = 0;
|
||||
else memcpy(&errlogs[errlogsnum-1], log, sizeof(Cl_log));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Cl_putlog - put message to log file with/without timestamp
|
||||
* @param timest - ==1 to put timestamp
|
||||
* @param log - pointer to log structure
|
||||
* @param lvl - message loglevel (if lvl > loglevel, message won't be printed)
|
||||
* @param fmt - format and the rest part of message
|
||||
* @return amount of symbols saved in file
|
||||
*/
|
||||
int Cl_putlogt(int timest, Cl_log *log, Cl_loglevel lvl, const char *fmt, ...){
|
||||
if(!log || !log->logpath || log->loglevel >= LOGLEVEL_NONE) return 0;
|
||||
if(lvl > log->loglevel) return 0;
|
||||
if(pthread_mutex_lock(&log->mutex)){
|
||||
WARN("Can't lock log mutex");
|
||||
return 0;
|
||||
}
|
||||
int i = 0;
|
||||
FILE *logfd = fopen(log->logpath, "a");
|
||||
if(!logfd) goto rtn;
|
||||
if(timest){
|
||||
char strtm[128];
|
||||
time_t t = time(NULL);
|
||||
struct tm *curtm = localtime(&t);
|
||||
strftime(strtm, 128, "%Y/%m/%d-%H:%M", curtm);
|
||||
i = fprintf(logfd, "%s\t", strtm);
|
||||
}
|
||||
va_list ar;
|
||||
va_start(ar, fmt);
|
||||
i += vfprintf(logfd, fmt, ar);
|
||||
va_end(ar);
|
||||
fclose(logfd);
|
||||
rtn:
|
||||
pthread_mutex_unlock(&log->mutex);
|
||||
return i;
|
||||
}
|
||||
67
cmdlnopts.h
Normal file
67
cmdlnopts.h
Normal file
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* This file is part of the Onion_test project.
|
||||
* Copyright 2020 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#ifndef CMDLNOPTS_H__
|
||||
#define CMDLNOPTS_H__
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
/*
|
||||
* here are some typedef's for global data
|
||||
*/
|
||||
typedef struct{
|
||||
int vacuum; // make "vacuum" operation
|
||||
int dumpUserDB; // dump users database
|
||||
int dumpSessDB; // dump session database
|
||||
int runServer; // run as server
|
||||
int useradd; // add user[s]
|
||||
char *port; // port to listen
|
||||
char *certfile; // file with SSL certificate
|
||||
char *keyfile; // file with SSL key
|
||||
char *usersdb; // users database name
|
||||
char *sessiondb; // session database name
|
||||
char **userdel; // user names to delete
|
||||
long long delatime; // minimal atime to delete sessions from DB
|
||||
char *delsession; // delete session by sessID or sockID
|
||||
int rest_pars_num; // number of rest parameters
|
||||
char** rest_pars; // the rest parameters: array of char*
|
||||
} glob_pars;
|
||||
|
||||
extern glob_pars G;
|
||||
|
||||
glob_pars *parse_args(int argc, char **argv);
|
||||
|
||||
typedef enum{
|
||||
LOGLEVEL_DBG, // all messages
|
||||
LOGLEVEL_MSG, // all without debug
|
||||
LOGLEVEL_WARN, // only warnings and errors
|
||||
LOGLEVEL_ERR, // only errors
|
||||
LOGLEVEL_NONE // no logs
|
||||
} Cl_loglevel;
|
||||
|
||||
typedef struct{
|
||||
char *logpath; // full path to logfile
|
||||
Cl_loglevel loglevel; // loglevel
|
||||
pthread_mutex_t mutex; // log mutex
|
||||
} Cl_log;
|
||||
|
||||
int Cl_createlog(Cl_log *log);
|
||||
int Cl_putlogt(int timest, Cl_log *log, Cl_loglevel lvl, const char *fmt, ...);
|
||||
|
||||
#endif // CMDLNOPTS_H__
|
||||
184
main.c
Normal file
184
main.c
Normal file
@ -0,0 +1,184 @@
|
||||
/*
|
||||
* This file is part of the Onion_test project.
|
||||
* Copyright 2020 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <crypt.h>
|
||||
#include <errno.h>
|
||||
#include <onion/block.h>
|
||||
#include <onion/exportlocal.h>
|
||||
#include <onion/handler.h>
|
||||
#include <onion/log.h>
|
||||
#include <onion/onion.h>
|
||||
#include <onion/shortcuts.h>
|
||||
#include <onion/types_internal.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <usefull_macros.h>
|
||||
|
||||
#include "auth.h"
|
||||
#include "cmdlnopts.h"
|
||||
|
||||
onion_connection_status get(_U_ onion_handler *h, onion_request *req, onion_response *res){
|
||||
sessinfo *session = qookieSession(req);
|
||||
if(!session){
|
||||
onion_response_write0(res, "NeedAuth");
|
||||
ONION_WARNING("try to run command without auth");
|
||||
return OCS_FORBIDDEN;
|
||||
}
|
||||
const char *path = onion_request_get_path(req);
|
||||
if(path && *path == 0) path = NULL;
|
||||
else printf("GET ask path %s\n", path);
|
||||
const char *param = getQdata(req, "param");
|
||||
if(param) printf("User set param=%s\n", param);
|
||||
if(getQdata(req, "getWSkey")){
|
||||
addWSkey(res, session);
|
||||
}
|
||||
freeSessInfo(&session);
|
||||
return OCS_CLOSE_CONNECTION;
|
||||
}
|
||||
|
||||
static onion *o = NULL;
|
||||
void signals(int signo){
|
||||
if(o) onion_free(o);
|
||||
closeSQLite();
|
||||
exit(signo);
|
||||
}
|
||||
|
||||
static void runServer(){
|
||||
o = onion_new(O_POOL);
|
||||
if(!(onion_flags(o) & O_SSL_AVAILABLE)){
|
||||
ONION_ERROR("SSL support is not available");
|
||||
signals(1);
|
||||
}
|
||||
int error = onion_set_certificate(o, O_SSL_CERTIFICATE_KEY, G.certfile, G.keyfile);
|
||||
if(error){
|
||||
ONION_ERROR("Cant set certificate and key files (%s, %s)", G.certfile, G.keyfile);
|
||||
signals(1);
|
||||
}
|
||||
onion_set_port(o, G.port);
|
||||
onion_url *url = onion_root_url(o);
|
||||
onion_url_add_handler(url, "^static/", onion_handler_export_local_new("static"));
|
||||
onion_url_add_with_data(url, "", onion_shortcut_internal_redirect, "static/index.html", NULL);
|
||||
onion_url_add(url, "^auth/", auth);
|
||||
onion_url_add(url, "^get/", get);
|
||||
signal(SIGTERM, signals);
|
||||
error = onion_listen(o);
|
||||
if(error){
|
||||
ONION_ERROR("Cant create the server: %s", strerror(errno));
|
||||
}
|
||||
onion_free(o);
|
||||
}
|
||||
|
||||
static char *getl(char *msg, int noempty){
|
||||
static char *inpstr = NULL;
|
||||
do{
|
||||
FREE(inpstr);
|
||||
printf("Enter %s: ", msg);
|
||||
size_t n = 0;
|
||||
if(getline(&inpstr, &n, stdin) < 0) FREE(inpstr);
|
||||
else{
|
||||
char *nl = strchr(inpstr, '\n');
|
||||
if(nl) *nl = 0;
|
||||
if(strlen(inpstr) < 1 && noempty){
|
||||
WARNX("Enter non-empty string");
|
||||
}else break;
|
||||
}
|
||||
}while(1);
|
||||
return inpstr;
|
||||
}
|
||||
|
||||
static void adduser(){
|
||||
userinfo *User = MALLOC(userinfo, 1);
|
||||
char *str;
|
||||
do{
|
||||
str = getl("username", 1);
|
||||
printf("Username: %s\n", str);
|
||||
User->username = strdup(str);
|
||||
do{
|
||||
str = getl("password", 1);
|
||||
if(strlen(str) < 4){
|
||||
WARNX("Bad password: not less than 4 symbols");
|
||||
}else break;
|
||||
}while(1);
|
||||
User->password = strdup(crypt(str, "$6$"));
|
||||
printf("PassHash: %s\n", User->password);
|
||||
str = getl("access level", 1);
|
||||
User->level = atoi(str);
|
||||
printf("Level: %d\n", User->level);
|
||||
str = getl("comment", 0);
|
||||
if(strlen(str)){
|
||||
User->comment = strdup(str);
|
||||
printf("Comment: %s", User->comment);
|
||||
}
|
||||
if(addUser(User)) continue;
|
||||
if(!(str = getl("next? (y/n)", 0)) || 0 != strcmp(str, "y")) break;
|
||||
}while(1);
|
||||
freeUserInfo(&User);
|
||||
}
|
||||
|
||||
extern char *onion_sessions_generate_id();
|
||||
|
||||
int main(int argc, char **argv){
|
||||
initial_setup();
|
||||
parse_args(argc, argv);
|
||||
if(!G.usersdb) ERRX("You should point users database file name");
|
||||
if(!G.sessiondb) ERRX("You should point session database file name");
|
||||
if(initSQLite(G.usersdb, G.sessiondb)) return 1;
|
||||
if(G.vacuum){
|
||||
printf("VAAAA\n");
|
||||
vacuumSQLite();
|
||||
}
|
||||
if(G.dumpUserDB) showAllUsers();
|
||||
if(G.userdel){
|
||||
for(int i = 0; G.userdel[i]; ++i){
|
||||
if(!deleteUser(G.userdel[i])) green("User %s deleted\n", G.userdel[i]);
|
||||
}
|
||||
}
|
||||
if(G.useradd) adduser();
|
||||
/*
|
||||
sessinfo *x = MALLOC(sessinfo, 1);
|
||||
x->data = strdup("some data");
|
||||
x->atime = time(NULL);
|
||||
x->username = strdup("luser");
|
||||
do{
|
||||
FREE(x->sessID);
|
||||
FREE(x->sockID);
|
||||
x->sessID = onion_sessions_generate_id();
|
||||
x->sockID = onion_sessions_generate_id();
|
||||
if(!addSession(x)){
|
||||
green("Insert session: ID=%s, sockid=%s, atime=%lld, user=%s, data=%s\n",
|
||||
x->sessID, x->sockID, x->atime, x->username, x->data);
|
||||
break;
|
||||
}
|
||||
}while(1);
|
||||
freeSessInfo(&x);
|
||||
*/
|
||||
if(G.dumpSessDB) showAllSessions();
|
||||
if(G.delsession){
|
||||
if(!deleteSession(G.delsession))
|
||||
green("Session with ID %s deleted\n", G.delsession);
|
||||
}
|
||||
if(G.delatime){
|
||||
if(G.delatime < 0) G.delatime = time(NULL) - 31556926LL;
|
||||
if(!deleteOldSessions((int64_t)G.delatime))
|
||||
green("All sessions with atime<%lld deleted\n", G.delatime);
|
||||
}
|
||||
if(G.runServer) runServer();
|
||||
closeSQLite();
|
||||
return 0;
|
||||
}
|
||||
156
static/admin.html
Normal file
156
static/admin.html
Normal file
@ -0,0 +1,156 @@
|
||||
<html>
|
||||
<head>
|
||||
<style type="text/css">
|
||||
.shadow{
|
||||
display:none;
|
||||
position:absolute;
|
||||
top:0;
|
||||
left:0;
|
||||
width:100%;
|
||||
height:100%;
|
||||
}
|
||||
.midmsg{
|
||||
position:fixed;
|
||||
top:50%;
|
||||
left:50%;
|
||||
margin-left:auto;
|
||||
margin-right:auto;
|
||||
}
|
||||
.C{text-align:center;}
|
||||
.R{text-align:right;}
|
||||
</style>
|
||||
<script src="/pass.js" type="text/javascript" language="javascript"></script>
|
||||
<script>
|
||||
var user="", passwd="", level="", url="";
|
||||
const CGI_PATH = "https://ishtar.sao.ru/cgi-bin/auth";
|
||||
function parseErr(txt){
|
||||
$('msg').innerHTML = "ïÛÉÂËÁ!<p></p>" + txt.replace("\n", "<br>");
|
||||
setTimeout(function(){$('msg').innerHTML = "";}, 3500);
|
||||
}
|
||||
function sendrequest(request, req_STR, onOK){
|
||||
var timeout_id
|
||||
request = new XMLHttpRequest();
|
||||
request.open("POST", CGI_PATH, true);
|
||||
request.setRequestHeader("Accept-Charset", "koi8-r");
|
||||
request.overrideMimeType("multipart/form-data; charset=koi8-r");
|
||||
request.setRequestHeader("Cookie", document.cookie);
|
||||
request.onreadystatechange=function(){
|
||||
if (request.readyState == 4){
|
||||
if (request.status == 200){
|
||||
clearTimeout(timeout_id);
|
||||
onOK(request);
|
||||
}
|
||||
else{
|
||||
clearTimeout(timeout_id);
|
||||
parseErr("ïÛÉÂËÁ ÐÅÒÅÄÁÞÉ ÚÁÐÒÏÓÁ. ðÏÐÒÏÂÕÊÔÅ ÅÝÅ ÒÁÚ.");
|
||||
}
|
||||
}
|
||||
}
|
||||
request.send(req_STR);
|
||||
timeout_id = setTimeout(function(){parseErr("ôÁÊÍÁÕÔ ÐÅÒÅÄÁÞÉ ÚÁÐÒÏÓÁ. ðÏÐÒÏÂÕÊÔÅ ÅÝÅ ÒÁÚ.");}, 3000);
|
||||
}
|
||||
function run(){
|
||||
var QS;
|
||||
sendrequest(QS, "admin=lsusers", lsusers);
|
||||
}
|
||||
function lsusers(QS){
|
||||
var ans = QS.responseText;
|
||||
var pars = ans.split("\n");
|
||||
var i, l, s;
|
||||
for(i=0, l=pars.length; i < l; i++){
|
||||
s = pars[i].split(";");
|
||||
if(pars[i].length < 2) continue;
|
||||
if(s.length != 3){
|
||||
parseErr(ans+"<br>str="+s);
|
||||
return;
|
||||
}
|
||||
addUsersString(s);
|
||||
}
|
||||
}
|
||||
function addUsersString(str){
|
||||
var ulist = $('userlist');
|
||||
var div = document.createElement('div');
|
||||
ulist.appendChild(div);
|
||||
var u = str[0];
|
||||
div.innerHTML = "<span>"+u+"</span> <span>"+str[1]+"</span> <span>"+str[2]+"</span> "+
|
||||
"<span onclick='delUser(\""+u+"\");'>delete</span> "+
|
||||
"<span onclick='editUser(\""+u+"\",\""+str[1]+"\",\""+str[2]+"\");'>edit</span>";
|
||||
}
|
||||
function useradd(arg){
|
||||
function pair(name){
|
||||
var str = "<div class='R'>"+name+"<input type=text id="+
|
||||
name+" onchange=\""+name+"=$('"+name+"').value;\""+
|
||||
" onblur=\""+name+"=$('"+name+"').value;\" value='"+eval(name)+"'></div>"
|
||||
return str;
|
||||
}
|
||||
var div = document.createElement('div');
|
||||
$("box").style.display = "block";
|
||||
div.className = "midmsg";
|
||||
$("box").appendChild(div);
|
||||
div.innerHTML = "<div class='C'>äÏÂÁ×ÉÔØ/ÉÚÍÅÎÉÔØ ÐÏÌØÚÏ×ÁÔÅÌÑ</div>"+
|
||||
pair("user")+pair("passwd")+pair("level")+pair("url")+
|
||||
"<div class='C'><button onclick='addUser(\""+arg+"\");'>OK</button>"+
|
||||
"<button onclick='cancelAdd();'>ïÔÍÅÎÁ</button></div>";
|
||||
}
|
||||
function chkAns(req, Msg){
|
||||
if(req.responseText.length > 0)
|
||||
parseErr(Msg);
|
||||
else{
|
||||
window.location.reload();
|
||||
}
|
||||
}
|
||||
function addUser(arg){
|
||||
var request;
|
||||
function cantadd(req){
|
||||
chkAns(req, "ÎÅ ÍÏÇÕ ÄÏÂÁ×ÉÔØ/ÒÅÄÁËÔÉÒÏ×ÁÔØ ÐÏÌØÚÏ×ÁÔÅÌÑ<br>"+req.responseText);
|
||||
}
|
||||
if(user==""){
|
||||
parseErr("ïÔÓÕÔÓÔ×ÕÅÔ ÉÍÑ ÐÏÌØÚÏ×ÁÔÅÌÑ");
|
||||
return;
|
||||
}
|
||||
if(passwd==""){
|
||||
parseErr("îÅ ÚÁÄÁÎ ÐÁÒÏÌØ ÐÏÌØÚÏ×ÁÔÅÌÑ");
|
||||
return;
|
||||
}
|
||||
if(level=="" || Number(level) < 0){
|
||||
parseErr("îÅ ÚÁÄÁÎ ÉÌÉ ÚÁÄÁÎ ÎÅ×ÅÒÎÏ ÕÒÏ×ÅÎØ ÄÏÓÔÕÐÁ ÐÏÌØÚÏ×ÁÔÅÌÑ");
|
||||
return;
|
||||
}
|
||||
if(url==""){
|
||||
parseErr("îÅ ÚÁÄÁÎ ÁÄÒÅÓ ÄÏÓÔÕÐÎÙÈ ÐÏÌØÚÏ×ÁÔÅÌÀ ÒÅÓÕÒÓÏ× (ÉÌÉ \"/\", ÅÓÌÉ ÄÏÓÔÕÐÎÏ ×ÓÅ)");
|
||||
return;
|
||||
}
|
||||
sendrequest(request,
|
||||
"admin="+arg+"&user="+user+"&passwd="+passwd+"&level="+level+"&URL="+url,
|
||||
cantadd);
|
||||
user = passwd = level = url = "";
|
||||
}
|
||||
function cancelAdd(){
|
||||
user = passwd = level = url = "";
|
||||
$("box").innerHTML = "";
|
||||
$("box").style.display = "none";
|
||||
}
|
||||
function delUser(username){
|
||||
var request;
|
||||
function cantdel(req){
|
||||
chkAns(req, "ÎÅ ÍÏÇÕ ÕÄÁÌÉÔØ ÐÏÌØÚÏ×ÁÔÅÌÑ<br>"+req.responseText);
|
||||
}
|
||||
sendrequest(request, "admin=userdel&user="+username, cantdel);
|
||||
}
|
||||
function editUser(username, ulevl, uurl){
|
||||
user = username; passwd = "";
|
||||
level = ulevl; url = uurl;
|
||||
useradd("usermod");
|
||||
}
|
||||
</script>
|
||||
<title>õÐÒÁ×ÌÅÎÉÅ ÐÏÌØÚÏ×ÁÔÅÌÑÍÉ</title>
|
||||
</head>
|
||||
<body onload="getcookie(CGI_PATH); run();">
|
||||
<div class="shadow" id="box"></div>
|
||||
<div id="inout" style="position: fixed; top: 10px; right: 10px; cursor: pointer; font-weight: bold;" onclick="inout();" >÷ÈÏÄ</div>
|
||||
<div onclick="useradd('useradd');">îÏ×ÙÊ ÐÏÌØÚÏ×ÁÔÅÌØ</div>
|
||||
<div id="userlist"></div>
|
||||
|
||||
<div id="msg" style="margin-top: 15px; color: red;"></div>
|
||||
</body>
|
||||
</html>
|
||||
71
static/auth.js
Normal file
71
static/auth.js
Normal file
@ -0,0 +1,71 @@
|
||||
auth = function(){
|
||||
var wsKey = "";
|
||||
function _ilogin(){
|
||||
$("inout").innerHTML = "Log in";
|
||||
$("inout").onclick = auth.login;
|
||||
}
|
||||
function _ilogout(){
|
||||
$("shadow").style.display = "none";
|
||||
$("inout").innerHTML = "Log out";
|
||||
$("inout").onclick = auth.logout;
|
||||
}
|
||||
function _wsk(request){
|
||||
var wsKey = request.responseText;
|
||||
if(wsKey) console.log("Web key received: " + wsKey);
|
||||
}
|
||||
function reqAuth(request){
|
||||
var txt = request.responseText;
|
||||
dbg("received " + txt);
|
||||
if(txt == "AuthOK"){ // cookies received
|
||||
sendrequest("get/?getWSkey", _wsk);
|
||||
_ilogout();
|
||||
}else if(txt == "NeedAuth"){
|
||||
_ilogin();
|
||||
}else{
|
||||
parseErr(txt);
|
||||
}
|
||||
}
|
||||
function init1(){
|
||||
sendrequest("auth/?check=1", reqAuth);
|
||||
var l = document.createElement('a');
|
||||
l.id = "inout";
|
||||
l.href = "#";
|
||||
var s1 = document.createElement('style');
|
||||
s1.type = 'text/css';
|
||||
s1.innerHTML = ".inout{position:absolute;top:0;right:0;background-color:green;"
|
||||
document.body.appendChild(s1);
|
||||
l.className = "inout";
|
||||
document.body.appendChild(l);
|
||||
var d = document.createElement('div');
|
||||
d.id = "shadow";
|
||||
var s = document.createElement('style');
|
||||
s.type = 'text/css';
|
||||
s.innerHTML = '.shadow{position:absolute;text-align:center;vertical-align:center;top:0;display:none;left:0;width:100%;height:100%;background-color:lightGrey;opacity:0.9;}';
|
||||
document.body.appendChild(s);
|
||||
d.innerHTML = "<div>Login:</div><div><input type=text id='login'></div><div>Password:</div><div><input type=password id='passwd'></div><button onclick='auth.send();'>OK</button>";
|
||||
d.className = "shadow";
|
||||
document.body.appendChild(d);
|
||||
}
|
||||
function login1(){
|
||||
$("shadow").style.display = "block";
|
||||
}
|
||||
function logout1(){
|
||||
sendrequest("auth/?LogOut=1", _ilogin);
|
||||
}
|
||||
function sendlogpass(){
|
||||
$("shadow").style.display = "none";
|
||||
var l = $("login").value, p = $("passwd").value;
|
||||
if(!l || !p){
|
||||
parseErr("give login and password");
|
||||
return;
|
||||
}
|
||||
var str = "auth/?login=" + l + "&passwd=" + p;
|
||||
sendrequest(str, reqAuth);
|
||||
}
|
||||
return{
|
||||
init: init1,
|
||||
login: login1,
|
||||
logout: logout1,
|
||||
send: sendlogpass
|
||||
};
|
||||
}();
|
||||
13
static/index.html
Normal file
13
static/index.html
Normal file
@ -0,0 +1,13 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Index</title>
|
||||
<script src="/static/requests.js" type="text/javascript" language="javascript"></script>
|
||||
<script src="/static/auth.js" type="text/javascript" language="javascript"></script>
|
||||
</head>
|
||||
<body onload="auth.init();">
|
||||
<p>Text
|
||||
<p>More text
|
||||
<p>
|
||||
<div id="errmsg" style='background-color: red;'></div>
|
||||
</body>
|
||||
</html>
|
||||
92
static/pass.html
Normal file
92
static/pass.html
Normal file
@ -0,0 +1,92 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>á×ÔÏÒÉÚÁÃÉÑ</title>
|
||||
<script>
|
||||
const CGI_PATH = "https://ishtar.sao.ru/cgi-bin/auth";
|
||||
var URL=new Array(), RTN=null, reqnmbr=0;
|
||||
function $(id){ return document.getElementById(id);}
|
||||
function getargs(){
|
||||
var QS = window.location.search.substring(1);
|
||||
var pars = QS.split("&");
|
||||
var i=0, s;
|
||||
while((s=pars[i++])){
|
||||
if(s.indexOf('URL') == 0){
|
||||
s = s.split('=');
|
||||
URL[URL.length] = s[1];
|
||||
}
|
||||
else if(s.indexOf('RTN') == 0){
|
||||
s = s.split('=');
|
||||
RTN = s[1];
|
||||
}
|
||||
}
|
||||
if(URL.length == 0){alert("ïÔÓÕÔÓÔ×ÕÅÔ URL ÄÌÑ Á×ÔÏÒÉÚÁÃÉÉ"); return;}
|
||||
if(RTN == null) RTN = URL[0];
|
||||
}
|
||||
function sendrequest(req_STR, onOK){
|
||||
var request = new XMLHttpRequest(), timeout_id;
|
||||
request.open("POST", CGI_PATH, true);
|
||||
request.setRequestHeader("Accept-Charset", "koi8-r");
|
||||
request.overrideMimeType("multipart/form-data; charset=koi8-r");
|
||||
request.onreadystatechange=function(){
|
||||
if (request.readyState == 4){
|
||||
if (request.status == 200){
|
||||
clearTimeout(timeout_id);
|
||||
if(onOK) onOK(request);
|
||||
}
|
||||
else{
|
||||
clearTimeout(timeout_id);
|
||||
parseErr("request sending error");
|
||||
}
|
||||
}
|
||||
}
|
||||
request.send(req_STR);
|
||||
timeout_id = setTimeout(function(){request.onreadystatechange=null; request.abort(); parseErr("request timeout");}, 3000);
|
||||
}
|
||||
function subm(id){
|
||||
var str, str1, i;
|
||||
var login = $('login').value;
|
||||
var pass = $('passwd').value;
|
||||
if(login == "" || pass == ""){
|
||||
if(id) $(id).focus();
|
||||
return;
|
||||
}
|
||||
var str = "login=" + login + " passwd=" + pass;
|
||||
for(i = 0; i < URL.length; i++){
|
||||
str1 = str + " URL=" + URL[i];
|
||||
sendrequest(str1);
|
||||
}
|
||||
}
|
||||
function onOK(request){
|
||||
var txt = request.responseText;
|
||||
if(txt.indexOf("KEY") != 0){
|
||||
parseErr(txt);
|
||||
return;
|
||||
}
|
||||
var n = txt.indexOf('\n');
|
||||
if(n) txt = txt.substring(0, n);
|
||||
var d = new Date();
|
||||
d.setTime(d.getTime() + 72e6); // ÓÒÏË ÄÅÊÓÔ×ÉÑ ËÕËÉ - 20 ÞÁÓÏ×
|
||||
txt += "; expires="+d.toGMTString();
|
||||
document.cookie = txt;
|
||||
if(++reqnmbr == URL.length) document.location.href = RTN;
|
||||
}
|
||||
function parseErr(txt){
|
||||
console.log("Error: " + txt);
|
||||
$('msg').innerHTML = "Error: " + txt;
|
||||
setTimeout(function(){$('msg').innerHTML = "";}, 3500);
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body onload="getargs();">
|
||||
<div align="center">
|
||||
<h2>ðÏÖÁÌÕÊÓÔÁ ××ÅÄÉÔÅ ÒÅÇÉÓÔÒÁÃÉÏÎÎÕÀ ÉÎÆÏÒÍÁÃÉÀ ÄÌÑ ÐÏÌÕÞÅÎÉÑ ÄÏÓÔÕÐÁ Ë ÓÅÒ×ÉÓÁÍ</h2>
|
||||
<div style="border: 1px solid; text-align: center; width: 200px; margin: 0 auto; padding: 5px;" id="pass">
|
||||
<div>éÍÑ:</div><div><input type=text id="login" onchange="subm('passwd');"></div>
|
||||
<div>ðÁÒÏÌØ:</div><div><input type=password id="passwd" onchange="subm('login');"></div><br>
|
||||
<div align=center><button onclick="subm();">OK</button></div>
|
||||
</div>
|
||||
<div id="msg" style="margin-top: 15px; color: red;"></div>
|
||||
</div>
|
||||
<body>
|
||||
</html>
|
||||
56
static/pass.js
Normal file
56
static/pass.js
Normal file
@ -0,0 +1,56 @@
|
||||
// move this file to the root html directory
|
||||
// change const's EXURL & PASSURL
|
||||
var KEY;
|
||||
const PASSURL="https://ishtar.sao.ru/pass";
|
||||
const EXURL = "https://ishtar.sao.ru/cgi-bin/auth";
|
||||
function $(id){
|
||||
return document.getElementById(id);
|
||||
}
|
||||
function checkcookie(){
|
||||
var txt = document.cookie;
|
||||
if(txt.length==0 || txt.indexOf('KEY')<0){
|
||||
$("inout").innerHTML = "÷ÏÊÔÉ";
|
||||
return 0;
|
||||
}
|
||||
else{
|
||||
$("inout").innerHTML = "÷ÙÊÔÉ";
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
function getcookie(){
|
||||
/* ÂÅÚ ÁÒÇÕÍÅÎÔÏ× - ÄÌÑ ÔÅËÕÝÅÊ ÓÔÒÁÎÉÃÙ,
|
||||
ËÁÖÄÙÊ ÁÒÇÕÍÅÎÔ - ÄÏÐ. "ÐÅÞÅÎØËÁ"
|
||||
*/
|
||||
var i, newurl = PASSURL+"?URL="+document.location.href;
|
||||
for(i = 0; i < getcookie.arguments.length; i++)
|
||||
newurl += "&URL=" + getcookie.arguments[i];
|
||||
if(!checkcookie())
|
||||
document.location.href = newurl;
|
||||
}
|
||||
function onEX(){
|
||||
var d = new Date();
|
||||
d.setTime(d.getTime() - 1000);
|
||||
var str = "KEY=; expires="+d.toGMTString()+"; path="+document.location.pathname;
|
||||
document.cookie = str;
|
||||
window.location.reload();
|
||||
}
|
||||
function exit(){
|
||||
var request = new XMLHttpRequest();
|
||||
request.open("POST", EXURL, true);
|
||||
request.setRequestHeader("Accept-Charset", "koi8-r");
|
||||
request.setRequestHeader("Cookie", document.cookie);
|
||||
request.overrideMimeType("multipart/form-data; charset=koi8-r");
|
||||
request.onreadystatechange=function(){
|
||||
if (request.readyState == 4){
|
||||
if (request.status == 200){
|
||||
onEX();
|
||||
}
|
||||
else alert("ïÛÉÂËÁ ÓÏÅÄÉÎÅÎÉÑ");
|
||||
}
|
||||
}
|
||||
request.send("")
|
||||
}
|
||||
function inout(){
|
||||
if(checkcookie()) exit();
|
||||
else getcookie();
|
||||
}
|
||||
43
static/requests.js
Normal file
43
static/requests.js
Normal file
@ -0,0 +1,43 @@
|
||||
const Debug = true;
|
||||
|
||||
var elementsCache = {};
|
||||
function $(id) {
|
||||
if (elementsCache[id] === undefined)
|
||||
elementsCache[id] = document.getElementById(id);
|
||||
return elementsCache[id];
|
||||
}
|
||||
|
||||
function dbg(text){
|
||||
if(Debug) console.log("Debug message: " + text);
|
||||
}
|
||||
|
||||
function sendrequest(req_STR, onOK, postdata){
|
||||
var request = new XMLHttpRequest(), timeout_id;
|
||||
dbg("send request " + req_STR);
|
||||
var method = postdata ? "POST" : "GET";
|
||||
request.open(method, req_STR, true);
|
||||
//request.setRequestHeader("Accept-Charset", "koi8-r");
|
||||
//request.overrideMimeType("multipart/form-data; charset=koi8-r");
|
||||
request.onreadystatechange=function(){
|
||||
if(request.readyState == 4){
|
||||
if(request.status == 200){
|
||||
clearTimeout(timeout_id);
|
||||
if(onOK) onOK(request);
|
||||
}
|
||||
else{
|
||||
clearTimeout(timeout_id);
|
||||
parseErr("request sending error");
|
||||
}
|
||||
}
|
||||
}
|
||||
request.send(postdata);
|
||||
timeout_id = setTimeout(function(){request.onreadystatechange=null; request.abort(); parseErr("request timeout");}, 5000);
|
||||
}
|
||||
|
||||
function parseErr(txt){
|
||||
console.log("Error: " + txt);
|
||||
var msgDiv = $('errmsg');
|
||||
if(!msgDiv) return;
|
||||
msgDiv.innerHTML = "Error: " + txt;
|
||||
setTimeout(function(){msgDiv.innerHTML = "";}, 3500);
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user