diff --git a/app/Config/Routes.php b/app/Config/Routes.php index 87d462a..a31d88f 100644 --- a/app/Config/Routes.php +++ b/app/Config/Routes.php @@ -113,14 +113,8 @@ $routes->group('admin/cloudflare', ['namespace' => 'App\Controllers\Admin\Cloudf }); $routes->group('firewall', function ($routes) { $routes->get('/', 'FirewallController::index'); - $routes->get('create', 'FirewallController::create_form'); - $routes->post('create', 'FirewallController::create'); $routes->get('view/(:alphanum)', 'FirewallController::view/$1'); - $routes->get('delete/(:alphanum)', 'FirewallController::delete/$1'); - $routes->get('sync/(:alphanum)', 'FirewallController::sync/$1'); $routes->get('toggle/(:alphanum)/(:any)', 'FirewallController::toggle/$1/$2'); - $routes->post('batchjob', 'FirewallController::batcjob'); - $routes->post('batchjob_delete', 'FirewallController::batcjob_delete'); $routes->get('reload/(:alphanum)', 'FirewallController::reload/$1'); $routes->get('download/(:alpha)', 'FirewallController::download/$1'); }); diff --git a/app/Controllers/Admin/Cloudflare/FirewallController.php b/app/Controllers/Admin/Cloudflare/FirewallController.php index 42e7c05..2bcd13d 100644 --- a/app/Controllers/Admin/Cloudflare/FirewallController.php +++ b/app/Controllers/Admin/Cloudflare/FirewallController.php @@ -54,73 +54,16 @@ class FirewallController extends CloudflareController return $options; } //전송된 데이터 - protected function getFormData(string $field, array $formDatas): array - { - switch ($field) { - case 'hosts': - $formDatas[$field] = explode("\n", $this->request->getVar($field)); - if (!is_array($formDatas[$field]) || !count($formDatas[$field])) { - throw new \Exception("호스트명이 정의되지 않았습니다."); - } - break; - default: - $formDatas = parent::getFormData($field, $formDatas); - break; - } - return $formDatas; - } private function init(string $action, array $fields = []): void { $this->action = $action; - $this->fields = count($fields) ? $fields : [$this->getModel()::PARENT, 'action', 'description', 'expression', 'ref', 'paused', 'updated_at', 'created_at']; + $this->fields = count($fields) ? $fields : [$this->getModel()::PARENT, 'mode', 'description', 'expression', 'enabled', 'updated_at', 'created_at']; $this->field_rules = $this->getModel()->getFieldRules($this->action, $this->fields); - $this->filter_fields = [$this->getModel()::PARENT, 'mode', 'paused']; + $this->filter_fields = [$this->getModel()::PARENT, 'mode', 'enabled']; $this->field_options = $this->getFormFieldOptions($this->filter_fields); - $this->batchjob_fields = ['mode', 'paused']; - } - //생성 - public function create_form(): RedirectResponse|string - { - $this->init('create', [$this->getModel()::PARENT, 'action', 'description', 'expression', 'paused']); - $this->field_rules = $this->getModel()->getFieldRules($this->action, $this->fields); - //부모데이터 정의 - $parent_field = $this->getModel()::PARENT; - $this->$parent_field = $this->request->getVar($parent_field) ?: DEFAULTS["EMPTY"]; - return $this->create_form_procedure(); - } - protected function create_process(): void - { - //DB작업도 Socket에서 다 처리하므로 parent::create_process()하면 않됨 - $this->create_validate($this->action, $this->fields); - $this->formDatas = $this->getFormDatas(); - //부모데이터정의 - $this->_zone_entity = $this->getZoneModel()->getEntityByPK($this->formDatas[$this->getModel()::PARENT]); - $this->entity = $this->getMyLibrary()->create( - $this->formDatas['mode'], - $this->formDatas['description'], - $this->formDatas['expression'], - $this->formDatas['paused'], - $this->formDatas['ref'] - ); - log_message("debug", message: "Firewall:{$this->entity->getTitle()} 생성 작업을 완료하였습니다."); - } - protected function create_process_result(): RedirectResponse|string - { - $this->init(__FUNCTION__); - return $this->view_process_result(); - } - public function create(): RedirectResponse|string - { - $this->init(__FUNCTION__, [$this->getModel()::PARENT, 'action', 'description', 'expression', 'paused']); - $this->field_rules = $this->getModel()->getFieldRules($this->action, $this->fields); - return $this->create_procedure(); + $this->batchjob_fields = []; } //수정 (modify,toggle,batchjob사용) - public function modify_form(int $uid): RedirectResponse|string - { - $this->init('modify', [$this->getModel()::PARENT, 'action', 'description', 'expression', 'paused']); - return $this->modify_form_procedure($uid); - } protected function modify_process(mixed $uid): void { //DB작업도 Socket에서 다 처리하므로 parent::modify_process($uid)하면 않됨 @@ -136,17 +79,6 @@ class FirewallController extends CloudflareController //Socket처리 $this->entity = $this->getMyLibrary()->modify($this->entity, $this->formDatas); } - public function modify(int $uid): RedirectResponse|string - { - $this->init(__FUNCTION__, [$this->getModel()::PARENT, 'action', 'description', 'expression', 'paused']); - return $this->modify_procedure($uid); - } - //일괄처리작업 - public function batcjob(): RedirectResponse - { - $this->init(__FUNCTION__); - return $this->batcjob_procedure(); - } //View //create_process_result에서 결과값을 entitys에 저장하고 호출하기때문에 아래와 같이 처리함 protected function view_process(mixed $uid): void @@ -174,20 +106,6 @@ class FirewallController extends CloudflareController $this->init(__FUNCTION__); return $this->view_procedure($uid); } - //삭제 - protected function delete_process(mixed $uid): void - { - //DB작업도 Socket에서 다 처리하므로 parent::delete_process($uid)하면 않됨 - //자신정보정의 - $this->entity = $this->getModel()->getEntityByPK($uid); - if ($this->entity === null) { - throw new \Exception("{$uid} 정보를 찾을수 없습니다."); - } - //부모데이터정의 - $this->_zone_entity = $this->getZoneModel()->getEntityByPK($this->entity->getParent()); - //Cloudflare 삭제 - $this->entity = $this->getMyLibrary()->delete($this->entity); - } // 리스트 protected function list_entitys_process(): array { @@ -229,23 +147,6 @@ class FirewallController extends CloudflareController $this->init(__FUNCTION__); return $this->download_procedure($output_type, $uid); } - //Sync작업 - protected function sync_process(string $uid): void - { - //자신정보정의 - $this->entity = $this->getModel()->getEntityByPK($uid); - if ($this->entity === null) { - throw new \Exception("{$uid} 정보를 찾을수 없습니다."); - } - //부모데이터정의 - $this->_zone_entity = $this->getZoneModel()->getEntityByPK($this->entity->getParent()); - //Socket처리 - $this->entity = $this->getMyLibrary()->sync($this->entity); - } - public function sync(string $uid): RedirectResponse - { - return $this->sync_procedure($uid); - } //reload Firewall By Zone protected function reload_process(mixed $uid): void { diff --git a/app/Entities/Cloudflare/FirewallEntity.php b/app/Entities/Cloudflare/FirewallEntity.php index 1dac62c..364e19d 100644 --- a/app/Entities/Cloudflare/FirewallEntity.php +++ b/app/Entities/Cloudflare/FirewallEntity.php @@ -9,7 +9,7 @@ class FirewallEntity extends CommonEntity { public function __toString() { - return "{$this->getPK()}|{$this->getParent()}|{$this->getTitle()}|{$this->attributes['host']}|{$this->attributes['content']}|{$this->attributes['proxied']}|{$this->attributes['fixed']}|{$this->attributes['locked']}"; + return "{$this->getPK()}|{$this->getParent()}|{$this->getTitle()}{$this->attributes['enabled']}"; } public function getPK(): string { @@ -28,4 +28,8 @@ class FirewallEntity extends CommonEntity { return $this->attributes[FirewallModel::PARENT]; } + public function getRulesetID(): string + { + return $this->attributes['rulesetid']; + } } diff --git a/app/Helpers/Cloudflare/FirewallHelper.php b/app/Helpers/Cloudflare/FirewallHelper.php index 8251dcd..8a5c604 100644 --- a/app/Helpers/Cloudflare/FirewallHelper.php +++ b/app/Helpers/Cloudflare/FirewallHelper.php @@ -34,17 +34,14 @@ class FirewallHelper extends MVCHelper case FirewallModel::TITLE: //host $form = form_input($field, $value, $extras); break; - case 'hosts': + case 'expression': $form = form_textarea($field, html_entity_decode($value), [ 'rows' => '5', ...$extras ]); break; - case 'expression': - $form = form_input($field, $value, ["placeholder" => "예)123.123.123.123", ...$extras]); - break; - case "action": - case "paused": + case "mode": + case "enabled": $form = form_dropdown($field, [ "" => lang($viewDatas['class_path'] . '.label.' . $field) . ' 선택', ] + $viewDatas['field_options'][$field], $value, $extras); @@ -81,6 +78,17 @@ class FirewallHelper extends MVCHelper case FirewallModel::TITLE: $value = parent::getFieldView($field, $viewDatas, ['class' => "label_hosts", ...$extras]); break; + case 'expression': + // 값이 40자 이상인 경우 자르고 '...' 추가 + if (mb_strlen($value) > 40) { + $value = '' . + htmlspecialchars(mb_substr($value, 0, 40), ENT_QUOTES) . '...'; + $value .= ''; + } + break; + case 'mode': + $value = $viewDatas['field_options'][$field][$value]; + break; default: $value = parent::getFieldView($field, $viewDatas, $extras); break; @@ -90,31 +98,25 @@ class FirewallHelper extends MVCHelper public function getListRowColor($entity): string { - return $entity->paused != 'on' ? 'class="table-danger"' : ""; + return $entity->enabled != 'on' ? 'class="table-danger"' : ""; } public function getListButton(string $action, array $viewDatas, array $extras = []): string { switch ($action) { + case 'create': + $action = ""; + break; case 'modify': - $checkbox = ""; - if ($viewDatas['entity']->fixed != 'on') { - $pk = $viewDatas['entity']->getPK(); - $oldBatchJobUids = old("batchjob_uids") ?? []; - $oldBatchJobUids = is_array($oldBatchJobUids) ? $oldBatchJobUids : [$oldBatchJobUids]; - $checkbox = form_checkbox([ - "id" => "checkbox_uid_{$pk}", - "name" => "batchjob_uids[]", - "value" => $pk, - "class" => "batchjobuids_checkboxs", - "checked" => in_array($pk, $oldBatchJobUids) - ]); - } - $extras = ["target" => "_self", ...$extras]; - $action = $checkbox . anchor( - current_url() . "/sync/" . $viewDatas['entity']->getPK(), - $viewDatas['cnt'], - $extras - ); + $action = $viewDatas['cnt']; + break; + case 'delete': + $action = ""; + break; + case 'batchjob': + $action = ""; + break; + case 'batchjob_delete': + $action = ""; break; default: $action = parent::getListButton($action, $viewDatas, $extras); diff --git a/app/Helpers/Cloudflare/ZoneHelper.php b/app/Helpers/Cloudflare/ZoneHelper.php index cd3c879..a01b30c 100644 --- a/app/Helpers/Cloudflare/ZoneHelper.php +++ b/app/Helpers/Cloudflare/ZoneHelper.php @@ -91,14 +91,16 @@ class ZoneHelper extends MVCHelper $this->old_parent = $viewDatas['entity']->getParent(); break; case ZoneModel::TITLE: - $value = anchor( - base_url() . $viewDatas['uri_path'] . 'record/reload/' . $viewDatas['entity']->getPK(), - ICONS["RELOAD"], - [ - "class" => "btn btn-sm btn-primary btn-circle", - "target" => "_self" - ] - ) . " " . + $value = sprintf( + "
%s %s%s
%s %s
", + anchor( + base_url() . $viewDatas['uri_path'] . 'record/reload/' . $viewDatas['entity']->getPK(), + ICONS["RELOAD"], + [ + "class" => "btn btn-sm btn-primary btn-circle", + "target" => "_self" + ] + ), anchor( base_url() . $viewDatas['uri_path'] . "record?zone_uid=" . $viewDatas['entity']->getPK(), ICONS["FLAG"], @@ -106,7 +108,25 @@ class ZoneHelper extends MVCHelper "class" => "btn btn-sm btn-primary btn-circle", "target" => "_self" ] - ) . " " . parent::getFieldView($field, $viewDatas, ['class' => "label_zones", ...$extras]); + ), + parent::getFieldView($field, $viewDatas, ['class' => "label_zones", ...$extras]), + anchor( + base_url() . $viewDatas['uri_path'] . 'firewall/reload/' . $viewDatas['entity']->getPK(), + ICONS["RELOAD"], + [ + "class" => "btn btn-sm btn-warning btn-circle", + "target" => "_self" + ] + ), + anchor( + base_url() . $viewDatas['uri_path'] . "firewall?zone_uid=" . $viewDatas['entity']->getPK(), + ICONS["FLAG"], + [ + "class" => "btn btn-sm btn-warning btn-circle", + "target" => "_self" + ] + ) + ); break; case 'name_servers': case 'original_name_servers': diff --git a/app/Language/en/Cloudflare/Firewall.php b/app/Language/en/Cloudflare/Firewall.php index 30e2868..aba4c87 100644 --- a/app/Language/en/Cloudflare/Firewall.php +++ b/app/Language/en/Cloudflare/Firewall.php @@ -1,27 +1,28 @@ "Firewall정보", - 'label' => [ - 'uid' => "번호", - 'zone_uid' => "도메인", + 'title' => "Firewall정보", + 'label' => [ + 'uid' => "번호", + 'zone_uid' => "도메인", + 'rulesetid' => "RulesetID", + 'mode' => "Action", 'description' => "설명", - 'action' => "Action", 'expression' => "Rule", - 'ref' => "REF", - 'paused' => "상태", + 'enabled' => "상태", 'updated_at' => "수정일", 'created_at' => "작성일", ], - "ZONE_UID" => [], - "ACTION" => [ - 'simulate' => 'simulate', + "ZONE_UID" => [], + "MODE" => [ + 'block' => 'block', + 'simulate' => 'simulate', 'ban' => 'ban', - 'challenge' => 'challenge', - 'js_challenge' => 'js_challenge', - 'managed_challenge' => 'managed_challenge', + 'challenge' => 'challenge', + 'js_challenge' => 'js_challenge', + 'managed_challenge' => 'managed_challenge', ], - "PAUSED" => [ - "on" => "사용", + "ENABLED" => [ + "on" => "사용", "off" => "사용 않함", ], ]; diff --git a/app/Models/Cloudflare/FirewallModel.php b/app/Models/Cloudflare/FirewallModel.php index 4c57c69..e2b6e50 100644 --- a/app/Models/Cloudflare/FirewallModel.php +++ b/app/Models/Cloudflare/FirewallModel.php @@ -16,7 +16,7 @@ class FirewallModel extends CommonModel protected $primaryKey = self::PK; protected $useAutoIncrement = false; protected $returnType = FirewallEntity::class; //object,array,entity명::class - protected $allowedFields = [self::PK, self::PARENT, self::TITLE, 'action', 'description', 'expression', 'paused', 'updated_at', 'crated_at']; + protected $allowedFields = [self::PK, self::PARENT, 'rulesetid', self::TITLE, 'mode', 'description', 'expression', 'enabled', 'updated_at', 'crated_at']; protected $useTimestamps = true; public function __construct() @@ -37,15 +37,18 @@ class FirewallModel extends CommonModel case self::PARENT: $rule = "required|trim|alpha_numeric"; break; + case 'rulesetid': + $rule = "required|trim|alpha_numeric"; + break; case self::TITLE: - case "action": + case "mode": case "expression": $rule = "required|trim|string"; break; case "ref": $rule = "if_exist|trim|string"; break; - case "locked": + case "enabled": $rule = "required|in_list[on,off]"; break; default: diff --git a/app/Services/Cloudflare/Firewall.php b/app/Services/Cloudflare/Firewall.php index 6d42a89..9775626 100644 --- a/app/Services/Cloudflare/Firewall.php +++ b/app/Services/Cloudflare/Firewall.php @@ -46,90 +46,40 @@ class Firewall extends Cloudflare } return $this->_accountModel; } - protected function getArrayByResult(\stdClass $result, array $formDatas = []): array + protected function getArrayByResult(\stdClass $rule, array $formDatas = []): array { // log_message("debug", var_export($result, true)); - $formDatas[FirewallModel::PK] = $result->id; - $formDatas[FirewallModel::PARENT] = $result->zone_id; - $formDatas[FirewallModel::TITLE] = $result->name; - $formDatas['action'] = $result->action; - $formDatas['description'] = $result->description; - $formDatas['priority'] = (int) $result->priority; - $formDatas['filter_id'] = $result->filter->id; - $formDatas['filter_expression'] = $result->filter->expression; - $formDatas['filter_paused'] = isset($result->filter->paused) && $result->filter->paused ? "off" : "on"; + $formDatas[FirewallModel::PK] = $rule->id; + $formDatas[FirewallModel::PARENT] = $this->getParentEntity()->getPK(); + $formDatas['mode'] = $rule->action; + $formDatas[FirewallModel::TITLE] = $rule->description; + $formDatas['expression'] = $rule->expression; + $formDatas['enabled'] = isset($rule->enabled) && $rule->enabled ? "on" : "off"; $formDatas['updated_at'] = date("Y-m-d H:i:s"); - $formDatas['created_at'] = $result->created_on; + $formDatas['created_at'] = $rule->last_updated; // log_message("debug", print_r($formDatas, true)); return $formDatas; } - public function create(string $mode, string $description, string $expression, string $paused, string $ref = "FIL-100"): FirewallEntity - { - //Socket용 - //호스트생성을 위해 Cloudflare에 전송 - $datas = [ - 'action' => [ - 'mode' => $mode, - 'response' => [ - 'body' => "This request has been rate-limited.", - 'content_type' => 'text/xml', - ], - 'timeout' => 86400, - ], - 'filter' => [ - 'description' => $description, - 'expression' => $expression, - 'paused' => $paused === 'on' ? true : false, - "ref" => $ref - ] - ]; - $response = $this->getMySocket()->post("zones/{$this->getParentEntity()->getPK()}/firewall/rules", $datas); - $body = json_decode($response->getBody()); - //DB생성 - return $this->getModel()->create($this->getArrayByResult($body->result)); - } public function modify(FirewallEntity $entity, array $formDatas): FirewallEntity { - //TTL값은 CDN(proxied)가 사용함일때는 무조건 1, 않함일때는 120이 적용 $datas = [ - 'action' => [ - 'mode' => $formDatas['mode'], - 'response' => [ - 'body' => "This request has been rate-limited.", - 'content_type' => 'text/xml', - ], - 'timeout' => 86400, - ], - 'filter' => [ - 'description' => $formDatas['description'], - 'expression' => $formDatas['expression'], - 'paused' => $formDatas['paused'] === 'on' ? true : false, - "ref" => $formDatas['ref'] - ] + 'id' => $entity->getPK(), // 수정할 rule의 ID + "action" => $entity->mode, + "description" => $entity->description, + "expression" => $entity->expression, + 'enabled' => isset($formDatas['enabled']) && $formDatas['enabled'] === "on" ? true : false // true/false 값으로 설정 ]; // 인코딩된 JSON을 확인 // throw new \Exception("Firewall:" . __FUNCTION__ . "\n" . json_encode($datas, JSON_PRETTY_PRINT) . "\n" . var_export($datas, true)); - $response = $this->getMySocket()->put("zones/{$this->getParentEntity()->getPK()}/firewall/rules/{$entity->getPK()}", $datas); + $response = $this->getMySocket()->patch("zones/{$this->getParentEntity()->getPK()}/rulesets/{$entity->getRulesetID()}/rules/{$entity->getPK()}", $datas); $body = json_decode($response->getBody()); - // DB 수정 - return $this->getModel()->modify($entity, $this->getArrayByResult($body->result)); - } - public function delete(FirewallEntity $entity): FirewallEntity - { - $this->getMySocket()->delete("zones/{$this->getParentEntity()->getPK()}/firewall/rules/{$entity->getPK()}"); - //DB삭제 - $this->getModel()->delete($entity->getPK()); - log_message("debug", $this->getModel()->getLastQuery()); - return $entity; - } - public function sync(FirewallEntity $entity): FirewallEntity - { - // 기존 Sync형태 - $response = $this->getMySocket()->get("zones/{$this->getParentEntity()->getPK()}/firewall/rules/{$entity->getPK()}"); - $body = json_decode($response->getBody()); - // DB수정 - // log_message("debug", var_export($cf->result, true)); - $entity = $this->getModel()->modify($entity, $this->getArrayByResult($body->result)); + foreach ($body->result->rules as $rule) { + if ($rule->id === $entity->getPK()) { + $formDatas = ['rulesetid' => $body->result->id]; + $formDatas = $this->getArrayByResult($rule, $formDatas); + $entity = $this->getModel()->modify($entity, $formDatas); + } + } return $entity; } //Reload @@ -138,19 +88,12 @@ class Firewall extends Cloudflare log_message("notice", "\n-----------Zone {$this->getParentEntity()->getTitle()}의 Firewall 처리 시작-----------"); $entitys = []; try { - $results_array = $this->reload_procedure("zones/{$this->getParentEntity()->getPK()}/dns_records"); - if (count(value: $results_array) > 0) { - foreach ($results_array as $results) { - foreach ($results as $result) { - $formDatas = $this->getArrayByResult($result); - $entitys[$formDatas[FirewallModel::PK]] = $this->getModel()->modify(new FirewallEntity(), $formDatas); - } - } - //부모키를 기준으로 CF에 존재하지 않는 데이터 DB삭제 - $this->getModel()->where(FirewallModel::PARENT, $this->getParentEntity()->getPK()); - $this->getModel()->whereNotIn(FirewallModel::PK, array_keys($entitys)); - $this->getModel()->delete(); - log_message("debug", $this->getModel()->getLastQuery()); + $response = $this->getMySocket()->get("zones/{$this->getParentEntity()->getPK()}//rulesets/phases/http_request_firewall_custom/entrypoint"); + $body = json_decode($response->getBody()); + foreach ($body->result->rules as $rule) { + $formDatas = ['rulesetid' => $body->result->id]; + $formDatas = $this->getArrayByResult($rule, $formDatas); + $entitys[$formDatas[FirewallModel::PK]] = $this->getModel()->modify(new FirewallEntity(), $formDatas); } } catch (\Exception $e) { log_message("error", $e->getMessage());