diff --git a/roles/api/files/replace_metadata.json b/roles/api/files/replace_metadata.json index 7bee67483..6902c1e10 100644 --- a/roles/api/files/replace_metadata.json +++ b/roles/api/files/replace_metadata.json @@ -4933,21 +4933,6 @@ } } }, - { - "name": "rules_with_owner", - "using": { - "manual_configuration": { - "column_mapping": { - "dev_id": "dev_id" - }, - "insertion_order": null, - "remote_table": { - "name": "view_rule_with_owner", - "schema": "public" - } - } - } - }, { "name": "tenant_to_devices", "using": { @@ -9031,8 +9016,10 @@ "domain_uid", "ext_mgm_data", "hide_in_gui", + "is_super_manager", "mgm_id", "mgm_name", + "mgm_uid", "multi_device_manager_id" ], "filter": { @@ -9127,6 +9114,7 @@ "domain_uid", "ext_mgm_data", "hide_in_gui", + "is_super_manager", "mgm_id", "mgm_name", "mgm_uid", @@ -9224,6 +9212,7 @@ "domain_uid", "ext_mgm_data", "hide_in_gui", + "is_super_manager", "mgm_id", "mgm_name", "mgm_uid", @@ -9247,6 +9236,7 @@ "domain_uid", "ext_mgm_data", "hide_in_gui", + "is_super_manager", "mgm_id", "mgm_name", "mgm_uid", @@ -9265,29 +9255,33 @@ "permission": { "columns": [ "clearing_import_ran", + "do_not_import", + "force_initial_import", + "hide_in_gui", + "is_super_manager", + "last_import_attempt_successful", "cloud_subscription_id", "cloud_tenant_id", "config_path", - "debug_level", - "dev_typ_id", - "do_not_import", "domain_uid", "ext_mgm_data", - "force_initial_import", - "hide_in_gui", - "import_credential_id", "importer_hostname", - "last_import_attempt", - "last_import_attempt_successful", "last_import_md5_complete_config", - "mgm_comment", - "mgm_create", - "mgm_id", "mgm_name", - "mgm_update", - "multi_device_manager_id", + "mgm_uid", + "rulebase_name", + "rulebase_uid", "ssh_hostname", - "ssh_port" + "debug_level", + "dev_typ_id", + "import_credential_id", + "mgm_id", + "multi_device_manager_id", + "ssh_port", + "mgm_comment", + "last_import_attempt", + "mgm_create", + "mgm_update" ], "filter": { "mgm_id": { @@ -9306,9 +9300,13 @@ "do_not_import", "ext_mgm_data", "hide_in_gui", + "is_super_manager", "mgm_id", "mgm_name", - "multi_device_manager_id" + "mgm_uid", + "multi_device_manager_id", + "rulebase_name", + "rulebase_uid" ], "filter": { "mgm_id": { @@ -9328,8 +9326,10 @@ "domain_uid", "ext_mgm_data", "hide_in_gui", + "is_super_manager", "mgm_id", "mgm_name", + "mgm_uid", "multi_device_manager_id" ], "filter": {}, @@ -9345,6 +9345,7 @@ "domain_uid", "ext_mgm_data", "hide_in_gui", + "is_super_manager", "mgm_id", "mgm_name", "mgm_uid", @@ -9368,6 +9369,7 @@ "domain_uid", "ext_mgm_data", "hide_in_gui", + "is_super_manager", "mgm_id", "mgm_name", "mgm_uid", @@ -14837,13 +14839,14 @@ "permission": { "columns": [ "parent_rule_id", + "removed", "rule_create", "rule_id", "rule_last_seen", - "removed", "xlate_rule", "access_rule", "active", + "is_global", "nat_rule", "rule_disabled", "rule_dst_neg", @@ -14858,6 +14861,7 @@ "dev_id", "last_change_admin", "mgm_id", + "rulebase_id", "rule_from_zone", "rule_num", "rule_to_zone", @@ -14875,7 +14879,6 @@ "rule_svc", "rule_svc_refs", "rule_track", - "rulebase_id", "rule_uid" ], "computed_fields": [ @@ -14890,13 +14893,14 @@ "permission": { "columns": [ "parent_rule_id", + "removed", "rule_create", "rule_id", "rule_last_seen", - "removed", "xlate_rule", "access_rule", "active", + "is_global", "nat_rule", "rule_disabled", "rule_dst_neg", @@ -14911,6 +14915,7 @@ "dev_id", "last_change_admin", "mgm_id", + "rulebase_id", "rule_from_zone", "rule_num", "rule_to_zone", @@ -14928,7 +14933,6 @@ "rule_svc", "rule_svc_refs", "rule_track", - "rulebase_id", "rule_uid" ], "computed_fields": [ @@ -14996,47 +15000,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" @@ -15050,13 +15055,14 @@ "permission": { "columns": [ "parent_rule_id", + "removed", "rule_create", "rule_id", "rule_last_seen", - "removed", "xlate_rule", "access_rule", "active", + "is_global", "nat_rule", "rule_disabled", "rule_dst_neg", @@ -15071,6 +15077,7 @@ "dev_id", "last_change_admin", "mgm_id", + "rulebase_id", "rule_from_zone", "rule_num", "rule_to_zone", @@ -15100,11 +15107,6 @@ "_in": "x-hasura-visible-managements" } }, - { - "dev_id": { - "_in": "x-hasura-visible-devices" - } - }, { "rule_relevant_for_tenant": { "_eq": "true" @@ -15120,13 +15122,14 @@ "permission": { "columns": [ "parent_rule_id", + "removed", "rule_create", "rule_id", "rule_last_seen", - "removed", "xlate_rule", "access_rule", "active", + "is_global", "nat_rule", "rule_disabled", "rule_dst_neg", @@ -15141,6 +15144,7 @@ "dev_id", "last_change_admin", "mgm_id", + "rulebase_id", "rule_from_zone", "rule_num", "rule_to_zone", @@ -15170,11 +15174,6 @@ "_in": "x-hasura-visible-managements" } }, - { - "dev_id": { - "_in": "x-hasura-visible-devices" - } - }, { "rule_relevant_for_tenant": { "_eq": "true" @@ -15190,13 +15189,14 @@ "permission": { "columns": [ "parent_rule_id", + "removed", "rule_create", "rule_id", "rule_last_seen", - "removed", "xlate_rule", "access_rule", "active", + "is_global", "nat_rule", "rule_disabled", "rule_dst_neg", @@ -15211,6 +15211,7 @@ "dev_id", "last_change_admin", "mgm_id", + "rulebase_id", "rule_from_zone", "rule_num", "rule_to_zone", @@ -15240,11 +15241,6 @@ "_in": "x-hasura-visible-managements" } }, - { - "dev_id": { - "_in": "x-hasura-visible-devices" - } - }, { "rule_relevant_for_tenant": { "_eq": "true" @@ -15260,13 +15256,14 @@ "permission": { "columns": [ "parent_rule_id", + "removed", "rule_create", "rule_id", "rule_last_seen", - "removed", "xlate_rule", "access_rule", "active", + "is_global", "nat_rule", "rule_disabled", "rule_dst_neg", @@ -15281,6 +15278,7 @@ "dev_id", "last_change_admin", "mgm_id", + "rulebase_id", "rule_from_zone", "rule_num", "rule_to_zone", @@ -17404,6 +17402,22 @@ }, "comment": "" }, + { + "role": "recertifier", + "permission": { + "columns": [ + "created", + "removed", + "is_global", + "name", + "uid", + "id", + "mgm_id" + ], + "filter": {} + }, + "comment": "" + }, { "role": "reporter", "permission": { @@ -17545,7 +17559,6 @@ "permission": { "columns": [ "created", - "removed", "from_rule_id", "removed", "is_global", @@ -17587,7 +17600,6 @@ "permission": { "columns": [ "created", - "removed", "from_rule_id", "removed", "is_global", @@ -17608,7 +17620,26 @@ "permission": { "columns": [ "created", + "from_rule_id", "removed", + "is_global", + "is_initial", + "is_section", + "from_rulebase_id", + "gw_id", + "id", + "link_type", + "to_rulebase_id" + ], + "filter": {} + }, + "comment": "" + }, + { + "role": "recertifier", + "permission": { + "columns": [ + "created", "from_rule_id", "removed", "is_global", @@ -17629,7 +17660,6 @@ "permission": { "columns": [ "created", - "removed", "from_rule_id", "removed", "is_global", @@ -17650,7 +17680,6 @@ "permission": { "columns": [ "created", - "removed", "from_rule_id", "removed", "is_global", @@ -23527,21 +23556,6 @@ "schema": "public" }, "object_relationships": [ - { - "name": "device", - "using": { - "manual_configuration": { - "column_mapping": { - "dev_id": "dev_id" - }, - "insertion_order": null, - "remote_table": { - "name": "device", - "schema": "public" - } - } - } - }, { "name": "dst_zone", "using": { @@ -23636,7 +23650,6 @@ "owner_name", "rule_name", "action_id", - "dev_id", "mgm_id", "owner_id", "recert_interval", @@ -23653,11 +23666,7 @@ "rule_uid", "rule_last_certified" ], - "filter": { - "dev_id": { - "_in": "x-hasura-visible-devices" - } - }, + "filter": {}, "allow_aggregations": true } }, @@ -23676,7 +23685,6 @@ "owner_name", "rule_name", "action_id", - "dev_id", "mgm_id", "owner_id", "recert_interval", @@ -23699,11 +23707,6 @@ "mgm_id": { "_in": "x-hasura-visible-managements" } - }, - { - "dev_id": { - "_in": "x-hasura-visible-devices" - } } ] }, @@ -23725,7 +23728,6 @@ "owner_name", "rule_name", "action_id", - "dev_id", "mgm_id", "owner_id", "recert_interval", @@ -23748,11 +23750,6 @@ "mgm_id": { "_in": "x-hasura-visible-managements" } - }, - { - "dev_id": { - "_in": "x-hasura-visible-devices" - } } ] }, @@ -23774,7 +23771,6 @@ "owner_name", "rule_name", "action_id", - "dev_id", "mgm_id", "owner_id", "recert_interval", diff --git a/roles/common/files/fwo-api-calls/owner/getRuleOwnerships.graphql b/roles/common/files/fwo-api-calls/owner/getRuleOwnerships.graphql index 148cb95d9..912ca11eb 100644 --- a/roles/common/files/fwo-api-calls/owner/getRuleOwnerships.graphql +++ b/roles/common/files/fwo-api-calls/owner/getRuleOwnerships.graphql @@ -3,7 +3,6 @@ query getRuleOwnerships ($ownerId: Int!) { rule_owner (where: {owner_id: {_eq: $ownerId}} order_by: { rule_metadata_id: asc }){ rule_metadatum { rule_metadata_id - dev_id rule_uid } } diff --git a/roles/common/files/fwo-api-calls/recertification/clearOpenRecerts.graphql b/roles/common/files/fwo-api-calls/recertification/clearOpenRecerts.graphql index 44cf37d21..d882321e4 100644 --- a/roles/common/files/fwo-api-calls/recertification/clearOpenRecerts.graphql +++ b/roles/common/files/fwo-api-calls/recertification/clearOpenRecerts.graphql @@ -1,8 +1,7 @@ -mutation clearOpenRecerts($ownerId: Int, $mgmId: Int) { +mutation clearOpenRecerts($ownerId: Int) { delete_recertification( where: { owner_id: { _eq: $ownerId } - rule_metadatum: { device: { mgm_id: { _eq: $mgmId } } } recert_date: { _is_null: true } } ) { diff --git a/roles/common/files/fwo-api-calls/recertification/getOpenRecerts.graphql b/roles/common/files/fwo-api-calls/recertification/getOpenRecerts.graphql index 0c96596aa..bf1533bf0 100644 --- a/roles/common/files/fwo-api-calls/recertification/getOpenRecerts.graphql +++ b/roles/common/files/fwo-api-calls/recertification/getOpenRecerts.graphql @@ -1,4 +1,4 @@ -query getFutureRecertsForOwners($ownerId: Int!, $mgmId: Int!) { +query getOpenRecerts($ownerId: Int!, $mgmId: Int!) { recert_get_one_owner_one_mgm( where: { recert_date: { _is_null: true } } args: { i_mgm_id: $mgmId, i_owner_id: $ownerId } @@ -13,5 +13,6 @@ query getFutureRecertsForOwners($ownerId: Int!, $mgmId: Int!) { next_recert_date recert_date comment + owner_recert_id } } diff --git a/roles/common/files/fwo-api-calls/recertification/recertify.graphql b/roles/common/files/fwo-api-calls/recertification/recertify.graphql index 33434f597..f5c4459c1 100644 --- a/roles/common/files/fwo-api-calls/recertification/recertify.graphql +++ b/roles/common/files/fwo-api-calls/recertification/recertify.graphql @@ -19,7 +19,4 @@ mutation recertify( ) { affected_rows } - ) { - affected_rows - } } diff --git a/roles/database/files/sql/creation/fworch-views-materialized.sql b/roles/database/files/sql/creation/fworch-views-materialized.sql index 91c7f1f30..3cfacac9f 100644 --- a/roles/database/files/sql/creation/fworch-views-materialized.sql +++ b/roles/database/files/sql/creation/fworch-views-materialized.sql @@ -158,13 +158,13 @@ DROP FUNCTION purge_view_rule_with_owner(); -- SmallOwnerChange: add MATERIALIZED for large installations CREATE MATERIALIZED VIEW view_rule_with_owner AS SELECT DISTINCT ar.rule_id, ar.owner_id, ar.owner_name, ar.matches, ar.recert_interval, ar.rule_last_certified, ar.rule_last_certifier, - r.rule_num_numeric, r.track_id, r.action_id, r.rule_from_zone, r.rule_to_zone, r.dev_id, r.mgm_id, r.rule_uid, + r.rule_num_numeric, r.track_id, r.action_id, r.rule_from_zone, r.rule_to_zone, r.mgm_id, r.rule_uid, r.rule_action, r.rule_name, r.rule_comment, r.rule_track, r.rule_src_neg, r.rule_dst_neg, r.rule_svc_neg, r.rule_head_text, r.rule_disabled, r.access_rule, r.xlate_rule, r.nat_rule FROM ( SELECT DISTINCT * FROM v_rule_with_rule_owner AS rul UNION SELECT DISTINCT * FROM v_rule_with_ip_owner AS ips) AS ar LEFT JOIN rule AS r USING (rule_id) GROUP BY ar.rule_id, ar.owner_id, ar.owner_name, ar.matches, ar.recert_interval, ar.rule_last_certified, ar.rule_last_certifier, - r.rule_num_numeric, r.track_id, r.action_id, r.rule_from_zone, r.rule_to_zone, r.dev_id, r.mgm_id, r.rule_uid, + r.rule_num_numeric, r.track_id, r.action_id, r.rule_from_zone, r.rule_to_zone, r.mgm_id, r.rule_uid, r.rule_action, r.rule_name, r.rule_comment, r.rule_track, r.rule_src_neg, r.rule_dst_neg, r.rule_svc_neg, r.rule_head_text, r.rule_disabled, r.access_rule, r.xlate_rule, r.nat_rule; diff --git a/roles/database/files/sql/idempotent/fworch-rule-recert.sql b/roles/database/files/sql/idempotent/fworch-rule-recert.sql index 53e1b16c5..282670883 100644 --- a/roles/database/files/sql/idempotent/fworch-rule-recert.sql +++ b/roles/database/files/sql/idempotent/fworch-rule-recert.sql @@ -106,7 +106,7 @@ BEGIN b_never_recertified := TRUE; SELECT INTO t_rule_created rule_metadata.rule_created FROM rule - LEFT JOIN rule_metadata ON (rule.rule_uid=rule_metadata.rule_uid AND rule.dev_id=rule_metadata.dev_id) + LEFT JOIN rule_metadata ON (rule.rule_uid=rule_metadata.rule_uid) WHERE rule_id=r_rule.rule_id; END IF; @@ -141,7 +141,7 @@ BEGIN i_owner_id AS owner_id FROM view_rule_with_owner LEFT JOIN rule USING (rule_id) - LEFT JOIN rule_metadata ON (rule.rule_uid=rule_metadata.rule_uid AND rule.dev_id=rule_metadata.dev_id) + LEFT JOIN rule_metadata ON (rule.rule_uid=rule_metadata.rule_uid) WHERE view_rule_with_owner.rule_id=r_rule.rule_id AND view_rule_with_owner.owner_id IS NULL; ELSE INSERT INTO recertification (rule_metadata_id, next_recert_date, rule_id, ip_match, owner_id) @@ -152,7 +152,7 @@ BEGIN i_owner_id AS owner_id FROM view_rule_with_owner LEFT JOIN rule USING (rule_id) - LEFT JOIN rule_metadata ON (rule.rule_uid=rule_metadata.rule_uid AND rule.dev_id=rule_metadata.dev_id) + LEFT JOIN rule_metadata ON (rule.rule_uid=rule_metadata.rule_uid) WHERE view_rule_with_owner.rule_id=r_rule.rule_id AND view_rule_with_owner.owner_id=i_owner_id; END IF; ELSE @@ -226,11 +226,12 @@ BEGIN SELECT I.start_time::timestamp + make_interval (days => o.recert_interval) AS value UNION SELECT C.recert_date + make_interval (days => o.recert_interval) AS value - ) AS temp_table)) + ) AS temp_table)), + NULL::bigint AS owner_recert_id FROM view_rule_with_owner V LEFT JOIN rule R USING (rule_id) - LEFT JOIN rule_metadata M ON (R.rule_uid=M.rule_uid AND R.dev_id=M.dev_id) + LEFT JOIN rule_metadata M ON (R.rule_uid=M.rule_uid) LEFT JOIN owner O ON (O.id=0) LEFT JOIN import_control I ON (R.rule_create=I.control_id) LEFT JOIN recertification C ON (M.rule_metadata_id=C.rule_metadata_id) @@ -253,11 +254,12 @@ BEGIN SELECT I.start_time::timestamp + make_interval (days => o.recert_interval) AS value UNION SELECT C.recert_date + make_interval (days => o.recert_interval) AS value - ) AS temp_table)) + ) AS temp_table)), + NULL::bigint AS owner_recert_id FROM view_rule_with_owner V LEFT JOIN rule R USING (rule_id) - LEFT JOIN rule_metadata M ON (R.rule_uid=M.rule_uid AND R.dev_id=M.dev_id) + LEFT JOIN rule_metadata M ON (R.rule_uid=M.rule_uid) LEFT JOIN owner O ON (V.owner_id=O.id) LEFT JOIN import_control I ON (R.rule_create=I.control_id) LEFT JOIN recertification C ON (M.rule_metadata_id=C.rule_metadata_id) diff --git a/roles/database/files/sql/idempotent/fworch-texts.sql b/roles/database/files/sql/idempotent/fworch-texts.sql index 8517bc263..1c042b215 100644 --- a/roles/database/files/sql/idempotent/fworch-texts.sql +++ b/roles/database/files/sql/idempotent/fworch-texts.sql @@ -562,6 +562,8 @@ INSERT INTO txt VALUES ('check_times', 'German', 'Prüfung Datumswerte'); INSERT INTO txt VALUES ('check_times', 'English', 'Check time values'); INSERT INTO txt VALUES ('select_device', 'German', 'Device(s) auswählen'); INSERT INTO txt VALUES ('select_device', 'English', 'Select device(s)'); +INSERT INTO txt VALUES ('select_management', 'German', 'Management(s) auswählen'); +INSERT INTO txt VALUES ('select_management', 'English', 'Select management(s)'); INSERT INTO txt VALUES ('tenant_vis_devices', 'German', 'Mandanten-Firewalls'); INSERT INTO txt VALUES ('tenant_vis_devices', 'English', 'Tenant firewalls'); INSERT INTO txt VALUES ('edit_vis_devices', 'German', 'Devices für Mandant'); @@ -1276,6 +1278,8 @@ INSERT INTO txt VALUES ('edit_interface', 'German', 'Schnittstelle bearbeit INSERT INTO txt VALUES ('edit_interface', 'English', 'Edit Interface'); INSERT INTO txt VALUES ('delete_interface', 'German', 'Schnittstelle löschen'); INSERT INTO txt VALUES ('delete_interface', 'English', 'Delete Interface'); +INSERT INTO txt VALUES ('propose_alternative', 'German', 'Alternative vorschlagen'); +INSERT INTO txt VALUES ('propose_alternative', 'English', 'Propose alternative'); INSERT INTO txt VALUES ('insert_forbidden', 'German', 'Einfügen verboten'); INSERT INTO txt VALUES ('insert_forbidden', 'English', 'Insert forbidden'); INSERT INTO txt VALUES ('func_reason', 'German', 'Fachliche Begründung'); diff --git a/roles/database/files/upgrade/9.0.sql b/roles/database/files/upgrade/9.0.sql index 725ad8031..63ad14b3f 100644 --- a/roles/database/files/upgrade/9.0.sql +++ b/roles/database/files/upgrade/9.0.sql @@ -1683,4 +1683,18 @@ insert into stm_dev_typ (dev_typ_id,dev_typ_name,dev_typ_version,dev_typ_manufac insert into stm_dev_typ (dev_typ_id,dev_typ_name,dev_typ_version,dev_typ_manufacturer,dev_typ_predef_svc,dev_typ_is_multi_mgmt,dev_typ_is_mgmt,is_pure_routing_device) VALUES (29,'Cisco Asa on FirePower','9','Cisco','',false,true,false) - ON CONFLICT (dev_typ_id) DO NOTHING; \ No newline at end of file + ON CONFLICT (dev_typ_id) DO NOTHING; + + +DROP MATERIALIZED VIEW IF EXISTS view_rule_with_owner; +CREATE MATERIALIZED VIEW view_rule_with_owner AS + SELECT DISTINCT ar.rule_id, ar.owner_id, ar.owner_name, ar.matches, ar.recert_interval, ar.rule_last_certified, ar.rule_last_certifier, + r.rule_num_numeric, r.track_id, r.action_id, r.rule_from_zone, r.rule_to_zone, r.mgm_id, r.rule_uid, + r.rule_action, r.rule_name, r.rule_comment, r.rule_track, r.rule_src_neg, r.rule_dst_neg, r.rule_svc_neg, + r.rule_head_text, r.rule_disabled, r.access_rule, r.xlate_rule, r.nat_rule + FROM ( SELECT DISTINCT * FROM v_rule_with_rule_owner AS rul UNION SELECT DISTINCT * FROM v_rule_with_ip_owner AS ips) AS ar + LEFT JOIN rule AS r USING (rule_id) + GROUP BY ar.rule_id, ar.owner_id, ar.owner_name, ar.matches, ar.recert_interval, ar.rule_last_certified, ar.rule_last_certifier, + r.rule_num_numeric, r.track_id, r.action_id, r.rule_from_zone, r.rule_to_zone, r.mgm_id, r.rule_uid, + r.rule_action, r.rule_name, r.rule_comment, r.rule_track, r.rule_src_neg, r.rule_dst_neg, r.rule_svc_neg, + r.rule_head_text, r.rule_disabled, r.access_rule, r.xlate_rule, r.nat_rule; diff --git a/roles/lib/files/FWO.Data/FwoOwner.cs b/roles/lib/files/FWO.Data/FwoOwner.cs index 2450d4cb2..616756b82 100644 --- a/roles/lib/files/FWO.Data/FwoOwner.cs +++ b/roles/lib/files/FWO.Data/FwoOwner.cs @@ -50,7 +50,7 @@ public class FwoOwner : FwoOwnerBase public bool RecertOverdue { get; set; } = false; public bool RecertUpcoming { get; set; } = false; - public long LastRecertId { get; set; } = 0; + public long? LastRecertId { get; set; } public FwoOwner() { } diff --git a/roles/lib/files/FWO.Data/Report/DeviceFilter.cs b/roles/lib/files/FWO.Data/Report/DeviceFilter.cs index 28639a107..5f7508d86 100644 --- a/roles/lib/files/FWO.Data/Report/DeviceFilter.cs +++ b/roles/lib/files/FWO.Data/Report/DeviceFilter.cs @@ -13,7 +13,10 @@ public class ManagementSelect [JsonProperty("name"), JsonPropertyName("name")] public string? Name { get; set; } - [JsonProperty("devices"), JsonPropertyName("devices")] + [JsonProperty("uid"), JsonPropertyName("uid")] + public string Uid { get; set; } = ""; + + [JsonProperty("devices"), JsonPropertyName("devices")] public List Devices { get; set; } = []; public ElementReference? UiReference { get; set; } diff --git a/roles/lib/files/FWO.Data/RuleMetadata.cs b/roles/lib/files/FWO.Data/RuleMetadata.cs index a1b63cb29..c298a8ba7 100644 --- a/roles/lib/files/FWO.Data/RuleMetadata.cs +++ b/roles/lib/files/FWO.Data/RuleMetadata.cs @@ -41,9 +41,6 @@ public class RuleMetadata [JsonProperty("recert_history"), JsonPropertyName("recert_history")] public List RecertHistory { get; set; } = []; - [JsonProperty("dev_id"), JsonPropertyName("dev_id")] - public int DeviceId { get; set; } - [JsonProperty("rule_uid"), JsonPropertyName("rule_uid")] public string? Uid { get; set; } = ""; diff --git a/roles/lib/files/FWO.Report.Filter/DynGraphqlQuery.cs b/roles/lib/files/FWO.Report.Filter/DynGraphqlQuery.cs index 17b96bac9..1169cd7e4 100644 --- a/roles/lib/files/FWO.Report.Filter/DynGraphqlQuery.cs +++ b/roles/lib/files/FWO.Report.Filter/DynGraphqlQuery.cs @@ -21,7 +21,28 @@ public class DynGraphqlQuery(string rawInput) public string UserObjWhereStatement { get; set; } = ""; public string ConnectionWhereStatement { get; set; } = ""; public string OwnerWhereStatement { get; set; } = ""; - public string OpenRulesTable { get; set; } = "rules("; + public string OpenRuleBaseTable { get; set; } = $@" rulebase_links {{ + linkType: stm_link_type {{ + name + id + }} + link_type + is_initial + is_global + is_section + gw_id + from_rule_id + from_rulebase_id + to_rulebase_id + created + removed + }} + }} + rulebases {{ + name + uid + id "; + public string OpenRulesTable { get; set; } = $@" rules ("; public string OpenChangeLogRulesTable { get; set; } = "changelog_rules("; public List QueryParameters { get; set; } = [ @@ -138,33 +159,13 @@ query rulesReport ({paramString}) id: mgm_id uid: mgm_uid name: mgm_name - devices ({GetDevWhereFilter(ref query, filter.ReportParams.DeviceFilter)}) + devices ({GetDevWhereFilter(filter.ReportParams.DeviceFilter)}) {{ id: dev_id name: dev_name uid: dev_uid - rulebase_links {{ - linkType: stm_link_type {{ - name - id - }} - link_type - is_initial - is_global - is_section - gw_id - from_rule_id - from_rulebase_id - to_rulebase_id - created - removed - }} - }} - rulebases {{ - name - uid - id - rules ( + {query.OpenRuleBaseTable} + {query.OpenRulesTable} {limitOffsetString} where: {{ access_rule: {{_eq: true}} {query.RuleWhereStatement} }} order_by: {{ rule_num_numeric: asc }} ) @@ -192,6 +193,7 @@ query rulesCertReport({paramString}) {{ id: dev_id name: dev_name + {query.OpenRuleBaseTable} {query.OpenRulesTable} where: {{ rule_metadatum: {{ recertifications_aggregate: {{ count: {{ filter: {{ _and: [{{owner: $ownerWhere}}, {{recert_date: {{_is_null: true}}}}, {{next_recert_date: {{_lte: $refdate1}}}}]}}, predicate: {{_gt: 0}}}}}}}} @@ -273,6 +275,7 @@ query natRulesReport ({paramString}) {{ id: dev_id name: dev_name + {query.OpenRuleBaseTable} {query.OpenRulesTable} {limitOffsetString} where: {{ nat_rule: {{_eq: true}}, ruleByXlateRule: {{}} {query.RuleWhereStatement} }} @@ -410,6 +413,7 @@ private static void SetRuleRecertFilter(ref DynGraphqlQuery query, ModellingFilt { query.RuleWhereStatement += $" {{ rule_metadatum: {{ recertifications: {{ owner_recert_id: {{_eq: {modellingFilter.OwnerRecertId} }}, recertified: {{ _eq: true }} }} }} }}"; } + private static void SetDeviceFilter(ref DynGraphqlQuery query, DeviceFilter? deviceFilter) { if (deviceFilter != null) @@ -417,7 +421,8 @@ private static void SetDeviceFilter(ref DynGraphqlQuery query, DeviceFilter? dev query.RelevantManagementIds = deviceFilter.GetSelectedManagements(); } } - private static string GetDevWhereFilter(ref DynGraphqlQuery query, DeviceFilter? deviceFilter) + + private static string GetDevWhereFilter(DeviceFilter deviceFilter) { string devWhereStatement = devWhereStringStart; bool first = true; @@ -628,15 +633,14 @@ private static void SetOwnerFilter(ref DynGraphqlQuery query, ModellingFilter? m if (modellingFilter != null) { // currently overruling tenant filter!! - // query.OpenRulesTable = $"rules: get_rules_for_owner(args: {{ownerid: {modellingFilter.SelectedOwner.Id} }}, "; - query.OpenRulesTable = $@" + query.OpenRuleBaseTable = $@" rulebase_links(order_by: {{order_no: asc}}) {{ rulebase_id order_no rulebase {{ id - name - rules: get_rules_for_owner(args: {{ownerid: {modellingFilter.SelectedOwner.Id} }}, "; + name "; + query.OpenRulesTable = $@" rules: get_rules_for_owner(args: {{ownerid: {modellingFilter.SelectedOwner.Id} }}, "; query.SelectedOwner = modellingFilter.SelectedOwner; } } diff --git a/roles/lib/files/FWO.Services/RuleTreeBuilder/RuleTreeBuilder.cs b/roles/lib/files/FWO.Services/RuleTreeBuilder/RuleTreeBuilder.cs index a1c5ce835..84da2f629 100644 --- a/roles/lib/files/FWO.Services/RuleTreeBuilder/RuleTreeBuilder.cs +++ b/roles/lib/files/FWO.Services/RuleTreeBuilder/RuleTreeBuilder.cs @@ -1,4 +1,3 @@ -using FWO.Basics; using FWO.Data; using FWO.Data.Report; @@ -10,22 +9,26 @@ public class RuleTreeBuilder : IRuleTreeBuilder /// The root item for the tree structure. /// public RuleTreeItem RuleTree { get; set; } = new(); + /// /// A Queue to process rulebases by rulebase links. /// public Queue<(RulebaseLink link, RulebaseReport rulebase)> RuleTreeBuilderQueue { get; set; } = new(); + /// /// The number of order numbers that were created during the process. /// public int CreatedOrderNumbersCount { get; set; } = 0; + /// /// A counter to easily create the order position (/order number) for the ordered layers on the top level. /// public int OrderedLayerCount { get; set; } = 0; + /// /// All of the processed rules. /// - private List _allRules = new(); + private readonly List _allRules = []; public RuleTreeBuilder() { @@ -37,16 +40,14 @@ public RuleTreeBuilder() /// public List BuildRuleTree() { - List lastPosition = new(); + List? lastPosition = []; // Start outer loop. - while (RuleTreeBuilderQueue.TryDequeue(out (RulebaseLink link, RulebaseReport rulebase) currentQueueItem)) { lastPosition = HandleRulebaseLinkQueueItem(currentQueueItem, lastPosition); // For concatenations: Set the returned last order number to the number of the last visited rule of the source rulebase if it differs from it. - if (RuleTreeBuilderQueue.TryPeek(out (RulebaseLink link, RulebaseReport rulebase) nextQueueItem) && nextQueueItem.link.LinkType == 4) { Rule? lastVisitedRuleOfNextRulebase = _allRules.LastOrDefault(rule => rule.RulebaseId == nextQueueItem.link.FromRulebaseId); @@ -55,10 +56,7 @@ public List BuildRuleTree() { string input = lastVisitedRuleOfNextRulebase.DisplayOrderNumberString; string result = input.Contains('|') ? input.Substring(0, input.IndexOf('|')) : input; - lastPosition = result - .Split('.') - .Select(int.Parse) - .ToList(); + lastPosition = [.. result.Split('.').Select(int.Parse)]; } } } @@ -69,72 +67,18 @@ public List BuildRuleTree() /// /// Recursive method that processes a rulebase link and the target rulebase to create ordernumbers and the integrate the rules in the rule tree. /// - private List HandleRulebaseLinkQueueItem((RulebaseLink link, RulebaseReport rulebase) currentQueueItem, List? lastPosition) + private List? HandleRulebaseLinkQueueItem((RulebaseLink link, RulebaseReport rulebase) currentQueueItem, List? lastPosition) { - List? nextPosition = null; - RuleTreeItem? nextParent = null; - // Get next link and rulebase if they exist. - (RulebaseLink link, RulebaseReport rulebase)? nextQueueItem = TryPeekNextQueueItem(); - // Prepare creation of order numbers. - - if (currentQueueItem.link.LinkType == 2 && !currentQueueItem.link.IsGlobal) - { - // Create position root and header item for new ordered layer - - OrderedLayerCount++; - nextPosition = new List { OrderedLayerCount }; - nextParent = RuleTree.AddItem(header: currentQueueItem.rulebase.Name ?? "", position: nextPosition.ToList(), addToChildren: true, addToFlatList: true, setLastAddedItem: true); - nextParent.IsOrderedLayerHeader = true; - lastPosition = nextPosition; - } - else if (currentQueueItem.link.IsSection) - { - // Get the starting point for the next position. - - nextPosition = lastPosition; - - // Get parent for header item. - - nextParent = GetSectionParent(nextPosition); - - // Create header item for section. - - nextParent = nextParent.AddItem(header: currentQueueItem.rulebase.Name ?? "", position: nextPosition.ToList(), addToChildren: true, addToFlatList: true, setLastAddedItem: true); - nextParent.IsSectionHeader = true; - } - else if (currentQueueItem.link.LinkType == 3) - { - nextParent = RuleTree.LastAddedItem as RuleTreeItem; - nextParent.IsInlineLayerRoot = true; - nextPosition = lastPosition.ToList(); - nextPosition.Add(0); - lastPosition = nextPosition; - - // Handle sections in inline layers without direct rules. - - if (nextQueueItem?.link is RulebaseLink nextLink && currentQueueItem.rulebase.Rules.Count() == 0 && nextLink.IsSection && nextLink.FromRulebaseId == currentQueueItem.link.NextRulebaseId) - { - lastPosition = HandleRulebaseLinkQueueItem(RuleTreeBuilderQueue.Dequeue(), lastPosition); - } - } - else if (currentQueueItem.link.LinkType == 4) - { - if (currentQueueItem.link.IsInitial) - { - nextParent = RuleTree; - } - - } + (List? nextPosition, RuleTreeItem? nextParent) = InitNextPositionAndParent(currentQueueItem, ref lastPosition, nextQueueItem); // Create order number. - List currentRules = currentQueueItem.rulebase.Rules.ToList(); + List currentRules = [.. currentQueueItem.rulebase.Rules]; foreach (Rule currentRule in currentRules) { - // Make clone if rule was alreade processed. - + // Make clone if rule was already processed. Rule rule = currentRule; if (_allRules.Contains(currentRule)) @@ -142,69 +86,107 @@ private List HandleRulebaseLinkQueueItem((RulebaseLink link, RulebaseReport rule = currentRule.CreateClone(); } - // Update next position. - - if (nextPosition == null) - { - nextPosition = lastPosition.ToList(); - } - else if (nextParent.GetPositionString() == OrderedLayerCount.ToString() && !nextParent.Children.Any()) - { - nextPosition.Add(0); - } - - if (nextPosition.Count == 0) - { - nextPosition.Add(0); - } - - // Update order number. - - nextPosition[nextPosition.Count() - 1] = nextPosition.Last() + 1; + UpdateNextPosition(nextPosition, nextParent, lastPosition); // Get and update tree item that holds currentRule as data. - - RuleTreeItem treeItem = nextParent.AddItem(addToChildren: true, addToFlatList: true, setLastAddedItem: true); + RuleTreeItem treeItem = nextParent != null ? nextParent.AddItem(addToChildren: true, addToFlatList: true, setLastAddedItem: true) : new(); treeItem.Data = rule; RuleTree.ElementsFlat.Add(treeItem); - treeItem.Position = nextPosition.ToList(); + treeItem.Position = [.. nextPosition]; rule.DisplayOrderNumberString = string.Join(".", treeItem.Position); treeItem.IsRule = true; - treeItem.Identifier = $"Rule (ID/UID): {rule.Id}/{rule.Uid}"; - RuleTree.LastAddedItem = treeItem; // Update order number, visited rules and last position. - CreatedOrderNumbersCount++; rule.OrderNumber = CreatedOrderNumbersCount; _allRules.Add(rule); lastPosition = nextPosition; // Handle inline layers. - - if (nextQueueItem?.link is RulebaseLink nextLink) + if (nextQueueItem?.link is RulebaseLink nextLink && + ((nextLink.LinkType == 3 && nextLink.FromRuleId == currentRule.Id) || + nextLink.IsSection && RuleTree.LastAddedItem?.Data?.RulebaseId == nextLink.FromRulebaseId && currentRule == currentRules.Last())) { - if ((nextLink.LinkType == 3 && nextLink.FromRuleId == currentRule.Id) || nextLink.IsSection && RuleTree.LastAddedItem.Data.RulebaseId == nextLink.FromRulebaseId && currentRule == currentRules.Last()) - { - nextQueueItem = RuleTreeBuilderQueue.Dequeue(); + nextQueueItem = RuleTreeBuilderQueue.Dequeue(); + lastPosition = HandleRulebaseLinkQueueItem(nextQueueItem.Value, lastPosition); + nextQueueItem = TryPeekNextQueueItem(); + } + } - lastPosition = HandleRulebaseLinkQueueItem(nextQueueItem.Value, lastPosition); + return lastPosition; + } - // Update current and next queue items in case this loop continues after handling an inline layer. + private (List?, RuleTreeItem?) InitNextPositionAndParent((RulebaseLink link, RulebaseReport rulebase) currentQueueItem, ref List? lastPosition, (RulebaseLink link, RulebaseReport rulebase)? nextQueueItem) + { + List? nextPosition = null; + RuleTreeItem? nextParent = null; + if (currentQueueItem.link.LinkType == 2 && !currentQueueItem.link.IsGlobal) + { + // Create position root and header item for new ordered layer + OrderedLayerCount++; + nextPosition = [OrderedLayerCount]; + nextParent = RuleTree.AddItem(header: currentQueueItem.rulebase.Name ?? "", position: [.. nextPosition], addToChildren: true, addToFlatList: true, setLastAddedItem: true); + nextParent.IsOrderedLayerHeader = true; + lastPosition = nextPosition; + } + else if (currentQueueItem.link.IsSection) + { + // Get the starting point for the next position. + nextPosition = lastPosition; - currentQueueItem = nextQueueItem.Value; + // Get parent for header item. + nextParent = nextPosition != null ? GetSectionParent(nextPosition) : new(); - nextQueueItem = TryPeekNextQueueItem(); - } + // Create header item for section. + nextParent = nextParent.AddItem(header: currentQueueItem.rulebase.Name ?? "", position: nextPosition?.ToList() ?? null, addToChildren: true, addToFlatList: true, setLastAddedItem: true); + nextParent.IsSectionHeader = true; + } + else if (currentQueueItem.link.LinkType == 3) + { + nextParent = RuleTree.LastAddedItem as RuleTreeItem; + if (nextParent != null) + { + nextParent.IsInlineLayerRoot = true; + } + nextPosition = lastPosition != null ? [.. lastPosition] : []; + nextPosition.Add(0); + lastPosition = nextPosition; + // Handle sections in inline layers without direct rules. + if (nextQueueItem?.link is RulebaseLink nextLink && currentQueueItem.rulebase.Rules.Length == 0 && nextLink.IsSection && nextLink.FromRulebaseId == currentQueueItem.link.NextRulebaseId) + { + lastPosition = HandleRulebaseLinkQueueItem(RuleTreeBuilderQueue.Dequeue(), lastPosition); } } + else if (currentQueueItem.link.LinkType == 4 && currentQueueItem.link.IsInitial) + { + nextParent = RuleTree; + } + return (nextPosition, nextParent); + } + + private void UpdateNextPosition(List? nextPosition, RuleTreeItem? nextParent, List? lastPosition) + { + if (nextPosition == null) + { + nextPosition = lastPosition != null ? [.. lastPosition] : []; + } + else if (nextParent != null && nextParent.GetPositionString() == OrderedLayerCount.ToString() && nextParent.Children.Count == 0) + { + nextPosition.Add(0); + } - return lastPosition; + if (nextPosition.Count == 0) + { + nextPosition.Add(0); + } + + // Update order number. + nextPosition[^1] = nextPosition[^1] + 1; } /// @@ -213,34 +195,22 @@ private List HandleRulebaseLinkQueueItem((RulebaseLink link, RulebaseReport public Queue<(RulebaseLink, RulebaseReport)>? BuildRulebaseLinkQueue(RulebaseLink[] links, RulebaseReport[] rulebases) { // Abort if their are no rulebase links or rulebases - - if (links.Count() == 0 || rulebases.Count() == 0) + if (links.Length == 0 || rulebases.Length == 0) { return null; } Queue<(RulebaseLink, RulebaseReport)> queue = new(); - Dictionary rulebaseMap = rulebases.ToDictionary(r => r.Id); // Make copy of link list, to be able to remove links without changing the original collection. - - List remainingLinks = links.ToList(); + List remainingLinks = [.. links]; // Start with initial link. - - RulebaseLink? current = remainingLinks.FirstOrDefault(l => l.IsInitial); - - if (current == null) - { - throw new InvalidOperationException("No initial RulebaseLink found."); - } - - + RulebaseLink? current = remainingLinks.FirstOrDefault(l => l.IsInitial) ?? throw new InvalidOperationException("No initial RulebaseLink found."); while (current != null) { // Get target rulebase to current link and enqueue its rules and the link. - if (!rulebaseMap.TryGetValue(current.NextRulebaseId, out RulebaseReport? report)) { throw new KeyNotFoundException($"No report found with ID {current.NextRulebaseId}"); @@ -250,34 +220,25 @@ private List HandleRulebaseLinkQueueItem((RulebaseLink link, RulebaseReport if (report != null) { - rulebase.Id = report.Id; rulebase.Name = report.Name; rulebase.RuleChanges = report.RuleChanges; rulebase.RuleStatistics = report.RuleStatistics; - rulebase.Rules = report.Rules.ToArray(); + rulebase.Rules = [.. report.Rules]; } queue.Enqueue((current, rulebase)); remainingLinks.Remove(current); // Get next link. - - List? candidates = remainingLinks + List? candidates = [.. remainingLinks .Where(l => l.FromRulebaseId == current.NextRulebaseId) - .OrderByDescending(l => l.FromRuleId.HasValue) - .ToList(); + .OrderByDescending(l => l.FromRuleId.HasValue)]; current = candidates.FirstOrDefault(); - - if (current == null) - { - current = remainingLinks.FirstOrDefault(); - } + current ??= remainingLinks.FirstOrDefault(); } - RuleTreeBuilderQueue = queue; - return queue; } @@ -299,21 +260,24 @@ private List HandleRulebaseLinkQueueItem((RulebaseLink link, RulebaseReport /// /// Returns the item that should be used for the section rule tree item in creation. /// - private RuleTreeItem GetSectionParent(List? nextPosition = null) + private RuleTreeItem GetSectionParent(List nextPosition) { - List? position = nextPosition.ToList(); + List? position = [.. nextPosition]; if (position.Last() == 0) position.Remove(position.Last()); - RuleTreeItem item = RuleTree.ElementsFlat.First(x => x.GetPositionString() == string.Join(".", position)) as RuleTreeItem; - + RuleTreeItem? item = RuleTree.ElementsFlat.FirstOrDefault(x => x.GetPositionString() == string.Join(".", position)) as RuleTreeItem; + if (item == null) + { + return new(); + } if (item.Data is Rule && (item.Parent as RuleTreeItem).IsOrderedLayerHeader && nextPosition.Last() != 0) { return item.Parent as RuleTreeItem; } - else if (item.IsOrderedLayerHeader) + if (item.IsOrderedLayerHeader) { return item; } - else if ((item.Parent as RuleTreeItem).IsSectionHeader) + if ((item.Parent as RuleTreeItem).IsSectionHeader) { if (nextPosition.Last() == 0) { @@ -321,18 +285,15 @@ private RuleTreeItem GetSectionParent(List? nextPosition = null) } return item.Parent.Parent as RuleTreeItem; } - else if ((item as RuleTreeItem).IsInlineLayerRoot) + if (item.IsInlineLayerRoot) { return item; } - else if ((item.Parent as RuleTreeItem).IsInlineLayerRoot) + if ((item.Parent as RuleTreeItem).IsInlineLayerRoot) { return item.Parent as RuleTreeItem; } - else - { - return new(); - } + return new(); } } } diff --git a/roles/middleware/files/FWO.Middleware.Server/RecertCheck.cs b/roles/middleware/files/FWO.Middleware.Server/RecertCheck.cs index 342ee4969..9b889df43 100644 --- a/roles/middleware/files/FWO.Middleware.Server/RecertCheck.cs +++ b/roles/middleware/files/FWO.Middleware.Server/RecertCheck.cs @@ -206,16 +206,12 @@ private async Task> GenerateRulesRecertificationReport(ApiConnection foreach (var management in reportData.ManagementData) { - foreach (var device in management.Devices.Where(d => d.ContainsRules())) + foreach(var rulebase in management.Rulebases) { - foreach (var rbLink in device.RulebaseLinks!) + foreach (var rule in rulebase.Rules) { - foreach (var rule in management.Rulebases[rbLink.NextRulebaseId].Rules) - { - rule.Metadata.UpdateRecertPeriods(owner.RecertInterval ?? globalConfig.RecertificationPeriod, 0); - rule.DeviceName = device.Name ?? ""; - rules.Add(rule); - } + rule.Metadata.UpdateRecertPeriods(owner.RecertInterval ?? globalConfig.RecertificationPeriod, 0); + rules.Add(rule); } } } diff --git a/roles/ui/files/FWO.UI/Pages/Certification.razor b/roles/ui/files/FWO.UI/Pages/Certification.razor index 2c6057123..890b3deaa 100644 --- a/roles/ui/files/FWO.UI/Pages/Certification.razor +++ b/roles/ui/files/FWO.UI/Pages/Certification.razor @@ -60,13 +60,13 @@ } else if (selectedOwner != null) { - + } - + @* ==== MAIN MIDDLE SECTION ==== *@ @@ -94,8 +94,8 @@ }
- @* *@ + @@ -151,7 +151,6 @@ private List collectedOwnerships = []; private DeviceFilter deviceFilter = new (); private List selectedRules = []; - private bool collapseDevices = false; private ReportBase? currentReport; private ReportTemplate reportParams = new (); @@ -176,11 +175,6 @@ deviceFilter.Managements = await apiConnection.SendQueryAsync>(DeviceQueries.getDevicesByManagement); await InvokeAsync(StateHasChanged); - if (deviceFilter.NumberMgmtDev() > userConfig.MinCollapseAllDevices) - { - collapseDevices = true; - await InvokeAsync(StateHasChanged); - } recertLookAheadDays = Convert.ToInt32(userConfig.RecertificationDisplayPeriod); } catch (Exception exception) @@ -269,17 +263,17 @@ private ReportParams prepareReportParams() { - List ownerList = []; + List selectedOwnerIdList = []; if (selectedOwner != null) { - ownerList.Add(selectedOwner.Id); + selectedOwnerIdList.Add(selectedOwner.Id); } return new ((int) ReportType.Recertification, deviceFilter) { RecertFilter = new() { - RecertOwnerList = ownerList, + RecertOwnerList = selectedOwnerIdList, RecertShowAnyMatch = true, RecertificationDisplayPeriod = recertLookAheadDays } @@ -291,19 +285,12 @@ rulesFound = false; foreach (var management in managementsReport) { - foreach (var device in management.Devices) + foreach (var rb in management.Rulebases) { - if (device.ContainsRules()) + foreach (var rule in rb.Rules) { rulesFound = true; - // TODO: implement recertification logic - // foreach (var rb in device.Rulebases) - // { - // foreach (var rule in rb.Rulebase.RuleMetadata[0].Rules!) - // { - // rule.Metadata.UpdateRecertPeriods(userConfig.RecertificationPeriod, userConfig.RecertificationNoticePeriod); - // } - // } + rule.Metadata.UpdateRecertPeriods(userConfig.RecertificationPeriod, userConfig.RecertificationNoticePeriod); } } } @@ -364,30 +351,16 @@ foreach (var management in managementsReport) { - // foreach (var device in management.Devices.Where(d => d.Rules != null)) - // { - // if (device.ContainsRules()) - // { - // TODO: implement recertification logic - // foreach (var rb in device.Rulebases) - // { - // foreach (var rule in rb.Rulebase.RuleMetadata[0].Rules) - // { - // if(rule.Metadata.Recert || rule.Metadata.ToBeRemoved) - // { - // // rule.RulebaseId = Rulebase.Id; - // Certifications.Add(rule); - // } - // } - // } - // current develop state: - // foreach (var rule in device.Rules!.Where(r => r.Metadata.Recert || r.Metadata.ToBeRemoved)) - // { - // rule.DeviceId = device.Id; - // Certifications.Add(rule); - // } - // } - //} + foreach (var rb in management.Rulebases) + { + foreach (var rule in rb.Rules) + { + if(rule.Metadata.Recert || rule.Metadata.ToBeRemoved) + { + Certifications.Add(rule); + } + } + } } } @@ -399,7 +372,7 @@ if (Certifications.Count > 0) { - RecertHandler recertHandler = new(apiConnection, userConfig); + RecertHandler recertHandler = new(apiConnection, userConfig); foreach(var certRule in Certifications) { if(await recertHandler.RecertifySingleRule(certRule, selectedOwner, actComment)) @@ -422,7 +395,6 @@ } } } - // refresh view ? } string txt = userConfig.GetText("recerts_executed") + recerts.ToString() + ", " + userConfig.GetText("decerts_executed") + decerts.ToString(); diff --git a/roles/ui/files/FWO.UI/Pages/Reporting/Report.razor b/roles/ui/files/FWO.UI/Pages/Reporting/Report.razor index d4f1347a3..e29f8b875 100644 --- a/roles/ui/files/FWO.UI/Pages/Reporting/Report.razor +++ b/roles/ui/files/FWO.UI/Pages/Reporting/Report.razor @@ -69,7 +69,14 @@ { } - + @if(actReportFilters.ReportType == ReportType.Recertification) + { + + } + else + { + + } } diff --git a/roles/ui/files/FWO.UI/Pages/Reporting/ReportRecertParamSelection.razor b/roles/ui/files/FWO.UI/Pages/Reporting/ReportRecertParamSelection.razor index 5bfe3b205..3a2ae1553 100644 --- a/roles/ui/files/FWO.UI/Pages/Reporting/ReportRecertParamSelection.razor +++ b/roles/ui/files/FWO.UI/Pages/Reporting/ReportRecertParamSelection.razor @@ -91,13 +91,16 @@ } } - private void SelectedOwnerChanged(FwoOwner newOwner) + private void SelectedOwnerChanged(FwoOwner? newOwner) { selectedOwner = newOwner; - RecertFilter.RecertOwnerList = new(); - if(newOwner != null) + if(newOwner == null) { - RecertFilter.RecertOwnerList.Add(newOwner.Id); + RecertFilter.RecertOwnerList = [.. ownerList.Select(o => o.Id)]; + } + else + { + RecertFilter.RecertOwnerList = [newOwner.Id]; } } } diff --git a/roles/ui/files/FWO.UI/Pages/Reporting/Reports/RuleBaseReport.razor b/roles/ui/files/FWO.UI/Pages/Reporting/Reports/RuleBaseReport.razor new file mode 100644 index 000000000..1f8f9a771 --- /dev/null +++ b/roles/ui/files/FWO.UI/Pages/Reporting/Reports/RuleBaseReport.razor @@ -0,0 +1,250 @@ +@using FWO.Report +@using FWO.Report.Data +@using FWO.Ui.Display + +@inject UserConfig userConfig + +@if(ruleDisplay != null) +{ + + + @if(SelectedReportType == ReportType.Recertification) + { + + + + + + + + + + + + + + @if(Recertification && RecertificationHistory) + { + // TODO: implement 2nd recert report containing latest recerts + + + + } + @if(Recertification && !ReadonlyMode) + { + + + + } + } + @if(Recertification && RecertificationHistory) + { + // TODO: implement 2nd recert report containing latest recerts + + } + + @if (EmptyColumns[1] == false) + { + + } + @if (EmptyColumns[2] == false) + { + + + + } + @if (EmptyColumns[3] == false) + { + + + + } + @if (EmptyColumns[4] == false) + { + + + + } + @if (EmptyColumns[5] == false) + { + + + + } + @if (EmptyColumns[6] == false) + { + + + + } + @if (EmptyColumns[7] == false && SelectedReportType != ReportType.NatRules) + { + + } + @if (EmptyColumns[8] == false && SelectedReportType != ReportType.NatRules) + { + + } + @if (EmptyColumns[9] == false) + { + + + + } + @if (EmptyColumns[10] == false) + { + + + + } + @if (EmptyColumns[11] == false) + { + + } + @if (EmptyColumns[12] == false) + { + + } + + + @if(context.Metadata != null) + { + + + + + + + @* + // TODO: not able to get this syntactically working, but implicit info is there: ToBeRemoved == NOT DecertificationDate IS NULL *@ + + + + @foreach(var recert in context.Metadata!.RecertHistory) + { + + } + } + + + + + + + + +
+
@(context.SectionHeader)
+
+} + +@code +{ + [Parameter] + public bool Recertification { get; set; } = false; + + [Parameter] + public bool ReadonlyMode { get; set; } = false; + + [Parameter] + public ReportType SelectedReportType { get; set; } + + [Parameter] + public List Rules { get; set; } = []; + + [Parameter] + public EventCallback> SelectedRulesChanged { get; set; } + + [Parameter] + public List SelectedRules { get; set; } = []; + + [Parameter] + public int RulesPerPage { get; set; } + + + private bool RecertificationHistory { get; set; } = false; + private ITable? reportTable; + private const int ColumnsCount = 13; + private bool[] EmptyColumns = new bool[ColumnsCount]; + + private NatRuleDisplayHtml? ruleDisplay; + + protected override void OnInitialized() + { + ruleDisplay = new NatRuleDisplayHtml(userConfig); + // TODO: are we using NatRuleDisplayHtml for non-NAT rules? + } + + private string getTableRowClasses(Rule rule) + { + string classes = ""; + if(rule.SectionHeader != null) + { + classes = "hide-all-but-second-child second-child-full-width "; + } + if(Recertification) + { + classes += rule.Metadata.Style; + } + return classes; + } + + private OutputLocation getActLocation() + { + return Recertification ? OutputLocation.certification : OutputLocation.report; + } + + private string getHistoryString(Recertification recert) + { + string username = new DistName(recert.UserDn).UserName; + return $"{userConfig.GetText(recert.Recertified ? "recertified_by" : "decertified_by")} {username} " + + $"{userConfig.GetText("as_owner")} {recert.FwoOwner?.Name} {userConfig.GetText("comment")}: {recert.Comment}"; + } +} 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 ee7d9c9bb..354347e5a 100644 --- a/roles/ui/files/FWO.UI/Pages/Reporting/Reports/RulesReport.razor +++ b/roles/ui/files/FWO.UI/Pages/Reporting/Reports/RulesReport.razor @@ -1,4 +1,5 @@ @using FWO.Report +@using FWO.Report.Data @using FWO.Ui.Display @inject UserConfig userConfig @@ -9,238 +10,190 @@ { management.AssignRuleNumbers(); - @foreach (var device in management.Devices.Select(dev => DeviceReportController.FromDeviceReport(dev))) + @if(Recertification || SelectedReportType == ReportType.Recertification) { - @if (device.ContainsRules() && ruleDisplay != null) + @foreach(var rulebase in management.Rulebases) { - - - - @if (EmptyColumns[0] == false) - { - - - - } - @if(SelectedReportType == ReportType.UnusedRules || SelectedReportType == ReportType.AppRules) - { - - - - } - @if(SelectedReportType == ReportType.Recertification) - { - - - - - - + var rulebaseRules = ReportRules.GetRulesByRulebaseId(rulebase.Id, management); + @if(rulebaseRules.Length > 0) + { + + + + } + } + } + else + { + @foreach (var device in management.Devices.Select(dev => DeviceReportController.FromDeviceReport(dev))) + { + @if (device.ContainsRules() && ruleDisplay != null) + { + +
- - - - - - - @if(Recertification && RecertificationHistory) + @if (EmptyColumns[0] == false) { - // TODO: implement 2nd recert report containing latest recerts - + } - @if(Recertification && !ReadonlyMode) + @if(SelectedReportType == ReportType.UnusedRules || SelectedReportType == ReportType.AppRules) { - + } - } - @if(Recertification && RecertificationHistory) - { - // TODO: implement 2nd recert report containing latest recerts - - } - - @if(SelectedReportType.IsComplianceReport()) - { - - - - - - - } - - @if (EmptyColumns[1] == false) - { - - } - @if (EmptyColumns[2] == false) - { - - - - } - @if (EmptyColumns[3] == false) - { - - - - } - @if (EmptyColumns[4] == false) - { - - - - } - @if (EmptyColumns[5] == false) - { - - - - } - @if (EmptyColumns[6] == false) - { - - - - } - @if(SelectedReportType == ReportType.NatRules) - { - + @if(SelectedReportType.IsComplianceReport()) + { + - + - + } + @if (EmptyColumns[1] == false) + { + + } + @if (EmptyColumns[2] == false) + { + - } - @if (EmptyColumns[7] == false && SelectedReportType != ReportType.NatRules) - { - - } - @if (EmptyColumns[8] == false && SelectedReportType != ReportType.NatRules) - { - - } - @if (EmptyColumns[9] == false) - { - - - - } - @if (EmptyColumns[10] == false) - { - - - - } - @if (EmptyColumns[11] == false) - { - - } - @if (EmptyColumns[12] == false) - { - - } - - - @if(context.Metadata != null) + } + @if (EmptyColumns[3] == false) { - - - - - - - @* - // TODO: not able to get this syntactically working, but implicit info is there: ToBeRemoved == NOT DecertificationDate IS NULL *@ - - - - @foreach(var recert in context.Metadata!.RecertHistory) + + + + } + @if (EmptyColumns[4] == false) + { + + + + } + @if (EmptyColumns[5] == false) + { + + + + } + @if (EmptyColumns[6] == false) + { + + + + } + @if(SelectedReportType == ReportType.NatRules) + { + + + + + + + + + + } + @if (EmptyColumns[7] == false && SelectedReportType != ReportType.NatRules) + { + + } + @if (EmptyColumns[8] == false && SelectedReportType != ReportType.NatRules) + { + + } + @if (EmptyColumns[9] == false) + { + + + + } + @if (EmptyColumns[10] == false) + { + + + + } + @if (EmptyColumns[11] == false) + { + + } + @if (EmptyColumns[12] == false) + { + + } + + + @if(context.Metadata != null) { - + + + + + + + @* + // TODO: not able to get this syntactically working, but implicit info is there: ToBeRemoved == NOT DecertificationDate IS NULL *@ + + + + @foreach(var recert in context.Metadata!.RecertHistory) + { + + } } - } - + - - - - - - -
-
@(context.SectionHeader)
-
-
+ + + +
@(context.SectionHeader)
+ + +
+ + +
+ } } } @@ -276,7 +229,6 @@ [Parameter] public List Managements { get; set; } = new (); - private bool RecertificationHistory { get; set; } = false; private ITable? reportTable; private const int ColumnsCount = 13; private bool[] EmptyColumns = new bool[ColumnsCount]; diff --git a/roles/ui/files/FWO.UI/Shared/ManagementSelection.razor b/roles/ui/files/FWO.UI/Shared/ManagementSelection.razor new file mode 100644 index 000000000..44b977fb4 --- /dev/null +++ b/roles/ui/files/FWO.UI/Shared/ManagementSelection.razor @@ -0,0 +1,95 @@ +@using FWO.Ui.Data +@using FWO.Data.Report + + +@inject UserConfig userConfig + + +
+ @if(ShowTitle) + { +
@(userConfig.GetText("select_management"))
+ } +
+ +
+

+ + + @foreach (ManagementSelect management in DeviceFilter.Managements) + { + if (management.Visible) + { +
+ + +
+ } + } +
+
+
+ +@code +{ + [CascadingParameter] + Action DisplayMessageInUi { get; set; } = DefaultInit.DoNothing; + + [Parameter] + public DeviceFilter DeviceFilter { get; set; } = new DeviceFilter(); + + [Parameter] + public EventCallback DeviceFilterChanged { get; set; } + + [Parameter] + public bool SelectAll { get; set; } = true; // state of the device select/clear all button + + [Parameter] + public EventCallback SelectAllChanged { get; set; } + + [Parameter] + public EventCallback CollapseAllChanged { get; set; } + + [Parameter] + public bool ShowTitle { get; set; } = true; + + + private string cssClass = "btn-group w-100 sticky-marker"; + private string selectAllText = ""; + private string clearAllText = ""; + private CollapseState collapseDevices = new CollapseState(); + + protected override void OnInitialized() + { + selectAllText = userConfig.GetText("select_all"); + clearAllText = userConfig.GetText("clear_all"); + DeviceFilter.Managements?.Sort((a, b) => a.Name?.CompareTo(b.Name) ?? -1); + } + + private async Task SetSelectAll() + { + if (DeviceFilter.AreAllDevicesSelected()) + SelectAll = false; + if (!DeviceFilter.IsAnyDeviceFilterSet()) + SelectAll = true; + await SelectAllChanged.InvokeAsync(SelectAll); + } + + private async Task ToggleSelectAll() + { + DeviceFilter.ApplyFullDeviceSelection(SelectAll); + SelectAll = !SelectAll; + await SelectAllChanged.InvokeAsync(SelectAll); + } + + private async Task ToggleSelectManagement(ManagementSelect management) + { + management.Selected = !management.Selected; + foreach (DeviceSelect device in management.Devices) + device.Selected = management.Selected; + await SetSelectAll(); + } +} diff --git a/roles/ui/files/FWO.UI/Shared/RuleSelector.razor b/roles/ui/files/FWO.UI/Shared/RuleSelector.razor index d84e7a6f3..3fb7a846f 100644 --- a/roles/ui/files/FWO.UI/Shared/RuleSelector.razor +++ b/roles/ui/files/FWO.UI/Shared/RuleSelector.razor @@ -9,7 +9,7 @@ @foreach(var rule in SelectedRules) {
-
@Devices.FirstOrDefault(d => d.Id == rule.Metadata.DeviceId)?.Name
+ @*
@Devices.FirstOrDefault(d => d.Id == rule.Metadata.DeviceId)?.Name
*@
@rule.Metadata.Uid
} @@ -19,21 +19,21 @@
-
@Devices.FirstOrDefault(d => d.Id == context.Metadata.DeviceId)?.Name
+ @*
@Devices.FirstOrDefault(d => d.Id == context.Metadata.DeviceId)?.Name
*@
@context.Metadata.Uid
-
+ @*
@dev.Name -
+
*@
@@ -84,12 +84,12 @@ { try { - Devices = await apiConnection.SendQueryAsync>(DeviceQueries.getDeviceDetails); + // Devices = await apiConnection.SendQueryAsync>(DeviceQueries.getDeviceDetails); // Devices = await apiConnection.SendQueryAsync>(AuthQueries.getVisibleDeviceIdsPerTenant, new { tenantId = userConfig.User.Tenant?.Id }, "getVisibleDeviceIdsPerTenant"); - if(Devices.Count > 0) + @* if(Devices.Count > 0) { await DeviceChanged(Devices[0]); - } + } *@ } catch (Exception exception) { @@ -97,7 +97,7 @@ } } - private async Task DeviceChanged(Device newDevice) + @* private async Task DeviceChanged(Device newDevice) { try { @@ -126,7 +126,7 @@ import_id_start = importId, import_id_end = importId }; - allRulesOfDevice = await apiConnection.SendQueryAsync>(FWO.Api.Client.Queries.RuleQueries.getRuleUidsOfDevice, RuleVariables); + // allRulesOfDevice = await apiConnection.SendQueryAsync>(RuleQueries.getRuleUidsOfDevice, RuleVariables); } actRule = allRulesOfDevice.FirstOrDefault() ?? new(); } @@ -134,7 +134,7 @@ { DisplayMessageInUi(exception, userConfig.GetText("fetch_data"), "", true); } - } + } *@ private void AddRule() {