From 9c63cfa67d7060e454c9903e33add2f9810ff050 Mon Sep 17 00:00:00 2001 From: Chris Date: Fri, 24 Oct 2025 14:56:46 +0200 Subject: [PATCH 01/23] add crosstables with fks, upgrade script und permissions --- roles/api/files/replace_metadata.json | 684 ++++++++++++++---- .../creation/fworch-create-foreign-keys.sql | 10 + .../sql/creation/fworch-create-tables.sql | 20 + roles/database/files/upgrade/9.0.sql | 45 ++ 4 files changed, 604 insertions(+), 155 deletions(-) diff --git a/roles/api/files/replace_metadata.json b/roles/api/files/replace_metadata.json index 31e65eccf..98278fc5e 100644 --- a/roles/api/files/replace_metadata.json +++ b/roles/api/files/replace_metadata.json @@ -3544,6 +3544,9 @@ "change_time", "unique_name" ], + "computed_fields": [ + "cl_rule_relevant_for_tenant" + ], "filter": {} } }, @@ -3571,6 +3574,9 @@ "change_time", "docu_time" ], + "computed_fields": [ + "cl_rule_relevant_for_tenant" + ], "filter": {}, "allow_aggregations": true } @@ -3630,6 +3636,9 @@ "change_time", "docu_time" ], + "computed_fields": [ + "cl_rule_relevant_for_tenant" + ], "filter": {} }, "comment": "" @@ -3756,6 +3765,9 @@ "change_time", "unique_name" ], + "computed_fields": [ + "cl_rule_relevant_for_tenant" + ], "filter": {}, "allow_aggregations": true } @@ -9307,6 +9319,7 @@ "hide_in_gui", "mgm_id", "mgm_name", + "mgm_uid", "multi_device_manager_id" ], "filter": { @@ -14799,12 +14812,14 @@ "permission": { "columns": [ "parent_rule_id", + "removed", "rule_create", "rule_id", "rule_last_seen", "xlate_rule", "access_rule", "active", + "is_global", "nat_rule", "rule_disabled", "rule_dst_neg", @@ -14819,6 +14834,7 @@ "dev_id", "last_change_admin", "mgm_id", + "rulebase_id", "rule_from_zone", "rule_num", "rule_to_zone", @@ -14850,12 +14866,14 @@ "permission": { "columns": [ "parent_rule_id", + "removed", "rule_create", "rule_id", "rule_last_seen", "xlate_rule", "access_rule", "active", + "is_global", "nat_rule", "rule_disabled", "rule_dst_neg", @@ -14870,6 +14888,7 @@ "dev_id", "last_change_admin", "mgm_id", + "rulebase_id", "rule_from_zone", "rule_num", "rule_to_zone", @@ -14954,47 +14973,48 @@ "role": "middleware-server", "permission": { "columns": [ + "parent_rule_id", + "removed", + "rule_create", + "rule_id", + "rule_last_seen", + "xlate_rule", "access_rule", - "action_id", "active", + "is_global", + "nat_rule", + "rule_disabled", + "rule_dst_neg", + "rule_implied", + "rule_src_neg", + "rule_svc_neg", + "rule_installon", + "rule_name", + "rule_ruleid", + "rule_time", + "action_id", "dev_id", "last_change_admin", "mgm_id", - "nat_rule", - "parent_rule_id", + "rulebase_id", + "rule_from_zone", + "rule_num", + "rule_to_zone", + "track_id", + "rule_custom_fields", + "rule_num_numeric", "parent_rule_type", - "removed", "rule_action", "rule_comment", - "rule_create", - "rule_custom_fields", - "rule_disabled", "rule_dst", - "rule_dst_neg", "rule_dst_refs", - "rule_from_zone", "rule_head_text", - "rule_id", - "rule_implied", - "rule_installon", - "rule_last_seen", - "rule_name", - "rule_num", - "rule_num_numeric", - "rule_ruleid", "rule_src", - "rule_src_neg", "rule_src_refs", "rule_svc", - "rule_svc_neg", "rule_svc_refs", - "rule_time", - "rule_to_zone", "rule_track", - "rule_uid", - "rulebase_id", - "track_id", - "xlate_rule" + "rule_uid" ], "computed_fields": [ "rule_relevant_for_tenant" @@ -15008,12 +15028,14 @@ "permission": { "columns": [ "parent_rule_id", + "removed", "rule_create", "rule_id", "rule_last_seen", "xlate_rule", "access_rule", "active", + "is_global", "nat_rule", "rule_disabled", "rule_dst_neg", @@ -15028,6 +15050,7 @@ "dev_id", "last_change_admin", "mgm_id", + "rulebase_id", "rule_from_zone", "rule_num", "rule_to_zone", @@ -15076,45 +15099,48 @@ "role": "recertifier", "permission": { "columns": [ - "parent_rule_id", - "rule_create", - "rule_id", - "rule_last_seen", - "xlate_rule", "access_rule", - "active", - "nat_rule", - "rule_disabled", - "rule_dst_neg", - "rule_implied", - "rule_src_neg", - "rule_svc_neg", - "rule_installon", - "rule_name", - "rule_ruleid", - "rule_time", "action_id", + "active", "dev_id", + "is_global", "last_change_admin", "mgm_id", - "rule_from_zone", - "rule_num", - "rule_to_zone", - "track_id", - "rule_custom_fields", - "rule_num_numeric", + "nat_rule", + "parent_rule_id", "parent_rule_type", + "removed", "rule_action", "rule_comment", + "rule_create", + "rule_custom_fields", + "rule_disabled", "rule_dst", + "rule_dst_neg", "rule_dst_refs", + "rule_from_zone", "rule_head_text", + "rule_id", + "rule_implied", + "rule_installon", + "rule_last_seen", + "rule_name", + "rule_num", + "rule_num_numeric", + "rule_ruleid", "rule_src", + "rule_src_neg", "rule_src_refs", "rule_svc", + "rule_svc_neg", "rule_svc_refs", + "rule_time", + "rule_to_zone", "rule_track", - "rule_uid" + "rule_uid", + "rulebase_id", + "track_id", + "xlate_rule" ], "computed_fields": [ "rule_relevant_for_tenant" @@ -15146,12 +15172,14 @@ "permission": { "columns": [ "parent_rule_id", + "removed", "rule_create", "rule_id", "rule_last_seen", "xlate_rule", "access_rule", "active", + "is_global", "nat_rule", "rule_disabled", "rule_dst_neg", @@ -15166,6 +15194,7 @@ "dev_id", "last_change_admin", "mgm_id", + "rulebase_id", "rule_from_zone", "rule_num", "rule_to_zone", @@ -15215,12 +15244,14 @@ "permission": { "columns": [ "parent_rule_id", + "removed", "rule_create", "rule_id", "rule_last_seen", "xlate_rule", "access_rule", "active", + "is_global", "nat_rule", "rule_disabled", "rule_dst_neg", @@ -15235,6 +15266,7 @@ "dev_id", "last_change_admin", "mgm_id", + "rulebase_id", "rule_from_zone", "rule_num", "rule_to_zone", @@ -15290,35 +15322,9 @@ }, { "table": { - "name": "rule_enforced_on_gateway", + "name": "rule_destination_to_zone", "schema": "public" }, - "object_relationships": [ - { - "name": "device", - "using": { - "foreign_key_constraint_on": "dev_id" - } - }, - { - "name": "importControlByRemoved", - "using": { - "foreign_key_constraint_on": "removed" - } - }, - { - "name": "import_control", - "using": { - "foreign_key_constraint_on": "created" - } - }, - { - "name": "rule", - "using": { - "foreign_key_constraint_on": "rule_id" - } - } - ], "insert_permissions": [ { "role": "importer", @@ -15327,22 +15333,36 @@ "columns": [ "created", "removed", - "dev_id", - "rule_id" + "rule_id", + "zone_id" ] }, "comment": "" } ], "select_permissions": [ + { + "role": "auditor", + "permission": { + "columns": [ + "created", + "removed", + "rule_id", + "zone_id" + ], + "filter": {}, + "allow_aggregations": true + }, + "comment": "" + }, { "role": "fw-admin", "permission": { "columns": [ "created", "removed", - "dev_id", - "rule_id" + "rule_id", + "zone_id" ], "filter": {}, "allow_aggregations": true @@ -15355,10 +15375,11 @@ "columns": [ "created", "removed", - "dev_id", - "rule_id" + "rule_id", + "zone_id" ], - "filter": {} + "filter": {}, + "allow_aggregations": true }, "comment": "" }, @@ -15368,8 +15389,21 @@ "columns": [ "created", "removed", - "dev_id", - "rule_id" + "rule_id", + "zone_id" + ], + "filter": {} + }, + "comment": "" + }, + { + "role": "modeller", + "permission": { + "columns": [ + "created", + "removed", + "rule_id", + "zone_id" ], "filter": {}, "allow_aggregations": true @@ -15382,14 +15416,11 @@ "columns": [ "created", "removed", - "dev_id", - "rule_id" + "rule_id", + "zone_id" ], - "filter": { - "dev_id": { - "_in": "x-hasura-visible-devices" - } - } + "filter": {}, + "allow_aggregations": true }, "comment": "" }, @@ -15399,14 +15430,11 @@ "columns": [ "created", "removed", - "dev_id", - "rule_id" + "rule_id", + "zone_id" ], - "filter": { - "dev_id": { - "_in": "x-hasura-visible-devices" - } - } + "filter": {}, + "allow_aggregations": true }, "comment": "" }, @@ -15416,8 +15444,8 @@ "columns": [ "created", "removed", - "dev_id", - "rule_id" + "rule_id", + "zone_id" ], "filter": {}, "allow_aggregations": true @@ -15432,8 +15460,8 @@ "columns": [ "created", "removed", - "dev_id", - "rule_id" + "rule_id", + "zone_id" ], "filter": {}, "check": {} @@ -15453,26 +15481,26 @@ }, { "table": { - "name": "rule_from", + "name": "rule_enforced_on_gateway", "schema": "public" }, "object_relationships": [ { - "name": "importControlByRfLastSeen", + "name": "device", "using": { - "foreign_key_constraint_on": "rf_last_seen" + "foreign_key_constraint_on": "dev_id" } }, { - "name": "import_control", + "name": "importControlByRemoved", "using": { - "foreign_key_constraint_on": "rf_create" + "foreign_key_constraint_on": "removed" } }, { - "name": "object", + "name": "import_control", "using": { - "foreign_key_constraint_on": "obj_id" + "foreign_key_constraint_on": "created" } }, { @@ -15480,19 +15508,182 @@ "using": { "foreign_key_constraint_on": "rule_id" } - }, - { - "name": "usr", - "using": { - "foreign_key_constraint_on": "user_id" - } } ], - "computed_fields": [ + "insert_permissions": [ { - "name": "rule_from_relevant_for_tenant", - "definition": { - "function": { + "role": "importer", + "permission": { + "check": {}, + "columns": [ + "created", + "removed", + "dev_id", + "rule_id" + ] + }, + "comment": "" + } + ], + "select_permissions": [ + { + "role": "fw-admin", + "permission": { + "columns": [ + "created", + "removed", + "dev_id", + "rule_id" + ], + "filter": {}, + "allow_aggregations": true + }, + "comment": "" + }, + { + "role": "importer", + "permission": { + "columns": [ + "created", + "removed", + "dev_id", + "rule_id" + ], + "filter": {} + }, + "comment": "" + }, + { + "role": "middleware-server", + "permission": { + "columns": [ + "created", + "removed", + "dev_id", + "rule_id" + ], + "filter": {}, + "allow_aggregations": true + }, + "comment": "" + }, + { + "role": "recertifier", + "permission": { + "columns": [ + "created", + "removed", + "dev_id", + "rule_id" + ], + "filter": { + "dev_id": { + "_in": "x-hasura-visible-devices" + } + } + }, + "comment": "" + }, + { + "role": "reporter", + "permission": { + "columns": [ + "created", + "removed", + "dev_id", + "rule_id" + ], + "filter": { + "dev_id": { + "_in": "x-hasura-visible-devices" + } + } + }, + "comment": "" + }, + { + "role": "reporter-viewall", + "permission": { + "columns": [ + "created", + "removed", + "dev_id", + "rule_id" + ], + "filter": {}, + "allow_aggregations": true + }, + "comment": "" + } + ], + "update_permissions": [ + { + "role": "importer", + "permission": { + "columns": [ + "created", + "removed", + "dev_id", + "rule_id" + ], + "filter": {}, + "check": {} + }, + "comment": "" + } + ], + "delete_permissions": [ + { + "role": "importer", + "permission": { + "filter": {} + }, + "comment": "" + } + ] + }, + { + "table": { + "name": "rule_from", + "schema": "public" + }, + "object_relationships": [ + { + "name": "importControlByRfLastSeen", + "using": { + "foreign_key_constraint_on": "rf_last_seen" + } + }, + { + "name": "import_control", + "using": { + "foreign_key_constraint_on": "rf_create" + } + }, + { + "name": "object", + "using": { + "foreign_key_constraint_on": "obj_id" + } + }, + { + "name": "rule", + "using": { + "foreign_key_constraint_on": "rule_id" + } + }, + { + "name": "usr", + "using": { + "foreign_key_constraint_on": "user_id" + } + } + ], + "computed_fields": [ + { + "name": "rule_from_relevant_for_tenant", + "definition": { + "function": { "name": "rule_from_relevant_for_tenant", "schema": "public" }, @@ -15525,14 +15716,15 @@ "role": "auditor", "permission": { "columns": [ + "active", + "negated", "obj_id", + "removed", "rf_create", "rf_last_seen", "rule_from_id", "rule_id", - "user_id", - "active", - "negated" + "user_id" ], "computed_fields": [ "rule_from_relevant_for_tenant" @@ -15545,14 +15737,15 @@ "role": "fw-admin", "permission": { "columns": [ + "active", + "negated", "obj_id", + "removed", "rf_create", "rf_last_seen", "rule_from_id", "rule_id", - "user_id", - "active", - "negated" + "user_id" ], "computed_fields": [ "rule_from_relevant_for_tenant" @@ -15597,6 +15790,9 @@ "rule_id", "user_id" ], + "computed_fields": [ + "rule_from_relevant_for_tenant" + ], "filter": {} }, "comment": "" @@ -15606,6 +15802,7 @@ "permission": { "columns": [ "obj_id", + "removed", "rf_create", "rf_last_seen", "rule_from_id", @@ -15647,14 +15844,15 @@ "role": "recertifier", "permission": { "columns": [ + "active", + "negated", "obj_id", + "removed", "rf_create", "rf_last_seen", "rule_from_id", "rule_id", - "user_id", - "active", - "negated" + "user_id" ], "computed_fields": [ "rule_from_relevant_for_tenant" @@ -15690,6 +15888,7 @@ "permission": { "columns": [ "obj_id", + "removed", "rf_create", "rf_last_seen", "rule_from_id", @@ -15732,6 +15931,7 @@ "permission": { "columns": [ "obj_id", + "removed", "rf_create", "rf_last_seen", "rule_from_id", @@ -16376,12 +16576,13 @@ "role": "auditor", "permission": { "columns": [ - "rule_id", - "svc_id", "active", + "negated", + "removed", "rs_create", "rs_last_seen", - "negated" + "rule_id", + "svc_id" ], "filter": {} } @@ -16390,12 +16591,13 @@ "role": "fw-admin", "permission": { "columns": [ + "active", + "negated", + "removed", "rs_create", "rs_last_seen", "rule_id", - "svc_id", - "active", - "negated" + "svc_id" ], "filter": {} } @@ -16437,12 +16639,13 @@ "role": "modeller", "permission": { "columns": [ - "rule_id", - "svc_id", "active", + "negated", + "removed", "rs_create", "rs_last_seen", - "negated" + "rule_id", + "svc_id" ], "filter": {}, "allow_aggregations": true @@ -16452,12 +16655,13 @@ "role": "recertifier", "permission": { "columns": [ - "rule_id", - "svc_id", "active", + "negated", + "removed", "rs_create", "rs_last_seen", - "negated" + "rule_id", + "svc_id" ], "filter": {}, "allow_aggregations": true @@ -16467,12 +16671,13 @@ "role": "reporter", "permission": { "columns": [ - "rule_id", - "svc_id", "active", + "negated", + "removed", "rs_create", "rs_last_seen", - "negated" + "rule_id", + "svc_id" ], "filter": {}, "allow_aggregations": true @@ -16482,12 +16687,13 @@ "role": "reporter-viewall", "permission": { "columns": [ - "rule_id", - "svc_id", "active", + "negated", + "removed", "rs_create", "rs_last_seen", - "negated" + "rule_id", + "svc_id" ], "filter": {} } @@ -16522,6 +16728,165 @@ } ] }, + { + "table": { + "name": "rule_source_to_zone", + "schema": "public" + }, + "insert_permissions": [ + { + "role": "importer", + "permission": { + "check": {}, + "columns": [ + "created", + "removed", + "rule_id", + "zone_id" + ] + }, + "comment": "" + } + ], + "select_permissions": [ + { + "role": "auditor", + "permission": { + "columns": [ + "created", + "removed", + "rule_id", + "zone_id" + ], + "filter": {}, + "allow_aggregations": true + }, + "comment": "" + }, + { + "role": "fw-admin", + "permission": { + "columns": [ + "created", + "removed", + "rule_id", + "zone_id" + ], + "filter": {}, + "allow_aggregations": true + }, + "comment": "" + }, + { + "role": "importer", + "permission": { + "columns": [ + "created", + "removed", + "rule_id", + "zone_id" + ], + "filter": {}, + "allow_aggregations": true + }, + "comment": "" + }, + { + "role": "middleware-server", + "permission": { + "columns": [ + "created", + "removed", + "rule_id", + "zone_id" + ], + "filter": {} + }, + "comment": "" + }, + { + "role": "modeller", + "permission": { + "columns": [ + "created", + "removed", + "rule_id", + "zone_id" + ], + "filter": {}, + "allow_aggregations": true + }, + "comment": "" + }, + { + "role": "recertifier", + "permission": { + "columns": [ + "created", + "removed", + "rule_id", + "zone_id" + ], + "filter": {}, + "allow_aggregations": true + }, + "comment": "" + }, + { + "role": "reporter", + "permission": { + "columns": [ + "created", + "removed", + "rule_id", + "zone_id" + ], + "filter": {}, + "allow_aggregations": true + }, + "comment": "" + }, + { + "role": "reporter-viewall", + "permission": { + "columns": [ + "created", + "removed", + "rule_id", + "zone_id" + ], + "filter": {}, + "allow_aggregations": true + }, + "comment": "" + } + ], + "update_permissions": [ + { + "role": "importer", + "permission": { + "columns": [ + "created", + "removed", + "rule_id", + "zone_id" + ], + "filter": {}, + "check": {} + }, + "comment": "" + } + ], + "delete_permissions": [ + { + "role": "importer", + "permission": { + "filter": {} + }, + "comment": "" + } + ] + }, { "table": { "name": "rule_svc_resolved", @@ -16771,14 +17136,15 @@ "role": "auditor", "permission": { "columns": [ + "active", + "negated", "obj_id", + "removed", "rt_create", "rt_last_seen", "rule_id", "rule_to_id", - "user_id", - "active", - "negated" + "user_id" ], "computed_fields": [ "rule_to_relevant_for_tenant" @@ -16793,6 +17159,7 @@ "active", "negated", "obj_id", + "removed", "rt_create", "rt_last_seen", "rule_id", @@ -16841,6 +17208,9 @@ "rule_to_id", "user_id" ], + "computed_fields": [ + "rule_to_relevant_for_tenant" + ], "filter": {} } }, @@ -16851,6 +17221,7 @@ "active", "negated", "obj_id", + "removed", "rt_create", "rt_last_seen", "rule_id", @@ -16890,14 +17261,15 @@ "role": "recertifier", "permission": { "columns": [ + "active", + "negated", "obj_id", + "removed", "rt_create", "rt_last_seen", "rule_id", "rule_to_id", - "user_id", - "active", - "negated" + "user_id" ], "computed_fields": [ "rule_to_relevant_for_tenant" @@ -16935,6 +17307,7 @@ "active", "negated", "obj_id", + "removed", "rt_create", "rt_last_seen", "rule_id", @@ -16974,14 +17347,15 @@ "role": "reporter-viewall", "permission": { "columns": [ + "active", + "negated", "obj_id", + "removed", "rt_create", "rt_last_seen", "rule_id", "rule_to_id", - "user_id", - "active", - "negated" + "user_id" ], "computed_fields": [ "rule_to_relevant_for_tenant" diff --git a/roles/database/files/sql/creation/fworch-create-foreign-keys.sql b/roles/database/files/sql/creation/fworch-create-foreign-keys.sql index 35c175151..51ce6ae0b 100755 --- a/roles/database/files/sql/creation/fworch-create-foreign-keys.sql +++ b/roles/database/files/sql/creation/fworch-create-foreign-keys.sql @@ -114,6 +114,11 @@ Alter table "rulebase_link" add CONSTRAINT fk_rulebase_link_created_import_contr Alter table "rulebase_link" add CONSTRAINT fk_rulebase_link_removed_import_control_control_id foreign key ("removed") references "import_control" ("control_id") on update restrict on delete cascade; +ALTER TABLE "rule_destination_to_zone" +ADD CONSTRAINT rule_destination_to_zone_rule_id_rule_rule_id_fkey FOREIGN KEY ("rule_id") REFERENCES "rule" ("rule_id"); +ALTER TABLE "rule_destination_to_zone" +ADD CONSTRAINT rule_destination_to_zone_zone_id_zone_zone_id_fkey FOREIGN KEY ("zone_id") REFERENCES "zone" ("zone_id"); + Alter table "rule_from" add foreign key ("obj_id") references "object" ("obj_id") on update restrict on delete cascade; Alter table "rule_from" add foreign key ("rf_create") references "import_control" ("control_id") on update restrict on delete cascade; Alter table "rule_from" add foreign key ("rf_last_seen") references "import_control" ("control_id") on update restrict on delete cascade; @@ -143,6 +148,11 @@ Alter table "rule_service" add foreign key ("rs_last_seen") references "import_ Alter table "rule_service" add foreign key ("rule_id") references "rule" ("rule_id") on update restrict on delete cascade; Alter table "rule_service" add foreign key ("svc_id") references "service" ("svc_id") on update restrict on delete cascade; +ALTER TABLE "rule_source_to_zone" +ADD CONSTRAINT rule_source_to_zone_rule_id_rule_rule_id_fkey FOREIGN KEY ("rule_id") REFERENCES "rule" ("rule_id"); +ALTER TABLE "rule_source_to_zone" +ADD CONSTRAINT rule_source_to_zone_zone_id_zone_zone_id_fkey FOREIGN KEY ("zone_id") REFERENCES "zone" ("zone_id"); + Alter table "rule_svc_resolved" add foreign key ("svc_id") references "service" ("svc_id") on update restrict on delete cascade; Alter table "rule_svc_resolved" add foreign key ("rule_id") references "rule" ("rule_id") on update restrict on delete cascade; Alter table "rule_svc_resolved" add foreign key ("mgm_id") references "management" ("mgm_id") on update restrict on delete cascade; diff --git a/roles/database/files/sql/creation/fworch-create-tables.sql b/roles/database/files/sql/creation/fworch-create-tables.sql index 0613d81a3..49e4395e6 100755 --- a/roles/database/files/sql/creation/fworch-create-tables.sql +++ b/roles/database/files/sql/creation/fworch-create-tables.sql @@ -349,6 +349,26 @@ Create table "zone" primary key ("zone_id") ); +--crosstabulation rule zone for source +Create table "rule_source_to_zone" +( + "rule_id" BIGINT NOT NULL, + "zone_id" Integer NOT NULL, + "created" BIGINT NOT NULL, + "removed" BIGINT, + primary key (rule_id, zone_id, created) +); + +--crosstabulation rule zone for destination +Create table "rule_destination_to_zone" +( + "rule_id" BIGINT NOT NULL, + "zone_id" Integer NOT NULL, + "created" BIGINT NOT NULL, + "removed" BIGINT, + primary key (rule_id, zone_id, created) +); + Create table "usr" ( "user_id" BIGSERIAL PRIMARY KEY, diff --git a/roles/database/files/upgrade/9.0.sql b/roles/database/files/upgrade/9.0.sql index 5eef2706d..a75b6c275 100644 --- a/roles/database/files/upgrade/9.0.sql +++ b/roles/database/files/upgrade/9.0.sql @@ -1612,3 +1612,48 @@ ON CONFLICT (config_key, config_user) DO NOTHING; -- ALTER TABLE "rule_from" DROP COLUMN IF EXISTS "rf_last_seen"; -- ALTER TABLE "rule_to" DROP COLUMN IF EXISTS "rt_last_seen"; -- ALTER TABLE "rule_service" DROP COLUMN IF EXISTS "rs_last_seen"; + + +-- add crosstabulations rules with zone for source and destination + +--crosstabulation rule zone for source +Create table IF NOT EXISTS "rule_source_to_zone" +( + "rule_id" BIGINT NOT NULL, + "zone_id" Integer NOT NULL, + "created" BIGINT NOT NULL, + "removed" BIGINT, + primary key (rule_id, zone_id, created) +); + +--crosstabulation rule zone for destination +Create table IF NOT EXISTS "rule_destination_to_zone" +( + "rule_id" BIGINT NOT NULL, + "zone_id" Integer NOT NULL, + "created" BIGINT NOT NULL, + "removed" BIGINT, + primary key (rule_id, zone_id, created) +); + +--crosstabulation rule zone for destination FKs +ALTER TABLE "rule_destination_to_zone" +DROP CONSTRAINT IF EXISTS rule_destination_to_zone_rule_id_rule_rule_id_fkey; +ALTER TABLE "rule_destination_to_zone" +DROP CONSTRAINT IF EXISTS rule_destination_to_zone_zone_id_zone_zone_id_fkey; + +ALTER TABLE "rule_destination_to_zone" +ADD CONSTRAINT rule_destination_to_zone_rule_id_rule_rule_id_fkey FOREIGN KEY ("rule_id") REFERENCES "rule" ("rule_id"); +ALTER TABLE "rule_destination_to_zone" +ADD CONSTRAINT rule_destination_to_zone_zone_id_zone_zone_id_fkey FOREIGN KEY ("zone_id") REFERENCES "zone" ("zone_id"); + +--crosstabulation rule zone for source FKs +ALTER TABLE "rule_source_to_zone" +DROP CONSTRAINT IF EXISTS rule_source_to_zone_rule_id_rule_rule_id_fkey; +ALTER TABLE "rule_source_to_zone" +DROP CONSTRAINT IF EXISTS rule_source_to_zone_zone_id_zone_zone_id_fkey; + +ALTER TABLE "rule_source_to_zone" +ADD CONSTRAINT rule_source_to_zone_rule_id_rule_rule_id_fkey FOREIGN KEY ("rule_id") REFERENCES "rule" ("rule_id"); +ALTER TABLE "rule_source_to_zone" +ADD CONSTRAINT rule_source_to_zone_zone_id_zone_zone_id_fkey FOREIGN KEY ("zone_id") REFERENCES "zone" ("zone_id"); \ No newline at end of file From bab4ef0dca2e5015646e3ddf1afb7afc531113a7 Mon Sep 17 00:00:00 2001 From: Chris Date: Mon, 27 Oct 2025 12:59:30 +0100 Subject: [PATCH 02/23] changed metadata.json --- roles/api/files/replace_metadata.json | 223 ++++++++++++++++++++++++-- 1 file changed, 213 insertions(+), 10 deletions(-) diff --git a/roles/api/files/replace_metadata.json b/roles/api/files/replace_metadata.json index 98278fc5e..81b1c2c74 100644 --- a/roles/api/files/replace_metadata.json +++ b/roles/api/files/replace_metadata.json @@ -9797,7 +9797,8 @@ "obj_sys_writecom", "active", "obj_create", - "obj_last_seen" + "obj_last_seen", + "removed" ], "filter": {}, "allow_aggregations": true @@ -9810,6 +9811,7 @@ "obj_create", "obj_id", "obj_last_seen", + "removed", "active", "initial_config", "obj_nat", @@ -9915,7 +9917,8 @@ "obj_sys_writecom", "active", "obj_create", - "obj_last_seen" + "obj_last_seen", + "removed" ], "filter": {}, "allow_aggregations": true @@ -9954,7 +9957,8 @@ "obj_sys_writecom", "active", "obj_create", - "obj_last_seen" + "obj_last_seen", + "removed" ], "filter": { "mgm_id": { @@ -9997,7 +10001,8 @@ "obj_sys_writecom", "active", "obj_create", - "obj_last_seen" + "obj_last_seen", + "removed" ], "filter": { "mgm_id": { @@ -10040,7 +10045,8 @@ "obj_sys_writecom", "active", "obj_create", - "obj_last_seen" + "obj_last_seen", + "removed" ], "filter": { "mgm_id": { @@ -10083,7 +10089,8 @@ "obj_sys_writecom", "active", "obj_create", - "obj_last_seen" + "obj_last_seen", + "removed" ], "filter": {}, "allow_aggregations": true @@ -10201,6 +10208,7 @@ "objgrp_member_id", "import_created", "import_last_seen", + "removed", "active", "negated" ], @@ -10215,6 +10223,7 @@ "import_created", "import_last_seen", "objgrp_id", + "removed", "objgrp_member_id", "active", "negated" @@ -10262,6 +10271,7 @@ "objgrp_member_id", "import_created", "import_last_seen", + "removed", "active", "negated" ], @@ -10277,6 +10287,7 @@ "objgrp_member_id", "import_created", "import_last_seen", + "removed", "active", "negated" ], @@ -10292,6 +10303,7 @@ "objgrp_member_id", "import_created", "import_last_seen", + "removed", "active", "negated" ], @@ -10307,6 +10319,7 @@ "objgrp_member_id", "import_created", "import_last_seen", + "removed", "active", "negated" ], @@ -10402,6 +10415,7 @@ "active", "import_created", "import_last_seen", + "removed", "negated" ], "filter": {}, @@ -10414,6 +10428,7 @@ "columns": [ "import_created", "import_last_seen", + "removed", "objgrp_flat_id", "objgrp_flat_member_id", "active", @@ -10460,6 +10475,7 @@ "columns": [ "objgrp_flat_id", "objgrp_flat_member_id", + "removed", "active", "import_created", "import_last_seen", @@ -10475,6 +10491,7 @@ "columns": [ "objgrp_flat_id", "objgrp_flat_member_id", + "removed", "active", "import_created", "import_last_seen", @@ -10490,6 +10507,7 @@ "columns": [ "objgrp_flat_id", "objgrp_flat_member_id", + "removed", "active", "import_created", "import_last_seen", @@ -10505,6 +10523,7 @@ "columns": [ "objgrp_flat_id", "objgrp_flat_member_id", + "removed", "active", "import_created", "import_last_seen", @@ -14852,6 +14871,7 @@ "rule_svc", "rule_svc_refs", "rule_track", + "rulebase_id", "rule_uid" ], "computed_fields": [ @@ -14906,6 +14926,7 @@ "rule_svc", "rule_svc_refs", "rule_track", + "rulebase_id", "rule_uid" ], "computed_fields": [ @@ -15032,6 +15053,7 @@ "rule_create", "rule_id", "rule_last_seen", + "removed", "xlate_rule", "access_rule", "active", @@ -16559,8 +16581,8 @@ "permission": { "check": {}, "columns": [ - "removed", "rs_create", + "removed", "rs_last_seen", "rule_id", "svc_id", @@ -17668,6 +17690,57 @@ }, "comment": "" }, + { + "role": "auditor", + "permission": { + "columns": [ + "created", + "removed", + "is_global", + "name", + "uid", + "id", + "mgm_id" + ], + "filter": {}, + "allow_aggregations": true + }, + "comment": "" + }, + { + "role": "reporter-viewall", + "permission": { + "columns": [ + "created", + "removed", + "is_global", + "name", + "uid", + "id", + "mgm_id" + ], + "filter": {}, + "allow_aggregations": true + }, + "comment": "" + }, + { + "role": "reporter", + "permission": { + "columns": [ + "created", + "removed", + "is_global", + "name", + "uid", + "id", + "mgm_id" + ], + "filter": {}, + "allow_aggregations": true + }, + "comment": "" + }, { "role": "middleware-server", "permission": { @@ -17812,8 +17885,88 @@ "permission": { "columns": [ "created", + "removed", "from_rule_id", + "is_global", + "is_initial", + "is_section", + "from_rulebase_id", + "gw_id", + "id", + "link_type", + "to_rulebase_id" + ], + "filter": {} + }, + "comment": "" + }, + { + "role": "auditor", + "permission": { + "columns": [ + "created", "removed", + "from_rule_id", + "is_global", + "is_initial", + "is_section", + "from_rulebase_id", + "gw_id", + "id", + "link_type", + "to_rulebase_id" + ], + "filter": {} + }, + "comment": "" + }, + { + "role": "reporter-viewall", + "permission": { + "columns": [ + "created", + "removed", + "from_rule_id", + "is_global", + "is_initial", + "is_section", + "from_rulebase_id", + "gw_id", + "id", + "link_type", + "to_rulebase_id" + ], + "filter": {} + }, + "comment": "" + }, + { + "role": "reporter", + "permission": { + "columns": [ + "created", + "removed", + "from_rule_id", + "is_global", + "is_initial", + "is_section", + "from_rulebase_id", + "gw_id", + "id", + "link_type", + "to_rulebase_id" + ], + "filter": {} + }, + "comment": "" + }, + { + "role": "modeller", + "permission": { + "columns": [ + "created", + "removed", + "from_rule_id", "is_global", "is_initial", "is_section", @@ -18119,6 +18272,7 @@ "active", "last_change_admin", "svc_create", + "removed", "svc_last_seen" ], "filter": {}, @@ -18132,6 +18286,7 @@ "svc_create", "svc_id", "svc_last_seen", + "removed", "active", "initial_config", "srv_keeponinstall", @@ -18217,6 +18372,7 @@ "svc_create", "svc_id", "svc_last_seen", + "removed", "active", "initial_config", "srv_keeponinstall", @@ -18288,7 +18444,8 @@ "active", "last_change_admin", "svc_create", - "svc_last_seen" + "svc_last_seen", + "removed" ], "filter": { "mgm_id": { @@ -18334,7 +18491,8 @@ "active", "last_change_admin", "svc_create", - "svc_last_seen" + "svc_last_seen", + "removed" ], "filter": { "mgm_id": { @@ -18380,6 +18538,7 @@ "active", "last_change_admin", "svc_create", + "removed", "svc_last_seen" ], "filter": { @@ -18425,6 +18584,7 @@ "svc_sync_delay_start", "active", "last_change_admin", + "removed", "svc_create", "svc_last_seen" ], @@ -20124,6 +20284,7 @@ "svcgrp_id", "svcgrp_member_id", "import_created", + "removed", "import_last_seen", "active", "negated" @@ -20138,6 +20299,7 @@ "columns": [ "import_created", "import_last_seen", + "removed", "svcgrp_id", "svcgrp_member_id", "active", @@ -20186,6 +20348,7 @@ "svcgrp_member_id", "import_created", "import_last_seen", + "removed", "active", "negated" ], @@ -20201,6 +20364,7 @@ "svcgrp_member_id", "import_created", "import_last_seen", + "removed", "active", "negated" ], @@ -20216,6 +20380,7 @@ "svcgrp_member_id", "import_created", "import_last_seen", + "removed", "active", "negated" ], @@ -20231,6 +20396,7 @@ "svcgrp_member_id", "import_created", "import_last_seen", + "removed", "active", "negated" ], @@ -20325,6 +20491,7 @@ "svcgrp_flat_member_id", "import_created", "import_last_seen", + "removed", "active", "negated" ], @@ -20340,6 +20507,7 @@ "negated", "import_created", "import_last_seen", + "removed", "svcgrp_flat_id", "svcgrp_flat_member_id" ], @@ -20386,6 +20554,7 @@ "svcgrp_flat_member_id", "import_created", "import_last_seen", + "removed", "active", "negated" ], @@ -20401,6 +20570,7 @@ "svcgrp_flat_member_id", "import_created", "import_last_seen", + "removed", "active", "negated" ], @@ -20416,6 +20586,7 @@ "svcgrp_flat_member_id", "import_created", "import_last_seen", + "removed", "active", "negated" ], @@ -20431,6 +20602,7 @@ "svcgrp_flat_member_id", "import_created", "import_last_seen", + "removed", "active", "negated" ], @@ -22632,6 +22804,7 @@ "usergrp_id", "usergrp_member_id", "import_created", + "removed", "import_last_seen", "active" ], @@ -22647,6 +22820,7 @@ "usergrp_member_id", "import_created", "import_last_seen", + "removed", "active" ], "filter": {}, @@ -22659,6 +22833,7 @@ "columns": [ "import_created", "import_last_seen", + "removed", "usergrp_id", "usergrp_member_id", "active" @@ -22674,6 +22849,7 @@ "usergrp_member_id", "import_created", "import_last_seen", + "removed", "active" ], "filter": {}, @@ -22720,6 +22896,7 @@ "usergrp_member_id", "import_created", "import_last_seen", + "removed", "active" ], "filter": {}, @@ -22734,6 +22911,7 @@ "usergrp_member_id", "import_created", "import_last_seen", + "removed", "active" ], "filter": {}, @@ -22748,6 +22926,7 @@ "usergrp_member_id", "import_created", "import_last_seen", + "removed", "active" ], "filter": {}, @@ -22762,6 +22941,7 @@ "usergrp_member_id", "import_created", "import_last_seen", + "removed", "active" ], "filter": {}, @@ -22776,6 +22956,7 @@ "usergrp_member_id", "import_created", "import_last_seen", + "removed", "active" ], "filter": {} @@ -22789,6 +22970,7 @@ "usergrp_member_id", "import_created", "import_last_seen", + "removed", "active" ], "filter": {}, @@ -22803,6 +22985,7 @@ "usergrp_member_id", "import_created", "import_last_seen", + "removed", "active" ], "filter": {}, @@ -22895,7 +23078,8 @@ "usergrp_flat_id", "usergrp_flat_member_id", "import_created", - "import_last_seen" + "import_last_seen", + "removed" ], "filter": {}, "allow_aggregations": true @@ -22907,6 +23091,7 @@ "columns": [ "import_created", "import_last_seen", + "removed", "usergrp_flat_id", "usergrp_flat_member_id", "active" @@ -22954,6 +23139,7 @@ "usergrp_flat_id", "usergrp_flat_member_id", "import_created", + "removed", "import_last_seen" ], "filter": {}, @@ -22968,6 +23154,7 @@ "usergrp_flat_id", "usergrp_flat_member_id", "import_created", + "removed", "import_last_seen" ], "filter": {}, @@ -22982,6 +23169,7 @@ "usergrp_flat_id", "usergrp_flat_member_id", "import_created", + "removed", "import_last_seen" ], "filter": {}, @@ -22996,6 +23184,7 @@ "usergrp_flat_id", "usergrp_flat_member_id", "import_created", + "removed", "import_last_seen" ], "filter": {} @@ -23292,6 +23481,7 @@ "time_restrict", "user_create", "user_last_seen", + "removed", "user_comment", "user_uid", "user_firstname", @@ -23310,6 +23500,7 @@ "user_create", "user_id", "user_last_seen", + "removed", "active", "user_authmethod", "user_firstname", @@ -23374,6 +23565,7 @@ "user_create", "user_id", "user_last_seen", + "removed", "active", "user_authmethod", "user_firstname", @@ -23418,6 +23610,7 @@ "time_restrict", "user_create", "user_last_seen", + "removed", "user_comment", "user_uid", "user_firstname", @@ -23453,6 +23646,7 @@ "time_restrict", "user_create", "user_last_seen", + "removed", "user_comment", "user_uid", "user_firstname", @@ -23488,6 +23682,7 @@ "time_restrict", "user_create", "user_last_seen", + "removed", "user_comment", "user_uid", "user_firstname", @@ -23523,6 +23718,7 @@ "time_restrict", "user_create", "user_last_seen", + "removed", "user_comment", "user_uid", "user_firstname", @@ -24005,6 +24201,7 @@ "zone_id", "zone_create", "zone_last_seen", + "removed", "mgm_id", "zone_name", "active" @@ -24019,6 +24216,7 @@ "columns": [ "zone_create", "zone_last_seen", + "removed", "active", "zone_name", "mgm_id", @@ -24051,6 +24249,7 @@ "columns": [ "zone_create", "zone_last_seen", + "removed", "active", "zone_name", "mgm_id", @@ -24067,6 +24266,7 @@ "zone_id", "zone_create", "zone_last_seen", + "removed", "mgm_id", "zone_name", "active" @@ -24086,6 +24286,7 @@ "zone_id", "zone_create", "zone_last_seen", + "removed", "mgm_id", "zone_name", "active" @@ -24105,6 +24306,7 @@ "zone_id", "zone_create", "zone_last_seen", + "removed", "mgm_id", "zone_name", "active" @@ -24125,6 +24327,7 @@ "mgm_id", "zone_create", "zone_last_seen", + "removed", "zone_id", "zone_name" ], From 5873c1ec7a4dd6eb8e61daed10178acb9202afe3 Mon Sep 17 00:00:00 2001 From: Chris Date: Mon, 27 Oct 2025 13:38:14 +0100 Subject: [PATCH 03/23] update replace_metadata --- roles/api/files/replace_metadata.json | 95 +-------------------------- 1 file changed, 1 insertion(+), 94 deletions(-) diff --git a/roles/api/files/replace_metadata.json b/roles/api/files/replace_metadata.json index 28b1e8c5e..c4462eedd 100644 --- a/roles/api/files/replace_metadata.json +++ b/roles/api/files/replace_metadata.json @@ -15414,20 +15414,7 @@ "allow_aggregations": true }, "comment": "" - }, - { - "role": "auditor", - "permission": { - "columns": [ - "created", - "removed", - "dev_id", - "rule_id" - ], - "filter": {} - }, - "comment": "" - }, + }, { "role": "middleware-server", "permission": { @@ -18000,86 +17987,6 @@ }, "comment": "" }, - { - "role": "modeller", - "permission": { - "columns": [ - "created", - "removed", - "from_rule_id", - "is_global", - "is_initial", - "is_section", - "from_rulebase_id", - "gw_id", - "id", - "link_type", - "to_rulebase_id" - ], - "filter": {} - }, - "comment": "" - }, - { - "role": "auditor", - "permission": { - "columns": [ - "created", - "removed", - "from_rule_id", - "is_global", - "is_initial", - "is_section", - "from_rulebase_id", - "gw_id", - "id", - "link_type", - "to_rulebase_id" - ], - "filter": {} - }, - "comment": "" - }, - { - "role": "reporter-viewall", - "permission": { - "columns": [ - "created", - "removed", - "from_rule_id", - "is_global", - "is_initial", - "is_section", - "from_rulebase_id", - "gw_id", - "id", - "link_type", - "to_rulebase_id" - ], - "filter": {} - }, - "comment": "" - }, - { - "role": "reporter", - "permission": { - "columns": [ - "created", - "removed", - "from_rule_id", - "is_global", - "is_initial", - "is_section", - "from_rulebase_id", - "gw_id", - "id", - "link_type", - "to_rulebase_id" - ], - "filter": {} - }, - "comment": "" - }, { "role": "modeller", "permission": { From a588fd58d7b787c89a47cb149922327e9685b15c Mon Sep 17 00:00:00 2001 From: cd Date: Fri, 31 Oct 2025 09:03:03 +0100 Subject: [PATCH 04/23] feat: allow rules to have multiple zones in reports #3834 --- .../fragments/ruleDetailsForReport.graphql | 22 ++++---- roles/lib/files/FWO.Data/NormalizedRule.cs | 6 +-- roles/lib/files/FWO.Data/Rule.cs | 12 ++--- .../Display/RuleChangeDisplayCsv.cs | 14 +++--- .../Display/RuleChangeDisplayHtml.cs | 14 +++--- .../Display/RuleChangeDisplayJson.cs | 18 +++---- .../FWO.Report/Display/RuleDisplayBase.cs | 50 +++++++++++++++++-- .../FWO.Report/Display/RuleDisplayCsv.cs | 16 ++++-- .../FWO.Report/Display/RuleDisplayJson.cs | 20 ++++++-- roles/lib/files/FWO.Report/ReportChanges.cs | 4 +- roles/lib/files/FWO.Report/ReportNatRules.cs | 6 +-- roles/lib/files/FWO.Report/ReportRules.cs | 4 +- roles/tests-unit/files/FWO.Test/ExportTest.cs | 6 +-- .../Pages/Reporting/ReportedRules.razor | 8 +-- .../Reporting/ReportedRulesForDiff.razor | 14 +++++- .../Pages/Reporting/Reports/RulesReport.razor | 8 +-- 16 files changed, 148 insertions(+), 74 deletions(-) diff --git a/roles/common/files/fwo-api-calls/rule/fragments/ruleDetailsForReport.graphql b/roles/common/files/fwo-api-calls/rule/fragments/ruleDetailsForReport.graphql index 46cb4cbde..1dc53d633 100644 --- a/roles/common/files/fwo-api-calls/rule/fragments/ruleDetailsForReport.graphql +++ b/roles/common/files/fwo-api-calls/rule/fragments/ruleDetailsForReport.graphql @@ -7,11 +7,13 @@ section_header: rule_head_text rule_comment rule_track - rule_disabled - src_zone: zone { - zone_name - zone_id - } + rule_disabled + rule_src_zones: rule_source_to_zones { + zone { + zone_name + zone_id + } + } rule_metadatum { rule_metadata_id rule_created @@ -51,10 +53,12 @@ ...networkObjectDetails } } - dst_zone: zoneByRuleToZone { - zone_name - zone_id - } + rule_dst_zones: rule_destination_to_zones { + zone { + zone_name + zone_id + } + } rule_tos(where: { rt_create: { _lte: $import_id_end } _or: [ diff --git a/roles/lib/files/FWO.Data/NormalizedRule.cs b/roles/lib/files/FWO.Data/NormalizedRule.cs index c0f1067be..e5b06ccb7 100644 --- a/roles/lib/files/FWO.Data/NormalizedRule.cs +++ b/roles/lib/files/FWO.Data/NormalizedRule.cs @@ -130,10 +130,10 @@ public static NormalizedRule FromRule(Rule rule) ParentRuleUid = rule.ParentRule?.Uid, LastHit = lastHitFormatted, RuleComment = rule.Comment, - RuleSrcZone = rule.SourceZone?.Name, - RuleDstZone = rule.DestinationZone?.Name, + RuleSrcZone = rule.RuleSourceZones?.Length > 0 ? string.Join(",", rule.RuleSourceZones.Select(z => z.Name)) : "", + RuleDstZone = rule.RuleDestinationZones?.Length > 0 ? string.Join(",", rule.RuleDestinationZones.Select(z => z.Name)) : "", RuleHeadText = rule.SectionHeader }; - } + } } } diff --git a/roles/lib/files/FWO.Data/Rule.cs b/roles/lib/files/FWO.Data/Rule.cs index 4dbbfb5a8..d3d20acf2 100644 --- a/roles/lib/files/FWO.Data/Rule.cs +++ b/roles/lib/files/FWO.Data/Rule.cs @@ -47,8 +47,8 @@ public class Rule [JsonProperty("rule_src_refs"), JsonPropertyName("rule_src_refs")] public string SourceRefs { get; set; } = ""; - [JsonProperty("src_zone"), JsonPropertyName("src_zone")] - public NetworkZone? SourceZone { get; set; } = new(); + [JsonProperty("rule_src_zones"), JsonPropertyName("rule_src_zones")] + public NetworkZone[] RuleSourceZones { get; set; } = []; [JsonProperty("rule_froms"), JsonPropertyName("rule_froms")] public NetworkLocation[] Froms { get; set; } = []; @@ -62,8 +62,8 @@ public class Rule [JsonProperty("rule_dst_refs"), JsonPropertyName("rule_dst_refs")] public string DestinationRefs { get; set; } = ""; - [JsonProperty("dst_zone"), JsonPropertyName("dst_zone")] - public NetworkZone? DestinationZone { get; set; } = new(); + [JsonProperty("rule_dst_zones"), JsonPropertyName("rule_dst_zones")] + public NetworkZone[] RuleDestinationZones { get; set; } = []; [JsonProperty("rule_tos"), JsonPropertyName("rule_tos")] public NetworkLocation[] Tos { get; set; } = []; @@ -168,11 +168,11 @@ public Rule(Rule rule) Service = rule.Service; SourceNegated = rule.SourceNegated; Source = rule.Source; - SourceZone = rule.SourceZone; + RuleSourceZones = rule.RuleSourceZones; Froms = rule.Froms; DestinationNegated = rule.DestinationNegated; Destination = rule.Destination; - DestinationZone = rule.DestinationZone; + RuleDestinationZones = rule.RuleDestinationZones; Tos = rule.Tos; Action = rule.Action; Track = rule.Track; diff --git a/roles/lib/files/FWO.Report/Display/RuleChangeDisplayCsv.cs b/roles/lib/files/FWO.Report/Display/RuleChangeDisplayCsv.cs index f3b651737..6475edabc 100644 --- a/roles/lib/files/FWO.Report/Display/RuleChangeDisplayCsv.cs +++ b/roles/lib/files/FWO.Report/Display/RuleChangeDisplayCsv.cs @@ -1,4 +1,4 @@ -using FWO.Basics; +using FWO.Basics; using FWO.Data; using FWO.Config.Api; using FWO.Report.Filter; @@ -41,9 +41,9 @@ public string DisplaySourceZone(RuleChange ruleChange) { switch (ruleChange.ChangeAction) { - case 'D': return OutputCsv(DisplaySourceZone(ruleChange.OldRule)); - case 'I': return OutputCsv(DisplaySourceZone(ruleChange.NewRule)); - case 'C': return OutputCsv(DisplayDiff(DisplaySourceZone(ruleChange.OldRule), DisplaySourceZone(ruleChange.NewRule))); + case 'D': return OutputCsv(ListNetworkZones(ruleChange.OldRule.RuleSourceZones)); + case 'I': return OutputCsv(ListNetworkZones(ruleChange.NewRule.RuleSourceZones)); + case 'C': return OutputCsv(DisplayDiff(ListNetworkZones(ruleChange.OldRule.RuleSourceZones), ListNetworkZones(ruleChange.NewRule.RuleSourceZones))); default: return ","; } } @@ -63,9 +63,9 @@ public string DisplayDestinationZone(RuleChange ruleChange) { switch (ruleChange.ChangeAction) { - case 'D': return OutputCsv(DisplayDestinationZone(ruleChange.OldRule)); - case 'I': return OutputCsv(DisplayDestinationZone(ruleChange.NewRule)); - case 'C': return OutputCsv(DisplayDiff(DisplayDestinationZone(ruleChange.OldRule), DisplayDestinationZone(ruleChange.NewRule))); + case 'D': return OutputCsv(ListNetworkZones(ruleChange.OldRule.RuleDestinationZones)); + case 'I': return OutputCsv(ListNetworkZones(ruleChange.NewRule.RuleDestinationZones)); + case 'C': return OutputCsv(DisplayDiff(ListNetworkZones(ruleChange.OldRule.RuleDestinationZones), ListNetworkZones(ruleChange.NewRule.RuleDestinationZones))); default: return ","; } } diff --git a/roles/lib/files/FWO.Report/Display/RuleChangeDisplayHtml.cs b/roles/lib/files/FWO.Report/Display/RuleChangeDisplayHtml.cs index b22988715..b65a3cb69 100644 --- a/roles/lib/files/FWO.Report/Display/RuleChangeDisplayHtml.cs +++ b/roles/lib/files/FWO.Report/Display/RuleChangeDisplayHtml.cs @@ -1,4 +1,4 @@ -using FWO.Data; +using FWO.Data; using FWO.Logging; using FWO.Config.Api; using FWO.Report; @@ -42,9 +42,9 @@ public string DisplaySourceZone(RuleChange ruleChange) { switch (ruleChange.ChangeAction) { - case 'D': return OutputHtmlDeleted(DisplaySourceZone(ruleChange.OldRule)); - case 'I': return OutputHtmlAdded(DisplaySourceZone(ruleChange.NewRule)); - case 'C': return DisplayDiff(DisplaySourceZone(ruleChange.OldRule), DisplaySourceZone(ruleChange.NewRule)); + case 'D': return OutputHtmlDeleted(DisplaySourceZones(ruleChange.OldRule)); + case 'I': return OutputHtmlAdded(DisplaySourceZones(ruleChange.NewRule)); + case 'C': return DisplayDiff(DisplaySourceZones(ruleChange.OldRule), DisplaySourceZones(ruleChange.NewRule)); default: ThrowErrorUnknowChangeAction(ruleChange.ChangeAction); return ""; } } @@ -66,9 +66,9 @@ public string DisplayDestinationZone(RuleChange ruleChange) { switch (ruleChange.ChangeAction) { - case 'D': return OutputHtmlDeleted(DisplayDestinationZone(ruleChange.OldRule)); - case 'I': return OutputHtmlAdded(DisplayDestinationZone(ruleChange.NewRule)); - case 'C': return DisplayDiff(DisplayDestinationZone(ruleChange.OldRule), DisplayDestinationZone(ruleChange.NewRule)); + case 'D': return OutputHtmlDeleted(DisplayDestinationZones(ruleChange.OldRule)); + case 'I': return OutputHtmlAdded(DisplayDestinationZones(ruleChange.NewRule)); + case 'C': return DisplayDiff(DisplayDestinationZones(ruleChange.OldRule), DisplayDestinationZones(ruleChange.NewRule)); default: ThrowErrorUnknowChangeAction(ruleChange.ChangeAction); return ""; } } diff --git a/roles/lib/files/FWO.Report/Display/RuleChangeDisplayJson.cs b/roles/lib/files/FWO.Report/Display/RuleChangeDisplayJson.cs index c02810d91..4ada88a45 100644 --- a/roles/lib/files/FWO.Report/Display/RuleChangeDisplayJson.cs +++ b/roles/lib/files/FWO.Report/Display/RuleChangeDisplayJson.cs @@ -1,4 +1,4 @@ -using FWO.Basics; +using FWO.Basics; using FWO.Data; using FWO.Config.Api; using FWO.Report.Filter; @@ -37,13 +37,13 @@ public string DisplayName(RuleChange ruleChange) } } - public string DisplaySourceZone(RuleChange ruleChange) + public string DisplaySourceZones(RuleChange ruleChange) { switch (ruleChange.ChangeAction) { - case 'D': return DisplaySourceZone(ruleChange.OldRule.SourceZone?.Name); - case 'I': return DisplaySourceZone(ruleChange.NewRule.SourceZone?.Name); - case 'C': return DisplaySourceZone(DisplayDiff(ruleChange.OldRule.SourceZone?.Name, ruleChange.NewRule.SourceZone?.Name)); + case 'D': return DisplayRuleSourceZones(ruleChange.OldRule.RuleSourceZones); + case 'I': return DisplayRuleSourceZones(ruleChange.NewRule.RuleSourceZones); + case 'C': return DisplayJsonArray("source zones", DisplayArrayDiff(ListNetworkZones(ruleChange.OldRule.RuleSourceZones), ListNetworkZones(ruleChange.NewRule.RuleSourceZones))); default: return ""; } } @@ -73,13 +73,13 @@ public string DisplaySource(RuleChange ruleChange, ReportType reportType) } } - public string DisplayDestinationZone(RuleChange ruleChange) + public string DisplayDestinationZones(RuleChange ruleChange) { switch (ruleChange.ChangeAction) { - case 'D': return DisplayDestinationZone(ruleChange.OldRule.DestinationZone?.Name); - case 'I': return DisplayDestinationZone(ruleChange.NewRule.DestinationZone?.Name); - case 'C': return DisplayDestinationZone(DisplayDiff(ruleChange.OldRule.DestinationZone?.Name, ruleChange.NewRule.DestinationZone?.Name)); + case 'D': return DisplayRuleDestinationZones(ruleChange.OldRule.RuleDestinationZones); + case 'I': return DisplayRuleDestinationZones(ruleChange.NewRule.RuleDestinationZones); + case 'C': return DisplayJsonArray("destination zones", DisplayArrayDiff(ListNetworkZones(ruleChange.OldRule.RuleDestinationZones), ListNetworkZones(ruleChange.NewRule.RuleDestinationZones))); default: return ""; } } diff --git a/roles/lib/files/FWO.Report/Display/RuleDisplayBase.cs b/roles/lib/files/FWO.Report/Display/RuleDisplayBase.cs index b5b89a21d..458fc8b01 100644 --- a/roles/lib/files/FWO.Report/Display/RuleDisplayBase.cs +++ b/roles/lib/files/FWO.Report/Display/RuleDisplayBase.cs @@ -1,4 +1,4 @@ -using System.Text; +using System.Text; using FWO.Basics; using FWO.Data; using FWO.Config.Api; @@ -80,14 +80,54 @@ public static string DisplayName(Rule rule) return rule.Name ?? ""; } - public static string DisplaySourceZone(Rule rule) + public static string DisplaySourceZones(Rule rule) { - return rule.SourceZone != null ? rule.SourceZone.Name : ""; + if (rule.RuleSourceZones.Length != 0) + { + string ruleZones = ""; + bool notFirst = false; + + foreach (NetworkZone networkZone in rule.RuleSourceZones) + { + if (notFirst) + { + ruleZones += "
"; + } + + ruleZones += networkZone.Name; + notFirst = true; + } + return ruleZones; + } + else + { + return ""; + } } - public static string DisplayDestinationZone(Rule rule) + public static string DisplayDestinationZones(Rule rule) { - return rule.DestinationZone != null ? rule.DestinationZone.Name : ""; + if (rule.RuleDestinationZones.Length != 0) + { + string ruleZones = ""; + bool notFirst = false; + + foreach (NetworkZone networkZone in rule.RuleDestinationZones) + { + if (notFirst) + { + ruleZones += "
"; + } + + ruleZones += networkZone.Name; + notFirst = true; + } + return ruleZones; + } + else + { + return ""; + } } public static string DisplayAction(Rule rule) diff --git a/roles/lib/files/FWO.Report/Display/RuleDisplayCsv.cs b/roles/lib/files/FWO.Report/Display/RuleDisplayCsv.cs index 485feabc1..3815ef8ee 100644 --- a/roles/lib/files/FWO.Report/Display/RuleDisplayCsv.cs +++ b/roles/lib/files/FWO.Report/Display/RuleDisplayCsv.cs @@ -1,4 +1,4 @@ -using FWO.Basics; +using FWO.Basics; using FWO.Data; using FWO.Config.Api; using System.Text; @@ -29,7 +29,7 @@ public string DisplayNameCsv(Rule rule) public string DisplaySourceZoneCsv(Rule rule) { - return OutputCsv(DisplaySourceZone(rule)); + return OutputCsv(ListNetworkZones(rule.RuleSourceZones)); } public string DisplaySourceCsv(Rule rule, ReportType reportType) @@ -39,7 +39,7 @@ public string DisplaySourceCsv(Rule rule, ReportType reportType) public string DisplayDestinationZoneCsv(Rule rule) { - return OutputCsv(DisplayDestinationZone(rule)); + return OutputCsv(ListNetworkZones(rule.RuleDestinationZones)); } public string DisplayDestinationCsv(Rule rule, ReportType reportType) @@ -159,5 +159,15 @@ private string DisplaySourceOrDestination(Rule rule, ReportType reportType , boo return result.ToString(); } + + protected string ListNetworkZones(NetworkZone[] networkZones) + { + List displayedZones = new List(); + foreach (NetworkZone networkZone in networkZones) + { + displayedZones.Add(Quote(networkZone.Name)); + } + return string.Join(",", displayedZones); + } } } diff --git a/roles/lib/files/FWO.Report/Display/RuleDisplayJson.cs b/roles/lib/files/FWO.Report/Display/RuleDisplayJson.cs index a576455ab..9e4077edd 100644 --- a/roles/lib/files/FWO.Report/Display/RuleDisplayJson.cs +++ b/roles/lib/files/FWO.Report/Display/RuleDisplayJson.cs @@ -1,4 +1,4 @@ -using FWO.Basics; +using FWO.Basics; using FWO.Data; using FWO.Config.Api; using FWO.Report.Filter; @@ -36,9 +36,9 @@ public string DisplayName(string? name) return DisplayJsonString("name", name); } - public string DisplaySourceZone(string? sourceZone) + public string DisplayRuleSourceZones(NetworkZone[] networkZones) { - return DisplayJsonString("source zone", sourceZone); + return DisplayJsonArray("source zones", ListNetworkZones(networkZones)); } public string DisplaySourceNegated(bool sourceNegated) @@ -51,9 +51,9 @@ public string DisplaySource(Rule rule, ReportType reportType) return DisplayJsonArray("source", ListNetworkLocations(rule, reportType, true)); } - public string DisplayDestinationZone(string? destinationZone) + public string DisplayRuleDestinationZones(NetworkZone[] networkZones) { - return DisplayJsonString("destination zone", destinationZone); + return DisplayJsonArray("destination zones", ListNetworkZones(networkZones)); } public string DisplayDestinationNegated(bool destinationNegated) @@ -128,5 +128,15 @@ protected string ListServices(Rule rule, ReportType reportType) } return ""; } + + protected string ListNetworkZones(NetworkZone[] networkZones) + { + List displayedZones = new List(); + foreach (NetworkZone networkZone in networkZones) + { + displayedZones.Add(Quote(networkZone.Name)); + } + return string.Join(",", displayedZones); + } } } diff --git a/roles/lib/files/FWO.Report/ReportChanges.cs b/roles/lib/files/FWO.Report/ReportChanges.cs index 6629d9617..38e22b8a5 100644 --- a/roles/lib/files/FWO.Report/ReportChanges.cs +++ b/roles/lib/files/FWO.Report/ReportChanges.cs @@ -293,10 +293,10 @@ private string ExportResolvedChangesToJson() report.Append(ruleChangeDisplayJson.DisplayChangeTime(ruleChange)); report.Append(ruleChangeDisplayJson.DisplayChangeAction(ruleChange)); report.Append(ruleChangeDisplayJson.DisplayName(ruleChange)); - report.Append(ruleChangeDisplayJson.DisplaySourceZone(ruleChange)); + report.Append(ruleChangeDisplayJson.DisplaySourceZones(ruleChange)); report.Append(ruleChangeDisplayJson.DisplaySourceNegated(ruleChange)); report.Append(ruleChangeDisplayJson.DisplaySource(ruleChange, ReportType)); - report.Append(ruleChangeDisplayJson.DisplayDestinationZone(ruleChange)); + report.Append(ruleChangeDisplayJson.DisplayDestinationZones(ruleChange)); report.Append(ruleChangeDisplayJson.DisplayDestinationNegated(ruleChange)); report.Append(ruleChangeDisplayJson.DisplayDestination(ruleChange, ReportType)); report.Append(ruleChangeDisplayJson.DisplayServiceNegated(ruleChange)); diff --git a/roles/lib/files/FWO.Report/ReportNatRules.cs b/roles/lib/files/FWO.Report/ReportNatRules.cs index 1d7381862..542557b34 100644 --- a/roles/lib/files/FWO.Report/ReportNatRules.cs +++ b/roles/lib/files/FWO.Report/ReportNatRules.cs @@ -1,4 +1,4 @@ -using FWO.Basics; +using FWO.Basics; using FWO.Config.Api; using FWO.Data; using FWO.Report.Filter; @@ -93,9 +93,9 @@ private void AppendNatRuleForDeviceHtml(ref StringBuilder report, int chapterNum report.AppendLine(""); report.AppendLine($"{RuleDisplayBase.DisplayNumber(rule)}"); report.AppendLine($"{RuleDisplayBase.DisplayName(rule)}"); - report.AppendLine($"{RuleDisplayBase.DisplaySourceZone(rule)}"); + report.AppendLine($"{RuleDisplayBase.DisplaySourceZones(rule)}"); report.AppendLine($"{ruleDisplay.DisplaySource(rule, OutputLocation.export, ReportType, chapterNumber)}"); - report.AppendLine($"{RuleDisplayBase.DisplayDestinationZone(rule)}"); + report.AppendLine($"{RuleDisplayBase.DisplayDestinationZones(rule)}"); report.AppendLine($"{ruleDisplay.DisplayDestination(rule, OutputLocation.export, ReportType, chapterNumber)}"); report.AppendLine($"{ruleDisplay.DisplayServices(rule, OutputLocation.export, ReportType, chapterNumber)}"); report.AppendLine($"{ruleDisplay.DisplayTranslatedSource(rule, OutputLocation.export, chapterNumber)}"); diff --git a/roles/lib/files/FWO.Report/ReportRules.cs b/roles/lib/files/FWO.Report/ReportRules.cs index 2534035a7..fadadb90c 100644 --- a/roles/lib/files/FWO.Report/ReportRules.cs +++ b/roles/lib/files/FWO.Report/ReportRules.cs @@ -588,9 +588,9 @@ private void AppendRulesForRulebaseHtml(ref StringBuilder report, RulebaseLink r report.AppendLine($"{RuleDisplayHtml.DisplayLastHit(rule.Metadata)}"); } report.AppendLine($"{RuleDisplayBase.DisplayName(rule)}"); - report.AppendLine($"{RuleDisplayBase.DisplaySourceZone(rule)}"); + report.AppendLine($"{RuleDisplayBase.DisplaySourceZones(rule)}"); report.AppendLine($"{ruleDisplayHtml.DisplaySource(rule, OutputLocation.export, ReportType, chapterNumber)}"); - report.AppendLine($"{RuleDisplayBase.DisplayDestinationZone(rule)}"); + report.AppendLine($"{RuleDisplayBase.DisplayDestinationZones(rule)}"); report.AppendLine($"{ruleDisplayHtml.DisplayDestination(rule, OutputLocation.export, ReportType, chapterNumber)}"); report.AppendLine($"{ruleDisplayHtml.DisplayServices(rule, OutputLocation.export, ReportType, chapterNumber)}"); report.AppendLine($"{RuleDisplayBase.DisplayAction(rule)}"); diff --git a/roles/tests-unit/files/FWO.Test/ExportTest.cs b/roles/tests-unit/files/FWO.Test/ExportTest.cs index 89b47134b..05075cbd3 100644 --- a/roles/tests-unit/files/FWO.Test/ExportTest.cs +++ b/roles/tests-unit/files/FWO.Test/ExportTest.cs @@ -1,4 +1,4 @@ -using NUnit.Framework; +using NUnit.Framework; using NUnit.Framework.Legacy; using FWO.Logging; using FWO.Report; @@ -738,10 +738,10 @@ private static Rule InitRule1(bool resolved) DisplayOrderNumber = 1, Track = "none", Uid = "uid1", - SourceZone = new NetworkZone() { Name = "srczn" }, + RuleSourceZones = new NetworkZone[] { new NetworkZone { Name = "srczn1" }, new NetworkZone { Name = "srczn2" }, new NetworkZone { Name = "srczn3" } }, SourceNegated = false, Froms = InitFroms(resolved), - DestinationZone = new NetworkZone() { Name = "dstzn" }, + RuleDestinationZones = new NetworkZone[] { new NetworkZone { Name = "dstzn1" }, new NetworkZone { Name = "dstzn2" }, new NetworkZone { Name = "dstzn3" } }, DestinationNegated = false, Tos = InitTos(resolved), ServiceNegated = false, diff --git a/roles/ui/files/FWO.UI/Pages/Reporting/ReportedRules.razor b/roles/ui/files/FWO.UI/Pages/Reporting/ReportedRules.razor index e055df97f..2269aead6 100644 --- a/roles/ui/files/FWO.UI/Pages/Reporting/ReportedRules.razor +++ b/roles/ui/files/FWO.UI/Pages/Reporting/ReportedRules.razor @@ -97,9 +97,9 @@ @if (SelectedReportType != ReportType.VarianceAnalysis && !HandleAsVariance) { - + } @@ -110,9 +110,9 @@ @if (SelectedReportType != ReportType.VarianceAnalysis && !HandleAsVariance) { - + } diff --git a/roles/ui/files/FWO.UI/Pages/Reporting/ReportedRulesForDiff.razor b/roles/ui/files/FWO.UI/Pages/Reporting/ReportedRulesForDiff.razor index a013334ca..6901ff51b 100644 --- a/roles/ui/files/FWO.UI/Pages/Reporting/ReportedRulesForDiff.razor +++ b/roles/ui/files/FWO.UI/Pages/Reporting/ReportedRulesForDiff.razor @@ -12,6 +12,11 @@ + + + + + + - + diff --git a/roles/ui/files/FWO.UI/Pages/Reporting/Reports/RulesReport.razor b/roles/ui/files/FWO.UI/Pages/Reporting/Reports/RulesReport.razor index c20067710..ab0a634c7 100644 --- a/roles/ui/files/FWO.UI/Pages/Reporting/Reports/RulesReport.razor +++ b/roles/ui/files/FWO.UI/Pages/Reporting/Reports/RulesReport.razor @@ -137,7 +137,7 @@ } @if (EmptyColumns[4] == false) { - + From 863508d2362577d6a18efa8216dc4f4c13c40b2b Mon Sep 17 00:00:00 2001 From: Yannik Hayn Date: Wed, 12 Nov 2025 14:18:06 +0100 Subject: [PATCH 14/23] feat(middleware): match naming to normalized config in importer --- roles/lib/files/FWO.Data/NormalizedRule.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/roles/lib/files/FWO.Data/NormalizedRule.cs b/roles/lib/files/FWO.Data/NormalizedRule.cs index 347b2f482..a0e59dc0b 100644 --- a/roles/lib/files/FWO.Data/NormalizedRule.cs +++ b/roles/lib/files/FWO.Data/NormalizedRule.cs @@ -80,11 +80,11 @@ public class NormalizedRule [JsonProperty("rule_comment"), JsonPropertyName("rule_comment")] public string? RuleComment { get; set; } - [JsonProperty("rule_from_zones"), JsonPropertyName("rule_from_zones")] - public string? RuleFromZones { get; set; } + [JsonProperty("rule_src_zone"), JsonPropertyName("rule_src_zone")] + public string? RuleSrcZone { get; set; } - [JsonProperty("rule_to_zones"), JsonPropertyName("rule_to_zones")] - public string? RuleToZones { get; set; } + [JsonProperty("rule_dst_zone"), JsonPropertyName("rule_dst_zone")] + public string? RuleDstZone { get; set; } [JsonProperty("rule_head_text"), JsonPropertyName("rule_head_text")] public string? RuleHeadText { get; set; } @@ -130,8 +130,8 @@ public static NormalizedRule FromRule(Rule rule) ParentRuleUid = rule.ParentRule?.Uid, LastHit = lastHitFormatted, RuleComment = rule.Comment, - RuleFromZones = rule.RuleFromZones?.Length > 0 ? string.Join("|", rule.RuleFromZones.Select(z => z.Content.Name)) : null, - RuleToZones = rule.RuleToZones?.Length > 0 ? string.Join("|", rule.RuleToZones.Select(z => z.Content.Name)) : null, + RuleSrcZone = rule.RuleFromZones?.Length > 0 ? string.Join("|", rule.RuleFromZones.Select(z => z.Content.Name)) : null, + RuleDstZone = rule.RuleToZones?.Length > 0 ? string.Join("|", rule.RuleToZones.Select(z => z.Content.Name)) : null, RuleHeadText = rule.SectionHeader }; } From 101f080f0912a3b0df95d9bd346dd1c9f945ee24 Mon Sep 17 00:00:00 2001 From: Yannik Hayn Date: Wed, 12 Nov 2025 15:05:07 +0100 Subject: [PATCH 15/23] fix(api): rollback zone refs --- .../files/fwo-api-calls/import/rollbackImport.graphql | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/roles/common/files/fwo-api-calls/import/rollbackImport.graphql b/roles/common/files/fwo-api-calls/import/rollbackImport.graphql index 401951609..090dc9b83 100644 --- a/roles/common/files/fwo-api-calls/import/rollbackImport.graphql +++ b/roles/common/files/fwo-api-calls/import/rollbackImport.graphql @@ -1,5 +1,4 @@ mutation rollbackImport($importId: bigint!) { - delete_rule(where: {rule_create: {_eq: $importId}}) { affected_rows } delete_rulebase(where: {created: {_eq: $importId}}) { affected_rows } delete_rulebase_link(where: {created: {_eq: $importId}}) { affected_rows } delete_object(where: {obj_create: {_eq: $importId}}) { affected_rows } @@ -18,7 +17,10 @@ mutation rollbackImport($importId: bigint!) { delete_rule_nwobj_resolved(where: {created: {_eq: $importId}}) { affected_rows } delete_rule_svc_resolved(where: {created: {_eq: $importId}}) { affected_rows } delete_rule_user_resolved(where: {created: {_eq: $importId}}) { affected_rows } + delete_rule_from_zones(where: {created: {_eq: $importId}}) { affected_rows } + delete_rule_to_zones(where: {created: {_eq: $importId}}) { affected_rows } delete_rule_enforced_on_gateway(where: {created: {_eq: $importId}}) { affected_rows } + delete_rule(where: {rule_create: {_eq: $importId}}) { affected_rows } update_rule(where: {removed: {_eq: $importId}}, _set: {removed: null}) { affected_rows } update_rulebase(where: {removed: {_eq: $importId}}, _set: {removed: null}) { affected_rows } update_rulebase_link(where: {removed: {_eq: $importId}}, _set: {removed: null}) { affected_rows } @@ -38,6 +40,8 @@ mutation rollbackImport($importId: bigint!) { update_rule_nwobj_resolved(where: {removed: {_eq: $importId}}, _set: {removed: null}) { affected_rows } update_rule_svc_resolved(where: {removed: {_eq: $importId}}, _set: {removed: null}) { affected_rows } update_rule_user_resolved(where: {removed: {_eq: $importId}}, _set: {removed: null}) { affected_rows } + update_rule_from_zones(where: {removed: {_eq: $importId}}, _set: {removed: null}) { affected_rows } + update_rule_to_zones(where: {removed: {_eq: $importId}}, _set: {removed: null}) { affected_rows } update_rule_enforced_on_gateway(where: {removed: {_eq: $importId}}, _set: {removed: null}) { affected_rows } delete_import_control(where: {control_id: {_eq: $importId}}) { affected_rows } } From fb34195e59ad79a76272d49512b1c9dbe121f2ad Mon Sep 17 00:00:00 2001 From: Yannik Hayn Date: Wed, 12 Nov 2025 15:37:27 +0100 Subject: [PATCH 16/23] feat(importer): handle multiple zones per rule from/to --- ...anagementForLatestNormalizedConfig.graphql | 12 ++-- .../getManagementForNormalizedConfig.graphql | 13 ++-- .../fwo-api-calls/rule/insertRuleRefs.graphql | 8 +++ .../fwo-api-calls/rule/updateRuleRefs.graphql | 18 +++++ .../model_controllers/fwconfig_import_rule.py | 67 ++++++++++++------- roles/importer/files/importer/models/rule.py | 9 +-- 6 files changed, 87 insertions(+), 40 deletions(-) diff --git a/roles/common/files/fwo-api-calls/report/getManagementForLatestNormalizedConfig.graphql b/roles/common/files/fwo-api-calls/report/getManagementForLatestNormalizedConfig.graphql index e096dc810..d9d1eb9f8 100644 --- a/roles/common/files/fwo-api-calls/report/getManagementForLatestNormalizedConfig.graphql +++ b/roles/common/files/fwo-api-calls/report/getManagementForLatestNormalizedConfig.graphql @@ -120,11 +120,15 @@ fragment ruleFragment on rule { rule_last_hit } rule_comment - src_zone: zone { - zone_name + rule_from_zones { + zone { + zone_name + } } - dst_zone: zoneByRuleToZone { - zone_name + rule_to_zones { + zone { + zone_name + } } section_header: rule_head_text } diff --git a/roles/common/files/fwo-api-calls/report/getManagementForNormalizedConfig.graphql b/roles/common/files/fwo-api-calls/report/getManagementForNormalizedConfig.graphql index 3588d6409..f54b0e978 100644 --- a/roles/common/files/fwo-api-calls/report/getManagementForNormalizedConfig.graphql +++ b/roles/common/files/fwo-api-calls/report/getManagementForNormalizedConfig.graphql @@ -167,11 +167,15 @@ fragment ruleFragment on rule { rule_last_hit } rule_comment - src_zone: zone { - zone_name + rule_from_zones { + zone { + zone_name + } } - dst_zone: zoneByRuleToZone { - zone_name + rule_to_zones { + zone { + zone_name + } } section_header: rule_head_text } @@ -193,6 +197,7 @@ fragment deviceFragment on device { } fragment ruleBaseLinkFragment on rulebase_link { + gw_id rule { rule_uid } diff --git a/roles/common/files/fwo-api-calls/rule/insertRuleRefs.graphql b/roles/common/files/fwo-api-calls/rule/insertRuleRefs.graphql index 153f64e02..72b434746 100644 --- a/roles/common/files/fwo-api-calls/rule/insertRuleRefs.graphql +++ b/roles/common/files/fwo-api-calls/rule/insertRuleRefs.graphql @@ -5,6 +5,8 @@ mutation insertRuleRefs( $ruleNwObjResolveds: [rule_nwobj_resolved_insert_input!]! $ruleSvcResolveds: [rule_svc_resolved_insert_input!]! $ruleUserResolveds: [rule_user_resolved_insert_input!]! + $ruleFromZones: [rule_from_zones_insert_input!]! + $ruleToZones: [rule_to_zones_insert_input!]! ) { insert_rule_from(objects: $ruleFroms) { affected_rows @@ -24,4 +26,10 @@ mutation insertRuleRefs( insert_rule_user_resolved(objects: $ruleUserResolveds) { affected_rows } + insert_rule_from_zones(objects: $ruleFromZones) { + affected_rows + } + insert_rule_to_zones(objects: $ruleToZones) { + affected_rows + } } \ No newline at end of file diff --git a/roles/common/files/fwo-api-calls/rule/updateRuleRefs.graphql b/roles/common/files/fwo-api-calls/rule/updateRuleRefs.graphql index 088bfffe3..1fdbad7e0 100644 --- a/roles/common/files/fwo-api-calls/rule/updateRuleRefs.graphql +++ b/roles/common/files/fwo-api-calls/rule/updateRuleRefs.graphql @@ -6,6 +6,8 @@ mutation updateRuleRefs( $ruleNwObjResolveds: [rule_nwobj_resolved_bool_exp!] $ruleSvcResolveds: [rule_svc_resolved_bool_exp!]! $ruleUserResolveds: [rule_user_resolved_bool_exp!]! + $ruleFromZones: [rule_from_zones_bool_exp!] + $ruleToZones: [rule_to_zones_bool_exp!] ) { update_rule_from(where: { _or: $ruleFroms @@ -58,4 +60,20 @@ mutation updateRuleRefs( }) { affected_rows } + update_rule_from_zones(where: { + _or: $ruleFromZones + removed: {_is_null: true} + }, _set: { + removed: $importId + }) { + affected_rows + } + update_rule_to_zones(where: { + _or: $ruleToZones + removed: {_is_null: true} + }, _set: { + removed: $importId + }) { + affected_rows + } } \ No newline at end of file diff --git a/roles/importer/files/importer/model_controllers/fwconfig_import_rule.py b/roles/importer/files/importer/model_controllers/fwconfig_import_rule.py index 244f3c601..8d6180747 100644 --- a/roles/importer/files/importer/model_controllers/fwconfig_import_rule.py +++ b/roles/importer/files/importer/model_controllers/fwconfig_import_rule.py @@ -34,6 +34,8 @@ class RefType(Enum): NWOBJ_RESOLVED = "rule_nwobj_resolved" SVC_RESOLVED = "rule_svc_resolved" USER_RESOLVED = "rule_user_resolved" + SRC_ZONE = "rule_from_zones" + DST_ZONE = "rule_to_zones" # this class is used for importing rules and rule refs into the FWO API class FwConfigImportRule(): @@ -251,6 +253,11 @@ def get_rule_refs(self, rule, is_prev=False) -> dict[RefType, list[str]]: froms = [] tos = [] users = [] + nwobj_resolveds = [] + svc_resolveds = [] + user_resolveds = [] + from_zones = [] + to_zones = [] for src_ref in rule.rule_src_refs.split(fwo_const.list_delimiter): user_ref = None if fwo_const.user_delimiter in src_ref: @@ -272,13 +279,17 @@ def get_rule_refs(self, rule, is_prev=False) -> dict[RefType, list[str]]: nwobj_resolveds = self.group_flats_mapper.get_network_object_flats([ref[0] for ref in froms + tos]) svc_resolveds = self.group_flats_mapper.get_service_object_flats(svcs) user_resolveds = self.group_flats_mapper.get_user_flats(users) + from_zones = rule.rule_src_zone.split(fwo_const.list_delimiter) if rule.rule_src_zone else [] + to_zones = rule.rule_dst_zone.split(fwo_const.list_delimiter) if rule.rule_dst_zone else [] return { RefType.SRC: froms, RefType.DST: tos, RefType.SVC: svcs, RefType.NWOBJ_RESOLVED: nwobj_resolveds, RefType.SVC_RESOLVED: svc_resolveds, - RefType.USER_RESOLVED: user_resolveds + RefType.USER_RESOLVED: user_resolveds, + RefType.SRC_ZONE: from_zones, + RefType.DST_ZONE: to_zones } def get_ref_objs(self, ref_type, ref_uid, prev_config: FwConfigNormalized): @@ -286,11 +297,16 @@ def get_ref_objs(self, ref_type, ref_uid, prev_config: FwConfigNormalized): nwobj_uid, user_uid = ref_uid return (prev_config.network_objects.get(nwobj_uid, None), prev_config.users.get(user_uid, None) if user_uid else None), \ (self.normalized_config.network_objects.get(nwobj_uid, None), self.normalized_config.users.get(user_uid, None) if user_uid else None) - if ref_type == RefType.NWOBJ_RESOLVED: + elif ref_type == RefType.NWOBJ_RESOLVED: return prev_config.network_objects.get(ref_uid, None), self.normalized_config.network_objects.get(ref_uid, None) - if ref_type == RefType.SVC or ref_type == RefType.SVC_RESOLVED: + elif ref_type == RefType.SVC or ref_type == RefType.SVC_RESOLVED: return prev_config.service_objects.get(ref_uid, None), self.normalized_config.service_objects.get(ref_uid, None) - return prev_config.users.get(ref_uid, None), self.normalized_config.users.get(ref_uid, None) + elif ref_type == RefType.USER_RESOLVED: + return prev_config.users.get(ref_uid, None), self.normalized_config.users.get(ref_uid, None) + elif ref_type == RefType.SRC_ZONE or ref_type == RefType.DST_ZONE: + return prev_config.zone_objects.get(ref_uid, None), self.normalized_config.zone_objects.get(ref_uid, None) + else: + raise FwoImporterError(f"unknown ref type: {ref_type}") def get_ref_remove_statement(self, ref_type, rule_uid, ref_uid): if ref_type == RefType.SRC or ref_type == RefType.DST: @@ -327,6 +343,15 @@ def get_ref_remove_statement(self, ref_type, rule_uid, ref_uid): {"user_id": {"_eq": self.uid2id_mapper.get_user_id(ref_uid, before_update=True)}} ] } + elif ref_type == RefType.SRC_ZONE or ref_type == RefType.DST_ZONE: + return { + "_and": [ + {"rule_id": {"_eq": self.uid2id_mapper.get_rule_id(rule_uid, before_update=True)}}, + {"zone_id": {"_eq": self.uid2id_mapper.get_zone_object_id(ref_uid, before_update=True)}} + ] + } + else: + raise FwoImporterError(f"unknown ref type: {ref_type}") def get_outdated_refs_to_remove(self, prev_rule: RuleNormalized, rule: RuleNormalized|None, prev_config, remove_all): @@ -379,7 +404,9 @@ def remove_outdated_refs(self, prev_config: FwConfigNormalized): 'ruleServices': all_refs_to_remove[RefType.SVC], 'ruleNwObjResolveds': all_refs_to_remove[RefType.NWOBJ_RESOLVED], 'ruleSvcResolveds': all_refs_to_remove[RefType.SVC_RESOLVED], - 'ruleUserResolveds': all_refs_to_remove[RefType.USER_RESOLVED] + 'ruleUserResolveds': all_refs_to_remove[RefType.USER_RESOLVED], + 'ruleFromZones': all_refs_to_remove[RefType.SRC_ZONE], + 'ruleToZones': all_refs_to_remove[RefType.DST_ZONE], } try: @@ -447,6 +474,12 @@ def get_ref_add_statement(self, ref_type, rule, ref_uid): "user_id": self.uid2id_mapper.get_user_id(ref_uid), "created": self.import_details.ImportId, } + elif ref_type == RefType.SRC_ZONE or ref_type == RefType.DST_ZONE: + return { + "rule_id": self.uid2id_mapper.get_rule_id(rule.rule_uid), + "zone_id": self.uid2id_mapper.get_zone_object_id(ref_uid), + "created": self.import_details.ImportId, + } def get_new_refs_to_add(self, rule, prev_rule, prev_config, add_all): @@ -497,7 +530,9 @@ def add_new_refs(self, prev_config: FwConfigNormalized): 'ruleServices': all_refs_to_add[RefType.SVC], 'ruleNwObjResolveds': all_refs_to_add[RefType.NWOBJ_RESOLVED], 'ruleSvcResolveds': all_refs_to_add[RefType.SVC_RESOLVED], - 'ruleUserResolveds': all_refs_to_add[RefType.USER_RESOLVED] + 'ruleUserResolveds': all_refs_to_add[RefType.USER_RESOLVED], + 'ruleFromZones': all_refs_to_add[RefType.SRC_ZONE], + 'ruleToZones': all_refs_to_add[RefType.DST_ZONE] } try: @@ -1170,22 +1205,6 @@ def prepare_rules_for_import(self, rules: list[RuleNormalized], rulebase_uid: st return prepared_rules def prepare_single_rule_for_import(self, rule: RuleNormalized, importDetails: ImportStateController, rulebase_id: int) -> Rule: - rule_from_zone_id = None - if rule.rule_src_zone is not None: - from_zones = rule.rule_src_zone.split(fwo_const.list_delimiter) - if len(from_zones) > 1: - logger = getFwoLogger() - logger.warning(f"rule {rule.rule_uid} has multiple source zones defined, only the first one will be used") - rule_from_zone_id = self.uid2id_mapper.get_zone_object_id(from_zones[0]) - - rule_to_zone_id = None - if rule.rule_dst_zone is not None: - to_zones = rule.rule_dst_zone.split(fwo_const.list_delimiter) - if len(to_zones) > 1: - logger = getFwoLogger() - logger.warning(f"rule {rule.rule_uid} has multiple destination zones defined, only the first one will be used") - rule_to_zone_id = self.uid2id_mapper.get_zone_object_id(to_zones[0]) - rule_for_import = Rule( mgm_id=importDetails.MgmDetails.CurrentMgmId, rule_num=rule.rule_num, @@ -1208,8 +1227,8 @@ def prepare_single_rule_for_import(self, rule: RuleNormalized, importDetails: Im rule_implied=rule.rule_implied, # parent_rule_id=rule.parent_rule_id, rule_comment=rule.rule_comment, - rule_from_zone=rule_from_zone_id, - rule_to_zone=rule_to_zone_id, + rule_from_zone=None, #TODO: to be removed or changed to string of joined zone names + rule_to_zone=None, #TODO: to be removed or changed to string of joined zone names access_rule=True, nat_rule=False, is_global=False, diff --git a/roles/importer/files/importer/models/rule.py b/roles/importer/files/importer/models/rule.py index 214d21b4d..bfc13eb87 100644 --- a/roles/importer/files/importer/models/rule.py +++ b/roles/importer/files/importer/models/rule.py @@ -67,14 +67,7 @@ def __eq__(self, other): if not isinstance(other, RuleNormalized): return NotImplemented # Compare all fields except 'last_hit' and 'rule_num' - exclude = {"last_hit", "rule_num", "rule_src_zone", "rule_dst_zone"} - # TODO: need to handle zones like this until we can import multiple zones properly - if self.rule_src_zone and other.rule_src_zone and \ - self.rule_src_zone.split(list_delimiter)[0] != other.rule_src_zone.split(list_delimiter)[0]: - return False - if self.rule_dst_zone and other.rule_dst_zone and \ - self.rule_dst_zone.split(list_delimiter)[0] != other.rule_dst_zone.split(list_delimiter)[0]: - return False + exclude = {"last_hit", "rule_num"} self_dict = self.model_dump(exclude=exclude) other_dict = other.model_dump(exclude=exclude) return self_dict == other_dict From bd01851bb65850bb096761350e12226ff380217f Mon Sep 17 00:00:00 2001 From: Yannik Hayn Date: Wed, 12 Nov 2025 16:06:21 +0100 Subject: [PATCH 17/23] feat(imporer): ensure zone order in rules --- roles/importer/files/importer/fortiadom5ff/fmgr_zone.py | 2 +- roles/lib/files/FWO.Data/NormalizedRule.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/roles/importer/files/importer/fortiadom5ff/fmgr_zone.py b/roles/importer/files/importer/fortiadom5ff/fmgr_zone.py index 1677cbfdf..f46809f11 100644 --- a/roles/importer/files/importer/fortiadom5ff/fmgr_zone.py +++ b/roles/importer/files/importer/fortiadom5ff/fmgr_zone.py @@ -65,4 +65,4 @@ def find_zones_in_normalized_config(native_zone_list : list, normalized_config_a break if not was_zone_found: raise FwoNormalizedConfigParseError('Could not find zone ' + nativ_zone + ' in normalized config.') - return zone_out_list + return sorted(zone_out_list) diff --git a/roles/lib/files/FWO.Data/NormalizedRule.cs b/roles/lib/files/FWO.Data/NormalizedRule.cs index a0e59dc0b..7e9beb4c9 100644 --- a/roles/lib/files/FWO.Data/NormalizedRule.cs +++ b/roles/lib/files/FWO.Data/NormalizedRule.cs @@ -130,8 +130,8 @@ public static NormalizedRule FromRule(Rule rule) ParentRuleUid = rule.ParentRule?.Uid, LastHit = lastHitFormatted, RuleComment = rule.Comment, - RuleSrcZone = rule.RuleFromZones?.Length > 0 ? string.Join("|", rule.RuleFromZones.Select(z => z.Content.Name)) : null, - RuleDstZone = rule.RuleToZones?.Length > 0 ? string.Join("|", rule.RuleToZones.Select(z => z.Content.Name)) : null, + RuleSrcZone = rule.RuleFromZones?.Length > 0 ? string.Join("|", rule.RuleFromZones.Select(z => z.Content.Name).Order()) : null, + RuleDstZone = rule.RuleToZones?.Length > 0 ? string.Join("|", rule.RuleToZones.Select(z => z.Content.Name).Order()) : null, RuleHeadText = rule.SectionHeader }; } From cd4c3870ac247c0593c415cceac9b89a9457cc0f Mon Sep 17 00:00:00 2001 From: Yannik Hayn Date: Wed, 12 Nov 2025 16:15:45 +0100 Subject: [PATCH 18/23] feat(db): more consistent naming --- roles/api/files/replace_metadata.json | 12 ++--- .../import/rollbackImport.graphql | 8 ++-- .../fwo-api-calls/rule/insertRuleRefs.graphql | 8 ++-- .../fwo-api-calls/rule/updateRuleRefs.graphql | 8 ++-- .../creation/fworch-create-foreign-keys.sql | 16 +++---- .../sql/creation/fworch-create-tables.sql | 4 +- roles/database/files/upgrade/9.0.sql | 44 +++++++++---------- .../model_controllers/fwconfig_import_rule.py | 4 +- 8 files changed, 52 insertions(+), 52 deletions(-) diff --git a/roles/api/files/replace_metadata.json b/roles/api/files/replace_metadata.json index 6cabc08f3..804c11450 100644 --- a/roles/api/files/replace_metadata.json +++ b/roles/api/files/replace_metadata.json @@ -14664,7 +14664,7 @@ "foreign_key_constraint_on": { "column": "rule_id", "table": { - "name": "rule_to_zones", + "name": "rule_to_zone", "schema": "public" } } @@ -14724,7 +14724,7 @@ "foreign_key_constraint_on": { "column": "rule_id", "table": { - "name": "rule_from_zones", + "name": "rule_from_zone", "schema": "public" } } @@ -15417,7 +15417,7 @@ }, { "table": { - "name": "rule_to_zones", + "name": "rule_to_zone", "schema": "public" }, "object_relationships": [ @@ -16859,7 +16859,7 @@ }, { "table": { - "name": "rule_from_zones", + "name": "rule_from_zone", "schema": "public" }, "object_relationships": [ @@ -24298,7 +24298,7 @@ "foreign_key_constraint_on": { "column": "zone_id", "table": { - "name": "rule_to_zones", + "name": "rule_to_zone", "schema": "public" } } @@ -24310,7 +24310,7 @@ "foreign_key_constraint_on": { "column": "zone_id", "table": { - "name": "rule_from_zones", + "name": "rule_from_zone", "schema": "public" } } diff --git a/roles/common/files/fwo-api-calls/import/rollbackImport.graphql b/roles/common/files/fwo-api-calls/import/rollbackImport.graphql index 090dc9b83..d0f99c0cf 100644 --- a/roles/common/files/fwo-api-calls/import/rollbackImport.graphql +++ b/roles/common/files/fwo-api-calls/import/rollbackImport.graphql @@ -17,8 +17,8 @@ mutation rollbackImport($importId: bigint!) { delete_rule_nwobj_resolved(where: {created: {_eq: $importId}}) { affected_rows } delete_rule_svc_resolved(where: {created: {_eq: $importId}}) { affected_rows } delete_rule_user_resolved(where: {created: {_eq: $importId}}) { affected_rows } - delete_rule_from_zones(where: {created: {_eq: $importId}}) { affected_rows } - delete_rule_to_zones(where: {created: {_eq: $importId}}) { affected_rows } + delete_rule_from_zone(where: {created: {_eq: $importId}}) { affected_rows } + delete_rule_to_zone(where: {created: {_eq: $importId}}) { affected_rows } delete_rule_enforced_on_gateway(where: {created: {_eq: $importId}}) { affected_rows } delete_rule(where: {rule_create: {_eq: $importId}}) { affected_rows } update_rule(where: {removed: {_eq: $importId}}, _set: {removed: null}) { affected_rows } @@ -40,8 +40,8 @@ mutation rollbackImport($importId: bigint!) { update_rule_nwobj_resolved(where: {removed: {_eq: $importId}}, _set: {removed: null}) { affected_rows } update_rule_svc_resolved(where: {removed: {_eq: $importId}}, _set: {removed: null}) { affected_rows } update_rule_user_resolved(where: {removed: {_eq: $importId}}, _set: {removed: null}) { affected_rows } - update_rule_from_zones(where: {removed: {_eq: $importId}}, _set: {removed: null}) { affected_rows } - update_rule_to_zones(where: {removed: {_eq: $importId}}, _set: {removed: null}) { affected_rows } + update_rule_from_zone(where: {removed: {_eq: $importId}}, _set: {removed: null}) { affected_rows } + update_rule_to_zone(where: {removed: {_eq: $importId}}, _set: {removed: null}) { affected_rows } update_rule_enforced_on_gateway(where: {removed: {_eq: $importId}}, _set: {removed: null}) { affected_rows } delete_import_control(where: {control_id: {_eq: $importId}}) { affected_rows } } diff --git a/roles/common/files/fwo-api-calls/rule/insertRuleRefs.graphql b/roles/common/files/fwo-api-calls/rule/insertRuleRefs.graphql index 72b434746..85cb22b2b 100644 --- a/roles/common/files/fwo-api-calls/rule/insertRuleRefs.graphql +++ b/roles/common/files/fwo-api-calls/rule/insertRuleRefs.graphql @@ -5,8 +5,8 @@ mutation insertRuleRefs( $ruleNwObjResolveds: [rule_nwobj_resolved_insert_input!]! $ruleSvcResolveds: [rule_svc_resolved_insert_input!]! $ruleUserResolveds: [rule_user_resolved_insert_input!]! - $ruleFromZones: [rule_from_zones_insert_input!]! - $ruleToZones: [rule_to_zones_insert_input!]! + $ruleFromZones: [rule_from_zone_insert_input!]! + $ruleToZones: [rule_to_zone_insert_input!]! ) { insert_rule_from(objects: $ruleFroms) { affected_rows @@ -26,10 +26,10 @@ mutation insertRuleRefs( insert_rule_user_resolved(objects: $ruleUserResolveds) { affected_rows } - insert_rule_from_zones(objects: $ruleFromZones) { + insert_rule_from_zone(objects: $ruleFromZones) { affected_rows } - insert_rule_to_zones(objects: $ruleToZones) { + insert_rule_to_zone(objects: $ruleToZones) { affected_rows } } \ No newline at end of file diff --git a/roles/common/files/fwo-api-calls/rule/updateRuleRefs.graphql b/roles/common/files/fwo-api-calls/rule/updateRuleRefs.graphql index 1fdbad7e0..5c8664105 100644 --- a/roles/common/files/fwo-api-calls/rule/updateRuleRefs.graphql +++ b/roles/common/files/fwo-api-calls/rule/updateRuleRefs.graphql @@ -6,8 +6,8 @@ mutation updateRuleRefs( $ruleNwObjResolveds: [rule_nwobj_resolved_bool_exp!] $ruleSvcResolveds: [rule_svc_resolved_bool_exp!]! $ruleUserResolveds: [rule_user_resolved_bool_exp!]! - $ruleFromZones: [rule_from_zones_bool_exp!] - $ruleToZones: [rule_to_zones_bool_exp!] + $ruleFromZones: [rule_from_zone_bool_exp!] + $ruleToZones: [rule_to_zone_bool_exp!] ) { update_rule_from(where: { _or: $ruleFroms @@ -60,7 +60,7 @@ mutation updateRuleRefs( }) { affected_rows } - update_rule_from_zones(where: { + update_rule_from_zone(where: { _or: $ruleFromZones removed: {_is_null: true} }, _set: { @@ -68,7 +68,7 @@ mutation updateRuleRefs( }) { affected_rows } - update_rule_to_zones(where: { + update_rule_to_zone(where: { _or: $ruleToZones removed: {_is_null: true} }, _set: { diff --git a/roles/database/files/sql/creation/fworch-create-foreign-keys.sql b/roles/database/files/sql/creation/fworch-create-foreign-keys.sql index 956a519af..4f9225c2b 100755 --- a/roles/database/files/sql/creation/fworch-create-foreign-keys.sql +++ b/roles/database/files/sql/creation/fworch-create-foreign-keys.sql @@ -114,10 +114,10 @@ Alter table "rulebase_link" add CONSTRAINT fk_rulebase_link_created_import_contr Alter table "rulebase_link" add CONSTRAINT fk_rulebase_link_removed_import_control_control_id foreign key ("removed") references "import_control" ("control_id") on update restrict on delete cascade; -ALTER TABLE "rule_to_zones" -ADD CONSTRAINT fk_rule_to_zones_rule_id_rule_rule_id FOREIGN KEY ("rule_id") REFERENCES "rule" ("rule_id"); -ALTER TABLE "rule_to_zones" -ADD CONSTRAINT fk_rule_to_zones_zone_id_zone_zone_id FOREIGN KEY ("zone_id") REFERENCES "zone" ("zone_id"); +ALTER TABLE "rule_to_zone" +ADD CONSTRAINT fk_rule_to_zone_rule_id_rule_rule_id FOREIGN KEY ("rule_id") REFERENCES "rule" ("rule_id"); +ALTER TABLE "rule_to_zone" +ADD CONSTRAINT fk_rule_to_zone_zone_id_zone_zone_id FOREIGN KEY ("zone_id") REFERENCES "zone" ("zone_id"); Alter table "rule_from" add foreign key ("obj_id") references "object" ("obj_id") on update restrict on delete cascade; Alter table "rule_from" add foreign key ("rf_create") references "import_control" ("control_id") on update restrict on delete cascade; @@ -148,10 +148,10 @@ Alter table "rule_service" add foreign key ("rs_last_seen") references "import_ Alter table "rule_service" add foreign key ("rule_id") references "rule" ("rule_id") on update restrict on delete cascade; Alter table "rule_service" add foreign key ("svc_id") references "service" ("svc_id") on update restrict on delete cascade; -ALTER TABLE "rule_from_zones" -ADD CONSTRAINT fk_rule_from_zones_rule_id_rule_rule_id FOREIGN KEY ("rule_id") REFERENCES "rule" ("rule_id"); -ALTER TABLE "rule_from_zones" -ADD CONSTRAINT fk_rule_from_zones_zone_id_zone_zone_id FOREIGN KEY ("zone_id") REFERENCES "zone" ("zone_id"); +ALTER TABLE "rule_from_zone" +ADD CONSTRAINT fk_rule_from_zone_rule_id_rule_rule_id FOREIGN KEY ("rule_id") REFERENCES "rule" ("rule_id"); +ALTER TABLE "rule_from_zone" +ADD CONSTRAINT fk_rule_from_zone_zone_id_zone_zone_id FOREIGN KEY ("zone_id") REFERENCES "zone" ("zone_id"); Alter table "rule_svc_resolved" add foreign key ("svc_id") references "service" ("svc_id") on update restrict on delete cascade; Alter table "rule_svc_resolved" add foreign key ("rule_id") references "rule" ("rule_id") on update restrict on delete cascade; diff --git a/roles/database/files/sql/creation/fworch-create-tables.sql b/roles/database/files/sql/creation/fworch-create-tables.sql index 44129d69c..98b945543 100755 --- a/roles/database/files/sql/creation/fworch-create-tables.sql +++ b/roles/database/files/sql/creation/fworch-create-tables.sql @@ -350,7 +350,7 @@ Create table "zone" ); --crosstabulation rule zone for source -Create table "rule_from_zones" +Create table "rule_from_zone" ( "rule_id" BIGINT NOT NULL, "zone_id" Integer NOT NULL, @@ -360,7 +360,7 @@ Create table "rule_from_zones" ); --crosstabulation rule zone for destination -Create table "rule_to_zones" +Create table "rule_to_zone" ( "rule_id" BIGINT NOT NULL, "zone_id" Integer NOT NULL, diff --git a/roles/database/files/upgrade/9.0.sql b/roles/database/files/upgrade/9.0.sql index 11e47eab6..e85194274 100644 --- a/roles/database/files/upgrade/9.0.sql +++ b/roles/database/files/upgrade/9.0.sql @@ -1681,7 +1681,7 @@ ON CONFLICT (config_key, config_user) DO NOTHING; -- add crosstabulations rules with zone for source and destination --crosstabulation rule zone for source -Create table IF NOT EXISTS "rule_from_zones" +Create table IF NOT EXISTS "rule_from_zone" ( "rule_id" BIGINT NOT NULL, "zone_id" Integer NOT NULL, @@ -1691,7 +1691,7 @@ Create table IF NOT EXISTS "rule_from_zones" ); --crosstabulation rule zone for destination -Create table IF NOT EXISTS "rule_to_zones" +Create table IF NOT EXISTS "rule_to_zone" ( "rule_id" BIGINT NOT NULL, "zone_id" Integer NOT NULL, @@ -1701,26 +1701,26 @@ Create table IF NOT EXISTS "rule_to_zones" ); --crosstabulation rule zone for destination FKs -ALTER TABLE "rule_to_zones" -DROP CONSTRAINT IF EXISTS fk_rule_to_zones_rule_id_rule_rule_id; -ALTER TABLE "rule_to_zones" -DROP CONSTRAINT IF EXISTS fk_rule_to_zones_zone_id_zone_zone_id; +ALTER TABLE "rule_to_zone" +DROP CONSTRAINT IF EXISTS fk_rule_to_zone_rule_id_rule_rule_id; +ALTER TABLE "rule_to_zone" +DROP CONSTRAINT IF EXISTS fk_rule_to_zone_zone_id_zone_zone_id; -ALTER TABLE "rule_to_zones" -ADD CONSTRAINT fk_rule_to_zones_rule_id_rule_rule_id FOREIGN KEY ("rule_id") REFERENCES "rule" ("rule_id"); -ALTER TABLE "rule_to_zones" -ADD CONSTRAINT fk_rule_to_zones_zone_id_zone_zone_id FOREIGN KEY ("zone_id") REFERENCES "zone" ("zone_id"); +ALTER TABLE "rule_to_zone" +ADD CONSTRAINT fk_rule_to_zone_rule_id_rule_rule_id FOREIGN KEY ("rule_id") REFERENCES "rule" ("rule_id"); +ALTER TABLE "rule_to_zone" +ADD CONSTRAINT fk_rule_to_zone_zone_id_zone_zone_id FOREIGN KEY ("zone_id") REFERENCES "zone" ("zone_id"); --crosstabulation rule zone for source FKs -ALTER TABLE "rule_from_zones" -DROP CONSTRAINT IF EXISTS fk_rule_from_zones_rule_id_rule_rule_id; -ALTER TABLE "rule_from_zones" -DROP CONSTRAINT IF EXISTS fk_rule_from_zones_zone_id_zone_zone_id; +ALTER TABLE "rule_from_zone" +DROP CONSTRAINT IF EXISTS fk_rule_from_zone_rule_id_rule_rule_id; +ALTER TABLE "rule_from_zone" +DROP CONSTRAINT IF EXISTS fk_rule_from_zone_zone_id_zone_zone_id; -ALTER TABLE "rule_from_zones" -ADD CONSTRAINT fk_rule_from_zones_rule_id_rule_rule_id FOREIGN KEY ("rule_id") REFERENCES "rule" ("rule_id"); -ALTER TABLE "rule_from_zones" -ADD CONSTRAINT fk_rule_from_zones_zone_id_zone_zone_id FOREIGN KEY ("zone_id") REFERENCES "zone" ("zone_id"); +ALTER TABLE "rule_from_zone" +ADD CONSTRAINT fk_rule_from_zone_rule_id_rule_rule_id FOREIGN KEY ("rule_id") REFERENCES "rule" ("rule_id"); +ALTER TABLE "rule_from_zone" +ADD CONSTRAINT fk_rule_from_zone_zone_id_zone_zone_id FOREIGN KEY ("zone_id") REFERENCES "zone" ("zone_id"); -- initial fill script for rule_from_zones and rule_to_zones @@ -1732,9 +1732,9 @@ DECLARE remaining_destination INT; BEGIN IF NOT EXISTS ( - SELECT 1 FROM rule_from_zones + SELECT 1 FROM rule_from_zone ) THEN - INSERT INTO rule_from_zones (rule_id, zone_id, created, removed) + INSERT INTO rule_from_zone (rule_id, zone_id, created, removed) SELECT rule_id, rule_from_zone, rule_create, removed FROM rule WHERE rule_from_zone IS NOT NULL; @@ -1745,9 +1745,9 @@ BEGIN END IF; IF NOT EXISTS ( - SELECT 1 FROM rule_to_zones + SELECT 1 FROM rule_to_zone ) THEN - INSERT INTO rule_to_zones (rule_id, zone_id, created, removed) + INSERT INTO rule_to_zone (rule_id, zone_id, created, removed) SELECT rule_id, rule_to_zone, rule_create, removed FROM rule WHERE rule_to_zone IS NOT NULL; diff --git a/roles/importer/files/importer/model_controllers/fwconfig_import_rule.py b/roles/importer/files/importer/model_controllers/fwconfig_import_rule.py index 8d6180747..a004a930b 100644 --- a/roles/importer/files/importer/model_controllers/fwconfig_import_rule.py +++ b/roles/importer/files/importer/model_controllers/fwconfig_import_rule.py @@ -34,8 +34,8 @@ class RefType(Enum): NWOBJ_RESOLVED = "rule_nwobj_resolved" SVC_RESOLVED = "rule_svc_resolved" USER_RESOLVED = "rule_user_resolved" - SRC_ZONE = "rule_from_zones" - DST_ZONE = "rule_to_zones" + SRC_ZONE = "rule_from_zone" + DST_ZONE = "rule_to_zone" # this class is used for importing rules and rule refs into the FWO API class FwConfigImportRule(): From ebf45081c8fafba4d76e5981982c9ae6cc7d44e3 Mon Sep 17 00:00:00 2001 From: Yannik Hayn Date: Wed, 12 Nov 2025 16:17:33 +0100 Subject: [PATCH 19/23] fix(importer): exclude zone fields from equality comparison in RuleNormalized --- roles/importer/files/importer/models/rule.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/roles/importer/files/importer/models/rule.py b/roles/importer/files/importer/models/rule.py index bfc13eb87..85b1d10f6 100644 --- a/roles/importer/files/importer/models/rule.py +++ b/roles/importer/files/importer/models/rule.py @@ -66,8 +66,10 @@ class RuleNormalized(BaseModel): def __eq__(self, other): if not isinstance(other, RuleNormalized): return NotImplemented - # Compare all fields except 'last_hit' and 'rule_num' - exclude = {"last_hit", "rule_num"} + # Compare all fields except 'last_hit' and 'rule_num' and zones + # Zones are excluded because they are currently not written to the rule directly, + # only linked through rule_from_zone and rule_to_zone tables (similar to _resolved tables) + exclude = {"last_hit", "rule_num", "rule_src_zone", "rule_dst_zone"} self_dict = self.model_dump(exclude=exclude) other_dict = other.model_dump(exclude=exclude) return self_dict == other_dict From 073a0ce0ccf3337e321521b6c1ebdd35b71b9189 Mon Sep 17 00:00:00 2001 From: cd Date: Thu, 13 Nov 2025 14:39:08 +0100 Subject: [PATCH 20/23] upgrade-Script - make sure column exists todo drop columns rule_from/to_zone in rule --- roles/database/files/upgrade/9.0.sql | 82 ++++++++++++++++++++-------- 1 file changed, 59 insertions(+), 23 deletions(-) diff --git a/roles/database/files/upgrade/9.0.sql b/roles/database/files/upgrade/9.0.sql index e85194274..f20d7b98f 100644 --- a/roles/database/files/upgrade/9.0.sql +++ b/roles/database/files/upgrade/9.0.sql @@ -1728,51 +1728,87 @@ DO $$ DECLARE inserted_source INT := 0; inserted_destination INT := 0; - remaining_source INT; - remaining_destination INT; + remaining_source INT:= 0; + remaining_destination INT:= 0; + col_exists_source BOOLEAN; + col_exists_destination BOOLEAN; + count_from_zone_in_rule_after_update INT:= 0; + count_to_zone_in_rule_after_update INT:= 0; + + BEGIN - IF NOT EXISTS ( - SELECT 1 FROM rule_from_zone - ) THEN + -- Check column rule_from_zone exists + SELECT EXISTS ( + SELECT 1 + FROM information_schema.columns + WHERE table_name='rule' + AND column_name='rule_from_zone' + ) INTO col_exists_source; + + -- Check column rule_to_zone exists + SELECT EXISTS ( + SELECT 1 + FROM information_schema.columns + WHERE table_name='rule' + AND column_name='rule_to_zone' + ) INTO col_exists_destination; + + IF col_exists_source AND NOT EXISTS (SELECT 1 FROM rule_from_zone) THEN INSERT INTO rule_from_zone (rule_id, zone_id, created, removed) SELECT rule_id, rule_from_zone, rule_create, removed FROM rule WHERE rule_from_zone IS NOT NULL; GET DIAGNOSTICS inserted_source = ROW_COUNT; + -- Count the existing rule_from_zone and rule_to_zone + SELECT COUNT(*) INTO remaining_source + FROM rule + WHERE rule_from_zone IS NOT NULL; + ELSE -- RAISE NOTICE 'Table does not exist or is not empty'; END IF; - IF NOT EXISTS ( - SELECT 1 FROM rule_to_zone - ) THEN + IF col_exists_destination AND NOT EXISTS (SELECT 1 FROM rule_to_zone) THEN INSERT INTO rule_to_zone (rule_id, zone_id, created, removed) SELECT rule_id, rule_to_zone, rule_create, removed FROM rule WHERE rule_to_zone IS NOT NULL; GET DIAGNOSTICS inserted_destination = ROW_COUNT; + -- Count the existing rule_from_zone and rule_to_zone + SELECT COUNT(*) INTO remaining_destination + FROM rule + WHERE rule_to_zone IS NOT NULL; + ELSE -- RAISE NOTICE 'Table does not exist or is not empty'; END IF; - -- Count the existing rule_from_zone and rule_to_zone - SELECT COUNT(*) INTO remaining_source - FROM rule - WHERE rule_from_zone IS NOT NULL; - - SELECT COUNT(*) INTO remaining_destination - FROM rule - WHERE rule_to_zone IS NOT NULL; - IF remaining_source + remaining_destination = inserted_source + inserted_destination THEN - UPDATE rule - SET rule_from_zone = NULL, - rule_to_zone = NULL - WHERE rule_from_zone IS NOT NULL - OR rule_to_zone IS NOT NULL; + IF (col_exists_source OR col_exists_destination) AND + (remaining_source + remaining_destination = inserted_source + inserted_destination) Then + UPDATE rule + SET rule_from_zone = NULL, + rule_to_zone = NULL + WHERE rule_from_zone IS NOT NULL + OR rule_to_zone IS NOT NULL; END IF; - + + IF (col_exists_source OR col_exists_destination) Then + SELECT COUNT(*) INTO count_from_zone_in_rule_after_update FROM rule WHERE rule_from_zone IS NOT NULL; + SELECT COUNT(*) INTO count_to_zone_in_rule_after_update FROM rule WHERE rule_to_zone IS NOT NULL; + + IF cnt_from > 0 OR cnt_to > 0 THEN + RAISE EXCEPTION 'Cannot drop columns: non-null values remain (% from_zone: %, % to_zone: %)', cnt_from, cnt_to; + END IF; + + --ALTER TABLE rule + --DROP CONSTRAINT IF EXISTS rule_rule_from_zone_fkey, + --DROP CONSTRAINT IF EXISTS rule_rule_to_zone_fkey; + + --ALTER TABLE rule + --DROP COLUMN IF EXISTS rule_from_zone, + --DROP COLUMN IF EXISTS rule_to_zone; END $$; From 589b2739fc685dbf134c0540926c72cb8570149e Mon Sep 17 00:00:00 2001 From: cd Date: Thu, 13 Nov 2025 15:40:41 +0100 Subject: [PATCH 21/23] changed variable Names --- roles/database/files/upgrade/9.0.sql | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/roles/database/files/upgrade/9.0.sql b/roles/database/files/upgrade/9.0.sql index f20d7b98f..778dc3470 100644 --- a/roles/database/files/upgrade/9.0.sql +++ b/roles/database/files/upgrade/9.0.sql @@ -1798,9 +1798,11 @@ BEGIN SELECT COUNT(*) INTO count_from_zone_in_rule_after_update FROM rule WHERE rule_from_zone IS NOT NULL; SELECT COUNT(*) INTO count_to_zone_in_rule_after_update FROM rule WHERE rule_to_zone IS NOT NULL; - IF cnt_from > 0 OR cnt_to > 0 THEN - RAISE EXCEPTION 'Cannot drop columns: non-null values remain (% from_zone: %, % to_zone: %)', cnt_from, cnt_to; - END IF; + IF count_from_zone_in_rule_after_update > 0 OR count_to_zone_in_rule_after_update > 0 THEN + RAISE EXCEPTION 'Cannot drop columns: non-null values remain (from_zone: %, to_zone: %)', count_from_zone_in_rule_after_update, count_to_zone_in_rule_after_update; + END IF; + + END IF; --ALTER TABLE rule --DROP CONSTRAINT IF EXISTS rule_rule_from_zone_fkey, From 0541af5e16865c6204a4159854fe939cd0bf09bb Mon Sep 17 00:00:00 2001 From: cd Date: Fri, 14 Nov 2025 09:04:11 +0100 Subject: [PATCH 22/23] add comment in Script --- roles/database/files/upgrade/9.0.sql | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/roles/database/files/upgrade/9.0.sql b/roles/database/files/upgrade/9.0.sql index 778dc3470..10093fca7 100644 --- a/roles/database/files/upgrade/9.0.sql +++ b/roles/database/files/upgrade/9.0.sql @@ -1803,11 +1803,12 @@ BEGIN END IF; END IF; - + --ALTER TABLE rule --DROP CONSTRAINT IF EXISTS rule_rule_from_zone_fkey, --DROP CONSTRAINT IF EXISTS rule_rule_to_zone_fkey; + --For dropping columns needed Views to be dropped/replaced where columns are included --ALTER TABLE rule --DROP COLUMN IF EXISTS rule_from_zone, --DROP COLUMN IF EXISTS rule_to_zone; From eb7bc349968854d8b7b52e8eeeb045e34dfbe330 Mon Sep 17 00:00:00 2001 From: cd Date: Mon, 17 Nov 2025 12:53:45 +0100 Subject: [PATCH 23/23] updated ExportTest for rule_from/to_zones --- roles/tests-unit/files/FWO.Test/ExportTest.cs | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/roles/tests-unit/files/FWO.Test/ExportTest.cs b/roles/tests-unit/files/FWO.Test/ExportTest.cs index 06ca742c1..798445274 100644 --- a/roles/tests-unit/files/FWO.Test/ExportTest.cs +++ b/roles/tests-unit/files/FWO.Test/ExportTest.cs @@ -67,7 +67,7 @@ public void RulesGenerateHtml() var reportRules = ConstructReportRules(false, query, userConfig, ReportType.Rules); - string expectedHtmlResult = "Rules Report

Rules Report

Time of configuration: 2023-04-20T15:50:04Z (UTC)

Generated on: Z (UTC)

Devices: TestMgt [Mock Device 1]

Filter: TestFilter



TestMgt


Mock Device 1

No.NameSource ZoneSourceDestination ZoneDestinationServicesActionTrackEnabledUidComment
TestRule1srczn TestIp1 (1.2.3.4/32)
 TestIp2 (127.0.0.1/32)
dstzn TestIpRange (1.2.3.4-1.2.3.5) TestService1 (443/TCP)acceptnoneYuid1comment1
TestRule2not
 TestUser1@ TestIp1 (1.2.3.4/32)
 TestUser1@ TestIp2 (127.0.0.1/32)
not
 TestUser2@ TestIpRange (1.2.3.4-1.2.3.5)
not
 TestService2 (6666-7777/UDP)
denynoneYuid2:123comment2

Network Objects

No.NameTypeIP AddressMembersUidComment
1TestIp1Network1.2.3.4/32
2TestIp2Network127.0.0.1/32
3TestIpRangeIP Range1.2.3.4-1.2.3.5

Network Services

No.NameTypeProtocolPortMembersUidComment
1TestService1TCP443
2TestService2UDP6666-7777

Users

No.NameTypeMembersUidComment
1TestUser1
2TestUser2Group

"; + string expectedHtmlResult = "Rules Report

Rules Report

Time of configuration: 2023-04-20T15:50:04Z (UTC)

Generated on: Z (UTC)

Devices: TestMgt [Mock Device 1]

Filter: TestFilter



TestMgt


Mock Device 1

No.NameSource ZoneSourceDestination ZoneDestinationServicesActionTrackEnabledUidComment
TestRule1srczn1
srczn2
srczn3
 TestIp1 (1.2.3.4/32)
 TestIp2 (127.0.0.1/32)
dstzn1
dstzn2
dstzn3
 TestIpRange (1.2.3.4-1.2.3.5) TestService1 (443/TCP)acceptnoneYuid1comment1
TestRule2not
 TestUser1@ TestIp1 (1.2.3.4/32)
 TestUser1@ TestIp2 (127.0.0.1/32)
not
 TestUser2@ TestIpRange (1.2.3.4-1.2.3.5)
not
 TestService2 (6666-7777/UDP)
denynoneYuid2:123comment2

Network Objects

No.NameTypeIP AddressMembersUidComment
1TestIp1Network1.2.3.4/32
2TestIp2Network127.0.0.1/32
3TestIpRangeIP Range1.2.3.4-1.2.3.5

Network Services

No.NameTypeProtocolPortMembersUidComment
1TestService1TCP443
2TestService2UDP6666-7777

Users

No.NameTypeMembersUidComment
1TestUser1
2TestUser2Group

"; string reportHtml = RemoveLinebreaks(RemoveGenDate(reportRules.ExportToHtml(), true)); @@ -83,7 +83,7 @@ public void ResolvedRulesGenerateHtml() Log.WriteInfo("Test Log", "starting rules report resolved html generation"); ReportRules reportRules = ConstructReportRules(true, query, userConfig, ReportType.ResolvedRules); - string expectedHtmlResult = "Rules Report (resolved)

Rules Report (resolved)

Time of configuration: 2023-04-20T15:50:04Z (UTC)

Generated on: Z (UTC)

Devices: TestMgt [Mock Device 1]

Filter: TestFilter


Table of content


TestMgt


Mock Device 1

No.NameSource ZoneSourceDestination ZoneDestinationServicesActionTrackEnabledUidComment
TestRule1srcznTestIp1 (1.2.3.4/32)
TestIp2 (127.0.0.1/32)
dstznTestIpRange (1.2.3.4-1.2.3.5)TestService1 (443/TCP)acceptnoneYuid1comment1
TestRule2not
TestUser1@TestIp1 (1.2.3.4/32)
TestUser1@TestIp2 (127.0.0.1/32)
not
TestUser2@TestIpRange (1.2.3.4-1.2.3.5)
not
TestService2 (6666-7777/UDP)
denynoneYuid2:123comment2

"; + string expectedHtmlResult = "Rules Report (resolved)

Rules Report (resolved)

Time of configuration: 2023-04-20T15:50:04Z (UTC)

Generated on: Z (UTC)

Devices: TestMgt [Mock Device 1]

Filter: TestFilter


Table of content


TestMgt


Mock Device 1

No.NameSource ZoneSourceDestination ZoneDestinationServicesActionTrackEnabledUidComment
TestRule1srczn1
srczn2
srczn3
TestIp1 (1.2.3.4/32)
TestIp2 (127.0.0.1/32)
dstzn1
dstzn2
dstzn3
TestIpRange (1.2.3.4-1.2.3.5)TestService1 (443/TCP)acceptnoneYuid1comment1
TestRule2not
TestUser1@TestIp1 (1.2.3.4/32)
TestUser1@TestIp2 (127.0.0.1/32)
not
TestUser2@TestIpRange (1.2.3.4-1.2.3.5)
not
TestService2 (6666-7777/UDP)
denynoneYuid2:123comment2

"; string reportHtml = RemoveLinebreaks(RemoveGenDate(reportRules.ExportToHtml(), true)); @@ -100,7 +100,7 @@ public void ResolvedRulesTechGenerateHtml() ReportRules reportRules = ConstructReportRules(true, query, userConfig, ReportType.ResolvedRulesTech); - string expectedHtmlResult = "Rules Report (technical)

Rules Report (technical)

Time of configuration: 2023-04-20T15:50:04Z (UTC)

Generated on: Z (UTC)

Devices: TestMgt [Mock Device 1]

Filter: TestFilter


Table of content


TestMgt


Mock Device 1

No.NameSource ZoneSourceDestination ZoneDestinationServicesActionTrackEnabledUidComment
TestRule1srczn1.2.3.4/32
127.0.0.1/32
dstzn1.2.3.4-1.2.3.5443/TCPacceptnoneYuid1comment1
TestRule2not
TestUser1@1.2.3.4/32
TestUser1@127.0.0.1/32
not
TestUser2@1.2.3.4-1.2.3.5
not
6666-7777/UDP
denynoneYuid2:123comment2

"; string reportHtml = RemoveLinebreaks(RemoveGenDate(reportRules.ExportToHtml(), true)); + string expectedHtmlResult = "Rules Report (technical)

Rules Report (technical)

Time of configuration: 2023-04-20T15:50:04Z (UTC)

Generated on: Z (UTC)

Devices: TestMgt [Mock Device 1]

Filter: TestFilter


Table of content


TestMgt


Mock Device 1

No.NameSource ZoneSourceDestination ZoneDestinationServicesActionTrackEnabledUidComment
TestRule1srczn1
srczn2
srczn3
1.2.3.4/32
127.0.0.1/32
dstzn1
dstzn2
dstzn3
1.2.3.4-1.2.3.5443/TCPacceptnoneYuid1comment1
TestRule2not
TestUser1@1.2.3.4/32
TestUser1@127.0.0.1/32
not
TestUser2@1.2.3.4-1.2.3.5
not
6666-7777/UDP
denynoneYuid2:123comment2

"; string reportHtml = RemoveLinebreaks(RemoveGenDate(reportRules.ExportToHtml(), true)); IEnumerable matches = reportHtml.GetMatches(ToCRegexPattern, ToCAnkerIdGroupName); reportHtml = reportHtml.ReplaceAll(matches, StaticAnkerId); @@ -114,7 +114,7 @@ public void UnusedRulesGenerateHtml() Log.WriteInfo("Test Log", "starting unused rules report html generation"); ReportRules reportRules = ConstructReportRules(false, query, userConfig, ReportType.UnusedRules); - string expectedHtmlResult = "Unused Rules Report

Unused Rules Report

Time of configuration: 2023-04-20T15:50:04Z (UTC)

Generated on: Z (UTC)

Devices: TestMgt [Mock Device 1]

Filter: TestFilter



TestMgt


Mock Device 1

No.Last HitNameSource ZoneSourceDestination ZoneDestinationServicesActionTrackEnabledUidComment
2022-04-19TestRule1srczn TestIp1 (1.2.3.4/32)
 TestIp2 (127.0.0.1/32)
dstzn TestIpRange (1.2.3.4-1.2.3.5) TestService1 (443/TCP)acceptnoneYuid1comment1
TestRule2not
 TestUser1@ TestIp1 (1.2.3.4/32)
 TestUser1@ TestIp2 (127.0.0.1/32)
not
 TestUser2@ TestIpRange (1.2.3.4-1.2.3.5)
not
 TestService2 (6666-7777/UDP)
denynoneYuid2:123comment2

Network Objects

No.NameTypeIP AddressMembersUidComment
1TestIp1Network1.2.3.4/32
2TestIp2Network127.0.0.1/32
3TestIpRangeIP Range1.2.3.4-1.2.3.5

Network Services

No.NameTypeProtocolPortMembersUidComment
1TestService1TCP443
2TestService2UDP6666-7777

Users

No.NameTypeMembersUidComment
1TestUser1
2TestUser2Group

"; + string expectedHtmlResult = "Unused Rules Report

Unused Rules Report

Time of configuration: 2023-04-20T15:50:04Z (UTC)

Generated on: Z (UTC)

Devices: TestMgt [Mock Device 1]

Filter: TestFilter



TestMgt


Mock Device 1

No.Last HitNameSource ZoneSourceDestination ZoneDestinationServicesActionTrackEnabledUidComment
2022-04-19TestRule1srczn1
srczn2
srczn3
 TestIp1 (1.2.3.4/32)
 TestIp2 (127.0.0.1/32)
dstzn1
dstzn2
dstzn3
 TestIpRange (1.2.3.4-1.2.3.5) TestService1 (443/TCP)acceptnoneYuid1comment1
TestRule2not
 TestUser1@ TestIp1 (1.2.3.4/32)
 TestUser1@ TestIp2 (127.0.0.1/32)
not
 TestUser2@ TestIpRange (1.2.3.4-1.2.3.5)
not
 TestService2 (6666-7777/UDP)
denynoneYuid2:123comment2

Network Objects

No.NameTypeIP AddressMembersUidComment
1TestIp1Network1.2.3.4/32
2TestIp2Network127.0.0.1/32
3TestIpRangeIP Range1.2.3.4-1.2.3.5

Network Services

No.NameTypeProtocolPortMembersUidComment
1TestService1TCP443
2TestService2UDP6666-7777

Users

No.NameTypeMembersUidComment
1TestUser1
2TestUser2Group

"; string reportHtml = RemoveLinebreaks(RemoveGenDate(reportRules.ExportToHtml(), true)); IEnumerable matches = reportHtml.GetMatches(ToCRegexPattern, ToCAnkerIdGroupName); @@ -324,7 +324,7 @@ public void ResolvedRulesGenerateCsv() "# report generator: Firewall Orchestrator - https://fwo.cactus.de/en" + "# data protection level: For internal use only#" + "\"management-name\",\"device-name\",\"rule-number\",\"rule-name\",\"source-zone\",\"source\",\"destination-zone\",\"destination\",\"service\",\"action\",\"track\",\"rule-enabled\",\"rule-uid\",\"rule-comment\"" + - "\"TestMgt\",\"Mock Device 1\",\"\",\"TestRule1\",\"srczn\",\"TestIp1 (1.2.3.4/32),TestIp2 (127.0.0.1/32)\",\"dstzn\",\"TestIpRange (1.2.3.4-1.2.3.5)\",\"TestService1 (443/TCP)\",\"accept\",\"none\",\"enabled\",\"uid1\",\"comment1\"" + + "\"TestMgt\",\"Mock Device 1\",\"\",\"TestRule1\",\"\"srczn1\",\"srczn2\",\"srczn3\"\",\"TestIp1 (1.2.3.4/32),TestIp2 (127.0.0.1/32)\",\"\"dstzn1\",\"dstzn2\",\"dstzn3\"\",\"TestIpRange (1.2.3.4-1.2.3.5)\",\"TestService1 (443/TCP)\",\"accept\",\"none\",\"enabled\",\"uid1\",\"comment1\"" + "\"TestMgt\",\"Mock Device 1\",\"\",\"TestRule2\",\"\",\"not(TestUser1@TestIp1 (1.2.3.4/32),TestUser1@TestIp2 (127.0.0.1/32))\",\"\",\"not(TestUser2@TestIpRange (1.2.3.4-1.2.3.5))\",\"not(TestService2 (6666-7777/UDP))\",\"deny\",\"none\",\"enabled\",\"uid2:123\",\"comment2\""; string csvExport = RemoveLinebreaks(RemoveGenDate(reportRules.ExportToCsv())); ClassicAssert.AreEqual(expectedCsvResult,csvExport); @@ -344,7 +344,7 @@ public void ResolvedRulesTechGenerateCsv() "# report generator: Firewall Orchestrator - https://fwo.cactus.de/en" + "# data protection level: For internal use only#" + "\"management-name\",\"device-name\",\"rule-number\",\"rule-name\",\"source-zone\",\"source\",\"destination-zone\",\"destination\",\"service\",\"action\",\"track\",\"rule-enabled\",\"rule-uid\",\"rule-comment\"" + - "\"TestMgt\",\"Mock Device 1\",\"\",\"TestRule1\",\"srczn\",\"1.2.3.4/32,127.0.0.1/32\",\"dstzn\",\"1.2.3.4-1.2.3.5\",\"443/TCP\",\"accept\",\"none\",\"enabled\",\"uid1\",\"comment1\"" + + "\"TestMgt\",\"Mock Device 1\",\"\",\"TestRule1\",\"\"srczn1\",\"srczn2\",\"srczn3\"\",\"1.2.3.4/32,127.0.0.1/32\",\"\"dstzn1\",\"dstzn2\",\"dstzn3\"\",\"1.2.3.4-1.2.3.5\",\"443/TCP\",\"accept\",\"none\",\"enabled\",\"uid1\",\"comment1\"" + "\"TestMgt\",\"Mock Device 1\",\"\",\"TestRule2\",\"\",\"not(TestUser1@1.2.3.4/32,TestUser1@127.0.0.1/32)\",\"\",\"not(TestUser2@1.2.3.4-1.2.3.5)\",\"not(6666-7777/UDP)\",\"deny\",\"none\",\"enabled\",\"uid2:123\",\"comment2\""; ClassicAssert.AreEqual(expectedCsvResult, RemoveLinebreaks(RemoveGenDate(reportRules.ExportToCsv()))); } @@ -418,12 +418,12 @@ public void RulesGenerateJson() "\"rules_aggregate\": {\"aggregate\": {\"count\": 0}}," + "\"rules\": [" + "{\"rule_id\": 0,\"rule_uid\": \"uid1\",\"mgm_id\": 0,\"rule_num_numeric\": 0,\"rule_name\": \"TestRule1\",\"rule_comment\": \"comment1\",\"rule_disabled\": false," + - "\"rule_services\": [{\"service\": {\"svc_id\": 1,\"svc_name\": \"TestService1\",\"svc_uid\": \"\",\"svc_port\": 443,\"svc_port_end\": 443,\"svc_source_port\": null,\"svc_source_port_end\": null,\"svc_code\": \"\",\"svc_timeout\": null,\"svc_typ_id\": null,\"active\": false,\"svc_create\": 0,\"svc_create_time\": {\"time\": \"0001-01-01T00:00:00\"},\"svc_last_seen\": 0,\"service_type\": {\"name\": \"\"},\"svc_comment\": \"\",\"svc_color_id\": null,\"stm_color\": {\"color_name\": \"\"},\"ip_proto_id\": null,\"protocol_name\": {\"id\": 6,\"name\": \"TCP\"},\"svc_member_names\": \"\",\"svc_member_refs\": \"\",\"svcgrps\": [],\"svcgrp_flats\": [],\"svc_rpcnr\": null}}]," + - "\"rule_svc_neg\": false,\"rule_svc\": \"\",\"rule_svc_refs\": \"\",\"rule_src_neg\": false,\"rule_src\": \"\",\"rule_src_refs\": \"\",\"src_zone\": {\"zone_id\": 0,\"zone_name\": \"srczn\"}," + + "\"rule_services\": [{\"service\": {\"svc_id\": 1,\"svc_name\": \"TestService1\",\"svc_uid\": \"\",\"svc_port\": 443,\"svc_port_end\": 443,\"svc_source_port\": null,\"svc_source_port_end\": null,\"svc_code\": \"\",\"svc_timeout\": null,\"svc_typ_id\": null,\"active\": false,\"svc_create\": 0,\"svc_create_time\": {\"time\": \"0001-01-01T00:00:00\"},\"svc_last_seen\": 0,\"service_type\": {\"name\": \"\"},\"svc_comment\": \"\",\"svc_color_id\": null,\"stm_color\": {\"color_name\": \"\"},\"ip_proto_id\": null,\"protocol_name\": {\"id\": 6,\"name\": \"TCP\"},\"svc_member_names\": \"\",\"svc_member_refs\": \"\",\"svcgrps\": [],\"svcgrp_flats\": [],\"svc_rpcnr\": null}}]," + + "\"rule_svc_neg\": false,\"rule_svc\": \"\",\"rule_svc_refs\": \"\",\"rule_src_neg\": false,\"rule_src\": \"\",\"rule_src_refs\": \"\",\"rule_from_zones\": [{\"zone\": {\"zone_id\": 0,\"zone_name\": \"srczn1\"}},{\"zone\": {\"zone_id\": 0,\"zone_name\": \"srczn2\"}},{\"zone\": {\"zone_id\": 0,\"zone_name\": \"srczn3\"}}]," + "\"rule_froms\": [" + "{\"object\": {\"obj_id\": 1,\"obj_name\": \"TestIp1\",\"obj_ip\": \"1.2.3.4/32\",\"obj_ip_end\": \"1.2.3.4/32\",\"obj_uid\": \"\",\"zone\": {\"zone_id\": 0,\"zone_name\": \"\"},\"active\": false,\"obj_create\": 0,\"obj_create_time\": {\"time\": \"0001-01-01T00:00:00\"},\"obj_last_seen\": 0,\"type\": {\"id\": 0,\"name\": \"network\"},\"obj_color\": {\"color_name\": \"\"},\"obj_comment\": \"\",\"obj_member_names\": \"\",\"obj_member_refs\": \"\",\"objgrps\": [],\"objgrp_flats\": []},\"usr\": {\"user_id\": 0,\"user_uid\": \"\",\"user_name\": \"\",\"user_comment\": \"\",\"user_lastname\": \"\",\"user_firstname\": \"\",\"usr_typ_id\": 0,\"type\": {\"usr_typ_name\": \"\"},\"user_create\": 0,\"user_create_time\": {\"time\": \"0001-01-01T00:00:00\"},\"user_last_seen\": 0,\"user_member_names\": \"\",\"user_member_refs\": \"\",\"usergrps\": [],\"usergrp_flats\": []}}," + - "{\"object\": {\"obj_id\": 2,\"obj_name\": \"TestIp2\",\"obj_ip\": \"127.0.0.1/32\",\"obj_ip_end\": \"127.0.0.1/32\",\"obj_uid\": \"\",\"zone\": {\"zone_id\": 0,\"zone_name\": \"\"},\"active\": false,\"obj_create\": 0,\"obj_create_time\": {\"time\": \"0001-01-01T00:00:00\"},\"obj_last_seen\": 0,\"type\": {\"id\": 0,\"name\": \"network\"},\"obj_color\": {\"color_name\": \"\"},\"obj_comment\": \"\",\"obj_member_names\": \"\",\"obj_member_refs\": \"\",\"objgrps\": [],\"objgrp_flats\": []},\"usr\": {\"user_id\": 0,\"user_uid\": \"\",\"user_name\": \"\",\"user_comment\": \"\",\"user_lastname\": \"\",\"user_firstname\": \"\",\"usr_typ_id\": 0,\"type\": {\"usr_typ_name\": \"\"},\"user_create\": 0,\"user_create_time\": {\"time\": \"0001-01-01T00:00:00\"},\"user_last_seen\": 0,\"user_member_names\": \"\",\"user_member_refs\": \"\",\"usergrps\": [],\"usergrp_flats\": []}}]," + - "\"rule_dst_neg\": false,\"rule_dst\": \"\",\"rule_dst_refs\": \"\",\"dst_zone\": {\"zone_id\": 0,\"zone_name\": \"dstzn\"}," + + "{\"object\": {\"obj_id\": 2,\"obj_name\": \"TestIp2\",\"obj_ip\": \"127.0.0.1/32\",\"obj_ip_end\": \"127.0.0.1/32\",\"obj_uid\": \"\",\"zone\": {\"zone_id\": 0,\"zone_name\": \"\"},\"active\": false,\"obj_create\": 0,\"obj_create_time\": {\"time\": \"0001-01-01T00:00:00\"},\"obj_last_seen\": 0,\"type\": {\"id\": 0,\"name\": \"network\"},\"obj_color\": {\"color_name\": \"\"},\"obj_comment\": \"\",\"obj_member_names\": \"\",\"obj_member_refs\": \"\",\"objgrps\": [],\"objgrp_flats\": []},\"usr\": {\"user_id\": 0,\"user_uid\": \"\",\"user_name\": \"\",\"user_comment\": \"\",\"user_lastname\": \"\",\"user_firstname\": \"\",\"usr_typ_id\": 0,\"type\": {\"usr_typ_name\": \"\"},\"user_create\": 0,\"user_create_time\": {\"time\": \"0001-01-01T00:00:00\"},\"user_last_seen\": 0,\"user_member_names\": \"\",\"user_member_refs\": \"\",\"usergrps\": [],\"usergrp_flats\": []}}]," + + "\"rule_dst_neg\": false,\"rule_dst\": \"\",\"rule_dst_refs\": \"\",\"rule_to_zones\": [{\"zone\": {\"zone_id\": 0,\"zone_name\": \"dstzn1\"}},{\"zone\": {\"zone_id\": 0,\"zone_name\": \"dstzn2\"}},{\"zone\": {\"zone_id\": 0,\"zone_name\": \"dstzn3\"}}]," + "\"rule_tos\": [" + "{\"object\": {\"obj_id\": 3,\"obj_name\": \"TestIpRange\",\"obj_ip\": \"1.2.3.4/32\",\"obj_ip_end\": \"1.2.3.5/32\",\"obj_uid\": \"\",\"zone\": {\"zone_id\": 0,\"zone_name\": \"\"},\"active\": false,\"obj_create\": 0,\"obj_create_time\": {\"time\": \"0001-01-01T00:00:00\"},\"obj_last_seen\": 0,\"type\": {\"id\": 0,\"name\": \"ip_range\"},\"obj_color\": {\"color_name\": \"\"},\"obj_comment\": \"\",\"obj_member_names\": \"\",\"obj_member_refs\": \"\",\"objgrps\": [],\"objgrp_flats\": []},\"usr\": {\"user_id\": 0,\"user_uid\": \"\",\"user_name\": \"\",\"user_comment\": \"\",\"user_lastname\": \"\",\"user_firstname\": \"\",\"usr_typ_id\": 0,\"type\": {\"usr_typ_name\": \"\"},\"user_create\": 0,\"user_create_time\": {\"time\": \"0001-01-01T00:00:00\"},\"user_last_seen\": 0,\"user_member_names\": \"\",\"user_member_refs\": \"\",\"usergrps\": [],\"usergrp_flats\": []}}]," + "\"rule_action\": \"accept\",\"rule_track\": \"none\",\"section_header\": \"\"," + @@ -431,12 +431,12 @@ public void RulesGenerateJson() "\"translate\": {\"rule_svc_neg\": false,\"rule_svc\": \"\",\"rule_services\": [],\"rule_src_neg\": false,\"rule_src\": \"\",\"rule_froms\": [],\"rule_dst_neg\": false,\"rule_dst\": \"\",\"rule_tos\": []}," + "\"owner_name\": \"\",\"owner_id\": null,\"matches\": \"\",\"rule_custom_fields\": \"\",\"rule_implied\": false,\"nat_rule\": false,\"rulebase_id\": 0,\"rule_num\": 0,\"rule_enforced_on_gateways\": [],\"rule_installon\": null,\"rule_time\": null,\"violations\": [],\"rulebase\": {\"id\": 0,\"name\": \"\",\"uid\": \"\",\"mgm_id\": 0,\"is_global\": false,\"created\": 0,\"removed\": 0,\"rules\": []},\"uiuser\": null,\"rule\": null,\"ChangeID\": \"\",\"AdoITID\": \"\",\"Compliance\": 0,\"ViolationDetails\": \"\",\"DisplayOrderNumberString\": \"\",\"DisplayOrderNumber\": 1,\"Certified\": false,\"DeviceName\": \"\",\"RulebaseName\": \"\",\"DisregardedFroms\": [],\"DisregardedTos\": [],\"DisregardedServices\": [],\"ShowDisregarded\": false}," + "{\"rule_id\": 0,\"rule_uid\": \"uid2:123\",\"mgm_id\": 0,\"rule_num_numeric\": 0,\"rule_name\": \"TestRule2\",\"rule_comment\": \"comment2\",\"rule_disabled\": false," + - "\"rule_services\": [{\"service\": {\"svc_id\": 2,\"svc_name\": \"TestService2\",\"svc_uid\": \"\",\"svc_port\": 6666,\"svc_port_end\": 7777,\"svc_source_port\": null,\"svc_source_port_end\": null,\"svc_code\": \"\",\"svc_timeout\": null,\"svc_typ_id\": null,\"active\": false,\"svc_create\": 0,\"svc_create_time\": {\"time\": \"0001-01-01T00:00:00\"},\"svc_last_seen\": 0,\"service_type\": {\"name\": \"\"},\"svc_comment\": \"\",\"svc_color_id\": null,\"stm_color\": {\"color_name\": \"\"},\"ip_proto_id\": null,\"protocol_name\": {\"id\": 17,\"name\": \"UDP\"},\"svc_member_names\": \"\",\"svc_member_refs\": \"\",\"svcgrps\": [],\"svcgrp_flats\": [],\"svc_rpcnr\": null}}]," + - "\"rule_svc_neg\": true,\"rule_svc\": \"\",\"rule_svc_refs\": \"\",\"rule_src_neg\": true,\"rule_src\": \"\",\"rule_src_refs\": \"\",\"src_zone\": {\"zone_id\": 0,\"zone_name\": \"\"}," + + "\"rule_services\": [{\"service\": {\"svc_id\": 2,\"svc_name\": \"TestService2\",\"svc_uid\": \"\",\"svc_port\": 6666,\"svc_port_end\": 7777,\"svc_source_port\": null,\"svc_source_port_end\": null,\"svc_code\": \"\",\"svc_timeout\": null,\"svc_typ_id\": null,\"active\": false,\"svc_create\": 0,\"svc_create_time\": {\"time\": \"0001-01-01T00:00:00\"},\"svc_last_seen\": 0,\"service_type\": {\"name\": \"\"},\"svc_comment\": \"\",\"svc_color_id\": null,\"stm_color\": {\"color_name\": \"\"},\"ip_proto_id\": null,\"protocol_name\": {\"id\": 17,\"name\": \"UDP\"},\"svc_member_names\": \"\",\"svc_member_refs\": \"\",\"svcgrps\": [],\"svcgrp_flats\": [],\"svc_rpcnr\": null}}]," + + "\"rule_svc_neg\": true,\"rule_svc\": \"\",\"rule_svc_refs\": \"\",\"rule_src_neg\": true,\"rule_src\": \"\",\"rule_src_refs\": \"\",\"rule_from_zones\": []," + "\"rule_froms\": [" + "{\"object\": {\"obj_id\": 1,\"obj_name\": \"TestIp1\",\"obj_ip\": \"1.2.3.4/32\",\"obj_ip_end\": \"1.2.3.4/32\",\"obj_uid\": \"\",\"zone\": {\"zone_id\": 0,\"zone_name\": \"\"},\"active\": false,\"obj_create\": 0,\"obj_create_time\": {\"time\": \"0001-01-01T00:00:00\"},\"obj_last_seen\": 0,\"type\": {\"id\": 0,\"name\": \"network\"},\"obj_color\": {\"color_name\": \"\"},\"obj_comment\": \"\",\"obj_member_names\": \"\",\"obj_member_refs\": \"\",\"objgrps\": [],\"objgrp_flats\": []},\"usr\": {\"user_id\": 1,\"user_uid\": \"\",\"user_name\": \"TestUser1\",\"user_comment\": \"\",\"user_lastname\": \"\",\"user_firstname\": \"\",\"usr_typ_id\": 0,\"type\": {\"usr_typ_name\": \"\"},\"user_create\": 0,\"user_create_time\": {\"time\": \"0001-01-01T00:00:00\"},\"user_last_seen\": 0,\"user_member_names\": \"\",\"user_member_refs\": \"\",\"usergrps\": [],\"usergrp_flats\": []}}," + - "{\"object\": {\"obj_id\": 2,\"obj_name\": \"TestIp2\",\"obj_ip\": \"127.0.0.1/32\",\"obj_ip_end\": \"127.0.0.1/32\",\"obj_uid\": \"\",\"zone\": {\"zone_id\": 0,\"zone_name\": \"\"},\"active\": false,\"obj_create\": 0,\"obj_create_time\": {\"time\": \"0001-01-01T00:00:00\"},\"obj_last_seen\": 0,\"type\": {\"id\": 0,\"name\": \"network\"},\"obj_color\": {\"color_name\": \"\"},\"obj_comment\": \"\",\"obj_member_names\": \"\",\"obj_member_refs\": \"\",\"objgrps\": [],\"objgrp_flats\": []},\"usr\": {\"user_id\": 1,\"user_uid\": \"\",\"user_name\": \"TestUser1\",\"user_comment\": \"\",\"user_lastname\": \"\",\"user_firstname\": \"\",\"usr_typ_id\": 0,\"type\": {\"usr_typ_name\": \"\"},\"user_create\": 0,\"user_create_time\": {\"time\": \"0001-01-01T00:00:00\"},\"user_last_seen\": 0,\"user_member_names\": \"\",\"user_member_refs\": \"\",\"usergrps\": [],\"usergrp_flats\": []}}]," + - "\"rule_dst_neg\": true,\"rule_dst\": \"\",\"rule_dst_refs\": \"\",\"dst_zone\": {\"zone_id\": 0,\"zone_name\": \"\"}," + + "{\"object\": {\"obj_id\": 2,\"obj_name\": \"TestIp2\",\"obj_ip\": \"127.0.0.1/32\",\"obj_ip_end\": \"127.0.0.1/32\",\"obj_uid\": \"\",\"zone\": {\"zone_id\": 0,\"zone_name\": \"\"},\"active\": false,\"obj_create\": 0,\"obj_create_time\": {\"time\": \"0001-01-01T00:00:00\"},\"obj_last_seen\": 0,\"type\": {\"id\": 0,\"name\": \"network\"},\"obj_color\": {\"color_name\": \"\"},\"obj_comment\": \"\",\"obj_member_names\": \"\",\"obj_member_refs\": \"\",\"objgrps\": [],\"objgrp_flats\": []},\"usr\": {\"user_id\": 1,\"user_uid\": \"\",\"user_name\": \"TestUser1\",\"user_comment\": \"\",\"user_lastname\": \"\",\"user_firstname\": \"\",\"usr_typ_id\": 0,\"type\": {\"usr_typ_name\": \"\"},\"user_create\": 0,\"user_create_time\": {\"time\": \"0001-01-01T00:00:00\"},\"user_last_seen\": 0,\"user_member_names\": \"\",\"user_member_refs\": \"\",\"usergrps\": [],\"usergrp_flats\": []}}]," + + "\"rule_dst_neg\": true,\"rule_dst\": \"\",\"rule_dst_refs\": \"\",\"rule_to_zones\": []," + "\"rule_tos\": [" + "{\"object\": {\"obj_id\": 3,\"obj_name\": \"TestIpRange\",\"obj_ip\": \"1.2.3.4/32\",\"obj_ip_end\": \"1.2.3.5/32\",\"obj_uid\": \"\",\"zone\": {\"zone_id\": 0,\"zone_name\": \"\"},\"active\": false,\"obj_create\": 0,\"obj_create_time\": {\"time\": \"0001-01-01T00:00:00\"},\"obj_last_seen\": 0,\"type\": {\"id\": 0,\"name\": \"ip_range\"},\"obj_color\": {\"color_name\": \"\"},\"obj_comment\": \"\",\"obj_member_names\": \"\",\"obj_member_refs\": \"\",\"objgrps\": [],\"objgrp_flats\": []},\"usr\": {\"user_id\": 2,\"user_uid\": \"\",\"user_name\": \"TestUser2\",\"user_comment\": \"\",\"user_lastname\": \"\",\"user_firstname\": \"\",\"usr_typ_id\": 0,\"type\": {\"usr_typ_name\": \"group\"},\"user_create\": 0,\"user_create_time\": {\"time\": \"0001-01-01T00:00:00\"},\"user_last_seen\": 0,\"user_member_names\": \"\",\"user_member_refs\": \"\",\"usergrps\": [],\"usergrp_flats\": []}}]," + "\"rule_action\": \"deny\",\"rule_track\": \"none\",\"section_header\": \"\"," +