Add the Bird Control Plane gateway, signed webhook ingestion, policy-gated writes, fail-closed production auto-activation, and RSA-OAEP bootstrap credential flow.
244 lines
7.9 KiB
PHP
244 lines
7.9 KiB
PHP
<?php
|
|
|
|
namespace traits;
|
|
|
|
use classes\db;
|
|
|
|
trait module_config_t
|
|
{
|
|
public string $module_name; // The name of the module
|
|
public array $config_classes; // An array of classes that contain config variables for the module, that's allowed to be updated by the user using the API
|
|
|
|
/**
|
|
* Set up the config variable
|
|
* @param string $module_name
|
|
* @return void
|
|
*/
|
|
function setupConfig(string $module_name): void
|
|
{
|
|
$this->module_name = $module_name;
|
|
}
|
|
|
|
/**
|
|
* Allow the user to update the config variables
|
|
* @param array $config_classes
|
|
* @return void
|
|
*/
|
|
function allowUpdate(array $config_classes): void
|
|
{
|
|
$this->config_classes = $config_classes;
|
|
}
|
|
|
|
/**
|
|
* Post config request
|
|
* @return bool
|
|
*/
|
|
function postConfigRequest(): bool
|
|
{
|
|
global $response;
|
|
$parameters = $response->getAllRequestParameters();
|
|
|
|
if (array_key_exists('variable', $parameters) || array_key_exists('value', $parameters)) {
|
|
if ($response->getRequestParameter('variable') === null || !$response->isRequestParameterSet('value')) {
|
|
$response->error('Variable and value not set', 400);
|
|
}
|
|
|
|
$variable = $response->getRequestParameter('variable');
|
|
$value = $response->getRequestParameter('value');
|
|
return $this->setAllowedConfigVariable((string)$variable, $value);
|
|
}
|
|
|
|
if ($parameters === []) {
|
|
$response->error('Variable and value not set', 400);
|
|
}
|
|
|
|
foreach ($parameters as $variable => $value) {
|
|
$this->setAllowedConfigVariable((string)$variable, $value);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private function setAllowedConfigVariable(string $variable, mixed $value): bool
|
|
{
|
|
global $response;
|
|
// Check if the variable is allowed to be updated
|
|
if (!self::isVariableAllowed($variable)) {
|
|
// Return an error message, telling the user that the variable is not allowed to be updated. With a list of allowed variables
|
|
$allowed_variables = [];
|
|
foreach ( $this->config_classes as $config_class ) {
|
|
$allowed_variables[] = (new $config_class())->getVariableName();
|
|
}
|
|
$allowed_variables = implode(', ', $allowed_variables);
|
|
$response->error("Variable not allowed to be updated. Allowed variables: $allowed_variables", 400);
|
|
}
|
|
// Update the config variable using the config class that contains the variable
|
|
foreach ( $this->config_classes as $config_class ) {
|
|
// Create a new instance of the config class
|
|
$tmp = new $config_class();
|
|
// Check if the variable matches the variable name in the config class
|
|
if ($tmp->getVariableName() == $variable) {
|
|
// Update the config variable
|
|
$tmp->setVariableValue($value);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Check if the variable is allowed to be updated
|
|
* @param string $variable
|
|
* @return bool
|
|
*/
|
|
function isVariableAllowed(string $variable): bool
|
|
{
|
|
foreach ( $this->config_classes as $config_class ) {
|
|
// Create a new instance of the config class
|
|
$tmp = new $config_class();
|
|
// Check if the variable matches the variable name in the config class
|
|
if ($tmp->getVariableName() == $variable) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
function getConfigRequest(): array
|
|
{
|
|
// Get the config variable
|
|
global /** @var db $db */
|
|
$db;
|
|
// Check if the request has a variable name
|
|
if (!isset($_GET['variable'])) {
|
|
return self::getConfig();
|
|
}
|
|
// Get the variable name
|
|
$variable = $_GET['variable'];
|
|
// Sanitize the variable name
|
|
$variable = $db->escape_string($variable);
|
|
$module = $db->escape_string($this->module_name);
|
|
// Get the config variable
|
|
$sql = "SELECT * FROM module_config WHERE module = '$module' AND variable = '$variable'";
|
|
return $this->extracted($db, $sql);
|
|
}
|
|
|
|
/**
|
|
* Get all config variables for the module
|
|
* @return array
|
|
*/
|
|
function getConfig(): array
|
|
{
|
|
global $db;
|
|
$module = $db->escape_string($this->module_name);
|
|
$sql = "SELECT * FROM module_config WHERE module = '$module'";
|
|
return $this->extracted($db, $sql);
|
|
}
|
|
|
|
|
|
/**
|
|
* @param $db
|
|
* @param string $sql
|
|
* @return array
|
|
*/
|
|
private function extracted($db, string $sql): array
|
|
{
|
|
$result = $db->query($sql);
|
|
$config = [];
|
|
$secretVariables = $this->secretConfigVariables();
|
|
while ($row = $result->fetch_assoc()) {
|
|
$variable = (string)$row['variable'];
|
|
$isSecret = isset($secretVariables[$variable]);
|
|
$rawValue = $row['value'];
|
|
$isSet = $rawValue !== null && trim((string)$rawValue) !== '';
|
|
$config[] = [
|
|
'module' => $row['module'],
|
|
'variable' => $variable,
|
|
'type' => $row['type'],
|
|
// Existing configuration screens use a non-empty value only as
|
|
// a "configured" indicator. Preserve that contract without
|
|
// returning the stored secret; new consumers should use isSet.
|
|
'value' => $isSecret ? ($isSet ? '[redacted]' : '') : $this->parseConfigVariableType($row['type'], $rawValue),
|
|
'isSecret' => $isSecret,
|
|
'isSet' => $isSet,
|
|
];
|
|
}
|
|
return $config;
|
|
}
|
|
|
|
/**
|
|
* @return array<string,true>
|
|
*/
|
|
private function secretConfigVariables(): array
|
|
{
|
|
$variables = [];
|
|
foreach ($this->config_classes ?? [] as $configClass) {
|
|
$definition = new $configClass();
|
|
if (($definition->config_variable_is_secret ?? false) !== true
|
|
|| !method_exists($definition, 'getVariableName')) {
|
|
continue;
|
|
}
|
|
$variables[(string)$definition->getVariableName()] = true;
|
|
}
|
|
return $variables;
|
|
}
|
|
|
|
function parseConfigVariableType($type, $value)
|
|
{
|
|
// If the variable is an int, convert it to an int
|
|
if ($type == 'integer' || $type == 'int') {
|
|
if ($value === null || $value === '') {
|
|
return null;
|
|
}
|
|
return (int)$value;
|
|
}
|
|
// If the variable is a boolean, convert it to a boolean
|
|
if ($type == 'bool') {
|
|
return $value == 'true';
|
|
}
|
|
// If the variable is a float, convert it to a float
|
|
if ($type == 'float') {
|
|
return (float)$value;
|
|
}
|
|
// If the variable is a JSON string, convert it to an array
|
|
if ($type == 'json') {
|
|
return json_decode($value, true);
|
|
}
|
|
// If the variable is a string, convert it to a string
|
|
if ($type == 'string') {
|
|
return (string)$value;
|
|
}
|
|
// If the variable is an array, convert it to an array
|
|
if ($type == 'array') {
|
|
return explode(',', $value);
|
|
}
|
|
// If the variable is a date, convert it to a date
|
|
if ($type == 'date') {
|
|
return date('Y-m-d', strtotime($value));
|
|
}
|
|
// If the variable is a time, convert it to a time
|
|
if ($type == 'time') {
|
|
return date('H:i:s', strtotime($value));
|
|
}
|
|
// If the variable is a datetime, convert it to a datetime
|
|
if ($type == 'datetime') {
|
|
return date('Y-m-d H:i:s', strtotime($value));
|
|
}
|
|
// If the variable is a timestamp, convert it to a timestamp
|
|
if ($type == 'timestamp') {
|
|
return strtotime($value);
|
|
}
|
|
return $value;
|
|
}
|
|
|
|
/**
|
|
* Get the module name
|
|
* @return string
|
|
*/
|
|
function getModuleName(): string
|
|
{
|
|
return $this->module_name;
|
|
}
|
|
}
|