feat: Implement module permissions system with database-driven access control

- Added `users_permissions` table for managing user permissions.
- Created `PermissionRepository` for handling permission logic.
- Refactored `controls\Users::permissions()` to utilize the new database structure.
- Introduced AJAX endpoint for saving user permissions.
- Enhanced user management UI with permission checkboxes.
- Added vacation management template for handling employee absences.
- Implemented tests for `PermissionRepository`.
This commit is contained in:
2026-02-26 20:17:03 +01:00
parent 76d3ac33a8
commit a4a35c8d62
35 changed files with 2654 additions and 901 deletions

View File

@@ -1,98 +0,0 @@
<?
namespace controls;
class BackendSites
{
static public function topic_delete()
{
if ( \factory\BackendSites::topic_delete( \S::get( 'id' ) ) )
{
\S::alert( 'Temat został usunięty' );
header( 'Location: /backend_sites/topics/' );
exit;
}
else
{
\S::alert( 'Nie można usunąć tematu' );
header( 'Location: /backend_sites/topics/' );
exit;
}
}
static public function topic_accept()
{
if ( \factory\BackendSites::topic_accept( \S::get( 'id' ) ) )
{
\S::alert( 'Temat został zaakceptowany' );
header( 'Location: /backend_sites/topics/' );
exit;
}
}
static public function topic_unaccept()
{
if ( \factory\BackendSites::topic_unaccept( \S::get( 'id' ) ) )
{
\S::alert( 'Temat został odrzucony' );
header( 'Location: /backend_sites/topics/' );
exit;
}
}
static public function topic_save()
{
$response = [ 'status' => 'error', 'msg' => 'Podczas zapisywania klienta wystąpił błąd. Proszę spróbować ponownie.' ];
$values = \S::json_to_array( \S::get( 'values' ) );
$id = \factory\BackendSites::topic_save(
(int)$values[ 'id' ], $values[ 'strona' ], $values[ 'kategoria' ], $values[ 'kategoria_id' ], $values['link'], $values[ 'temat' ], $values['wygeneruj_temat'], $values[ 'data_publikacji' ], $values[ 'opublikowany' ], $values[ 'zaakceptowany' ]
);
if ( $id )
$response = [ 'status' => 'ok', 'msg' => 'Projekt został zapisany.', 'id' => $id ];
echo json_encode( $response );
exit;
}
static public function topic_edit()
{
return \Tpl::view( 'backend_sites/topic_edit', [
'topic' => \factory\BackendSites::topic( (int)\S::get( 'id' ) )
] );
}
static public function topics()
{
return \Tpl::view( 'backend_sites/topics' );
}
static public function collective_topics()
{
return \Tpl::view( 'backend_sites/collective_topics' );
}
static public function collective_topic_edit()
{
return \Tpl::view( 'backend_sites/collective_topic_edit',[
'collective_topic' => \factory\BackendSites::collective_topic( \S::get( 'id' ) ),
] );
}
static public function collective_topic_save()
{
$response = [ 'status' => 'error', 'msg' => 'Podczas zapisywania klienta wystąpił błąd. Proszę spróbować ponownie.' ];
$values = \S::json_to_array( \S::get( 'values' ) );
$id = \factory\BackendSites::collective_topic_save(
(int)$values[ 'id' ], $values[ 'strona' ], $values[ 'kategoria' ], $values[ 'kategoria_id' ], $values[ 'temat_ogolny' ], $values[ 'data_przetworzenia' ], $values[ 'przetworzony' ]
);
if ( $id )
$response = [ 'status' => 'ok', 'msg' => 'Projekt został zapisany.', 'id' => $id ];
echo json_encode( $response );
exit;
}
}

View File

