diff --git a/app/Config/Routes.php b/app/Config/Routes.php index 723971b..9ab0788 100644 --- a/app/Config/Routes.php +++ b/app/Config/Routes.php @@ -15,6 +15,8 @@ $routes->group('cli', ['namespace' => 'App\Controllers\CLI'], function ($routes) $routes->group('cloudflare', function ($routes) { $routes->cli('reload', 'Cloudflare::reload'); $routes->cli('reload/(:num)', 'Cloudflare::reload/$1'); + $routes->cli('auditlog', 'Cloudflare::auditlog'); + $routes->cli('auditlog/(:num)', 'Cloudflare::auditlog/$1'); }); }); $routes->get('/', 'Home::index'); @@ -27,17 +29,6 @@ $routes->group('/user', function ($routes) { $routes->get('google_login', 'UserController::google_login'); $routes->get('logout', 'UserController::logout'); }); -$routes->group('/cloudflare', ['namespace' => 'App\Controllers\Cloudflare'], function ($routes) { - $routes->group('zone', function ($routes) { - $routes->post('webhook', 'ZoneController::webhook'); - }); - $routes->group('record', function ($routes) { - $routes->post('webhook', 'RecordController::webhook'); - }); - $routes->group('firewall', function ($routes) { - $routes->post('webhook', 'FirewallController::webhook'); - }); -}); $routes->group('admin', ['namespace' => 'App\Controllers\Admin', 'filter' => 'authFilter:manager'], function ($routes) { $routes->get('/', 'Home::index'); }); @@ -96,6 +87,12 @@ $routes->group('admin/cloudflare', ['namespace' => 'App\Controllers\Admin\Cloudf $routes->get('reload/(:num)', 'AccountController::reload/$1'); $routes->get('download/(:alphanum)', 'AccountController::download/$1'); }); + $routes->group('auditlog', function ($routes) { + $routes->get('/', 'AuditLogController::index'); + $routes->get('view/(:alphanum)', 'AuditLogController::view/$1'); + $routes->get('reload/(:num)', 'AuditLogController::reload/$1'); + $routes->get('download/(:alphanum)', 'AuditLogController::download/$1'); + }); $routes->group('zone', function ($routes) { $routes->get('/', 'ZoneController::index'); $routes->get('create', 'ZoneController::create_form'); diff --git a/app/Controllers/Admin/Cloudflare/AuditLogController.php b/app/Controllers/Admin/Cloudflare/AuditLogController.php new file mode 100644 index 0000000..7deeb1b --- /dev/null +++ b/app/Controllers/Admin/Cloudflare/AuditLogController.php @@ -0,0 +1,79 @@ +title = lang("{$this->getService()->class_path}.title"); + $this->helper = new AuditLogHelper(); + } + protected function getModel(): AuditLogModel + { + if ($this->_model === null) { + $this->_model = new AuditLogModel(); + } + return $this->_model; + } + protected function getService(): AuditLogService + { + if ($this->service === null) { + $this->service = new AuditLogService(); + } + return $this->service; + } + protected function getFormFieldOption(string $field, array $options = []): array + { + switch ($field) { + case $this->getModel()::PARENT: + // $this->getZoneModel()->where('status', 'active'); + $options[$field] = $this->getZoneModel()->getFormFieldOption($field); + // echo $this->getAccountModel()->getLastQuery(); + // dd($options); + break; + default: + $options = parent::getFormFieldOption($field, $options); + break; + } + return $options; + } + private function init(string $action, array $fields = []): void + { + $this->action = $action; + $this->fields = count($fields) ? $fields : [$this->getModel()::PARENT, $this->getModel()::TITLE, 'actor', 'interface', 'resource_id', 'resource_type', 'status', 'updated_at', 'created_at']; + $this->field_rules = $this->getModel()->getFieldRules($this->action, $this->fields); + $this->filter_fields = [$this->getModel()::PARENT, 'status']; + $this->field_options = $this->getFormFieldOptions($this->filter_fields); + $this->batchjob_fields = []; + } + //View + public function view(string $uid): RedirectResponse|string + { + $this->init(__FUNCTION__); + return $this->view_procedure($uid); + } + // 리스트 + public function index(): string + { + $this->init(__FUNCTION__); + return $this->list_procedure(); + } + // Download + public function download(string $output_type, mixed $uid = false): DownloadResponse|string + { + $this->init(__FUNCTION__); + return $this->download_procedure($output_type, $uid); + } +} diff --git a/app/Controllers/CLI/Cloudflare.php b/app/Controllers/CLI/Cloudflare.php index 14ab342..f0a3f9f 100644 --- a/app/Controllers/CLI/Cloudflare.php +++ b/app/Controllers/CLI/Cloudflare.php @@ -6,11 +6,12 @@ use App\Controllers\BaseController; use App\Entities\Cloudflare\AccountEntity; use App\Entities\Cloudflare\AuthEntity; use App\Entities\Cloudflare\ZoneEntity; -use App\Services\Cloudflare\AccountService; -use App\Services\Cloudflare\ZoneService; -use App\Services\Cloudflare\RecordService; -use App\Services\Cloudflare\FirewallService; use App\Models\Cloudflare\AuthModel; +use App\Services\Cloudflare\AccountService; +use App\Services\Cloudflare\AuditLogService; +use App\Services\Cloudflare\FirewallService; +use App\Services\Cloudflare\RecordService; +use App\Services\Cloudflare\ZoneService; use CodeIgniter\HTTP\RequestInterface; use CodeIgniter\HTTP\ResponseInterface; use Psr\Log\LoggerInterface; @@ -68,4 +69,38 @@ class Cloudflare extends BaseController ); } } + + public function auditlog(mixed $uid = false): void + { + //Transaction Start + // $this->_db->transStart(); + try { + $auth_model = model(AuthModel::class); + if (is_numeric($uid) && $uid > 0) { + $auth_model->where(AuthModel::PK, intval($uid)); + } else { + $auth_model->where('status', DEFAULTS["STATUS"]); + } + $auth_entitys = $auth_model->getEntitys(); + foreach ($auth_entitys as $auth_entity) { + $account = new AccountService(); + $account_entitys = $account->reload($auth_entity); + $auditlog = new AuditLogService(); + foreach ($account_entitys as $account_entity) { + $auditlog->auditlog($account_entity); + } + } + log_message("notice", "AuditLogs 작업을 완료하였습니다."); + // $this->_db->transCommit(); + } catch (\Exception $e) { + //Transaction Rollback + // $this->_db->transRollback(); + log_message( + "error", + "Reload 작업을 실패하였습니다.\n--------------\n" . + $e->getMessage() . + "\n--------------\n" + ); + } + } } diff --git a/app/Database/update.txt b/app/Database/update.txt index c129b42..079db55 100644 --- a/app/Database/update.txt +++ b/app/Database/update.txt @@ -22,4 +22,19 @@ ALTER TABLE cloudflareaccount DROP column auth_uid; 5. id unique key 추가 ALTER TABLE cloudflareaccount ADD UNIQUE key cloudflareaccount_ibuk_1 (id); -ALTER TABLE cloudflareaccount ADD UNIQUE key cloudflareaccount_ibuk_2 (authkey); \ No newline at end of file +ALTER TABLE cloudflareaccount ADD UNIQUE key cloudflareaccount_ibuk_2 (authkey); + +6. auditlog용 table추가 +CREATE TABLE cloudflareauditlog ( + uid varchar(255) NOT NULL COMMENT 'id', + zone_uid varchar(100) NOT NULL COMMENT 'newValueJson->zone_id', + action varchar(100) NOT NULL COMMENT 'action->type', + actor varchar(100) NOT NULL COMMENT 'actor->type', + interface varchar(100) NULL COMMENT 'interface', + resource_id varchar(100) NOT NULL COMMENT 'resource->id', + resource_type varchar(50) NOT NULL COMMENT 'resource->type', + status varchar(10) NOT NULL COMMENT 'action->result', + updated_at timestamp NULL DEFAULT NULL, + created_at timestamp NOT NULL COMMENT 'when', + PRIMARY KEY (uid) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci COMMENT='cloudflare Auditlog 정보'; \ No newline at end of file diff --git a/app/Entities/Cloudflare/AuditLogEntity.php b/app/Entities/Cloudflare/AuditLogEntity.php new file mode 100644 index 0000000..ea5f001 --- /dev/null +++ b/app/Entities/Cloudflare/AuditLogEntity.php @@ -0,0 +1,31 @@ +getPK()}|{$this->getTitle()}|{$this->attributes['type']}|{$this->attributes['status']}"; + } + public function getPK(): string + { + return $this->attributes[AuditLogModel::PK]; + } + public function getTitle(): string + { + return $this->attributes[AuditLogModel::TITLE]; + } + public function setTitle(string $title): void + { + $this->attributes[AuditLogModel::TITLE] = $title; + } + //Common Function + public function getParent(): string + { + return $this->attributes[AuditLogModel::PARENT]; + } +} diff --git a/app/Helpers/Cloudflare/AuditLogHelper.php b/app/Helpers/Cloudflare/AuditLogHelper.php new file mode 100644 index 0000000..f659b64 --- /dev/null +++ b/app/Helpers/Cloudflare/AuditLogHelper.php @@ -0,0 +1,102 @@ + "form-control", "required" => "", ...$extras] : ["class" => "form-control", ...$extras]; + } + $value = $value ?: DEFAULTS['EMPTY']; + switch ($field) { + case AuditLogModel::PARENT: + //기존 작성하던값old($field)가 있으면 그값을 넣고 없으면 부모값이 있으면 넣고 없으면 entiy가 있으면 그값을 넣고 없으면 디폴트값을 넣는다. + $value = $value ?: (isset($viewDatas[$field]) ? $viewDatas[$field] : (isset($viewDatas['entity']) ? $viewDatas['entity']->getParent() : DEFAULTS['EMPTY'])); + + $extra_class = isset($extras['class']) ? 'select-field ' . $extras['class'] : 'select-field'; + $form = form_dropdown($field, [ + "" => lang($viewDatas['class_path'] . '.label.' . $field) . ' 선택', + ] + $viewDatas['field_options'][$field], $value, [ + 'class' => $extra_class, + ...array_diff_key($extras, ['class' => '']) + ]); + break; + case AuditLogModel::TITLE: + $form = form_input($field, $value, ["placeholder" => "예)test@exmaple.com", ...$extras]); + break; + default: + $form = parent::getFieldForm($field, $value, $viewDatas, $extras); + break; + } + return $form; + } // + public function getFieldView(string $field, array $viewDatas, array $extras = []): string + { + $value = $viewDatas['entity']->$field ?: DEFAULTS['EMPTY']; + switch ($field) { + case AuditLogModel::PARENT: + $value = " " . + preg_replace("/(\w+)@(.+)/", "$1", $viewDatas['field_options'][$field][$value]) + . ""; + break; + case AuditLogModel::TITLE: + $value = form_label( + $value, + 'view', + [ + "data-src" => current_url() . '/view/' . $viewDatas['entity']->getPK(), + "data-bs-toggle" => "modal", + "data-bs-target" => "#index_action_form", + "style" => "color: blue; cursor: pointer; font-weight:bold;", + ...$extras, + ] + ); + break; + case 'status': + $value = $viewDatas['field_options'][$field][$value]; + break; + default: + $value = parent::getFieldView($field, $viewDatas, $extras); + break; + } + return $value; + } // + public function getListRowColor($entity): string + { + return $entity->status != 'true' ? 'class="table-danger"' : ""; + } + public function getListButton(string $action, array $viewDatas, array $extras = []): string + { + switch ($action) { + case 'create': + $action = ""; + break; + case 'modify': + $action = $viewDatas['cnt']; + break; + case 'delete': + $action = ""; + break; + case 'batchjob': + $action = ""; + break; + case 'batchjob_delete': + $action = ""; + break; + default: + $action = parent::getListButton($action, $viewDatas, $extras); + break; + } + return $action; + } +} diff --git a/app/Language/en/Cloudflare/AuditLog.php b/app/Language/en/Cloudflare/AuditLog.php new file mode 100644 index 0000000..c4eceda --- /dev/null +++ b/app/Language/en/Cloudflare/AuditLog.php @@ -0,0 +1,20 @@ + "AuditLog정보", + 'label' => [ + 'uid' => "번호", + 'zone_uid' => "도메인", + 'action' => "Action", + 'actor' => "작업자", + 'interface' => "작업형식", + 'resource_id' => "자원ID", + 'resource_type' => "자원형식", + 'status' => "상태", + 'updated_at' => "수정일", + 'created_at' => "작성일", + ], + "STATUS" => [ + "true" => "완료", + "false" => "실패", + ], +]; diff --git a/app/Models/Cloudflare/AuditLogModel.php b/app/Models/Cloudflare/AuditLogModel.php new file mode 100644 index 0000000..90d83bb --- /dev/null +++ b/app/Models/Cloudflare/AuditLogModel.php @@ -0,0 +1,93 @@ +table}.{$field}]" : ""; + break; + case self::PARENT: + $rule = "required|trim|alpha_numeric"; + break; + case self::TITLE: + case 'actor': + case 'resource_id': + case 'resource_type': + $rule = "required|trim|string"; + break; + case 'interface': + $rule = "if_exist|trim|string"; + break; + case "status": + $rule = "if_exist|in_list[true,false]"; + break; + default: + $rule = parent::getFieldRule($action, $field); + break; + } + return $rule; + } + public function getFormFieldInputOption(string $field, array $options = []): array + { + switch ($field) { + default: + $this->orderBy(self::TITLE, 'asc'); + $options = parent::getFormFieldInputOption($field, $options); + break; + } + return $options; + } + public function getEntityByPK(string $uid): null|AuditLogEntity + { + $this->where(self::PK, $uid); + return $this->getEntity(); + } + public function getEntityByID(string $id): null|AuditLogEntity + { + $this->where(self::TITLE, $id); + return $this->getEntity(); + } + public function getEntitysByParent(ZoneEntity $zone_entity) + { + $this->where(self::PARENT, $zone_entity->getPK()); + return $this->getEntitys(); + } + //create용 + public function create(array $formDatas = []): AuditLogEntity + { + return $this->create_process(new AuditLogEntity(), $formDatas); + } + //modify용 + public function modify(AuditLogEntity $entity, array $formDatas): AuditLogEntity + { + return $this->modify_process($entity, $formDatas); + } +} diff --git a/app/Services/Cloudflare/AccountService.php b/app/Services/Cloudflare/AccountService.php index 948a2d9..3edeef9 100644 --- a/app/Services/Cloudflare/AccountService.php +++ b/app/Services/Cloudflare/AccountService.php @@ -5,6 +5,7 @@ namespace App\Services\Cloudflare; use App\Entities\Cloudflare\AccountEntity; use App\Entities\Cloudflare\AuthEntity; use App\Models\Cloudflare\AccountModel; +use stdClass; class AccountService extends CloudflareService { @@ -114,35 +115,4 @@ class AccountService extends CloudflareService log_message("notice", message: "\n-----------Auth {$this->getParentEntity()->getTitle()}의 Account 처리[" . count($entitys) . "개] 완료-----------"); return $entitys; } - - public function audit(AuthEntity $parent_entity, AccountEntity $entity): array - { - //부모데이터정의 - $this->setParentEntity($parent_entity); - log_message("notice", "\n----------Auth {$this->getParentEntity()->getTitle()}의 Account 처리 시작-----------"); - $entitys = []; - try { - $results = $this->reload_procedure("accounts/{$entity->getPK()}/audit_logs?since=" . date("Y-m-d") . "T00:00:00"); - foreach ($results as $result) { - if (isset($result->action->result) && $result->action->result && isset($result->metadata->newValueJson->zone_id)) { - //해당 Zone을 Sync작업한다 - $zone_service = new ZoneService(); - $zone_entity = $zone_service->getEntityByPK($result->metadata->newValueJson->zone_id); - $zone_entity = $zone_service->sync($entity, $zone_entity); - //해당 Zone의 Record reload작업한다 - $record_service = new RecordService(); - $record_service->reload($zone_entity); - //해당 Zone의 Firewall reload작업한다 - $firewall_service = new FirewallService(); - $firewall_service->reload($zone_entity); - log_message("debug", "{$entity->getTitle()} Account 의 {$zone_entity->getTitle()} Sync및 Record,Firewall Reload 처리작업"); - } - } - } catch (\Exception $e) { - log_message("error", $e->getMessage()); - throw new \Exception($e->getMessage()); - } - log_message("notice", message: "\n-----------Auth {$this->getParentEntity()->getTitle()}의 Account 처리[" . count($entitys) . "개] 완료-----------"); - return $entitys; - } } diff --git a/app/Services/Cloudflare/AuditLogService.php b/app/Services/Cloudflare/AuditLogService.php new file mode 100644 index 0000000..ffb42ee --- /dev/null +++ b/app/Services/Cloudflare/AuditLogService.php @@ -0,0 +1,96 @@ +class_name = "AuditLog"; + parent::__construct(); + $this->class_path .= $this->class_name; + } + protected function getModel(): AuditLogModel + { + if ($this->_model === null) { + $this->_model = new AuditLogModel(); + } + return $this->_model; + } + protected function getAccountModel(): AccountModel + { + if ($this->_accountModel === null) { + $this->_accountModel = new AccountModel(); + } + return $this->_accountModel; + } + + protected function getArrayByResult(\stdClass $result, array $formDatas = []): array + { + $formDatas[AuditLogModel::PK] = $result->id; + $formDatas[AuditLogModel::PARENT] = $result->newValueJson->zone_id; + $formDatas[AuditLogModel::TITLE] = $result->action->type; + $formDatas['actor'] = $result->actor->type; + $formDatas['interface'] = $result->interface; + $formDatas['resource_id'] = $result->resource->id; + $formDatas['resource_type'] = $result->resource->type; + $formDatas['status'] = $result->action->result ? "true" : "false"; + $formDatas['updated_at'] = date("Y-m-d H:i:s"); + $formDatas['created_at'] = $result->when; + return $formDatas; + } + + private function auditlog_process(AuditLogEntity $entity): void + { + //해당 Zone을 Sync작업한다 + $zone_service = new ZoneService(); + $zone_entity = $zone_service->getEntityByPK($entity->getParent()); + // $zone_entity = $zone_service->sync($entity, $zone_entity); + // //해당 Zone의 Record reload작업한다 + // $record_service = new RecordService(); + // $record_service->reload($zone_entity); + // //해당 Zone의 Firewall reload작업한다 + // $firewall_service = new FirewallService(); + // $firewall_service->reload($zone_entity); + log_message("debug", "AuditLog Process의 {$zone_entity->getTitle()} Sync및 Record,Firewall Reload 처리작업"); + } + public function auditlog(AccountEntity $account_entity): void + { + //Socket인증정보 정의 + $auth_entity = $this->getAuthModel()->getEntityByPK($account_entity->getParent()); + if ($auth_entity === null) { + throw new \Exception("해당 계정정보를 찾을수 없습니다."); + } + $this->setAuthEntity($auth_entity); + + log_message("notice", "\n----------Account {$account_entity->getTitle()}의 AuditLog 처리 시작-----------"); + try { + $response = $this->getMySocket()->get("accounts/{$account_entity->getPK()}/audit_logs?since=" . date("Y-m-d") . "T00:00:00"); + $body = json_decode($response->getBody()); + foreach ($body->result as $result) { + if (isset($result->action->result) && $result->action->result && isset($result->newValueJson->zone_id)) { + log_message("debug", var_export($result->newValueJson, true)); + $entity = $this->getModel()->getEntityByPK($result->id); + if ($entity === null) { + $entity = $this->getModel()->create($this->getArrayByResult($result)); + $this->auditlog_process($entity); + } + } + } + } catch (\Exception $e) { + log_message("error", $e->getMessage()); + throw new \Exception($e->getMessage()); + } + log_message("notice", message: "\n-----------Account {$account_entity->getTitle()}의 AuditLog 처리 완료-----------"); + } +} diff --git a/app/Services/Cloudflare/ZoneService.php b/app/Services/Cloudflare/ZoneService.php index 56a2263..7cc0054 100644 --- a/app/Services/Cloudflare/ZoneService.php +++ b/app/Services/Cloudflare/ZoneService.php @@ -46,7 +46,7 @@ class ZoneService extends CloudflareService } return $this->_model; } - public function getEntityByPK(string $uid): ZoneEntity + public function getEntityByPK(string $uid): null|ZoneEntity { return $this->getModel()->getEntityByPK($uid); } diff --git a/app/Views/layouts/admin/left_menu/cloudflare.php b/app/Views/layouts/admin/left_menu/cloudflare.php index a836849..2e4b093 100644 --- a/app/Views/layouts/admin/left_menu/cloudflare.php +++ b/app/Views/layouts/admin/left_menu/cloudflare.php @@ -11,6 +11,9 @@
+