diff --git a/config/config.sample.php b/config/config.sample.php index 22f045f..c4404d6 100644 --- a/config/config.sample.php +++ b/config/config.sample.php @@ -21,6 +21,12 @@ define('FEDIINFO', [ 'pubkey' => ROOT.'/public/static/pub.pem', 'privkey' => ROOT.'/data/priv.pem', ]); +define('OPENPROVIDER', [ + 'username' => '', + 'password' => '', + 'ip' => '0.0.0.0', + 'owner_handle' => 'HK912631-JP', +]); define('MAILER_ENABLED', false); define('LOGGING_ENABLED', true); diff --git a/src/Site/Controller/Page.php b/src/Site/Controller/Page.php index 5adcc3d..dd94c46 100644 --- a/src/Site/Controller/Page.php +++ b/src/Site/Controller/Page.php @@ -5,11 +5,20 @@ use Site\Controller\Mods; use Std\Lib\Auth; use Std\Lib\Template; + +use Std\Lib\Openprovider; + class Page { use Mods; public function about(array $params): void { try { + $op = new OpenProvider(); + $op->login(); + // kys($op->getToken()); + // kys($op->checkDomainAvailable(['076.moe', '076.xxx', '076.nigger'])); + // kys($op->listTlds()); + kys($op->searchCustomers()); $tmpl = new Template('/'); $pagetit = '新ページ'; $description = 'PHPフレームワークについて'; diff --git a/src/Std/Lib/Curl.php b/src/Std/Lib/Curl.php index dfb8662..f33f0bf 100644 --- a/src/Std/Lib/Curl.php +++ b/src/Std/Lib/Curl.php @@ -22,7 +22,12 @@ class Curl { private bool $verifySSL = true; private string $username = ''; private string $password = ''; + private string $token = ''; private string $referer = ''; + private string $authType = ''; + + // デバッグ用 + private string $DEBUG_REQ_HDR = ''; // レスポンス関連のプロパティ private array $responseHeaders = []; @@ -195,9 +200,35 @@ class Curl { * @param string $password パスワード * @return Curl このインスタンス(メソッドチェーン用) */ - public function setBasicAuth(string $username, string $password): Curl { - $this->username = $username; - $this->password = $password; + public function setBasicAuth(string $username = '', string $password = ''): Curl { + if ($username !== '') $this->username = $username; + if ($password !== '') $this->password = $password; + $this->authType = 'Basic'; + return $this; + } + + /** + * トークン認証の資格情報を設定する + * + * @param string $token セッショントークン + * @return Curl このインスタンス(メソッドチェーン用) + */ + public function setBearerAuth(string $token = ''): Curl { + if ($token !== '') $this->token = $token; + $this->authType = 'Bearer'; + return $this; + } + + /** + * 認証を取りけソ + * + * @return Curl このインスタンス(メソッドチェーン用) + */ + public function unsetAuth(): Curl { + $this->username = ''; + $this->password = ''; + $this->token = ''; + $this->authType = ''; return $this; } @@ -300,14 +331,16 @@ class Curl { $path .= '?'.$parsed['query']; } - // Basic認証 + // 認証 $authHeader = ''; - if (!empty($this->username) && !empty($this->password)) { + if (!empty($this->authType) && $this->authType === 'Basic') { + assert_not_null($this->username); + assert_not_null($this->password); $authHeader = "Authorization: Basic " .base64_encode($this->username.':'.$this->password)."\r\n"; - } elseif (isset($parsed['user']) && isset($parsed['pass'])) { - $authHeader = "Authorization: Basic " - .base64_encode($parsed['user'].':'.$parsed['pass'])."\r\n"; + } else if (!empty($this->authType) && $this->authType === 'Bearer') { + assert_not_null($this->token); + $authHeader = "Authorization: Bearer ".$this->token."\r\n"; } // 送信するHTTPリクエストを構築 @@ -368,6 +401,8 @@ class Curl { $request .= $httpData; } + $this->DEBUG_REQ_HDR = $request; + if ($this->verbose && $this->stderr) { fwrite($this->stderr, "* リクエストヘッダー:\n{$request}\n"); } @@ -573,6 +608,15 @@ class Curl { return $this->info; } + /** + * リクエスト情報を取得する + * + * @return string 情報の配列 + */ + public function getRequestHeader(): string { + return $this->DEBUG_REQ_HDR; + } + // 機能性メソッド /** diff --git a/src/Std/Lib/Openprovider.php b/src/Std/Lib/Openprovider.php new file mode 100644 index 0000000..51a9a66 --- /dev/null +++ b/src/Std/Lib/Openprovider.php @@ -0,0 +1,302 @@ +user = OPENPROVIDER['username']; + $this->pass = OPENPROVIDER['password']; + $this->ip = OPENPROVIDER['ip']; + } + + /*** + * トークンの受け取り。 + * このライブリリーを使ったら、一回「login()」を実行する事が必須となります。 + * + * @return Result 結果 + */ + public function login(): \Result { + $curl = new Curl("{$this->BASEURL}/auth/login"); + + $payload = [ + 'username' => $this->user, + 'password' => $this->pass, + 'ip' => $this->ip ?: '0.0.0.0', + ]; + + $curl->setMethod("POST"); + $curl->setPostRaw(json_encode($payload)); + $curl->addHeader("Content-Type", "application/json"); + $curl->addHeader("Accept", "application/json"); + + $res = $curl->execute(); + if (!$res->isSuccess) { + $err = "CURLの実行に失敗: ".$curl->message; + assert_unless_success($res, $err); + return \Result::Error($err); + } + + $body = $curl->getResponseBody(); + if ($curl->getResponseCode() != 200) { + $err = json_decode($body, true); + assert_not_null($err, "エラーの受け取りに失敗。"); + assert($curl->getResponseCode() == 200, $err['desc']); + return \Result::Error(); + } + assert_not_null($body, "返事ボディーは空です。"); + + $data = json_decode($body, true); + if (isset($data['data']['token'])) { + $this->token = $data['data']['token']; + $this->reseller_id = $data['data']['reseller_id']; + return \Result::Success(); + } + + return \Result::Error("ログインに失敗。"); + } + + // カスタマー + + // ドメイン + + //// ドメイン + + /*** + * ドメインを登録可能かどうかの確認。 + * + * @param array $domains ドメイン名リスト。例:['076.moe', '076.co.jp', '076.com'] + * @param bool $with_price 値段を表示するかどうか。デフォルトは「false」 + * @return Result|array 結果。配列の場合:'domain'と'status'はいつでもあり、'reason', 'premium', 'is_premium'が多分あり、そして'price'が「with_price」が「true」の場合のみ。 + */ + public function checkDomainAvailable(array $domains, bool $with_price = false): \Result|array { + $domainList = []; + foreach ($domains as $d) { + $domain = explode('.', $d); + assert_exists($domain[0]); + assert_exists($domain[1]); + if (count($domain) !== 2) { + return \Result::Error("不正なドメイン形: {$d}"); + } + + $domainList[] = [ + 'name' => $domain[0], + 'extension' => $domain[1], + ]; + } + + $payload = [ + 'domains' => $domainList, + 'with_price' => $with_price, + ]; + + $curl = $this->setupCurl('/domains/check', 'POST', $payload); + $data = $this->curlResult($curl); + if (isset($data['data']['results'])) return $data['data']['results']; + + return \Result::Error("ドメインの確認に失敗。"); + } + + /** + * ドメイン名の登録。 + * + * @param string $name 登録したいドメイン名(例:"076studio.jp") + * @param int $period 年間(デフォルト=1年) + * @param string $owner_handle + * @param string $admin_handle + * @param string $tech_handle + * @param string $billing_handle + * @param string $reseller_handle + * @param array $name_servers ネームサーバー(4つまで、デフォルトはOpenProviderさんのネームサーバー) + * @param string $autorenew 自動的に更新するかどうか(可能な値:on|off|default、デフォルト=default) + * @return Result|array 結果。配列の場合:'activation_date'(リアルタイムで成功したのみ), 'auth_code'(TLDが対応している場合のみ), 'expiration_date', 'id', 'renewal_date', 'status'(ACT又はREQ) + */ + public function registerDomain(string $name, int $period = 1, string $owner_handle = '', + string $admin_handle = '', string $tech_handle = '', + string $billing_handle = '', string $reseller_handle = '', + array $name_servers = [], string $autorenew = 'default'): \Result|array { + if ($owner_handle === '') $owner_handle = OPENPROVIDER['owner_handle']; + if ($admin_handle === '') $admin_handle = $owner_handle; + if ($tech_handle === '') $tech_handle = $owner_handle; + if ($billing_handle === '') $billing_handle = $owner_handle; + if ($reseller_handle === '') $reseller_handle = $owner_handle; + + $parts = explode('.', $name, 2); + assert_exists($parts[0]); + assert_exists($parts[1]); + $domain = [ + 'name' => $parts[0], + 'extension' => $parts[1], + ]; + + $ns = []; + if (empty($name_servers)) $name_servers = ['ns1.openprovider.nl', 'ns2.openprovider.be', 'ns3.openprovider.eu']; + foreach ($name_servers as $n) { + $ns[] = ['name' => $n]; + } + + $payload = [ + 'name' => $domain, + 'period' => $period, + 'owner_handle' => $owner_handle, + 'admin_handle' => $admin_handle, + 'tech_handle' => $tech_handle, + 'billing_handle' => $billing_handle, + 'reseller_handle' => $reseller_handle, + 'name_servers' => $ns, + 'autorenew' => $autorenew, + ]; + + $curl = $this->setupCurl('/domains/', 'POST', $payload); + kys('TODO'); + $data = $this->curlResult($curl); + if (isset($data['data'])) return $data['data']; + + return \Result::Error("ドメインの確認に失敗。"); + } + + //// 追加データ + + //// 顧客様の追加データ + + /** + * 顧客様の検索 + * @param array $query 検索クエリー(可能:handle_pattern (string), email_pattern (string), first_name_pattern (string), last_name_pattern (string), company_name_pattern (string), comment_pattern (string), with_additional_data (bool), limit (int = 100、最大=1000), offset (int = 0) + */ + public function searchCustomers(array $query = []): \Result|array { + foreach ($query as $k => $v) { if (is_bool($v)) $query[$k] = $v ? '1' : '0'; } + + $uri = '/customers?'.http_build_query($query, '', '&', PHP_QUERY_RFC3986); + $curl = $this->setupCurl($uri); + kys("TODO: 問題"); + $data = $this->curlResult($curl); + kys($data); + if (isset($data['data'])) return $data['data']; + + return \Result::Error("TLD一覧の受け取りに失敗。"); + } + + //// ドメイン値段 + + //// 認証コード + + //// TLD + + /** + * TLD一覧 + * + * @param array $query 検索クエリー(可能:limit (int),offset (int),order (string),order_by (string),extensions(array), name_pattern (string), only_names (bool), with_description (bool),with_restrictions (bool)with_usage_count (bool)with_application_mode (bool),with_price (bool),with_level_prices (bool)is_active (bool)is_new_gtld (bool),status (string),application_mode (string)) + */ + public function listTlds(array $query = []): \Result|array { + if (empty($query)) $query = [ 'limit' => 10, 'offset' => 0, 'order' => 'ASC' ]; + + foreach ($query as $k => $v) { + if (is_bool($v)) $query[$k] = $v ? '1' : '0'; + } + + $uri = '/tlds?'.http_build_query($query, '', '&', PHP_QUERY_RFC3986); + // kys($uri); + $curl = $this->setupCurl($uri); + kys("TODO: JSONの問題"); + $data = $this->curlResult($curl); + kys($data); + if (isset($data['data'])) return $data['data']; + + return \Result::Error("TLD一覧の受け取りに失敗。"); + } + + /** + * TLDの受け取り + */ + + // 請求 + //// 請求書 + //// 支払 + //// トランザクション + + // DNS + //// DomainToken + //// ネームサーバー + //// NSグループ + //// テンプレート + //// ゾーン + //// ゾーンレコード + + // Easydmarc + + // ライセンス + + // リセラー・顧客様 + + // SpamExpert + + // SSL証明書 + + //////// + + /** + * トークンの受け取り。 + * + * @return string トークン + */ + public function getToken(): string { + return $this->token; + } + + /** + * リセラーIDの受け取り。 + * + * @return int リセラーID + */ + public function getResellerId(): int { + return $this->reseller_id; + } + + ////////// + + private function setupCurl(string $uri, string $resType = 'GET', array $payload = []): Curl { + $curl = new Curl("{$this->BASEURL}{$uri}"); + + $curl->setMethod($resType); + if (!empty($payload)) $curl->setPostRaw(json_encode($payload)); + $curl->setBearerAuth($this->token); + $curl->addHeader('Content-Type', 'application/json'); + $curl->addHeader('Accept', 'application/json'); + + return $curl; + } + + private function curlResult(Curl &$curl): \Result|array { + $res = $curl->execute(); + // kys($curl->getRequestHeader()); + if (!$res->isSuccess) { + $err = "CURL実行に失敗: {$curl->message}"; + assert_unless_success($res, $err); + return \Result::Error($err); + } + + $body = $curl->getResponseBody(); + // kys($body); + // kys($curl->getResponseHeaders()); + if ($curl->getResponseCode() != 200) { + $err = json_decode($body, true); + assert_not_null($err, "エラーの受け取りに失敗。"); + assert($curl->getResponseCode() == 200, $err['desc']); + return \Result::Error(); + } + assert_not_null($body, "返事ボディーは空です。"); + + // $body = str_ + kys($body); + return json_decode($body, true); + } +} \ No newline at end of file diff --git a/src/Std/Test/LibMarkdown.php b/src/Std/Test/LibMarkdown.php index 562f840..39570a8 100644 --- a/src/Std/Test/LibMarkdown.php +++ b/src/Std/Test/LibMarkdown.php @@ -35,7 +35,6 @@ class Constants { } $test->describe('マークダウン', function($test): void { - $test->it('メタデータをパーシング出来るはず', function($test): void { $md = new Markdown('feature-test', '/blog/'); $test->assertNotNull($md); diff --git a/src/Std/Test/LibOpenprovider.php b/src/Std/Test/LibOpenprovider.php new file mode 100644 index 0000000..8d1fe94 --- /dev/null +++ b/src/Std/Test/LibOpenprovider.php @@ -0,0 +1,62 @@ + true, + 'verboseOutput' => true +]); + +$test->describe('認証', function($test): void { + $test->it('トークンを受け取れるはず', function($test): void { + $op = new OpenProvider(); + $res = $op->login(); + + $expect = Res::Success(); + $test->assertEquals($expect, $res); + }); + + $test->it('トークンを受け取れるはず', function($test): void {}); +}); + +$test->describe('ドメイン', function($test): void { + $test->it('ドメイン名があるかのうか確認出来るはず', function($test): void { + $op = new OpenProvider(); + $op->login(); + $res = $op->checkDomainAvailable(['076.moe', '076.xxx', '076.nigger']); + + $expect = [ + 0 => [ + 'domain' => '076.nigger', + 'status' => 'active', + 'reason' => 'Your domain request contains an invalid extension!', + ], + 1 => [ + 'domain' => '076.moe', + 'status' => 'active', + 'reason' => 'In use', + ], + 2 => [ + 'domain' => '076.xxx', + 'status' => 'free', + ], + ]; + $test->assertEquals($expect, $res); + }); + + $test->it('ドメイン名(値段付き)があるかのうか確認出来るはず', function($test): void { + $op = new OpenProvider(); + $op->login(); + $res = isset($op->checkDomainAvailable(['076.moe'], true)[0]['price']['product']); + + $expect = true; + $expect = Res::Success(); + $test->assertEquals($expect, $res); + }); +}); + +$test->printSummary(); \ No newline at end of file diff --git a/util.php b/util.php index 32fbda7..a39a4f2 100644 --- a/util.php +++ b/util.php @@ -268,6 +268,13 @@ function randstr(): string { return bin2hex(random_bytes($len)); } +function assert_exists(mixed $assertion, Throwable|string|null $description = null): bool { + if (ini_get('zend.assertions') < 1) trigger_error('assert機能性が無効です。有効するには、php.iniで「zend.assertions = 1」に設定して下さい。', E_USER_WARNING); + if (isset($assertion)) return true; + assert(false, $description ?? 'Assertion failed: value is not null'); + return false; +} + function assert_null(mixed $assertion, Throwable|string|null $description = null): bool { if (ini_get('zend.assertions') < 1) trigger_error('assert機能性が無効です。有効するには、php.iniで「zend.assertions = 1」に設定して下さい。', E_USER_WARNING); if (is_null($assertion)) return true;