new file: CHANGELOG.md

modified:   README.md
	modified:   support-provisioning-portal/assets/portal.css
	modified:   support-provisioning-portal/assets/portal.js
	modified:   support-provisioning-portal/includes/class-spp-activator.php
	modified:   support-provisioning-portal/includes/class-spp-admin-page.php
	modified:   support-provisioning-portal/includes/class-spp-http-proxmox-client.php
	modified:   support-provisioning-portal/includes/class-spp-mock-proxmox-client.php
	new file:   support-provisioning-portal/includes/class-spp-permissions.php
	modified:   support-provisioning-portal/includes/class-spp-plugin.php
	modified:   support-provisioning-portal/includes/class-spp-repository.php
	modified:   support-provisioning-portal/includes/class-spp-rest-controller.php
	modified:   support-provisioning-portal/includes/class-spp-shortcode.php
	modified:   support-provisioning-portal/includes/interface-spp-proxmox-client.php
	modified:   support-provisioning-portal/support-provisioning-portal.php
This commit is contained in:
Sven Steinert
2026-04-24 15:13:42 +02:00
parent aee79ddbfa
commit 2c1949bf1e
15 changed files with 1900 additions and 170 deletions

View File

@@ -0,0 +1,181 @@
<?php
if (!defined('ABSPATH')) {
exit;
}
final class SPP_Permissions
{
public const META_KEY = 'spp_permissions';
public const VIEW_PORTAL = 'view_portal';
public const CREATE_DEPLOYMENTS = 'create_deployments';
public const START_DEPLOYMENTS = 'start_deployments';
public const STOP_DEPLOYMENTS = 'stop_deployments';
public const PROLONG_DEPLOYMENTS = 'prolong_deployments';
public const REFRESH_DEPLOYMENT_IPS = 'refresh_deployment_ips';
public const DELETE_DEPLOYMENTS = 'delete_deployments';
public const MANAGE_ALL_DEPLOYMENTS = 'manage_all_deployments';
public const MANAGE_TEMPLATES = 'manage_templates';
public const MANAGE_SETTINGS = 'manage_settings';
public const MANAGE_PERMISSIONS = 'manage_permissions';
/**
* @return array<string, string>
*/
public static function definitions(): array
{
return [
self::VIEW_PORTAL => 'Open portal and view deployments',
self::CREATE_DEPLOYMENTS => 'Create deployments',
self::START_DEPLOYMENTS => 'Start deployments',
self::STOP_DEPLOYMENTS => 'Stop deployments',
self::PROLONG_DEPLOYMENTS => 'Prolong deployments',
self::REFRESH_DEPLOYMENT_IPS => 'Refresh IP addresses',
self::DELETE_DEPLOYMENTS => 'Delete deployments',
self::MANAGE_ALL_DEPLOYMENTS => 'View and manage all deployments',
self::MANAGE_TEMPLATES => 'Manage templates',
self::MANAGE_SETTINGS => 'Manage Proxmox settings and quotas',
self::MANAGE_PERMISSIONS => 'Manage user rights',
];
}
/**
* @return array<string, array<int, string>>
*/
public static function groups(): array
{
return [
'Portal' => [
self::VIEW_PORTAL,
self::CREATE_DEPLOYMENTS,
],
'Lifecycle' => [
self::START_DEPLOYMENTS,
self::STOP_DEPLOYMENTS,
self::PROLONG_DEPLOYMENTS,
self::REFRESH_DEPLOYMENT_IPS,
self::DELETE_DEPLOYMENTS,
],
'Administration' => [
self::MANAGE_ALL_DEPLOYMENTS,
self::MANAGE_TEMPLATES,
self::MANAGE_SETTINGS,
self::MANAGE_PERMISSIONS,
],
];
}
/**
* @param array<mixed> $permissions
* @return array<int, string>
*/
public static function sanitize_permissions(array $permissions): array
{
$valid = array_keys(self::definitions());
$selected = [];
foreach ($permissions as $permission) {
$permission = sanitize_key((string) $permission);
if (in_array($permission, $valid, true) && !in_array($permission, $selected, true)) {
$selected[] = $permission;
}
}
$portal_rights = array_diff($selected, [
self::VIEW_PORTAL,
self::MANAGE_TEMPLATES,
self::MANAGE_SETTINGS,
self::MANAGE_PERMISSIONS,
]);
if (!empty($portal_rights) && !in_array(self::VIEW_PORTAL, $selected, true)) {
$selected[] = self::VIEW_PORTAL;
}
return array_values(array_intersect($valid, $selected));
}
public function current_user_has(string $permission): bool
{
if (!array_key_exists($permission, self::definitions())) {
return false;
}
if ($this->user_has(get_current_user_id(), $permission)) {
return true;
}
return $this->has_bootstrap_access();
}
public function current_user_has_any(): bool
{
return !empty($this->current_user_permissions());
}
/**
* @return array<int, string>
*/
public function current_user_permissions(): array
{
if ($this->has_bootstrap_access()) {
return array_keys(self::definitions());
}
return $this->for_user(get_current_user_id());
}
public function user_has(int $user_id, string $permission): bool
{
if (!array_key_exists($permission, self::definitions())) {
return false;
}
return in_array($permission, $this->for_user($user_id), true);
}
/**
* @return array<int, string>
*/
public function for_user(int $user_id): array
{
if ($user_id < 1) {
return [];
}
$permissions = get_user_meta($user_id, self::META_KEY, true);
return is_array($permissions) ? self::sanitize_permissions($permissions) : [];
}
/**
* @return array<int, int>
*/
public function user_ids_with_permission(string $permission): array
{
if (!array_key_exists($permission, self::definitions())) {
return [];
}
global $wpdb;
$like = '%"' . $wpdb->esc_like($permission) . '"%';
$ids = $wpdb->get_col(
$wpdb->prepare(
"SELECT user_id FROM {$wpdb->usermeta} WHERE meta_key = %s AND meta_value LIKE %s",
self::META_KEY,
$like
)
);
return array_values(array_map('intval', is_array($ids) ? $ids : []));
}
private function has_bootstrap_access(): bool
{
return is_user_logged_in()
&& current_user_can('manage_options')
&& empty($this->user_ids_with_permission(self::MANAGE_PERMISSIONS));
}
}