@@ -6,40 +6,23 @@ class Users
public static function permissions( $user_id, $module = '', $action = '' )
{
// Pyziak Jacek
$permissions[ 1 ][ 'tasks' ] = true;
$permissions[ 1 ][ 'projects' ] = true;
$permissions[ 1 ][ 'finances' ] = true;
$permissions[ 1 ][ 'wiki' ] = true;
$permissions[ 1 ][ 'crm' ] = true;
$permissions[ 1 ][ 'work_time' ] = true;
$permissions[ 1 ][ 'zaplecze' ] = true;
// Pyziak Grzegorz
$permissions[ 3 ][ 'tasks' ] = true;
$permissions[ 3 ][ 'projects' ] = true;
$permissions[ 3 ][ 'finances' ] = true;
$permissions[ 3 ][ 'wiki' ] = true;
$permissions[ 3 ][ 'crm' ] = true;
$permissions[ 3 ][ 'work_time' ] = true;
$permissions[ 3 ][ 'zaplecze' ] = true;
// Roman Pyrih
$permissions[ 5 ][ 'tasks' ] = true;
$permissions[ 5 ][ 'projects' ] = false;
$permissions[ 5 ][ 'finances' ] = false;
$permissions[ 5 ][ 'wiki' ] = true;
$permissions[ 5 ][ 'crm' ] = false;
$permissions[ 5 ][ 'work_time' ] = false;
// Superadmin has full access
if ( (int)$user_id === 1 )
return true;
if ( $action and isset( $permissions[ $user_id ][ $module ][ $action ] ) )
// Cache permissions per user to avoid repeated DB queries
static $cache = [];
if ( !isset( $cache[ $user_id ] ) )
{
return $permissions[ $user_id ][ $module ][ $action ];
$repo = new \Domain\Users\PermissionRepository();
$cache[ $user_id ] = $repo -> byUserId( (int)$user_id );
}
if ( isset( $permissions[ $user_id ][ $module ] ) )
{
return $permissions[ $user_id ][ $module ];
}
if ( $module && isset( $cache[ $user_id ][ $module ] ) )
return (bool)$cache[ $user_id ][ $module ];
// If module not in permissions list, allow by default
return true;
}
@@ -54,16 +37,53 @@ class Users
exit;
}
public static function settings_save()
public static function password_change()
{
global $mdb, $user;
if ( \factory\Users::settings_save( $user[ 'id' ], \S::get( 'pushover_api' ), \S::get( 'pushover_user' ) ) )
if ( !$user )
{
$user = $mdb -> get( 'users', '*', [ 'id' => $user[ 'id' ] ] );
\S::set_session( 'user', $user );
\S::alert( 'Ustawienia zostały zapisane.' );
header( 'Location: /' );
exit;
}
$password_old = \S::get( 'password_old' );
$password_new = \S::get( 'password_new' );
if ( !$password_old || !$password_new )
{
\S::alert( 'Wypełnij oba pola.' );
header( 'Location: /users/settings/' );
exit;
}
$db_user = $mdb -> get( 'users', '*', [ 'id' => $user['id'] ] );
if ( !$db_user )
{
\S::alert( 'Stare hasło jest nieprawidłowe.' );
header( 'Location: /users/settings/' );
exit;
}
$password_ok = password_verify( $password_old, $db_user['password'] )
|| md5( $password_old ) === $db_user['password'];
if ( !$password_ok )
{
\S::alert( 'Stare hasło jest nieprawidłowe.' );
header( 'Location: /users/settings/' );
exit;
}
$mdb -> update( 'users', [
'password' => password_hash( $password_new, PASSWORD_BCRYPT )
], [
'id' => $user['id']
] );
\S::alert( 'Hasło zostało zmienione.' );
header( 'Location: /users/settings/' );
exit;
}
@@ -84,23 +104,22 @@ class Users
public static function login()
{
if ( $user = \factory\Users::login( \S::get( 'email' ), md5( \S::get( 'password' ) ) ) )
global $mdb;
if ( $user = \factory\Users::login( \S::get( 'email' ), \S::get( 'password' ) ) )
{
// zapamiętaj logowanie
$domain = preg_replace( '#^(http(s)?://)?w{3}\.#', '$1', $_SERVER['SERVER_NAME'] );
$cookie_name = str_replace( '.', '-', $domain );
if ( \S::get( 'remember' ) )
{
$domain = preg_replace( '#^(http(s)?://)?w{3}\.#', '$1', $_SERVER['SERVER_NAME'] );
$cookie_name = str_replace( '.', '-', $domain );
$value = [ 'email' => \S::get( 'email' ), 'hash' => md5( \S::get( 'password' ) ) ];
$value = json_encode( $value );
setcookie( $cookie_name, $value, strtotime( "+1 year" ), "/", $domain );
$token = bin2hex( random_bytes( 32 ) );
$mdb -> update( 'users', [ 'remember_token' => $token ], [ 'id' => $user['id'] ] );
setcookie( $cookie_name, $token, strtotime( "+1 year" ), "/", $domain, true, true );
}
else
{
$domain = preg_replace( '#^(http(s)?://)?w{3}\.#', '$1', $_SERVER['SERVER_NAME'] );
$cookie_name = str_replace( '.', '-', $domain );
$mdb -> update( 'users', [ 'remember_token' => null ], [ 'id' => $user['id'] ] );
setcookie( $cookie_name, "", strtotime( "-1 year" ), "/", $domain );
}