Implement access control enhancements in ChatRoom and related classes
- Introduced a new overloaded method `accessAllowed` in `ChatRoom` to include user age verification for adult rooms. - Added `falukant_user_id` method in `ChatUser` to retrieve user ID for age checks. - Implemented `getUserAge` method in `ChatUser` to fetch and decrypt user birthdate from the database for age validation. - Updated `roomAllowed` methods in `Server` and `SSLServer` to utilize the new access control logic, ensuring proper user context is considered. - Enhanced debugging output for better traceability during access checks.
This commit is contained in:
@@ -559,7 +559,40 @@ namespace Yc
|
|||||||
|
|
||||||
bool ChatRoom::accessAllowed(std::string userName, std::string password)
|
bool ChatRoom::accessAllowed(std::string userName, std::string password)
|
||||||
{
|
{
|
||||||
return (_allowedUsers.size() == 0 || _password == "" || _password == password || std::find(_allowedUsers.begin(), _allowedUsers.end(), userName) != _allowedUsers.end());
|
return accessAllowed(userName, password, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ChatRoom::accessAllowed(std::string userName, std::string password, std::shared_ptr<ChatUser> user)
|
||||||
|
{
|
||||||
|
// Basis-Zugriffsprüfung (Passwort, erlaubte User)
|
||||||
|
bool basicAccess = (_allowedUsers.size() == 0 || _password == "" || _password == password || std::find(_allowedUsers.begin(), _allowedUsers.end(), userName) != _allowedUsers.end());
|
||||||
|
|
||||||
|
if (!basicAccess) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Altersprüfung: Wenn min_age >= 18 (Adult-Mode), muss User mindestens 18 sein
|
||||||
|
int minAge = _room.min_age();
|
||||||
|
if (minAge >= 18 && _database && user) {
|
||||||
|
int falukantUserId = user->falukant_user_id();
|
||||||
|
if (falukantUserId > 0) {
|
||||||
|
int userAge = ChatUser::getUserAge(_database, falukantUserId);
|
||||||
|
if (userAge > 0 && userAge < 18) {
|
||||||
|
#ifdef YC_DEBUG
|
||||||
|
std::cout << "[Debug] ChatRoom: User " << userName << " (age " << userAge << ") denied access to adult room (min_age: " << minAge << ")" << std::endl;
|
||||||
|
#endif
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Wenn kein falukant_user_id vorhanden, verweigere Zugang zu Adult-Räumen
|
||||||
|
#ifdef YC_DEBUG
|
||||||
|
std::cout << "[Debug] ChatRoom: User " << userName << " (no falukant_user_id) denied access to adult room (min_age: " << minAge << ")" << std::endl;
|
||||||
|
#endif
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ChatRoom::userIsInRoom(std::string userName)
|
bool ChatRoom::userIsInRoom(std::string userName)
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ namespace Yc
|
|||||||
void sendToAllUsers(ChatUser::MsgType type, Json::Value message);
|
void sendToAllUsers(ChatUser::MsgType type, Json::Value message);
|
||||||
unsigned int addDice(std::shared_ptr<ChatUser> user, int diceValue);
|
unsigned int addDice(std::shared_ptr<ChatUser> user, int diceValue);
|
||||||
bool accessAllowed(std::string userName, std::string password);
|
bool accessAllowed(std::string userName, std::string password);
|
||||||
|
bool accessAllowed(std::string userName, std::string password, std::shared_ptr<ChatUser> user);
|
||||||
bool userIsInRoom(std::string userName);
|
bool userIsInRoom(std::string userName);
|
||||||
std::shared_ptr<ChatUser> findUserByName(std::string userName);
|
std::shared_ptr<ChatUser> findUserByName(std::string userName);
|
||||||
std::shared_ptr<ChatUser> findUserByToken(std::string token);
|
std::shared_ptr<ChatUser> findUserByToken(std::string token);
|
||||||
|
|||||||
@@ -16,6 +16,8 @@
|
|||||||
#include <netinet/tcp.h>
|
#include <netinet/tcp.h>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
|
#include <ctime>
|
||||||
|
#include <iomanip>
|
||||||
|
|
||||||
namespace Yc
|
namespace Yc
|
||||||
{
|
{
|
||||||
@@ -446,6 +448,11 @@ namespace Yc
|
|||||||
return _token;
|
return _token;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int ChatUser::falukant_user_id() const
|
||||||
|
{
|
||||||
|
return _user.falukant_user_id();
|
||||||
|
}
|
||||||
|
|
||||||
bool ChatUser::validateToken(std::string token)
|
bool ChatUser::validateToken(std::string token)
|
||||||
{
|
{
|
||||||
return (token == _token);
|
return (token == _token);
|
||||||
@@ -461,6 +468,73 @@ namespace Yc
|
|||||||
return _wsi != nullptr;
|
return _wsi != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int ChatUser::getUserAge(std::shared_ptr<Database> database, int falukantUserId)
|
||||||
|
{
|
||||||
|
if (!database || falukantUserId <= 0) {
|
||||||
|
return 0; // Kein Alter verfügbar
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Hole verschlüsseltes Geburtsdatum aus user_param mit JOIN auf type.user_param
|
||||||
|
std::string query = R"(
|
||||||
|
SELECT up.value
|
||||||
|
FROM community.user_param up
|
||||||
|
JOIN "type".user_param tp ON up.param_type_id = tp.id
|
||||||
|
WHERE up.user_id = )" + std::to_string(falukantUserId) + R"(
|
||||||
|
AND tp.description = 'birthdate'
|
||||||
|
LIMIT 1;
|
||||||
|
)";
|
||||||
|
auto result = database->exec(query);
|
||||||
|
|
||||||
|
if (result.size() > 0 && !result[0]["value"].is_null()) {
|
||||||
|
std::string encryptedValue = result[0]["value"].as<std::string>();
|
||||||
|
|
||||||
|
// Entschlüssele den Wert
|
||||||
|
std::string decryptedValue = Yc::Lib::Tools::decryptAES256ECB(encryptedValue);
|
||||||
|
|
||||||
|
if (decryptedValue.empty()) {
|
||||||
|
#ifdef YC_DEBUG
|
||||||
|
std::cerr << "[Debug] ChatUser: Failed to decrypt birthdate for user ID " << falukantUserId << std::endl;
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse Datum (Format: YYYY-MM-DD)
|
||||||
|
std::tm tm = {};
|
||||||
|
std::istringstream ss(decryptedValue);
|
||||||
|
ss >> std::get_time(&tm, "%Y-%m-%d");
|
||||||
|
|
||||||
|
if (ss.fail()) {
|
||||||
|
#ifdef YC_DEBUG
|
||||||
|
std::cerr << "[Debug] ChatUser: Failed to parse birthdate: " << decryptedValue << std::endl;
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Berechne Alter
|
||||||
|
std::time_t now = std::time(nullptr);
|
||||||
|
std::tm* nowTm = std::localtime(&now);
|
||||||
|
|
||||||
|
int age = nowTm->tm_year + 1900 - (tm.tm_year + 1900);
|
||||||
|
if (nowTm->tm_mon < tm.tm_mon ||
|
||||||
|
(nowTm->tm_mon == tm.tm_mon && nowTm->tm_mday < tm.tm_mday)) {
|
||||||
|
age--;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef YC_DEBUG
|
||||||
|
std::cout << "[Debug] ChatUser: Loaded age from database for user ID " << falukantUserId << ": " << age << std::endl;
|
||||||
|
#endif
|
||||||
|
return age;
|
||||||
|
}
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
#ifdef YC_DEBUG
|
||||||
|
std::cerr << "[Debug] ChatUser: Error loading age from database: " << e.what() << std::endl;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0; // Kein Alter verfügbar
|
||||||
|
}
|
||||||
|
|
||||||
std::string ChatUser::loadColorFromDatabase(std::shared_ptr<Database> database, std::string userName)
|
std::string ChatUser::loadColorFromDatabase(std::shared_ptr<Database> database, std::string userName)
|
||||||
{
|
{
|
||||||
if (!database) {
|
if (!database) {
|
||||||
|
|||||||
@@ -41,10 +41,12 @@ namespace Yc
|
|||||||
~ChatUser();
|
~ChatUser();
|
||||||
std::string name() const;
|
std::string name() const;
|
||||||
std::string getToken() const;
|
std::string getToken() const;
|
||||||
|
int falukant_user_id() const;
|
||||||
bool validateToken(std::string token);
|
bool validateToken(std::string token);
|
||||||
bool isUser(std::shared_ptr<ChatUser> toValidate);
|
bool isUser(std::shared_ptr<ChatUser> toValidate);
|
||||||
bool isWebSocket() const;
|
bool isWebSocket() const;
|
||||||
static std::string loadColorFromDatabase(std::shared_ptr<Database> database, std::string userName);
|
static std::string loadColorFromDatabase(std::shared_ptr<Database> database, std::string userName);
|
||||||
|
static int getUserAge(std::shared_ptr<Database> database, int falukantUserId);
|
||||||
void sendMsg(MsgType type, std::string message, std::string userName, std::string color);
|
void sendMsg(MsgType type, std::string message, std::string userName, std::string color);
|
||||||
void sendMsg(MsgType type, const char *message, std::string userName, std::string color);
|
void sendMsg(MsgType type, const char *message, std::string userName, std::string color);
|
||||||
void sendMsg(MsgType type, Json::Value message, std::string userName, std::string color);
|
void sendMsg(MsgType type, Json::Value message, std::string userName, std::string color);
|
||||||
|
|||||||
@@ -224,6 +224,10 @@ namespace Yc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool Server::roomAllowed(std::string roomName, std::string userName, std::string password){
|
bool Server::roomAllowed(std::string roomName, std::string userName, std::string password){
|
||||||
|
return roomAllowed(roomName, userName, password, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Server::roomAllowed(std::string roomName, std::string userName, std::string password, std::shared_ptr<ChatUser> user){
|
||||||
#ifdef YC_DEBUG
|
#ifdef YC_DEBUG
|
||||||
std::cout << "[Debug] roomAllowed called with roomName: '" << roomName << "', userName: '" << userName << "'" << std::endl;
|
std::cout << "[Debug] roomAllowed called with roomName: '" << roomName << "', userName: '" << userName << "'" << std::endl;
|
||||||
std::cout << "[Debug] Available rooms: ";
|
std::cout << "[Debug] Available rooms: ";
|
||||||
@@ -237,7 +241,7 @@ namespace Yc {
|
|||||||
#ifdef YC_DEBUG
|
#ifdef YC_DEBUG
|
||||||
std::cout << "[Debug] Checking room: '" << room->name() << "' against requested: '" << roomName << "'" << std::endl;
|
std::cout << "[Debug] Checking room: '" << room->name() << "' against requested: '" << roomName << "'" << std::endl;
|
||||||
#endif
|
#endif
|
||||||
if (room->name() == roomName && room->accessAllowed(userName, password)) {
|
if (room->name() == roomName && room->accessAllowed(userName, password, user)) {
|
||||||
#ifdef YC_DEBUG
|
#ifdef YC_DEBUG
|
||||||
std::cout << "[Debug] Room found and access allowed!" << std::endl;
|
std::cout << "[Debug] Room found and access allowed!" << std::endl;
|
||||||
#endif
|
#endif
|
||||||
@@ -251,7 +255,7 @@ namespace Yc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool Server::changeRoom(std::shared_ptr<ChatUser> user, std::string newRoom, std::string password) {
|
bool Server::changeRoom(std::shared_ptr<ChatUser> user, std::string newRoom, std::string password) {
|
||||||
if (!roomAllowed(newRoom, user->name(), password)) {
|
if (!roomAllowed(newRoom, user->name(), password, user)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ namespace Yc {
|
|||||||
std::vector<std::string> roomList();
|
std::vector<std::string> roomList();
|
||||||
Json::Value jsonRoomList();
|
Json::Value jsonRoomList();
|
||||||
bool roomAllowed(std::string roomName, std::string userName, std::string password);
|
bool roomAllowed(std::string roomName, std::string userName, std::string password);
|
||||||
|
bool roomAllowed(std::string roomName, std::string userName, std::string password, std::shared_ptr<ChatUser> user);
|
||||||
bool changeRoom(std::shared_ptr<ChatUser> user, std::string newRoom, std::string password);
|
bool changeRoom(std::shared_ptr<ChatUser> user, std::string newRoom, std::string password);
|
||||||
int _socket;
|
int _socket;
|
||||||
std::shared_ptr<Yc::Lib::Config> _config;
|
std::shared_ptr<Yc::Lib::Config> _config;
|
||||||
|
|||||||
@@ -892,8 +892,12 @@ std::vector<std::string> SSLServer::roomList() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool SSLServer::roomAllowed(const std::string& roomName, const std::string& userName, const std::string& password) {
|
bool SSLServer::roomAllowed(const std::string& roomName, const std::string& userName, const std::string& password) {
|
||||||
|
return roomAllowed(roomName, userName, password, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SSLServer::roomAllowed(const std::string& roomName, const std::string& userName, const std::string& password, std::shared_ptr<ChatUser> user) {
|
||||||
for (auto &room: _rooms) {
|
for (auto &room: _rooms) {
|
||||||
if (room->name() == roomName && room->accessAllowed(userName, password)) {
|
if (room->name() == roomName && room->accessAllowed(userName, password, user)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -901,7 +905,7 @@ bool SSLServer::roomAllowed(const std::string& roomName, const std::string& user
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool SSLServer::changeRoom(std::shared_ptr<ChatUser> user, const std::string& newRoom, const std::string& password) {
|
bool SSLServer::changeRoom(std::shared_ptr<ChatUser> user, const std::string& newRoom, const std::string& password) {
|
||||||
if (!roomAllowed(newRoom, user->name(), password)) {
|
if (!roomAllowed(newRoom, user->name(), password, user)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ public:
|
|||||||
// Room management
|
// Room management
|
||||||
std::vector<std::string> roomList();
|
std::vector<std::string> roomList();
|
||||||
bool roomAllowed(const std::string& roomName, const std::string& userName, const std::string& password);
|
bool roomAllowed(const std::string& roomName, const std::string& userName, const std::string& password);
|
||||||
|
bool roomAllowed(const std::string& roomName, const std::string& userName, const std::string& password, std::shared_ptr<ChatUser> user);
|
||||||
bool changeRoom(std::shared_ptr<ChatUser> user, const std::string& newRoom, const std::string& password);
|
bool changeRoom(std::shared_ptr<ChatUser> user, const std::string& newRoom, const std::string& password);
|
||||||
|
|
||||||
// User management
|
// User management
|
||||||
|
|||||||
@@ -2,6 +2,14 @@
|
|||||||
|
|
||||||
#include <random>
|
#include <random>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
|
#include <openssl/evp.h>
|
||||||
|
#include <openssl/kdf.h>
|
||||||
|
#include <openssl/err.h>
|
||||||
|
#include <iomanip>
|
||||||
|
#include <sstream>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
namespace Yc {
|
namespace Yc {
|
||||||
namespace Lib {
|
namespace Lib {
|
||||||
@@ -29,5 +37,84 @@ namespace Yc {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string Tools::decryptAES256ECB(const std::string& encryptedHex) {
|
||||||
|
try {
|
||||||
|
// Lade SECRET_KEY aus Umgebungsvariable
|
||||||
|
const char* secretEnv = std::getenv("SECRET_KEY");
|
||||||
|
std::string secret = secretEnv ? secretEnv : "DEV_FALLBACK_SECRET";
|
||||||
|
|
||||||
|
// Generiere 32-Byte Schlüssel mit scrypt (wie in Node.js: scryptSync(secret, 'salt', 32))
|
||||||
|
unsigned char key[32];
|
||||||
|
const char* salt = "salt";
|
||||||
|
|
||||||
|
if (EVP_PBE_scrypt(secret.c_str(), secret.length(),
|
||||||
|
(const unsigned char*)salt, strlen(salt),
|
||||||
|
16384, 8, 1, 0, // N=16384, r=8, p=1, maxmem=0 (unbegrenzt)
|
||||||
|
key, 32) != 1) {
|
||||||
|
#ifdef YC_DEBUG
|
||||||
|
std::cerr << "[Debug] Tools: Error generating scrypt key" << std::endl;
|
||||||
|
#endif
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Konvertiere Hex-String zu Bytes
|
||||||
|
std::vector<unsigned char> encryptedBytes;
|
||||||
|
for (size_t i = 0; i < encryptedHex.length(); i += 2) {
|
||||||
|
if (i + 1 >= encryptedHex.length()) break;
|
||||||
|
std::string byteStr = encryptedHex.substr(i, 2);
|
||||||
|
unsigned char byte = static_cast<unsigned char>(std::stoul(byteStr, nullptr, 16));
|
||||||
|
encryptedBytes.push_back(byte);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (encryptedBytes.empty()) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Entschlüssele mit AES-256-ECB (ECB verwendet keinen IV, daher nullptr)
|
||||||
|
EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
|
||||||
|
if (!ctx) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (EVP_DecryptInit_ex(ctx, EVP_aes_256_ecb(), nullptr, key, nullptr) != 1) {
|
||||||
|
EVP_CIPHER_CTX_free(ctx);
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Padding aktivieren (PKCS7)
|
||||||
|
EVP_CIPHER_CTX_set_padding(ctx, 1);
|
||||||
|
|
||||||
|
std::vector<unsigned char> decryptedBytes(encryptedBytes.size() + EVP_CIPHER_block_size(EVP_aes_256_ecb()));
|
||||||
|
int outlen = 0;
|
||||||
|
int finallen = 0;
|
||||||
|
|
||||||
|
if (EVP_DecryptUpdate(ctx, decryptedBytes.data(), &outlen,
|
||||||
|
encryptedBytes.data(), encryptedBytes.size()) != 1) {
|
||||||
|
EVP_CIPHER_CTX_free(ctx);
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (EVP_DecryptFinal_ex(ctx, decryptedBytes.data() + outlen, &finallen) != 1) {
|
||||||
|
EVP_CIPHER_CTX_free(ctx);
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
EVP_CIPHER_CTX_free(ctx);
|
||||||
|
|
||||||
|
// Kombiniere Output
|
||||||
|
int totalLen = outlen + finallen;
|
||||||
|
if (totalLen > 0 && totalLen <= static_cast<int>(decryptedBytes.size())) {
|
||||||
|
return std::string(reinterpret_cast<const char*>(decryptedBytes.data()), totalLen);
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
#ifdef YC_DEBUG
|
||||||
|
std::cerr << "[Debug] Tools: Error decrypting: " << e.what() << std::endl;
|
||||||
|
#endif
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Lib
|
} // namespace Lib
|
||||||
} // namespace Yp
|
} // namespace Yp
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ namespace Yc {
|
|||||||
public:
|
public:
|
||||||
Tools();
|
Tools();
|
||||||
static std::string generateRandomString(size_t length = 16);
|
static std::string generateRandomString(size_t length = 16);
|
||||||
|
static std::string decryptAES256ECB(const std::string& encryptedHex);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Lib
|
} // namespace Lib
|
||||||
|
|||||||
Reference in New Issue
Block a user