forked from fa/breadcrumb-the-shire
311 lines
9.6 KiB
PHP
311 lines
9.6 KiB
PHP
|
|
<?php
|
||
|
|
|
||
|
|
namespace MintyPHP\Module\Helpdesk\Service;
|
||
|
|
|
||
|
|
use MintyPHP\Service\Settings\SettingsCryptoGatewayInterface;
|
||
|
|
use MintyPHP\Service\Settings\SettingsMetadataGateway;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Gateway for helpdesk BC connection settings stored in the DB settings table.
|
||
|
|
*
|
||
|
|
* All secrets (passwords, client secrets) are encrypted via SettingsCryptoGateway.
|
||
|
|
* Setting keys are prefixed with 'helpdesk.' to avoid collisions.
|
||
|
|
*/
|
||
|
|
class HelpdeskSettingsGateway
|
||
|
|
{
|
||
|
|
public const KEY_AUTH_MODE = 'helpdesk.bc_auth_mode';
|
||
|
|
public const KEY_ODATA_BASE_URL = 'helpdesk.bc_odata_base_url';
|
||
|
|
public const KEY_COMPANY_NAME = 'helpdesk.bc_company_name';
|
||
|
|
public const KEY_BASIC_USER = 'helpdesk.bc_basic_user';
|
||
|
|
public const KEY_BASIC_PASSWORD_ENC = 'helpdesk.bc_basic_password_enc';
|
||
|
|
public const KEY_OAUTH_TENANT_ID = 'helpdesk.bc_oauth_tenant_id';
|
||
|
|
public const KEY_OAUTH_CLIENT_ID = 'helpdesk.bc_oauth_client_id';
|
||
|
|
public const KEY_OAUTH_CLIENT_SECRET_ENC = 'helpdesk.bc_oauth_client_secret_enc';
|
||
|
|
public const KEY_OAUTH_TOKEN_ENDPOINT = 'helpdesk.bc_oauth_token_endpoint';
|
||
|
|
|
||
|
|
public const AUTH_MODE_BASIC = 'basic';
|
||
|
|
public const AUTH_MODE_OAUTH2 = 'oauth2';
|
||
|
|
|
||
|
|
public const DEFAULT_ODATA_BASE_URL = 'https://bc.icoreon.de:7048/BusinessCentral-NUP/ODataV4';
|
||
|
|
public const DEFAULT_COMPANY_NAME = 'breadcrumb mediasolutions GmbH';
|
||
|
|
|
||
|
|
public function __construct(
|
||
|
|
private readonly SettingsMetadataGateway $settingsMetadataGateway,
|
||
|
|
private readonly SettingsCryptoGatewayInterface $settingsCryptoGateway
|
||
|
|
) {
|
||
|
|
}
|
||
|
|
|
||
|
|
public function getAuthMode(): string
|
||
|
|
{
|
||
|
|
$value = $this->settingsMetadataGateway->getValue(self::KEY_AUTH_MODE);
|
||
|
|
$value = trim((string) ($value ?? ''));
|
||
|
|
|
||
|
|
return $value === self::AUTH_MODE_OAUTH2 ? self::AUTH_MODE_OAUTH2 : self::AUTH_MODE_BASIC;
|
||
|
|
}
|
||
|
|
|
||
|
|
public function setAuthMode(string $mode): bool
|
||
|
|
{
|
||
|
|
$mode = trim($mode);
|
||
|
|
if (!in_array($mode, [self::AUTH_MODE_BASIC, self::AUTH_MODE_OAUTH2], true)) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
return $this->settingsMetadataGateway->set(self::KEY_AUTH_MODE, $mode, 'helpdesk.bc_auth_mode');
|
||
|
|
}
|
||
|
|
|
||
|
|
public function getODataBaseUrl(): string
|
||
|
|
{
|
||
|
|
$value = $this->settingsMetadataGateway->getValue(self::KEY_ODATA_BASE_URL);
|
||
|
|
$value = trim((string) ($value ?? ''));
|
||
|
|
|
||
|
|
return $value !== '' ? rtrim($value, '/') : self::DEFAULT_ODATA_BASE_URL;
|
||
|
|
}
|
||
|
|
|
||
|
|
public function setODataBaseUrl(?string $url): bool
|
||
|
|
{
|
||
|
|
$url = trim((string) ($url ?? ''));
|
||
|
|
if ($url !== '' && !preg_match('#^https://#i', $url)) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
return $this->settingsMetadataGateway->set(
|
||
|
|
self::KEY_ODATA_BASE_URL,
|
||
|
|
$url !== '' ? rtrim($url, '/') : null,
|
||
|
|
'helpdesk.bc_odata_base_url'
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function getCompanyName(): string
|
||
|
|
{
|
||
|
|
$value = $this->settingsMetadataGateway->getValue(self::KEY_COMPANY_NAME);
|
||
|
|
$value = trim((string) ($value ?? ''));
|
||
|
|
|
||
|
|
return $value !== '' ? $value : self::DEFAULT_COMPANY_NAME;
|
||
|
|
}
|
||
|
|
|
||
|
|
public function setCompanyName(?string $name): bool
|
||
|
|
{
|
||
|
|
$name = trim((string) ($name ?? ''));
|
||
|
|
|
||
|
|
return $this->settingsMetadataGateway->set(
|
||
|
|
self::KEY_COMPANY_NAME,
|
||
|
|
$name !== '' ? $name : null,
|
||
|
|
'helpdesk.bc_company_name'
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function getBasicUser(): ?string
|
||
|
|
{
|
||
|
|
$value = $this->settingsMetadataGateway->getValue(self::KEY_BASIC_USER);
|
||
|
|
$value = trim((string) ($value ?? ''));
|
||
|
|
|
||
|
|
return $value !== '' ? $value : null;
|
||
|
|
}
|
||
|
|
|
||
|
|
public function setBasicUser(?string $user): bool
|
||
|
|
{
|
||
|
|
$user = trim((string) ($user ?? ''));
|
||
|
|
|
||
|
|
return $this->settingsMetadataGateway->set(
|
||
|
|
self::KEY_BASIC_USER,
|
||
|
|
$user !== '' ? $user : null,
|
||
|
|
'helpdesk.bc_basic_user'
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function getBasicPassword(): ?string
|
||
|
|
{
|
||
|
|
$encrypted = $this->settingsMetadataGateway->getValue(self::KEY_BASIC_PASSWORD_ENC);
|
||
|
|
$encrypted = trim((string) ($encrypted ?? ''));
|
||
|
|
if ($encrypted === '') {
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
|
||
|
|
try {
|
||
|
|
$decrypted = $this->settingsCryptoGateway->decryptString($encrypted);
|
||
|
|
} catch (\Throwable) {
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
|
||
|
|
$decrypted = trim($decrypted);
|
||
|
|
|
||
|
|
return $decrypted !== '' ? $decrypted : null;
|
||
|
|
}
|
||
|
|
|
||
|
|
public function setBasicPassword(?string $password): bool
|
||
|
|
{
|
||
|
|
$password = trim((string) ($password ?? ''));
|
||
|
|
if ($password === '') {
|
||
|
|
return $this->settingsMetadataGateway->set(
|
||
|
|
self::KEY_BASIC_PASSWORD_ENC,
|
||
|
|
null,
|
||
|
|
'helpdesk.bc_basic_password_enc'
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
try {
|
||
|
|
$encrypted = $this->settingsCryptoGateway->encryptString($password);
|
||
|
|
} catch (\Throwable) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
return $this->settingsMetadataGateway->set(
|
||
|
|
self::KEY_BASIC_PASSWORD_ENC,
|
||
|
|
$encrypted,
|
||
|
|
'helpdesk.bc_basic_password_enc'
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function getOAuthTenantId(): ?string
|
||
|
|
{
|
||
|
|
$value = $this->settingsMetadataGateway->getValue(self::KEY_OAUTH_TENANT_ID);
|
||
|
|
$value = trim((string) ($value ?? ''));
|
||
|
|
|
||
|
|
return $value !== '' ? $value : null;
|
||
|
|
}
|
||
|
|
|
||
|
|
public function setOAuthTenantId(?string $tenantId): bool
|
||
|
|
{
|
||
|
|
$tenantId = trim((string) ($tenantId ?? ''));
|
||
|
|
|
||
|
|
return $this->settingsMetadataGateway->set(
|
||
|
|
self::KEY_OAUTH_TENANT_ID,
|
||
|
|
$tenantId !== '' ? $tenantId : null,
|
||
|
|
'helpdesk.bc_oauth_tenant_id'
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function getOAuthClientId(): ?string
|
||
|
|
{
|
||
|
|
$value = $this->settingsMetadataGateway->getValue(self::KEY_OAUTH_CLIENT_ID);
|
||
|
|
$value = trim((string) ($value ?? ''));
|
||
|
|
|
||
|
|
return $value !== '' ? $value : null;
|
||
|
|
}
|
||
|
|
|
||
|
|
public function setOAuthClientId(?string $clientId): bool
|
||
|
|
{
|
||
|
|
$clientId = trim((string) ($clientId ?? ''));
|
||
|
|
|
||
|
|
return $this->settingsMetadataGateway->set(
|
||
|
|
self::KEY_OAUTH_CLIENT_ID,
|
||
|
|
$clientId !== '' ? $clientId : null,
|
||
|
|
'helpdesk.bc_oauth_client_id'
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function getOAuthClientSecret(): ?string
|
||
|
|
{
|
||
|
|
$encrypted = $this->settingsMetadataGateway->getValue(self::KEY_OAUTH_CLIENT_SECRET_ENC);
|
||
|
|
$encrypted = trim((string) ($encrypted ?? ''));
|
||
|
|
if ($encrypted === '') {
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
|
||
|
|
try {
|
||
|
|
$decrypted = $this->settingsCryptoGateway->decryptString($encrypted);
|
||
|
|
} catch (\Throwable) {
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
|
||
|
|
$decrypted = trim($decrypted);
|
||
|
|
|
||
|
|
return $decrypted !== '' ? $decrypted : null;
|
||
|
|
}
|
||
|
|
|
||
|
|
public function setOAuthClientSecret(?string $secret): bool
|
||
|
|
{
|
||
|
|
$secret = trim((string) ($secret ?? ''));
|
||
|
|
if ($secret === '') {
|
||
|
|
return $this->settingsMetadataGateway->set(
|
||
|
|
self::KEY_OAUTH_CLIENT_SECRET_ENC,
|
||
|
|
null,
|
||
|
|
'helpdesk.bc_oauth_client_secret_enc'
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
try {
|
||
|
|
$encrypted = $this->settingsCryptoGateway->encryptString($secret);
|
||
|
|
} catch (\Throwable) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
return $this->settingsMetadataGateway->set(
|
||
|
|
self::KEY_OAUTH_CLIENT_SECRET_ENC,
|
||
|
|
$encrypted,
|
||
|
|
'helpdesk.bc_oauth_client_secret_enc'
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function getOAuthTokenEndpoint(): ?string
|
||
|
|
{
|
||
|
|
$value = $this->settingsMetadataGateway->getValue(self::KEY_OAUTH_TOKEN_ENDPOINT);
|
||
|
|
$value = trim((string) ($value ?? ''));
|
||
|
|
|
||
|
|
return $value !== '' ? $value : null;
|
||
|
|
}
|
||
|
|
|
||
|
|
public function setOAuthTokenEndpoint(?string $endpoint): bool
|
||
|
|
{
|
||
|
|
$endpoint = trim((string) ($endpoint ?? ''));
|
||
|
|
if ($endpoint !== '' && !preg_match('#^https://#i', $endpoint)) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
return $this->settingsMetadataGateway->set(
|
||
|
|
self::KEY_OAUTH_TOKEN_ENDPOINT,
|
||
|
|
$endpoint !== '' ? $endpoint : null,
|
||
|
|
'helpdesk.bc_oauth_token_endpoint'
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Build the full OData entity URL for a given entity set.
|
||
|
|
*/
|
||
|
|
public function buildEntityUrl(string $entitySet): string
|
||
|
|
{
|
||
|
|
$baseUrl = $this->getODataBaseUrl();
|
||
|
|
$company = rawurlencode($this->getCompanyName());
|
||
|
|
|
||
|
|
return $baseUrl . "/Company('" . $company . "')/" . $entitySet;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Validate that required settings for the current auth mode are present.
|
||
|
|
*
|
||
|
|
* @return array<string> List of missing setting descriptions (empty = valid)
|
||
|
|
*/
|
||
|
|
public function validateConfiguration(): array
|
||
|
|
{
|
||
|
|
$missing = [];
|
||
|
|
|
||
|
|
if ($this->getBasicUser() === null && $this->getAuthMode() === self::AUTH_MODE_BASIC) {
|
||
|
|
$missing[] = 'BC Basic Auth User';
|
||
|
|
}
|
||
|
|
if ($this->getBasicPassword() === null && $this->getAuthMode() === self::AUTH_MODE_BASIC) {
|
||
|
|
$missing[] = 'BC Basic Auth Password';
|
||
|
|
}
|
||
|
|
if ($this->getAuthMode() === self::AUTH_MODE_OAUTH2) {
|
||
|
|
if ($this->getOAuthClientId() === null) {
|
||
|
|
$missing[] = 'BC OAuth2 Client ID';
|
||
|
|
}
|
||
|
|
if ($this->getOAuthClientSecret() === null) {
|
||
|
|
$missing[] = 'BC OAuth2 Client Secret';
|
||
|
|
}
|
||
|
|
if ($this->getOAuthTokenEndpoint() === null) {
|
||
|
|
$missing[] = 'BC OAuth2 Token Endpoint';
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return $missing;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Check whether the configuration is complete for the current auth mode.
|
||
|
|
*/
|
||
|
|
public function isConfigured(): bool
|
||
|
|
{
|
||
|
|
return $this->validateConfiguration() === [];
|
||
|
|
}
|
||
|
|
}
|