Files
nibiru-framework.com/application/module/users/plugins/user.php
stephan 48c839d927 Initial public push: docs cosmos v4 + AI module + framework groundwork
This is the snapshot the production landing site (nibiru-framework.com) is
deployed from. Brings together the recent splash + docs migration to the v4
"Cosmos" design system, the new in-framework AI module, and the framework
groundwork that backs the framework-reference extraction.

What lands:
- docs/: Astro + Starlight site with the v4 dark cosmic palette, GalaxyHero
  canvas constellation, Mission Control chat (wired to /api/oracle →
  api.neuronetz.ai via providers.mjs Ollama), 5-panel MMVC stage
  (Model · AI · Module · Controller · View), translated EN/DE/JA/ES/FR
  content, PWA + sitemap + llms.txt + Umami analytics.
- docs/design-system/: canonical mockup bundle (source/index-v2.html for
  splash, source/docs-system.html + preview/ for docs, SPEC.md, tokens).
- docs/scripts/extraction/framework-reference-v2.md: deep framework
  reference (~1.6k lines, file:line citations, every public factory and
  idiom — basis for the LoRA training corpus.
- application/module/ai/: AI module with chat / embed / RAG / agent
  plugins, plus pdoQuery / httpGet / fileRead tools and Modelfile +
  smoke-test in training/.
- application/module/users/: user / ACL / form-factory traits used as the
  reference plugin pattern for the framework docs.
- application/settings/config/database/: schema + seed migrations
  including the AI module tables (200–203).
- Form factory + autogenerator changes the framework-reference-v2 covers.

Production secrets stay out: docs/.env, settings.production.ini and
ai.production.ini are all gitignored (.example files are in tree).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 15:22:18 +02:00

516 lines
21 KiB
PHP
Executable File

<?php
namespace Nibiru\Module\Users\Plugins;
/**
* Class Users
* @package Nibiru\Module\Users\Plugins
* @author: Stephan Kasdorf
* @date: 04.09.20
* @copyright: 2020 Nibiru Framework, you may copy the code,
* but have to inform the author about where it
* is used. So happy copying.
* @licence: BSD 4-Old License
*/
use Nibiru\Auth;
use Nibiru\Controller;
use Nibiru\Config;
use Nibiru\Module\Error\Plugins\Message;
use Nibiru\Module\Memcached\Plugins\Memcached;
use Nibiru\Module\Users\Traits\UserForm;
use Nibiru\Module\Users\Users;
use Nibiru\Factory\Db;
use Nibiru\IView;
use Nibiru\Pdo;
use Nibiru\Model\WarehouseLoach;
class User extends Users
{
use UserForm;
//check from auth in order to determine if user may login to the system
private bool $_auth;
private object $userRegistry;
private string $status;
private int $userId;
public function __construct()
{
parent::__construct();
return $this;
}
/**
* @desc authenticates user in the system and stores the session in order to verify the
* the login, then set the session and return true if the user is logged in.
* Will update the timeanddate_time field in the timeanddate table with the current time.
* @return bool
*/
public function validate( ): bool
{
if(array_key_exists('login', Controller::getInstance()->getRequest('', true)) && array_key_exists('password', Controller::getInstance()->getRequest('', true)))
{
$this->_auth = Auth::getInstance()->auth(
Controller::getInstance()->getRequest('login'),
Controller::getInstance()->getRequest('password'));
if($this->_auth)
{
$timeanddateToUser = Db::loadModel('WarehouseLoach\timeanddateToUser')->selectDatasetByFieldWhere(['name' => 'user_id', 'value' => Controller::getInstance()->getSession('auth')['user_id']]);
$timeanddate_id = array_shift($timeanddateToUser)['timeanddate_id'];
Db::loadModel('WarehouseLoach\timeanddate')->updateRowByFieldWhere('timeanddate_id', $timeanddate_id, 'timeanddate_time', date('Y-m-d H:i:s'));
$this->setUserIdToSessionCookie();
}
}
else
{
$this->_auth = $this->isAuthorized();
}
return $this->_auth;
}
/**
* @desc will return true if the user is authorized otherwise false
* @return bool
*/
public function isAuthorized(): bool
{
if(array_key_exists('auth', Controller::getInstance()->getSession('', true)))
{
if(array_key_exists('login', Controller::getInstance()->getSession('auth')))
{
return true;
}
else
{
return false;
}
}
else
{
return false;
}
}
/**
* @desc will log out the user and destroy the session
* @return bool
*/
public function revokeAuthorized(): bool
{
setcookie('user_id', '', [
'expires' => time() - 3600, // set the cookie to expire one hour ago
'path' => '/',
'secure' => false,
'httponly' => true,
]);
Memcached::init()->runServer()->delete($_SESSION['auth']['user_id']);
unset($_SESSION['auth']);
return true;
}
/**
* @desc will update the password of the user, check if the value $user_id is set, if not it will use the current logged in user.
* @param int $user_id
* @param string $password
* @return bool
*/
public function updatePassword(string $password, int $user_id): bool
{
if($user_id == 0)
{
$user_id = $_SESSION['auth']['user_id'];
}
return Pdo::query('UPDATE user SET user_pass = DES_ENCRYPT("'.$password.'", "'.Config::getInstance()->getConfig()[IView::NIBIRU_SECURITY]["password_hash"].'") WHERE user_id = '.$user_id.';');
}
/**
* @desc will return true if the standard user is present otherwise false
* @return bool
* @throws \Exception
*/
public function checkForStandardUser(): bool
{
$userTable = Db::loadModel('WarehouseLoach\user')->loadTableAsArray();
if(sizeof($userTable)==0)
{
return false;
}
else
{
$standardUser = array_search(self::getUsersRegistry()->user_login, array_column($userTable, 'user_login'));
if($standardUser === false)
{
return false;
}
else
{
return true;
}
}
}
/**
* @desc will check if the user exists in the database
* @param string $user_login
* @return bool
*/
public function checkIfUserExists(string $user_login): bool
{
$userTable = Db::loadModel('WarehouseLoach\user')->loadTableAsArray();
$user = array_search($user_login, array_column($userTable, 'user_login'));
if($user === false)
{
return false;
}
else
{
return true;
}
}
/**
* @desc create an object that loads the userdata from the registry, if the user data is not passed by the method
* parameter. Also prepares the user data for user updates in the database.
* @param array $user_data
* @param bool $isUpdate
* @return bool
*/
public function loadUser( array $user_data = [], bool $isUpdate = false ): bool
{
$this->userRegistry = new \stdClass();
if(sizeof($user_data) == 0)
{
foreach(self::REGISTRY_USER_KEYS as $key)
{
$this->userRegistry->$key = self::getUsersRegistry()->$key;
}
return Message::loadInfo('User loaded', ['info' => json_decode(json_encode($this->userRegistry), true)]);
}
else
{
try {
if(!$this->checkIfUserExists($user_data['user_login']) || $isUpdate)
{
foreach(self::REGISTRY_USER_KEYS as $key)
{
if($user_data[$key] == self::FORM_CHECKBOX_ON || $user_data[$key] == self::FORM_CHECKBOX_OFF)
{
$user_data[$key] = self::FORM_CHECKBOX_SWITCH[$user_data[$key]];
}
$this->userRegistry->$key = $user_data[$key];
}
if($isUpdate)
{
$this->userRegistry->user_id = $user_data['user_id'];
}
return Message::loadInfo('User loaded', ['info' => json_decode(json_encode($this->userRegistry), true)]);
} else
{
throw new \Exception('User already exists');
}
} catch (\Exception $e) {
return Message::loadError($e);
}
}
}
/**
* @desc will create the initial user for the system, if no user is setup.
* Can be found in the application/module/users/settings/users.ini
* file.
* @return int|bool
* @throws \Exception
*/
public function createStandardUser(): int|bool
{
try {
Db::loadModel('WarehouseLoach\user')->insertArrayIntoTable([
'user_firstname' => $this->userRegistry->user_firstname,
'user_lastname' => $this->userRegistry->user_lastname,
'user_login' => $this->userRegistry->user_login,
'user_account_active' => $this->userRegistry->user_active
]);
$lastUserId = Db::loadModel('WarehouseLoach\user')->lastInsertId();
Db::loadModel('WarehouseLoach\timeanddate')->insertArrayIntoTable([
]);
$lastTimeanddateId = Db::loadModel('WarehouseLoach\timeanddate')->lastInsertId();
Db::loadModel('WarehouseLoach\timeanddateToUser')->insertArrayIntoTable([
'timeanddate_id' => $lastTimeanddateId,
'user_id' => $lastUserId
]);
Db::loadModel('WarehouseLoach\userToAcl')->insertArrayIntoTable([
'user_id' => $lastUserId,
'acl_id' => $this->userRegistry->user_acl_id
]);
Db::loadModel('WarehouseLoach\account')->insertArrayIntoTable([
'account_name' => $this->userRegistry->user_account_name,
'account_email' => $this->userRegistry->user_account_email,
'account_active' => $this->userRegistry->user_account_active
]);
$lastAccountId = Db::loadModel('WarehouseLoach\account')->lastInsertId();
Db::loadModel('WarehouseLoach\timeanddate')->insertArrayIntoTable([
]);
$lastTimeanddateId = Db::loadModel('WarehouseLoach\timeanddate')->lastInsertId();
Db::loadModel('WarehouseLoach\timeanddateToAccount')->insertArrayIntoTable([
'account_id' => $lastAccountId,
'timeanddate_id' => $lastTimeanddateId
]);
Db::loadModel('WarehouseLoach\userToAccount')->insertArrayIntoTable([
'account_id' => $lastAccountId,
'user_id' => $lastUserId
]);
$setPassword = Pdo::query('UPDATE user SET user_pass = DES_ENCRYPT("'.$this->userRegistry->user_pass.'", "'.Config::getInstance()->getConfig()[IView::NIBIRU_SECURITY]["password_hash"].'") WHERE user_id = '.$lastUserId.';');
if(empty($setPassword))
{
return $lastUserId;
}
else
{
throw new \Exception('Could not set password for user '.$lastUserId . ' with exception value: ' . $setPassword . '!');
}
} catch (\Exception $e) {
return Message::loadError($e);
}
}
/**
* @desc will return the role of the logged in user
* @return array
* @throws \Exception
*/
public function getRoleByLoggedInUser(): array
{
$userToAcl = Db::loadModel('WarehouseLoach\userToAcl')->selectRowByFieldWhere([
'field' => 'user_id',
'value' => $_SESSION['auth']['user_id']
]);
return Db::loadModel('WarehouseLoach\acl')->selectRowByFieldWhere([
'field' => 'acl_id',
'value' => $userToAcl['acl_id']
]);
}
/**
* @desc will load all users from the database and return them as an array
* @return array
*/
public function loadUsers(): array
{
return Pdo::queryString('select u.user_id as uid,
u.user_firstname as vorname,
u.user_lastname as nachname,
u.user_login as login,
u.user_account_active as aktiv,
a.acl_role as berechtigung,
tad.timeanddate_date as datum_benutzer,
tad.timeanddate_time as zeit_benutzer,
ac.account_name as konto,
ac.account_email as konto_email,
ac.account_active as konto_aktiv,
tadc.timeanddate_date as datum_konto,
tadc.timeanddate_time as zeit_konto
from warehouse_loach.user as u
join warehouse_loach.user_to_acl as uta on u.user_id = uta.user_id
join warehouse_loach.acl as a on uta.acl_id = a.acl_id
join warehouse_loach.timeanddate_to_user as ttu on u.user_id = ttu.user_id
join warehouse_loach.timeanddate as tad on ttu.timeanddate_id = tad.timeanddate_id
join user_to_account uta2 on u.user_id = uta2.user_id
join account ac on uta2.account_id = ac.account_id
join timeanddate_to_account tta on ac.account_id = tta.account_id
join timeanddate tadc on tta.timeanddate_id = tadc.timeanddate_id;', true);
}
/**
* @desc will load a user by the given id
* @param int $user_id
* @return array
*/
public function loadUserById( int $user_id ): array
{
$user = Pdo::queryString('select u.user_id as uid,
u.user_firstname as user_firstname,
u.user_lastname as user_lastname,
u.user_login as user_login,
DES_DECRYPT(u.user_pass, "'.Config::getInstance()->getConfig()[IView::NIBIRU_SECURITY]["password_hash"].'") as user_pass,
u.user_account_active as user_account_active,
a.acl_id as user_role_id,
ac.account_name as user_account_name,
ac.account_active as user_account_active,
ac.account_email as user_account_email
from warehouse_loach.user as u
join warehouse_loach.user_to_acl as uta on u.user_id = uta.user_id
join warehouse_loach.acl as a on uta.acl_id = a.acl_id
join warehouse_loach.timeanddate_to_user as ttu on u.user_id = ttu.user_id
join warehouse_loach.timeanddate as tad on ttu.timeanddate_id = tad.timeanddate_id
join user_to_account uta2 on u.user_id = uta2.user_id
join account ac on uta2.account_id = ac.account_id
join timeanddate_to_account tta on ac.account_id = tta.account_id
join timeanddate tadc on tta.timeanddate_id = tadc.timeanddate_id
where u.user_id = '.$user_id.';', true);
$user = array_shift($user);
return $user;
}
/**
* @desc will delete a user by the given id
* @param int $user_id
* @return bool
*/
public function deleteUserById( int $user_id ): bool
{
try {
$user = $this->loadUserById($user_id);
if($user['user_role_id'] == 1)
{
unset($user);
throw new \Exception('You can not delete the superuser!');
}
else
{
$userToAccount = Pdo::queryString('SELECT account_id FROM user_to_account WHERE user_id = '.$user_id.';', true);
$account_id = array_shift($userToAccount)["account_id"];
$timeanddateToUser = Pdo::queryString('SELECT timeanddate_id FROM timeanddate_to_user WHERE user_id = '.$user_id.';', true);
$timeanddate_id = array_shift($timeanddateToUser)["timeanddate_id"];
Pdo::query('DELETE FROM user_to_acl WHERE user_id = '.$user_id.';');
Pdo::query('DELETE FROM user_to_account WHERE user_id = '.$user_id.';');
Pdo::query('DELETE FROM timeanddate_to_user WHERE user_id = '.$user_id.';');
Pdo::query('DELETE FROM timeanddate_to_account WHERE account_id = '.$account_id.';');
Pdo::query('DELETE FROM user WHERE user_id = '.$user_id.';');
Pdo::query('DELETE FROM account WHERE account_id = '.$account_id.';');
Pdo::query('DELETE FROM timeanddate WHERE timeanddate_id = '.$timeanddate_id.';');
unset($user['user_pass']);
return Message::loadInfo("User deleted successfully", $user);
}
} catch (\Exception $e) {
return Message::loadError($e);
}
}
/**
* @desc will set the user active or inactive
* @param int $user_id
* @param int $active
* @return bool
*/
public function setUserActive( int $user_id, int $active ): bool
{
try {
if($active)
{
$state = '<span class="badge badge-success">Aktiv</span>';
}
else
{
$state = '<span class="badge badge-danger">Inaktiv</span>';
}
$user = $this->loadUserById($user_id);
if($user['user_role_id'] == 1)
{
unset($user);
throw new \Exception('You can not deactivate the superuser!');
}
else
{
Db::loadModel('WarehouseLoach\User')->updateRowByFieldWhere('user_id', $user_id, 'user_account_active', $active);
unset($user['user_pass']);
return Message::loadInfo("User updated active state successfully: $state", $user);
}
} catch (\Exception $e) {
return Message::loadError($e);
}
}
/**
* @desc will update the user by the given user array
* @return bool
* @throws \Exception
*/
public function updateUser(): bool
{
try {
$user = (array) $this->userRegistry;
$userString = '';
foreach(WarehouseLoach\User::TABLE['fields'] as $field)
{
if(array_key_exists($field, $user))
{
if($field == 'user_pass')
{
$userString .= $field.' = DES_ENCRYPT("'.$user[$field].'", "'.Config::getInstance()->getConfig()[IView::NIBIRU_SECURITY]["password_hash"].'"), ';
}
else
{
if($field != 'user_id')
{
$userString .= $field.' = "'.$user[$field].'", ';
}
}
}
}
Pdo::query('UPDATE user SET '.substr($userString, 0, -2).' WHERE user_id = '.$user['user_id'].';');
$userToAccount = Db::loadModel('WarehouseLoach\userToAccount')->selectDatasetByFieldWhere(['name' => 'user_id', 'value' => $user['user_id']]);
$account_id = array_shift($userToAccount)['account_id'];
if(isset($account_id))
{
$accountString = '';
foreach(WarehouseLoach\Account::TABLE['fields'] as $field)
{
foreach($user as $key => $value)
{
if(strstr($key, $field))
{
$accountString .= $field.' = "'.$user[$key].'", ';
}
}
}
Pdo::query('UPDATE account SET '.substr($accountString, 0, -2).' WHERE account_id = '.$account_id.';');
}
else
{
throw new \Exception('Did not get valid Account ID, the database could be corrupted for that user! user_id: ' . $user['user_id']);
}
Db::loadModel('WarehouseLoach\userToAcl')->updateRowByFieldWhere('user_id', $user['user_id'], 'acl_id', $user['user_acl_id']);
} catch (\Exception $e) {
Message::loadError($e);
}
}
/**
* @desc will set the user_id in a cookie, in order to find the memcached session
* @return void
*/
public function setUserIdToSessionCookie(): void
{
if(!array_key_exists('user_id', $_COOKIE) && array_key_exists('auth', $_SESSION))
{
$ivLength = openssl_cipher_iv_length($this->getUsersRegistry()->{self::USER_ID_ENCRYPTION});
$iv = openssl_random_pseudo_bytes($ivLength);
$encryptedUserId = openssl_encrypt($_SESSION['auth']['user_id'], $this->getUsersRegistry()->{self::USER_ID_ENCRYPTION}, $this->getUsersRegistry()->{self::USER_ID_ENCRYPTION_KEY}, 0, $iv);
$cookieValue = base64_encode($encryptedUserId . '::' . $iv);
setcookie('user_id', $cookieValue, [
'expires' => time() + (86400 * 30),
'path' => '/',
'secure' => false, // only send cookie over https
'httponly' => true, // make the cookie inaccessible to javascript
]);
}
}
/**
* @desc will get the user_id from the cookie
* @return int
*/
public function getUserIdFromSessionCookie(): int
{
if(array_key_exists('user_id', $_COOKIE))
{
list($encryptedUserId, $iv) = explode('::', base64_decode($_COOKIE['user_id']), 2);
$decryptedUserId = openssl_decrypt($encryptedUserId, $this->getUsersRegistry()->{self::USER_ID_ENCRYPTION}, $this->getUsersRegistry()->{self::USER_ID_ENCRYPTION_KEY}, 0, $iv);
return $decryptedUserId;
}
else
{
return 0;
}
}
}