diff --git a/app/config/errors.php b/app/config/errors.php index f0b857731b..b95f27915d 100644 --- a/app/config/errors.php +++ b/app/config/errors.php @@ -768,10 +768,22 @@ return [ ], /** Health */ - Exception::QUEUE_SIZE_EXCEEDED => [ - 'name' => Exception::QUEUE_SIZE_EXCEEDED, + Exception::HEALTH_QUEUE_SIZE_EXCEEDED => [ + 'name' => Exception::HEALTH_QUEUE_SIZE_EXCEEDED, 'description' => 'Queue size threshold hit.', 'code' => 503, 'publish' => false ], + + Exception::HEALTH_CERTIFICATE_EXPIRED => [ + 'name' => Exception::HEALTH_CERTIFICATE_EXPIRED, + 'description' => 'The SSL certificate for the specified domain has expired and is no longer valid.', + 'code' => 404, + ], + + Exception::HEALTH_INVALID_HOST => [ + 'name' => Exception::HEALTH_INVALID_HOST, + 'description' => 'Failed to establish a connection to the specified domain. Please verify the domain name and ensure that the server is running and accessible.', + 'code' => 404, + ], ]; diff --git a/app/controllers/api/health.php b/app/controllers/api/health.php index 3b875f7156..38a2faa754 100644 --- a/app/controllers/api/health.php +++ b/app/controllers/api/health.php @@ -7,6 +7,7 @@ use Appwrite\Utopia\Response; use Utopia\App; use Utopia\Config\Config; use Utopia\Database\Document; +use Utopia\Domains\Validator\PublicDomain; use Utopia\Pools\Group; use Utopia\Queue\Client; use Utopia\Queue\Connection; @@ -14,7 +15,9 @@ use Utopia\Registry\Registry; use Utopia\Storage\Device; use Utopia\Storage\Device\Local; use Utopia\Storage\Storage; +use Utopia\Validator\Domain; use Utopia\Validator\Integer; +use Utopia\Validator\Multiple; use Utopia\Validator\Text; use Utopia\Validator\WhiteList; @@ -356,7 +359,7 @@ App::get('/v1/health/queue/webhooks') $size = $client->getQueueSize(); if ($size >= $threshold) { - throw new Exception(Exception::QUEUE_SIZE_EXCEEDED, "Queue size threshold hit. Current size is {$size} and threshold is {$threshold}."); + throw new Exception(Exception::HEALTH_QUEUE_SIZE_EXCEEDED, "Queue size threshold hit. Current size is {$size} and threshold is {$threshold}."); } $response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE); @@ -383,12 +386,62 @@ App::get('/v1/health/queue/logs') $size = $client->getQueueSize(); if ($size >= $threshold) { - throw new Exception(Exception::QUEUE_SIZE_EXCEEDED, "Queue size threshold hit. Current size is {$size} and threshold is {$threshold}."); + throw new Exception(Exception::HEALTH_QUEUE_SIZE_EXCEEDED, "Queue size threshold hit. Current size is {$size} and threshold is {$threshold}."); } $response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE); }, ['response']); +App::get('/v1/health/certificate') + ->desc('Get the SSL certificate for a domain') + ->groups(['api', 'health']) + ->label('scope', 'health.read') + ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) + ->label('sdk.namespace', 'health') + ->label('sdk.method', 'getCertificate') + ->label('sdk.description', '/docs/references/health/get-certificate.md') + ->label('sdk.response.code', Response::STATUS_CODE_OK) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_HEALTH_CERTIFICATE) + ->param('domain', null, new Multiple([new Domain(), new PublicDomain()]), Multiple::TYPE_STRING, 'Domain name') + ->inject('response') + ->action(function (string $domain, Response $response) { + if (filter_var($domain, FILTER_VALIDATE_URL)) { + $domain = parse_url($domain, PHP_URL_HOST); + } + + $sslContext = stream_context_create([ + "ssl" => [ + "capture_peer_cert" => true + ] + ]); + $sslSocket = stream_socket_client("ssl://" . $domain . ":443", $errno, $errstr, 30, STREAM_CLIENT_CONNECT, $sslContext); + if (!$sslSocket) { + throw new Exception(Exception::HEALTH_INVALID_HOST); + } + + $streamContextParams = stream_context_get_params($sslSocket); + $peerCertificate = $streamContextParams['options']['ssl']['peer_certificate']; + $certificatePayload = openssl_x509_parse($peerCertificate); + + + $sslExpiration = $certificatePayload['validTo_time_t']; + $status = ($sslExpiration < time()) ? 'fail' : 'pass'; + + if ($status == 'fail') { + throw new Exception(Exception::HEALTH_CERTIFICATE_EXPIRED); + } + + $response->dynamic(new Document([ + 'name' => $certificatePayload['name'], + 'subjectSN' => $certificatePayload['subject']['CN'], + 'issuerOrganisation' => $certificatePayload['issuer']['O'], + 'validFrom' => $certificatePayload['validFrom_time_t'], + 'validTo' => $certificatePayload['validTo_time_t'], + 'signatureTypeSN' => $certificatePayload['signatureTypeSN'], + ]), Response::MODEL_HEALTH_CERTIFICATE); + }, ['response']); + App::get('/v1/health/queue/certificates') ->desc('Get certificates queue') ->groups(['api', 'health']) @@ -410,7 +463,7 @@ App::get('/v1/health/queue/certificates') $size = $client->getQueueSize(); if ($size >= $threshold) { - throw new Exception(Exception::QUEUE_SIZE_EXCEEDED, "Queue size threshold hit. Current size is {$size} and threshold is {$threshold}."); + throw new Exception(Exception::HEALTH_QUEUE_SIZE_EXCEEDED, "Queue size threshold hit. Current size is {$size} and threshold is {$threshold}."); } $response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE); @@ -437,7 +490,7 @@ App::get('/v1/health/queue/builds') $size = $client->getQueueSize(); if ($size >= $threshold) { - throw new Exception(Exception::QUEUE_SIZE_EXCEEDED, "Queue size threshold hit. Current size is {$size} and threshold is {$threshold}."); + throw new Exception(Exception::HEALTH_QUEUE_SIZE_EXCEEDED, "Queue size threshold hit. Current size is {$size} and threshold is {$threshold}."); } $response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE); @@ -465,7 +518,7 @@ App::get('/v1/health/queue/databases') $size = $client->getQueueSize(); if ($size >= $threshold) { - throw new Exception(Exception::QUEUE_SIZE_EXCEEDED, "Queue size threshold hit. Current size is {$size} and threshold is {$threshold}."); + throw new Exception(Exception::HEALTH_QUEUE_SIZE_EXCEEDED, "Queue size threshold hit. Current size is {$size} and threshold is {$threshold}."); } $response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE); @@ -492,7 +545,7 @@ App::get('/v1/health/queue/deletes') $size = $client->getQueueSize(); if ($size >= $threshold) { - throw new Exception(Exception::QUEUE_SIZE_EXCEEDED, "Queue size threshold hit. Current size is {$size} and threshold is {$threshold}."); + throw new Exception(Exception::HEALTH_QUEUE_SIZE_EXCEEDED, "Queue size threshold hit. Current size is {$size} and threshold is {$threshold}."); } $response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE); @@ -519,7 +572,7 @@ App::get('/v1/health/queue/mails') $size = $client->getQueueSize(); if ($size >= $threshold) { - throw new Exception(Exception::QUEUE_SIZE_EXCEEDED, "Queue size threshold hit. Current size is {$size} and threshold is {$threshold}."); + throw new Exception(Exception::HEALTH_QUEUE_SIZE_EXCEEDED, "Queue size threshold hit. Current size is {$size} and threshold is {$threshold}."); } $response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE); @@ -546,7 +599,7 @@ App::get('/v1/health/queue/messaging') $size = $client->getQueueSize(); if ($size >= $threshold) { - throw new Exception(Exception::QUEUE_SIZE_EXCEEDED, "Queue size threshold hit. Current size is {$size} and threshold is {$threshold}."); + throw new Exception(Exception::HEALTH_QUEUE_SIZE_EXCEEDED, "Queue size threshold hit. Current size is {$size} and threshold is {$threshold}."); } $response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE); @@ -573,7 +626,7 @@ App::get('/v1/health/queue/migrations') $size = $client->getQueueSize(); if ($size >= $threshold) { - throw new Exception(Exception::QUEUE_SIZE_EXCEEDED, "Queue size threshold hit. Current size is {$size} and threshold is {$threshold}."); + throw new Exception(Exception::HEALTH_QUEUE_SIZE_EXCEEDED, "Queue size threshold hit. Current size is {$size} and threshold is {$threshold}."); } $response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE); @@ -600,7 +653,7 @@ App::get('/v1/health/queue/functions') $size = $client->getQueueSize(); if ($size >= $threshold) { - throw new Exception(Exception::QUEUE_SIZE_EXCEEDED, "Queue size threshold hit. Current size is {$size} and threshold is {$threshold}."); + throw new Exception(Exception::HEALTH_QUEUE_SIZE_EXCEEDED, "Queue size threshold hit. Current size is {$size} and threshold is {$threshold}."); } $response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE); @@ -723,7 +776,7 @@ App::get('/v1/health/queue/failed/:name') $failed = $client->countFailedJobs(); if ($failed >= $threshold) { - throw new Exception(Exception::QUEUE_SIZE_EXCEEDED, "Queue failed jobs threshold hit. Current size is {$failed} and threshold is {$threshold}."); + throw new Exception(Exception::HEALTH_QUEUE_SIZE_EXCEEDED, "Queue failed jobs threshold hit. Current size is {$failed} and threshold is {$threshold}."); } $response->dynamic(new Document([ 'size' => $failed ]), Response::MODEL_HEALTH_QUEUE); diff --git a/composer.json b/composer.json index 94b1d8f41f..41baf29d9a 100644 --- a/composer.json +++ b/composer.json @@ -50,7 +50,7 @@ "utopia-php/cli": "0.15.*", "utopia-php/config": "0.2.*", "utopia-php/database": "0.45.*", - "utopia-php/domains": "0.3.*", + "utopia-php/domains": "0.5.*", "utopia-php/dsn": "0.1.*", "utopia-php/framework": "0.33.*", "utopia-php/image": "0.5.*", diff --git a/composer.lock b/composer.lock index 7507243862..e72cc04a15 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "35dcde03d0eb9a0d27de653b9fa038d4", + "content-hash": "fd03f97115d752d1a94b533ccf570109", "packages": [ { "name": "adhocore/jwt", @@ -815,16 +815,16 @@ }, { "name": "symfony/polyfill-php80", - "version": "v1.28.0", + "version": "v1.29.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5" + "reference": "87b68208d5c1188808dd7839ee1e6c8ec3b02f1b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/6caa57379c4aec19c0a12a38b59b26487dcfe4b5", - "reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/87b68208d5c1188808dd7839ee1e6c8ec3b02f1b", + "reference": "87b68208d5c1188808dd7839ee1e6c8ec3b02f1b", "shasum": "" }, "require": { @@ -832,9 +832,6 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.28-dev" - }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" @@ -878,7 +875,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.28.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.29.0" }, "funding": [ { @@ -894,7 +891,7 @@ "type": "tidelift" } ], - "time": "2023-01-26T09:26:14+00:00" + "time": "2024-01-29T20:11:03+00:00" }, { "name": "utopia-php/abuse", @@ -1190,16 +1187,16 @@ }, { "name": "utopia-php/database", - "version": "0.45.5", + "version": "0.45.6", "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "0b66a017f817a910acb83e6aea92bccea9571fe6" + "reference": "c7cc6d57683a4c13d9772dbeea343adb72c443fd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/0b66a017f817a910acb83e6aea92bccea9571fe6", - "reference": "0b66a017f817a910acb83e6aea92bccea9571fe6", + "url": "https://api.github.com/repos/utopia-php/database/zipball/c7cc6d57683a4c13d9772dbeea343adb72c443fd", + "reference": "c7cc6d57683a4c13d9772dbeea343adb72c443fd", "shasum": "" }, "require": { @@ -1240,22 +1237,22 @@ ], "support": { "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/0.45.5" + "source": "https://github.com/utopia-php/database/tree/0.45.6" }, - "time": "2024-01-08T17:08:15+00:00" + "time": "2024-02-01T02:33:43+00:00" }, { "name": "utopia-php/domains", - "version": "0.3.2", + "version": "0.5.0", "source": { "type": "git", "url": "https://github.com/utopia-php/domains.git", - "reference": "aaa8c9a96c69ccb397997b1f4f2299c66f77eefb" + "reference": "bf07f60326f8389f378ddf6fcde86217e5cfe18c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/domains/zipball/aaa8c9a96c69ccb397997b1f4f2299c66f77eefb", - "reference": "aaa8c9a96c69ccb397997b1f4f2299c66f77eefb", + "url": "https://api.github.com/repos/utopia-php/domains/zipball/bf07f60326f8389f378ddf6fcde86217e5cfe18c", + "reference": "bf07f60326f8389f378ddf6fcde86217e5cfe18c", "shasum": "" }, "require": { @@ -1300,9 +1297,9 @@ ], "support": { "issues": "https://github.com/utopia-php/domains/issues", - "source": "https://github.com/utopia-php/domains/tree/0.3.2" + "source": "https://github.com/utopia-php/domains/tree/0.5.0" }, - "time": "2023-07-19T16:39:24+00:00" + "time": "2024-01-03T22:04:27+00:00" }, { "name": "utopia-php/dsn", @@ -1353,16 +1350,16 @@ }, { "name": "utopia-php/framework", - "version": "0.33.1", + "version": "0.33.2", "source": { "type": "git", "url": "https://github.com/utopia-php/http.git", - "reference": "b745607aa1875554a0ad52e28f6db918da1ce11c" + "reference": "b1423ca3e3b61c6c4c2e619d2cb80672809a19f3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/http/zipball/b745607aa1875554a0ad52e28f6db918da1ce11c", - "reference": "b745607aa1875554a0ad52e28f6db918da1ce11c", + "url": "https://api.github.com/repos/utopia-php/http/zipball/b1423ca3e3b61c6c4c2e619d2cb80672809a19f3", + "reference": "b1423ca3e3b61c6c4c2e619d2cb80672809a19f3", "shasum": "" }, "require": { @@ -1392,9 +1389,9 @@ ], "support": { "issues": "https://github.com/utopia-php/http/issues", - "source": "https://github.com/utopia-php/http/tree/0.33.1" + "source": "https://github.com/utopia-php/http/tree/0.33.2" }, - "time": "2024-01-17T16:48:32+00:00" + "time": "2024-01-31T10:35:59+00:00" }, { "name": "utopia-php/image", @@ -2471,16 +2468,16 @@ }, { "name": "doctrine/deprecations", - "version": "1.1.2", + "version": "1.1.3", "source": { "type": "git", "url": "https://github.com/doctrine/deprecations.git", - "reference": "4f2d4f2836e7ec4e7a8625e75c6aa916004db931" + "reference": "dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/deprecations/zipball/4f2d4f2836e7ec4e7a8625e75c6aa916004db931", - "reference": "4f2d4f2836e7ec4e7a8625e75c6aa916004db931", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab", + "reference": "dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab", "shasum": "" }, "require": { @@ -2512,9 +2509,9 @@ "homepage": "https://www.doctrine-project.org/", "support": { "issues": "https://github.com/doctrine/deprecations/issues", - "source": "https://github.com/doctrine/deprecations/tree/1.1.2" + "source": "https://github.com/doctrine/deprecations/tree/1.1.3" }, - "time": "2023-09-27T20:04:15+00:00" + "time": "2024-01-30T19:34:25+00:00" }, { "name": "doctrine/instantiator", @@ -4772,16 +4769,16 @@ }, { "name": "symfony/polyfill-ctype", - "version": "v1.28.0", + "version": "v1.29.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb" + "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", - "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ef4d7e442ca910c4764bce785146269b30cb5fc4", + "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4", "shasum": "" }, "require": { @@ -4795,9 +4792,6 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.28-dev" - }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" @@ -4834,7 +4828,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.28.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.29.0" }, "funding": [ { @@ -4850,20 +4844,20 @@ "type": "tidelift" } ], - "time": "2023-01-26T09:26:14+00:00" + "time": "2024-01-29T20:11:03+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.28.0", + "version": "v1.29.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "42292d99c55abe617799667f454222c54c60e229" + "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/42292d99c55abe617799667f454222c54c60e229", - "reference": "42292d99c55abe617799667f454222c54c60e229", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9773676c8a1bb1f8d4340a62efe641cf76eda7ec", + "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec", "shasum": "" }, "require": { @@ -4877,9 +4871,6 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.28-dev" - }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" @@ -4917,7 +4908,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.28.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.29.0" }, "funding": [ { @@ -4933,7 +4924,7 @@ "type": "tidelift" } ], - "time": "2023-07-28T09:04:16+00:00" + "time": "2024-01-29T20:11:03+00:00" }, { "name": "textalk/websocket", @@ -5133,5 +5124,5 @@ "platform-overrides": { "php": "8.0" }, - "plugin-api-version": "2.6.0" + "plugin-api-version": "2.3.0" } diff --git a/docs/references/health/get-certificate.md b/docs/references/health/get-certificate.md new file mode 100644 index 0000000000..bf1eeb8384 --- /dev/null +++ b/docs/references/health/get-certificate.md @@ -0,0 +1 @@ +Get the SSL certificate for a domain \ No newline at end of file diff --git a/src/Appwrite/Extend/Exception.php b/src/Appwrite/Extend/Exception.php index 195eedb353..43095334ad 100644 --- a/src/Appwrite/Extend/Exception.php +++ b/src/Appwrite/Extend/Exception.php @@ -234,7 +234,9 @@ class Exception extends \Exception public const REALTIME_POLICY_VIOLATION = 'realtime_policy_violation'; /** Health */ - public const QUEUE_SIZE_EXCEEDED = 'queue_size_exceeded'; + public const HEALTH_QUEUE_SIZE_EXCEEDED = 'health_queue_size_exceeded'; + public const HEALTH_CERTIFICATE_EXPIRED = 'health_certificate_expired'; + public const HEALTH_INVALID_HOST = 'health_invalid_host'; protected string $type = ''; protected array $errors = []; diff --git a/src/Appwrite/Utopia/Response.php b/src/Appwrite/Utopia/Response.php index 8643f3be81..4aad61ac2d 100644 --- a/src/Appwrite/Utopia/Response.php +++ b/src/Appwrite/Utopia/Response.php @@ -70,6 +70,7 @@ use Appwrite\Utopia\Response\Model\Token; use Appwrite\Utopia\Response\Model\Webhook; use Appwrite\Utopia\Response\Model\Preferences; use Appwrite\Utopia\Response\Model\HealthAntivirus; +use Appwrite\Utopia\Response\Model\HealthCertificate; use Appwrite\Utopia\Response\Model\HealthQueue; use Appwrite\Utopia\Response\Model\HealthStatus; use Appwrite\Utopia\Response\Model\HealthTime; @@ -253,6 +254,7 @@ class Response extends SwooleResponse public const MODEL_HEALTH_QUEUE = 'healthQueue'; public const MODEL_HEALTH_TIME = 'healthTime'; public const MODEL_HEALTH_ANTIVIRUS = 'healthAntivirus'; + public const MODEL_HEALTH_CERTIFICATE = 'healthCertificate'; public const MODEL_HEALTH_STATUS_LIST = 'healthStatusList'; // Console @@ -390,6 +392,7 @@ class Response extends SwooleResponse ->setModel(new HealthAntivirus()) ->setModel(new HealthQueue()) ->setModel(new HealthStatus()) + ->setModel(new HealthCertificate()) ->setModel(new HealthTime()) ->setModel(new HealthVersion()) ->setModel(new Metric()) diff --git a/src/Appwrite/Utopia/Response/Model/HealthCertificate.php b/src/Appwrite/Utopia/Response/Model/HealthCertificate.php new file mode 100644 index 0000000000..e4990acc0a --- /dev/null +++ b/src/Appwrite/Utopia/Response/Model/HealthCertificate.php @@ -0,0 +1,71 @@ +addRule('name', [ + 'type' => self::TYPE_STRING, + 'description' => 'Certificate name', + 'default' => '', + 'example' => '/CN=www.google.com', + ]) + ->addRule('subjectSN', [ + 'type' => self::TYPE_STRING, + 'description' => 'Subject SN', + 'default' => 'www.google.com', + 'example' => '', + ]) + ->addRule('issuerOrganisation', [ + 'type' => self::TYPE_STRING, + 'description' => 'Issuer organisation', + 'default' => 'Google Trust Services LLC', + 'example' => '', + ]) + ->addRule('validFrom', [ + 'type' => self::TYPE_STRING, + 'description' => 'Valid from', + 'default' => '', + 'example' => '1704200998', + ]) + ->addRule('validTo', [ + 'type' => self::TYPE_STRING, + 'description' => 'Valid to', + 'default' => '', + 'example' => '1711458597', + ]) + ->addRule('signatureTypeSN', [ + 'type' => self::TYPE_STRING, + 'description' => 'Signature type SN', + 'default' => '', + 'example' => 'RSA-SHA256', + ]) + ; + } + + /** + * Get Name + * + * @return string + */ + public function getName(): string + { + return 'Health Certificate'; + } + + /** + * Get Type + * + * @return string + */ + public function getType(): string + { + return Response::MODEL_HEALTH_CERTIFICATE; + } +} diff --git a/tests/e2e/Services/Health/HealthCustomServerTest.php b/tests/e2e/Services/Health/HealthCustomServerTest.php index 3ea1b884a7..c817222c48 100644 --- a/tests/e2e/Services/Health/HealthCustomServerTest.php +++ b/tests/e2e/Services/Health/HealthCustomServerTest.php @@ -424,4 +424,74 @@ class HealthCustomServerTest extends Scope return []; } + + public function testCertificateValidity(): array + { + /** + * Test for SUCCESS + */ + $response = $this->client->call(Client::METHOD_GET, '/health/certificate?domain=www.google.com', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), []); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals('/CN=www.google.com', $response['body']['name']); + $this->assertEquals('www.google.com', $response['body']['subjectSN']); + $this->assertEquals('Google Trust Services LLC', $response['body']['issuerOrganisation']); + $this->assertIsInt($response['body']['validFrom']); + $this->assertIsInt($response['body']['validTo']); + + $response = $this->client->call(Client::METHOD_GET, '/health/certificate?domain=appwrite.io', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), []); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals('/CN=appwrite.io', $response['body']['name']); + $this->assertEquals('appwrite.io', $response['body']['subjectSN']); + $this->assertEquals("Let's Encrypt", $response['body']['issuerOrganisation']); + $this->assertIsInt($response['body']['validFrom']); + $this->assertIsInt($response['body']['validTo']); + + $response = $this->client->call(Client::METHOD_GET, '/health/certificate?domain=https://google.com', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), []); + + $this->assertEquals(200, $response['headers']['status-code']); + + /** + * Test for FAILURE + */ + $response = $this->client->call(Client::METHOD_GET, '/health/certificate?domain=localhost', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), []); + + $this->assertEquals(400, $response['headers']['status-code']); + + $response = $this->client->call(Client::METHOD_GET, '/health/certificate?domain=doesnotexist.com', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), []); + + $this->assertEquals(404, $response['headers']['status-code']); + + $response = $this->client->call(Client::METHOD_GET, '/health/certificate?domain=www.google.com/usr/src/local', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), []); + + $this->assertEquals(400, $response['headers']['status-code']); + + $response = $this->client->call(Client::METHOD_GET, '/health/certificate?domain=', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), []); + + $this->assertEquals(400, $response['headers']['status-code']); + + return []; + } }